Release 0.9.39.
[wine/gsoc-2012-control.git] / programs / cmd / builtins.c
bloba7cc2b1e7df8405360ae7c832f513eee8decc2f3
1 /*
2 * CMD - Wine-compatible command line interface - built-in functions.
4 * Copyright (C) 1999 D A Pickles
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 * NOTES:
23 * On entry to each function, global variables quals, param1, param2 contain
24 * the qualifiers (uppercased and concatenated) and parameters entered, with
25 * environment-variable and batch parameter substitution already done.
29 * FIXME:
30 * - No support for pipes, shell parameters
31 * - Lots of functionality missing from builtins
32 * - Messages etc need international support
35 #define WIN32_LEAN_AND_MEAN
37 #include "wcmd.h"
38 #include <shellapi.h>
39 #include "wine/debug.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(cmd);
43 void WCMD_execute (WCHAR *orig_command, WCHAR *parameter, WCHAR *substitution);
45 struct env_stack *saved_environment;
46 struct env_stack *pushd_directories;
48 extern HINSTANCE hinst;
49 extern WCHAR inbuilt[][10];
50 extern int echo_mode, verify_mode, defaultColor;
51 extern WCHAR quals[MAX_PATH], param1[MAX_PATH], param2[MAX_PATH];
52 extern BATCH_CONTEXT *context;
53 extern DWORD errorlevel;
55 static const WCHAR dotW[] = {'.','\0'};
56 static const WCHAR dotdotW[] = {'.','.','\0'};
57 static const WCHAR slashW[] = {'\\','\0'};
58 static const WCHAR starW[] = {'*','\0'};
59 static const WCHAR equalW[] = {'=','\0'};
60 static const WCHAR fslashW[] = {'/','\0'};
61 static const WCHAR onW[] = {'O','N','\0'};
62 static const WCHAR offW[] = {'O','F','F','\0'};
63 static const WCHAR parmY[] = {'/','Y','\0'};
64 static const WCHAR parmNoY[] = {'/','-','Y','\0'};
65 static const WCHAR nullW[] = {'\0'};
67 /****************************************************************************
68 * WCMD_clear_screen
70 * Clear the terminal screen.
73 void WCMD_clear_screen (void) {
75 /* Emulate by filling the screen from the top left to bottom right with
76 spaces, then moving the cursor to the top left afterwards */
77 CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
78 HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
80 if (GetConsoleScreenBufferInfo(hStdOut, &consoleInfo))
82 COORD topLeft;
83 DWORD screenSize;
85 screenSize = consoleInfo.dwSize.X * (consoleInfo.dwSize.Y + 1);
87 topLeft.X = 0;
88 topLeft.Y = 0;
89 FillConsoleOutputCharacter(hStdOut, ' ', screenSize, topLeft, &screenSize);
90 SetConsoleCursorPosition(hStdOut, topLeft);
94 /****************************************************************************
95 * WCMD_change_tty
97 * Change the default i/o device (ie redirect STDin/STDout).
100 void WCMD_change_tty (void) {
102 WCMD_output (WCMD_LoadMessage(WCMD_NYI));
106 /****************************************************************************
107 * WCMD_copy
109 * Copy a file or wildcarded set.
110 * FIXME: No wildcard support
113 void WCMD_copy (void) {
115 WIN32_FIND_DATA fd;
116 HANDLE hff;
117 BOOL force, status;
118 WCHAR outpath[MAX_PATH], inpath[MAX_PATH], *infile, copycmd[3];
119 DWORD len;
120 static const WCHAR copyCmdW[] = {'C','O','P','Y','C','M','D','\0'};
122 if (param1[0] == 0x00) {
123 WCMD_output (WCMD_LoadMessage(WCMD_NOARG));
124 return;
127 if ((strchrW(param1,'*') != NULL) && (strchrW(param1,'%') != NULL)) {
128 WCMD_output (WCMD_LoadMessage(WCMD_NYI));
129 return;
132 /* If no destination supplied, assume current directory */
133 if (param2[0] == 0x00) {
134 strcpyW(param2, dotW);
137 GetFullPathName (param2, sizeof(outpath)/sizeof(WCHAR), outpath, NULL);
138 if (outpath[strlenW(outpath) - 1] == '\\')
139 outpath[strlenW(outpath) - 1] = '\0';
140 hff = FindFirstFile (outpath, &fd);
141 if (hff != INVALID_HANDLE_VALUE) {
142 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
143 GetFullPathName (param1, sizeof(inpath)/sizeof(WCHAR), inpath, &infile);
144 strcatW (outpath, slashW);
145 strcatW (outpath, infile);
147 FindClose (hff);
150 /* /-Y has the highest priority, then /Y and finally the COPYCMD env. variable */
151 if (strstrW (quals, parmNoY))
152 force = FALSE;
153 else if (strstrW (quals, parmY))
154 force = TRUE;
155 else {
156 len = GetEnvironmentVariable (copyCmdW, copycmd, sizeof(copycmd)/sizeof(WCHAR));
157 force = (len && len < (sizeof(copycmd)/sizeof(WCHAR)) && ! lstrcmpiW (copycmd, parmY));
160 if (!force) {
161 hff = FindFirstFile (outpath, &fd);
162 if (hff != INVALID_HANDLE_VALUE) {
163 WCHAR buffer[MAXSTRING];
165 FindClose (hff);
167 wsprintf(buffer, WCMD_LoadMessage(WCMD_OVERWRITE), outpath);
168 force = WCMD_ask_confirm(buffer, FALSE, NULL);
170 else force = TRUE;
172 if (force) {
173 status = CopyFile (param1, outpath, FALSE);
174 if (!status) WCMD_print_error ();
178 /****************************************************************************
179 * WCMD_create_dir
181 * Create a directory.
183 * this works recursivly. so mkdir dir1\dir2\dir3 will create dir1 and dir2 if
184 * they do not already exist.
187 static BOOL create_full_path(WCHAR* path)
189 int len;
190 WCHAR *new_path;
191 BOOL ret = TRUE;
193 new_path = HeapAlloc(GetProcessHeap(),0,(strlenW(path) * sizeof(WCHAR))+1);
194 strcpyW(new_path,path);
196 while ((len = strlenW(new_path)) && new_path[len - 1] == '\\')
197 new_path[len - 1] = 0;
199 while (!CreateDirectory(new_path,NULL))
201 WCHAR *slash;
202 DWORD last_error = GetLastError();
203 if (last_error == ERROR_ALREADY_EXISTS)
204 break;
206 if (last_error != ERROR_PATH_NOT_FOUND)
208 ret = FALSE;
209 break;
212 if (!(slash = strrchrW(new_path,'\\')) && ! (slash = strrchrW(new_path,'/')))
214 ret = FALSE;
215 break;
218 len = slash - new_path;
219 new_path[len] = 0;
220 if (!create_full_path(new_path))
222 ret = FALSE;
223 break;
225 new_path[len] = '\\';
227 HeapFree(GetProcessHeap(),0,new_path);
228 return ret;
231 void WCMD_create_dir (void) {
233 if (param1[0] == 0x00) {
234 WCMD_output (WCMD_LoadMessage(WCMD_NOARG));
235 return;
237 if (!create_full_path(param1)) WCMD_print_error ();
240 /****************************************************************************
241 * WCMD_delete
243 * Delete a file or wildcarded set.
245 * Note on /A:
246 * - Testing shows /A is repeatable, eg. /a-r /ar matches all files
247 * - Each set is a pattern, eg /ahr /as-r means
248 * readonly+hidden OR nonreadonly system files
249 * - The '-' applies to a single field, ie /a:-hr means read only
250 * non-hidden files
253 BOOL WCMD_delete (WCHAR *command, BOOL expectDir) {
255 int argno = 0;
256 int argsProcessed = 0;
257 WCHAR *argN = command;
258 BOOL foundAny = FALSE;
259 static const WCHAR parmA[] = {'/','A','\0'};
260 static const WCHAR parmQ[] = {'/','Q','\0'};
261 static const WCHAR parmP[] = {'/','P','\0'};
262 static const WCHAR parmS[] = {'/','S','\0'};
263 static const WCHAR parmF[] = {'/','F','\0'};
265 /* If not recursing, clear error flag */
266 if (expectDir) errorlevel = 0;
268 /* Loop through all args */
269 while (argN) {
270 WCHAR *thisArg = WCMD_parameter (command, argno++, &argN);
271 WCHAR argCopy[MAX_PATH];
273 if (argN && argN[0] != '/') {
275 WIN32_FIND_DATA fd;
276 HANDLE hff;
277 WCHAR fpath[MAX_PATH];
278 WCHAR *p;
279 BOOL handleParm = TRUE;
280 BOOL found = FALSE;
281 static const WCHAR anyExt[]= {'.','*','\0'};
283 strcpyW(argCopy, thisArg);
284 WINE_TRACE("del: Processing arg %s (quals:%s)\n",
285 wine_dbgstr_w(argCopy), wine_dbgstr_w(quals));
286 argsProcessed++;
288 /* If filename part of parameter is * or *.*, prompt unless
289 /Q supplied. */
290 if ((strstrW (quals, parmQ) == NULL) && (strstrW (quals, parmP) == NULL)) {
292 WCHAR drive[10];
293 WCHAR dir[MAX_PATH];
294 WCHAR fname[MAX_PATH];
295 WCHAR ext[MAX_PATH];
297 /* Convert path into actual directory spec */
298 GetFullPathName (argCopy, sizeof(fpath)/sizeof(WCHAR), fpath, NULL);
299 WCMD_splitpath(fpath, drive, dir, fname, ext);
301 /* Only prompt for * and *.*, not *a, a*, *.a* etc */
302 if ((strcmpW(fname, starW) == 0) &&
303 (*ext == 0x00 || (strcmpW(ext, anyExt) == 0))) {
304 BOOL ok;
305 WCHAR question[MAXSTRING];
306 static const WCHAR fmt[] = {'%','s',' ','\0'};
308 /* Note: Flag as found, to avoid file not found message */
309 found = TRUE;
311 /* Ask for confirmation */
312 wsprintf(question, fmt, fpath);
313 ok = WCMD_ask_confirm(question, TRUE, NULL);
315 /* Abort if answer is 'N' */
316 if (!ok) continue;
320 /* First, try to delete in the current directory */
321 hff = FindFirstFile (argCopy, &fd);
322 if (hff == INVALID_HANDLE_VALUE) {
323 handleParm = FALSE;
324 } else {
325 found = TRUE;
328 /* Support del <dirname> by just deleting all files dirname\* */
329 if (handleParm && (strchrW(argCopy,'*') == NULL) && (strchrW(argCopy,'?') == NULL)
330 && (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
331 WCHAR modifiedParm[MAX_PATH];
332 static const WCHAR slashStar[] = {'\\','*','\0'};
334 strcpyW(modifiedParm, argCopy);
335 strcatW(modifiedParm, slashStar);
336 FindClose(hff);
337 found = TRUE;
338 WCMD_delete(modifiedParm, FALSE);
340 } else if (handleParm) {
342 /* Build the filename to delete as <supplied directory>\<findfirst filename> */
343 strcpyW (fpath, argCopy);
344 do {
345 p = strrchrW (fpath, '\\');
346 if (p != NULL) {
347 *++p = '\0';
348 strcatW (fpath, fd.cFileName);
350 else strcpyW (fpath, fd.cFileName);
351 if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
352 BOOL ok = TRUE;
353 WCHAR *nextA = strstrW (quals, parmA);
355 /* Handle attribute matching (/A) */
356 if (nextA != NULL) {
357 ok = FALSE;
358 while (nextA != NULL && !ok) {
360 WCHAR *thisA = (nextA+2);
361 BOOL stillOK = TRUE;
363 /* Skip optional : */
364 if (*thisA == ':') thisA++;
366 /* Parse each of the /A[:]xxx in turn */
367 while (*thisA && *thisA != '/') {
368 BOOL negate = FALSE;
369 BOOL attribute = FALSE;
371 /* Match negation of attribute first */
372 if (*thisA == '-') {
373 negate=TRUE;
374 thisA++;
377 /* Match attribute */
378 switch (*thisA) {
379 case 'R': attribute = (fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY);
380 break;
381 case 'H': attribute = (fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN);
382 break;
383 case 'S': attribute = (fd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM);
384 break;
385 case 'A': attribute = (fd.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE);
386 break;
387 default:
388 WCMD_output (WCMD_LoadMessage(WCMD_SYNTAXERR));
391 /* Now check result, keeping a running boolean about whether it
392 matches all parsed attribues so far */
393 if (attribute && !negate) {
394 stillOK = stillOK;
395 } else if (!attribute && negate) {
396 stillOK = stillOK;
397 } else {
398 stillOK = FALSE;
400 thisA++;
403 /* Save the running total as the final result */
404 ok = stillOK;
406 /* Step on to next /A set */
407 nextA = strstrW (nextA+1, parmA);
411 /* /P means prompt for each file */
412 if (ok && strstrW (quals, parmP) != NULL) {
413 WCHAR question[MAXSTRING];
415 /* Ask for confirmation */
416 wsprintf(question, WCMD_LoadMessage(WCMD_DELPROMPT), fpath);
417 ok = WCMD_ask_confirm(question, FALSE, NULL);
420 /* Only proceed if ok to */
421 if (ok) {
423 /* If file is read only, and /F supplied, delete it */
424 if (fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY &&
425 strstrW (quals, parmF) != NULL) {
426 SetFileAttributes(fpath, fd.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY);
429 /* Now do the delete */
430 if (!DeleteFile (fpath)) WCMD_print_error ();
434 } while (FindNextFile(hff, &fd) != 0);
435 FindClose (hff);
438 /* Now recurse into all subdirectories handling the parameter in the same way */
439 if (strstrW (quals, parmS) != NULL) {
441 WCHAR thisDir[MAX_PATH];
442 int cPos;
444 WCHAR drive[10];
445 WCHAR dir[MAX_PATH];
446 WCHAR fname[MAX_PATH];
447 WCHAR ext[MAX_PATH];
449 /* Convert path into actual directory spec */
450 GetFullPathName (argCopy, sizeof(thisDir)/sizeof(WCHAR), thisDir, NULL);
451 WCMD_splitpath(thisDir, drive, dir, fname, ext);
453 strcpyW(thisDir, drive);
454 strcatW(thisDir, dir);
455 cPos = strlenW(thisDir);
457 WINE_TRACE("Searching recursively in '%s'\n", wine_dbgstr_w(thisDir));
459 /* Append '*' to the directory */
460 thisDir[cPos] = '*';
461 thisDir[cPos+1] = 0x00;
463 hff = FindFirstFile (thisDir, &fd);
465 /* Remove residual '*' */
466 thisDir[cPos] = 0x00;
468 if (hff != INVALID_HANDLE_VALUE) {
469 DIRECTORY_STACK *allDirs = NULL;
470 DIRECTORY_STACK *lastEntry = NULL;
472 do {
473 if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
474 (strcmpW(fd.cFileName, dotdotW) != 0) &&
475 (strcmpW(fd.cFileName, dotW) != 0)) {
477 DIRECTORY_STACK *nextDir;
478 WCHAR subParm[MAX_PATH];
480 /* Work out search parameter in sub dir */
481 strcpyW (subParm, thisDir);
482 strcatW (subParm, fd.cFileName);
483 strcatW (subParm, slashW);
484 strcatW (subParm, fname);
485 strcatW (subParm, ext);
486 WINE_TRACE("Recursive, Adding to search list '%s'\n", wine_dbgstr_w(subParm));
488 /* Allocate memory, add to list */
489 nextDir = (DIRECTORY_STACK *) HeapAlloc(GetProcessHeap(),0,sizeof(DIRECTORY_STACK));
490 if (allDirs == NULL) allDirs = nextDir;
491 if (lastEntry != NULL) lastEntry->next = nextDir;
492 lastEntry = nextDir;
493 nextDir->next = NULL;
494 nextDir->dirName = HeapAlloc(GetProcessHeap(),0,
495 (strlenW(subParm)+1) * sizeof(WCHAR));
496 strcpyW(nextDir->dirName, subParm);
498 } while (FindNextFile(hff, &fd) != 0);
499 FindClose (hff);
501 /* Go through each subdir doing the delete */
502 while (allDirs != NULL) {
503 DIRECTORY_STACK *tempDir;
505 tempDir = allDirs->next;
506 found |= WCMD_delete (allDirs->dirName, FALSE);
508 HeapFree(GetProcessHeap(),0,allDirs->dirName);
509 HeapFree(GetProcessHeap(),0,allDirs);
510 allDirs = tempDir;
514 /* Keep running total to see if any found, and if not recursing
515 issue error message */
516 if (expectDir) {
517 if (!found) {
518 errorlevel = 1;
519 WCMD_output (WCMD_LoadMessage(WCMD_FILENOTFOUND), argCopy);
522 foundAny |= found;
526 /* Handle no valid args */
527 if (argsProcessed == 0) {
528 WCMD_output (WCMD_LoadMessage(WCMD_NOARG));
531 return foundAny;
534 /****************************************************************************
535 * WCMD_echo
537 * Echo input to the screen (or not). We don't try to emulate the bugs
538 * in DOS (try typing "ECHO ON AGAIN" for an example).
541 void WCMD_echo (const WCHAR *command) {
543 int count;
545 if ((command[0] == '.') && (command[1] == 0)) {
546 WCMD_output (newline);
547 return;
549 if (command[0]==' ')
550 command++;
551 count = strlenW(command);
552 if (count == 0) {
553 if (echo_mode) WCMD_output (WCMD_LoadMessage(WCMD_ECHOPROMPT), onW);
554 else WCMD_output (WCMD_LoadMessage(WCMD_ECHOPROMPT), offW);
555 return;
557 if (lstrcmpiW(command, onW) == 0) {
558 echo_mode = 1;
559 return;
561 if (lstrcmpiW(command, offW) == 0) {
562 echo_mode = 0;
563 return;
565 WCMD_output_asis (command);
566 WCMD_output (newline);
570 /**************************************************************************
571 * WCMD_for
573 * Batch file loop processing.
574 * FIXME: We don't exhaustively check syntax. Any command which works in MessDOS
575 * will probably work here, but the reverse is not necessarily the case...
578 void WCMD_for (WCHAR *p) {
580 WIN32_FIND_DATA fd;
581 HANDLE hff;
582 WCHAR *cmd, *item;
583 WCHAR set[MAX_PATH], param[MAX_PATH];
584 int i;
585 const WCHAR inW[] = {'i', 'n', '\0'};
586 const WCHAR doW[] = {'d', 'o', '\0'};
588 if (lstrcmpiW (WCMD_parameter (p, 1, NULL), inW)
589 || lstrcmpiW (WCMD_parameter (p, 3, NULL), doW)
590 || (param1[0] != '%')) {
591 WCMD_output (WCMD_LoadMessage(WCMD_SYNTAXERR));
592 return;
594 lstrcpynW (set, WCMD_parameter (p, 2, NULL), sizeof(set)/sizeof(WCHAR));
595 WCMD_parameter (p, 4, &cmd);
596 strcpyW (param, param1);
599 * If the parameter within the set has a wildcard then search for matching files
600 * otherwise do a literal substitution.
603 i = 0;
604 while (*(item = WCMD_parameter (set, i, NULL))) {
605 static const WCHAR wildcards[] = {'*','?','\0'};
606 if (strpbrkW (item, wildcards)) {
607 hff = FindFirstFile (item, &fd);
608 if (hff == INVALID_HANDLE_VALUE) {
609 return;
611 do {
612 WCMD_execute (cmd, param, fd.cFileName);
613 } while (FindNextFile(hff, &fd) != 0);
614 FindClose (hff);
616 else {
617 WCMD_execute (cmd, param, item);
619 i++;
623 /*****************************************************************************
624 * WCMD_Execute
626 * Execute a command after substituting variable text for the supplied parameter
629 void WCMD_execute (WCHAR *orig_cmd, WCHAR *param, WCHAR *subst) {
631 WCHAR *new_cmd, *p, *s, *dup;
632 int size;
634 size = strlenW (orig_cmd);
635 new_cmd = (WCHAR *) LocalAlloc (LMEM_FIXED | LMEM_ZEROINIT, size);
636 dup = s = WCMD_strdupW(orig_cmd);
638 while ((p = strstrW (s, param))) {
639 *p = '\0';
640 size += strlenW (subst);
641 new_cmd = (WCHAR *) LocalReAlloc ((HANDLE)new_cmd, size, 0);
642 strcatW (new_cmd, s);
643 strcatW (new_cmd, subst);
644 s = p + strlenW (param);
646 strcatW (new_cmd, s);
647 WCMD_process_command (new_cmd);
648 free (dup);
649 LocalFree ((HANDLE)new_cmd);
653 /**************************************************************************
654 * WCMD_give_help
656 * Simple on-line help. Help text is stored in the resource file.
659 void WCMD_give_help (WCHAR *command) {
661 int i;
663 command = WCMD_strtrim_leading_spaces(command);
664 if (strlenW(command) == 0) {
665 WCMD_output_asis (WCMD_LoadMessage(WCMD_ALLHELP));
667 else {
668 for (i=0; i<=WCMD_EXIT; i++) {
669 if (CompareString (LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
670 param1, -1, inbuilt[i], -1) == 2) {
671 WCMD_output_asis (WCMD_LoadMessage(i));
672 return;
675 WCMD_output (WCMD_LoadMessage(WCMD_NOCMDHELP), param1);
677 return;
680 /****************************************************************************
681 * WCMD_go_to
683 * Batch file jump instruction. Not the most efficient algorithm ;-)
684 * Prints error message if the specified label cannot be found - the file pointer is
685 * then at EOF, effectively stopping the batch file.
686 * FIXME: DOS is supposed to allow labels with spaces - we don't.
689 void WCMD_goto (void) {
691 WCHAR string[MAX_PATH];
693 if (param1[0] == 0x00) {
694 WCMD_output (WCMD_LoadMessage(WCMD_NOARG));
695 return;
697 if (context != NULL) {
698 WCHAR *paramStart = param1;
699 static const WCHAR eofW[] = {':','e','o','f','\0'};
701 /* Handle special :EOF label */
702 if (lstrcmpiW (eofW, param1) == 0) {
703 context -> skip_rest = TRUE;
704 return;
707 /* Support goto :label as well as goto label */
708 if (*paramStart == ':') paramStart++;
710 SetFilePointer (context -> h, 0, NULL, FILE_BEGIN);
711 while (WCMD_fgets (string, sizeof(string)/sizeof(WCHAR), context -> h)) {
712 if ((string[0] == ':') && (lstrcmpiW (&string[1], paramStart) == 0)) return;
714 WCMD_output (WCMD_LoadMessage(WCMD_NOTARGET));
716 return;
719 /*****************************************************************************
720 * WCMD_pushd
722 * Push a directory onto the stack
725 void WCMD_pushd (WCHAR *command) {
726 struct env_stack *curdir;
727 WCHAR *thisdir;
728 static const WCHAR parmD[] = {'/','D','\0'};
730 if (strchrW(command, '/') != NULL) {
731 SetLastError(ERROR_INVALID_PARAMETER);
732 WCMD_print_error();
733 return;
736 curdir = LocalAlloc (LMEM_FIXED, sizeof (struct env_stack));
737 thisdir = LocalAlloc (LMEM_FIXED, 1024 * sizeof(WCHAR));
738 if( !curdir || !thisdir ) {
739 LocalFree(curdir);
740 LocalFree(thisdir);
741 WINE_ERR ("out of memory\n");
742 return;
745 /* Change directory using CD code with /D parameter */
746 strcpyW(quals, parmD);
747 GetCurrentDirectoryW (1024, thisdir);
748 errorlevel = 0;
749 WCMD_setshow_default(command);
750 if (errorlevel) {
751 LocalFree(curdir);
752 LocalFree(thisdir);
753 return;
754 } else {
755 curdir -> next = pushd_directories;
756 curdir -> strings = thisdir;
757 if (pushd_directories == NULL) {
758 curdir -> u.stackdepth = 1;
759 } else {
760 curdir -> u.stackdepth = pushd_directories -> u.stackdepth + 1;
762 pushd_directories = curdir;
767 /*****************************************************************************
768 * WCMD_popd
770 * Pop a directory from the stack
773 void WCMD_popd (void) {
774 struct env_stack *temp = pushd_directories;
776 if (!pushd_directories)
777 return;
779 /* pop the old environment from the stack, and make it the current dir */
780 pushd_directories = temp->next;
781 SetCurrentDirectoryW(temp->strings);
782 LocalFree (temp->strings);
783 LocalFree (temp);
786 /****************************************************************************
787 * WCMD_if
789 * Batch file conditional.
790 * FIXME: Much more syntax checking needed!
793 void WCMD_if (WCHAR *p) {
795 int negate = 0, test = 0;
796 WCHAR condition[MAX_PATH], *command, *s;
797 static const WCHAR notW[] = {'n','o','t','\0'};
798 static const WCHAR errlvlW[] = {'e','r','r','o','r','l','e','v','e','l','\0'};
799 static const WCHAR existW[] = {'e','x','i','s','t','\0'};
800 static const WCHAR defdW[] = {'d','e','f','i','n','e','d','\0'};
801 static const WCHAR eqeqW[] = {'=','=','\0'};
803 if (!lstrcmpiW (param1, notW)) {
804 negate = 1;
805 strcpyW (condition, param2);
807 else {
808 strcpyW (condition, param1);
810 if (!lstrcmpiW (condition, errlvlW)) {
811 if (errorlevel >= atoiW(WCMD_parameter (p, 1+negate, NULL))) test = 1;
812 WCMD_parameter (p, 2+negate, &command);
814 else if (!lstrcmpiW (condition, existW)) {
815 if (GetFileAttributes(WCMD_parameter (p, 1+negate, NULL)) != INVALID_FILE_ATTRIBUTES) {
816 test = 1;
818 WCMD_parameter (p, 2+negate, &command);
820 else if (!lstrcmpiW (condition, defdW)) {
821 if (GetEnvironmentVariable(WCMD_parameter (p, 1+negate, NULL), NULL, 0) > 0) {
822 test = 1;
824 WCMD_parameter (p, 2+negate, &command);
826 else if ((s = strstrW (p, eqeqW))) {
827 s += 2;
828 if (!lstrcmpiW (condition, WCMD_parameter (s, 0, NULL))) test = 1;
829 WCMD_parameter (s, 1, &command);
831 else {
832 WCMD_output (WCMD_LoadMessage(WCMD_SYNTAXERR));
833 return;
835 if (test != negate) {
836 command = WCMD_strdupW(command);
837 WCMD_process_command (command);
838 free (command);
842 /****************************************************************************
843 * WCMD_move
845 * Move a file, directory tree or wildcarded set of files.
848 void WCMD_move (void) {
850 int status;
851 WIN32_FIND_DATA fd;
852 HANDLE hff;
853 WCHAR input[MAX_PATH];
854 WCHAR output[MAX_PATH];
855 WCHAR drive[10];
856 WCHAR dir[MAX_PATH];
857 WCHAR fname[MAX_PATH];
858 WCHAR ext[MAX_PATH];
860 if (param1[0] == 0x00) {
861 WCMD_output (WCMD_LoadMessage(WCMD_NOARG));
862 return;
865 /* If no destination supplied, assume current directory */
866 if (param2[0] == 0x00) {
867 strcpyW(param2, dotW);
870 /* If 2nd parm is directory, then use original filename */
871 /* Convert partial path to full path */
872 GetFullPathName (param1, sizeof(input)/sizeof(WCHAR), input, NULL);
873 GetFullPathName (param2, sizeof(output)/sizeof(WCHAR), output, NULL);
874 WINE_TRACE("Move from '%s'('%s') to '%s'\n", wine_dbgstr_w(input),
875 wine_dbgstr_w(param1), wine_dbgstr_w(output));
877 /* Split into components */
878 WCMD_splitpath(input, drive, dir, fname, ext);
880 hff = FindFirstFile (input, &fd);
881 while (hff != INVALID_HANDLE_VALUE) {
882 WCHAR dest[MAX_PATH];
883 WCHAR src[MAX_PATH];
884 DWORD attribs;
886 WINE_TRACE("Processing file '%s'\n", wine_dbgstr_w(fd.cFileName));
888 /* Build src & dest name */
889 strcpyW(src, drive);
890 strcatW(src, dir);
892 /* See if dest is an existing directory */
893 attribs = GetFileAttributes(output);
894 if (attribs != INVALID_FILE_ATTRIBUTES &&
895 (attribs & FILE_ATTRIBUTE_DIRECTORY)) {
896 strcpyW(dest, output);
897 strcatW(dest, slashW);
898 strcatW(dest, fd.cFileName);
899 } else {
900 strcpyW(dest, output);
903 strcatW(src, fd.cFileName);
905 WINE_TRACE("Source '%s'\n", wine_dbgstr_w(src));
906 WINE_TRACE("Dest '%s'\n", wine_dbgstr_w(dest));
908 /* Check if file is read only, otherwise move it */
909 attribs = GetFileAttributes(src);
910 if ((attribs != INVALID_FILE_ATTRIBUTES) &&
911 (attribs & FILE_ATTRIBUTE_READONLY)) {
912 SetLastError(ERROR_ACCESS_DENIED);
913 status = 0;
914 } else {
915 BOOL ok = TRUE;
917 /* If destination exists, prompt unless /Y supplied */
918 if (GetFileAttributes(dest) != INVALID_FILE_ATTRIBUTES) {
919 BOOL force = FALSE;
920 WCHAR copycmd[MAXSTRING];
921 int len;
923 /* /-Y has the highest priority, then /Y and finally the COPYCMD env. variable */
924 if (strstrW (quals, parmNoY))
925 force = FALSE;
926 else if (strstrW (quals, parmY))
927 force = TRUE;
928 else {
929 const WCHAR copyCmdW[] = {'C','O','P','Y','C','M','D','\0'};
930 len = GetEnvironmentVariable (copyCmdW, copycmd, sizeof(copycmd)/sizeof(WCHAR));
931 force = (len && len < (sizeof(copycmd)/sizeof(WCHAR))
932 && ! lstrcmpiW (copycmd, parmY));
935 /* Prompt if overwriting */
936 if (!force) {
937 WCHAR question[MAXSTRING];
938 WCHAR yesChar[10];
940 strcpyW(yesChar, WCMD_LoadMessage(WCMD_YES));
942 /* Ask for confirmation */
943 wsprintf(question, WCMD_LoadMessage(WCMD_OVERWRITE), dest);
944 ok = WCMD_ask_confirm(question, FALSE, NULL);
946 /* So delete the destination prior to the move */
947 if (ok) {
948 if (!DeleteFile (dest)) {
949 WCMD_print_error ();
950 errorlevel = 1;
951 ok = FALSE;
957 if (ok) {
958 status = MoveFile (src, dest);
959 } else {
960 status = 1; /* Anything other than 0 to prevent error msg below */
964 if (!status) {
965 WCMD_print_error ();
966 errorlevel = 1;
969 /* Step on to next match */
970 if (FindNextFile(hff, &fd) == 0) {
971 FindClose(hff);
972 hff = INVALID_HANDLE_VALUE;
973 break;
978 /****************************************************************************
979 * WCMD_pause
981 * Wait for keyboard input.
984 void WCMD_pause (void) {
986 DWORD count;
987 WCHAR string[32];
989 WCMD_output (anykey);
990 WCMD_ReadFile (GetStdHandle(STD_INPUT_HANDLE), string,
991 sizeof(string)/sizeof(WCHAR), &count, NULL);
994 /****************************************************************************
995 * WCMD_remove_dir
997 * Delete a directory.
1000 void WCMD_remove_dir (WCHAR *command) {
1002 int argno = 0;
1003 int argsProcessed = 0;
1004 WCHAR *argN = command;
1005 static const WCHAR parmS[] = {'/','S','\0'};
1006 static const WCHAR parmQ[] = {'/','Q','\0'};
1008 /* Loop through all args */
1009 while (argN) {
1010 WCHAR *thisArg = WCMD_parameter (command, argno++, &argN);
1011 if (argN && argN[0] != '/') {
1012 WINE_TRACE("rd: Processing arg %s (quals:%s)\n", wine_dbgstr_w(thisArg),
1013 wine_dbgstr_w(quals));
1014 argsProcessed++;
1016 /* If subdirectory search not supplied, just try to remove
1017 and report error if it fails (eg if it contains a file) */
1018 if (strstrW (quals, parmS) == NULL) {
1019 if (!RemoveDirectory (thisArg)) WCMD_print_error ();
1021 /* Otherwise use ShFileOp to recursively remove a directory */
1022 } else {
1024 SHFILEOPSTRUCT lpDir;
1026 /* Ask first */
1027 if (strstrW (quals, parmQ) == NULL) {
1028 BOOL ok;
1029 WCHAR question[MAXSTRING];
1030 static const WCHAR fmt[] = {'%','s',' ','\0'};
1032 /* Ask for confirmation */
1033 wsprintf(question, fmt, thisArg);
1034 ok = WCMD_ask_confirm(question, TRUE, NULL);
1036 /* Abort if answer is 'N' */
1037 if (!ok) return;
1040 /* Do the delete */
1041 lpDir.hwnd = NULL;
1042 lpDir.pTo = NULL;
1043 lpDir.pFrom = thisArg;
1044 lpDir.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI;
1045 lpDir.wFunc = FO_DELETE;
1046 if (SHFileOperation(&lpDir)) WCMD_print_error ();
1051 /* Handle no valid args */
1052 if (argsProcessed == 0) {
1053 WCMD_output (WCMD_LoadMessage(WCMD_NOARG));
1054 return;
1059 /****************************************************************************
1060 * WCMD_rename
1062 * Rename a file.
1065 void WCMD_rename (void) {
1067 int status;
1068 HANDLE hff;
1069 WIN32_FIND_DATA fd;
1070 WCHAR input[MAX_PATH];
1071 WCHAR *dotDst = NULL;
1072 WCHAR drive[10];
1073 WCHAR dir[MAX_PATH];
1074 WCHAR fname[MAX_PATH];
1075 WCHAR ext[MAX_PATH];
1076 DWORD attribs;
1078 errorlevel = 0;
1080 /* Must be at least two args */
1081 if (param1[0] == 0x00 || param2[0] == 0x00) {
1082 WCMD_output (WCMD_LoadMessage(WCMD_NOARG));
1083 errorlevel = 1;
1084 return;
1087 /* Destination cannot contain a drive letter or directory separator */
1088 if ((strchrW(param1,':') != NULL) || (strchrW(param1,'\\') != NULL)) {
1089 SetLastError(ERROR_INVALID_PARAMETER);
1090 WCMD_print_error();
1091 errorlevel = 1;
1092 return;
1095 /* Convert partial path to full path */
1096 GetFullPathName (param1, sizeof(input)/sizeof(WCHAR), input, NULL);
1097 WINE_TRACE("Rename from '%s'('%s') to '%s'\n", wine_dbgstr_w(input),
1098 wine_dbgstr_w(param1), wine_dbgstr_w(param2));
1099 dotDst = strchrW(param2, '.');
1101 /* Split into components */
1102 WCMD_splitpath(input, drive, dir, fname, ext);
1104 hff = FindFirstFile (input, &fd);
1105 while (hff != INVALID_HANDLE_VALUE) {
1106 WCHAR dest[MAX_PATH];
1107 WCHAR src[MAX_PATH];
1108 WCHAR *dotSrc = NULL;
1109 int dirLen;
1111 WINE_TRACE("Processing file '%s'\n", wine_dbgstr_w(fd.cFileName));
1113 /* FIXME: If dest name or extension is *, replace with filename/ext
1114 part otherwise use supplied name. This supports:
1115 ren *.fred *.jim
1116 ren jim.* fred.* etc
1117 However, windows has a more complex algorithum supporting eg
1118 ?'s and *'s mid name */
1119 dotSrc = strchrW(fd.cFileName, '.');
1121 /* Build src & dest name */
1122 strcpyW(src, drive);
1123 strcatW(src, dir);
1124 strcpyW(dest, src);
1125 dirLen = strlenW(src);
1126 strcatW(src, fd.cFileName);
1128 /* Build name */
1129 if (param2[0] == '*') {
1130 strcatW(dest, fd.cFileName);
1131 if (dotSrc) dest[dirLen + (dotSrc - fd.cFileName)] = 0x00;
1132 } else {
1133 strcatW(dest, param2);
1134 if (dotDst) dest[dirLen + (dotDst - param2)] = 0x00;
1137 /* Build Extension */
1138 if (dotDst && (*(dotDst+1)=='*')) {
1139 if (dotSrc) strcatW(dest, dotSrc);
1140 } else if (dotDst) {
1141 if (dotDst) strcatW(dest, dotDst);
1144 WINE_TRACE("Source '%s'\n", wine_dbgstr_w(src));
1145 WINE_TRACE("Dest '%s'\n", wine_dbgstr_w(dest));
1147 /* Check if file is read only, otherwise move it */
1148 attribs = GetFileAttributes(src);
1149 if ((attribs != INVALID_FILE_ATTRIBUTES) &&
1150 (attribs & FILE_ATTRIBUTE_READONLY)) {
1151 SetLastError(ERROR_ACCESS_DENIED);
1152 status = 0;
1153 } else {
1154 status = MoveFile (src, dest);
1157 if (!status) {
1158 WCMD_print_error ();
1159 errorlevel = 1;
1162 /* Step on to next match */
1163 if (FindNextFile(hff, &fd) == 0) {
1164 FindClose(hff);
1165 hff = INVALID_HANDLE_VALUE;
1166 break;
1171 /*****************************************************************************
1172 * WCMD_dupenv
1174 * Make a copy of the environment.
1176 static WCHAR *WCMD_dupenv( const WCHAR *env )
1178 WCHAR *env_copy;
1179 int len;
1181 if( !env )
1182 return NULL;
1184 len = 0;
1185 while ( env[len] )
1186 len += (strlenW(&env[len]) + 1);
1188 env_copy = LocalAlloc (LMEM_FIXED, (len+1) * sizeof (WCHAR) );
1189 if (!env_copy)
1191 WINE_ERR("out of memory\n");
1192 return env_copy;
1194 memcpy (env_copy, env, len*sizeof (WCHAR));
1195 env_copy[len] = 0;
1197 return env_copy;
1200 /*****************************************************************************
1201 * WCMD_setlocal
1203 * setlocal pushes the environment onto a stack
1204 * Save the environment as unicode so we don't screw anything up.
1206 void WCMD_setlocal (const WCHAR *s) {
1207 WCHAR *env;
1208 struct env_stack *env_copy;
1209 WCHAR cwd[MAX_PATH];
1211 /* DISABLEEXTENSIONS ignored */
1213 env_copy = LocalAlloc (LMEM_FIXED, sizeof (struct env_stack));
1214 if( !env_copy )
1216 WINE_ERR ("out of memory\n");
1217 return;
1220 env = GetEnvironmentStringsW ();
1222 env_copy->strings = WCMD_dupenv (env);
1223 if (env_copy->strings)
1225 env_copy->next = saved_environment;
1226 saved_environment = env_copy;
1228 /* Save the current drive letter */
1229 GetCurrentDirectory (MAX_PATH, cwd);
1230 env_copy->u.cwd = cwd[0];
1232 else
1233 LocalFree (env_copy);
1235 FreeEnvironmentStringsW (env);
1239 /*****************************************************************************
1240 * WCMD_endlocal
1242 * endlocal pops the environment off a stack
1243 * Note: When searching for '=', search from WCHAR position 1, to handle
1244 * special internal environment variables =C:, =D: etc
1246 void WCMD_endlocal (void) {
1247 WCHAR *env, *old, *p;
1248 struct env_stack *temp;
1249 int len, n;
1251 if (!saved_environment)
1252 return;
1254 /* pop the old environment from the stack */
1255 temp = saved_environment;
1256 saved_environment = temp->next;
1258 /* delete the current environment, totally */
1259 env = GetEnvironmentStringsW ();
1260 old = WCMD_dupenv (GetEnvironmentStringsW ());
1261 len = 0;
1262 while (old[len]) {
1263 n = strlenW(&old[len]) + 1;
1264 p = strchrW(&old[len] + 1, '=');
1265 if (p)
1267 *p++ = 0;
1268 SetEnvironmentVariableW (&old[len], NULL);
1270 len += n;
1272 LocalFree (old);
1273 FreeEnvironmentStringsW (env);
1275 /* restore old environment */
1276 env = temp->strings;
1277 len = 0;
1278 while (env[len]) {
1279 n = strlenW(&env[len]) + 1;
1280 p = strchrW(&env[len] + 1, '=');
1281 if (p)
1283 *p++ = 0;
1284 SetEnvironmentVariableW (&env[len], p);
1286 len += n;
1289 /* Restore current drive letter */
1290 if (IsCharAlpha(temp->u.cwd)) {
1291 WCHAR envvar[4];
1292 WCHAR cwd[MAX_PATH];
1293 static const WCHAR fmt[] = {'=','%','c',':','\0'};
1295 wsprintf(envvar, fmt, temp->u.cwd);
1296 if (GetEnvironmentVariable(envvar, cwd, MAX_PATH)) {
1297 WINE_TRACE("Resetting cwd to %s\n", wine_dbgstr_w(cwd));
1298 SetCurrentDirectory(cwd);
1302 LocalFree (env);
1303 LocalFree (temp);
1306 /*****************************************************************************
1307 * WCMD_setshow_attrib
1309 * Display and optionally sets DOS attributes on a file or directory
1311 * FIXME: Wine currently uses the Unix stat() function to get file attributes.
1312 * As a result only the Readonly flag is correctly reported, the Archive bit
1313 * is always set and the rest are not implemented. We do the Right Thing anyway.
1315 * FIXME: No SET functionality.
1319 void WCMD_setshow_attrib (void) {
1321 DWORD count;
1322 HANDLE hff;
1323 WIN32_FIND_DATA fd;
1324 WCHAR flags[9] = {' ',' ',' ',' ',' ',' ',' ',' ','\0'};
1326 if (param1[0] == '-') {
1327 WCMD_output (WCMD_LoadMessage(WCMD_NYI));
1328 return;
1331 if (strlenW(param1) == 0) {
1332 static const WCHAR slashStarW[] = {'\\','*','\0'};
1334 GetCurrentDirectory (sizeof(param1)/sizeof(WCHAR), param1);
1335 strcatW (param1, slashStarW);
1338 hff = FindFirstFile (param1, &fd);
1339 if (hff == INVALID_HANDLE_VALUE) {
1340 WCMD_output (WCMD_LoadMessage(WCMD_FILENOTFOUND), param1);
1342 else {
1343 do {
1344 if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
1345 static const WCHAR fmt[] = {'%','s',' ',' ',' ','%','s','\n','\0'};
1346 if (fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
1347 flags[0] = 'H';
1349 if (fd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) {
1350 flags[1] = 'S';
1352 if (fd.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) {
1353 flags[2] = 'A';
1355 if (fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
1356 flags[3] = 'R';
1358 if (fd.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) {
1359 flags[4] = 'T';
1361 if (fd.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) {
1362 flags[5] = 'C';
1364 WCMD_output (fmt, flags, fd.cFileName);
1365 for (count=0; count < 8; count++) flags[count] = ' ';
1367 } while (FindNextFile(hff, &fd) != 0);
1369 FindClose (hff);
1372 /*****************************************************************************
1373 * WCMD_setshow_default
1375 * Set/Show the current default directory
1378 void WCMD_setshow_default (WCHAR *command) {
1380 BOOL status;
1381 WCHAR string[1024];
1382 WCHAR cwd[1024];
1383 WCHAR *pos;
1384 WIN32_FIND_DATA fd;
1385 HANDLE hff;
1386 static const WCHAR parmD[] = {'/','D','\0'};
1388 WINE_TRACE("Request change to directory '%s'\n", wine_dbgstr_w(command));
1390 /* Skip /D and trailing whitespace if on the front of the command line */
1391 if (CompareString (LOCALE_USER_DEFAULT,
1392 NORM_IGNORECASE | SORT_STRINGSORT,
1393 command, 2, parmD, -1) == 2) {
1394 command += 2;
1395 while (*command && *command==' ') command++;
1398 GetCurrentDirectory (sizeof(cwd)/sizeof(WCHAR), cwd);
1399 if (strlenW(command) == 0) {
1400 strcatW (cwd, newline);
1401 WCMD_output (cwd);
1403 else {
1404 /* Remove any double quotes, which may be in the
1405 middle, eg. cd "C:\Program Files"\Microsoft is ok */
1406 pos = string;
1407 while (*command) {
1408 if (*command != '"') *pos++ = *command;
1409 command++;
1411 *pos = 0x00;
1413 /* Search for approprate directory */
1414 WINE_TRACE("Looking for directory '%s'\n", wine_dbgstr_w(string));
1415 hff = FindFirstFile (string, &fd);
1416 while (hff != INVALID_HANDLE_VALUE) {
1417 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1418 WCHAR fpath[MAX_PATH];
1419 WCHAR drive[10];
1420 WCHAR dir[MAX_PATH];
1421 WCHAR fname[MAX_PATH];
1422 WCHAR ext[MAX_PATH];
1423 static const WCHAR fmt[] = {'%','s','%','s','%','s','\0'};
1425 /* Convert path into actual directory spec */
1426 GetFullPathName (string, sizeof(fpath)/sizeof(WCHAR), fpath, NULL);
1427 WCMD_splitpath(fpath, drive, dir, fname, ext);
1429 /* Rebuild path */
1430 wsprintf(string, fmt, drive, dir, fd.cFileName);
1432 FindClose(hff);
1433 hff = INVALID_HANDLE_VALUE;
1434 break;
1437 /* Step on to next match */
1438 if (FindNextFile(hff, &fd) == 0) {
1439 FindClose(hff);
1440 hff = INVALID_HANDLE_VALUE;
1441 break;
1445 /* Change to that directory */
1446 WINE_TRACE("Really changing to directory '%s'\n", wine_dbgstr_w(string));
1448 status = SetCurrentDirectory (string);
1449 if (!status) {
1450 errorlevel = 1;
1451 WCMD_print_error ();
1452 return;
1453 } else {
1455 /* Restore old directory if drive letter would change, and
1456 CD x:\directory /D (or pushd c:\directory) not supplied */
1457 if ((strstrW(quals, parmD) == NULL) &&
1458 (param1[1] == ':') && (toupper(param1[0]) != toupper(cwd[0]))) {
1459 SetCurrentDirectory(cwd);
1463 /* Set special =C: type environment variable, for drive letter of
1464 change of directory, even if path was restored due to missing
1465 /D (allows changing drive letter when not resident on that
1466 drive */
1467 if ((string[1] == ':') && IsCharAlpha (string[0])) {
1468 WCHAR env[4];
1469 strcpyW(env, equalW);
1470 memcpy(env+1, string, 2 * sizeof(WCHAR));
1471 env[3] = 0x00;
1472 WINE_TRACE("Setting '%s' to '%s'\n", wine_dbgstr_w(env), wine_dbgstr_w(string));
1473 SetEnvironmentVariable(env, string);
1477 return;
1480 /****************************************************************************
1481 * WCMD_setshow_date
1483 * Set/Show the system date
1484 * FIXME: Can't change date yet
1487 void WCMD_setshow_date (void) {
1489 WCHAR curdate[64], buffer[64];
1490 DWORD count;
1491 static const WCHAR parmT[] = {'/','T','\0'};
1493 if (strlenW(param1) == 0) {
1494 if (GetDateFormat (LOCALE_USER_DEFAULT, 0, NULL, NULL,
1495 curdate, sizeof(curdate)/sizeof(WCHAR))) {
1496 WCMD_output (WCMD_LoadMessage(WCMD_CURRENTDATE), curdate);
1497 if (strstrW (quals, parmT) == NULL) {
1498 WCMD_output (WCMD_LoadMessage(WCMD_NEWDATE));
1499 WCMD_ReadFile (GetStdHandle(STD_INPUT_HANDLE),
1500 buffer, sizeof(buffer)/sizeof(WCHAR), &count, NULL);
1501 if (count > 2) {
1502 WCMD_output (WCMD_LoadMessage(WCMD_NYI));
1506 else WCMD_print_error ();
1508 else {
1509 WCMD_output (WCMD_LoadMessage(WCMD_NYI));
1513 /****************************************************************************
1514 * WCMD_compare
1516 static int WCMD_compare( const void *a, const void *b )
1518 int r;
1519 const WCHAR * const *str_a = a, * const *str_b = b;
1520 r = CompareString( LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
1521 *str_a, -1, *str_b, -1 );
1522 if( r == CSTR_LESS_THAN ) return -1;
1523 if( r == CSTR_GREATER_THAN ) return 1;
1524 return 0;
1527 /****************************************************************************
1528 * WCMD_setshow_sortenv
1530 * sort variables into order for display
1531 * Optionally only display those who start with a stub
1532 * returns the count displayed
1534 static int WCMD_setshow_sortenv(const WCHAR *s, const WCHAR *stub)
1536 UINT count=0, len=0, i, displayedcount=0, stublen=0;
1537 const WCHAR **str;
1539 if (stub) stublen = strlenW(stub);
1541 /* count the number of strings, and the total length */
1542 while ( s[len] ) {
1543 len += (strlenW(&s[len]) + 1);
1544 count++;
1547 /* add the strings to an array */
1548 str = LocalAlloc (LMEM_FIXED | LMEM_ZEROINIT, count * sizeof (WCHAR*) );
1549 if( !str )
1550 return 0;
1551 str[0] = s;
1552 for( i=1; i<count; i++ )
1553 str[i] = str[i-1] + strlenW(str[i-1]) + 1;
1555 /* sort the array */
1556 qsort( str, count, sizeof (WCHAR*), WCMD_compare );
1558 /* print it */
1559 for( i=0; i<count; i++ ) {
1560 if (!stub || CompareString (LOCALE_USER_DEFAULT,
1561 NORM_IGNORECASE | SORT_STRINGSORT,
1562 str[i], stublen, stub, -1) == 2) {
1563 /* Don't display special internal variables */
1564 if (str[i][0] != '=') {
1565 WCMD_output_asis(str[i]);
1566 WCMD_output_asis(newline);
1567 displayedcount++;
1572 LocalFree( str );
1573 return displayedcount;
1576 /****************************************************************************
1577 * WCMD_setshow_env
1579 * Set/Show the environment variables
1582 void WCMD_setshow_env (WCHAR *s) {
1584 LPVOID env;
1585 WCHAR *p;
1586 int status;
1587 static const WCHAR parmP[] = {'/','P','\0'};
1589 errorlevel = 0;
1590 if (param1[0] == 0x00 && quals[0] == 0x00) {
1591 env = GetEnvironmentStrings ();
1592 WCMD_setshow_sortenv( env, NULL );
1593 return;
1596 /* See if /P supplied, and if so echo the prompt, and read in a reply */
1597 if (CompareString (LOCALE_USER_DEFAULT,
1598 NORM_IGNORECASE | SORT_STRINGSORT,
1599 s, 2, parmP, -1) == 2) {
1600 WCHAR string[MAXSTRING];
1601 DWORD count;
1603 s += 2;
1604 while (*s && *s==' ') s++;
1606 /* If no parameter, or no '=' sign, return an error */
1607 if (!(*s) || ((p = strchrW (s, '=')) == NULL )) {
1608 WCMD_output (WCMD_LoadMessage(WCMD_NOARG));
1609 return;
1612 /* Output the prompt */
1613 *p++ = '\0';
1614 if (strlenW(p) != 0) WCMD_output(p);
1616 /* Read the reply */
1617 WCMD_ReadFile (GetStdHandle(STD_INPUT_HANDLE), string,
1618 sizeof(string)/sizeof(WCHAR), &count, NULL);
1619 if (count > 1) {
1620 string[count-1] = '\0'; /* ReadFile output is not null-terminated! */
1621 if (string[count-2] == '\r') string[count-2] = '\0'; /* Under Windoze we get CRLF! */
1622 WINE_TRACE("set /p: Setting var '%s' to '%s'\n", wine_dbgstr_w(s),
1623 wine_dbgstr_w(string));
1624 status = SetEnvironmentVariable (s, string);
1627 } else {
1628 DWORD gle;
1629 p = strchrW (s, '=');
1630 if (p == NULL) {
1631 env = GetEnvironmentStrings ();
1632 if (WCMD_setshow_sortenv( env, s ) == 0) {
1633 WCMD_output (WCMD_LoadMessage(WCMD_MISSINGENV), s);
1634 errorlevel = 1;
1636 return;
1638 *p++ = '\0';
1640 if (strlenW(p) == 0) p = NULL;
1641 status = SetEnvironmentVariable (s, p);
1642 gle = GetLastError();
1643 if ((!status) & (gle == ERROR_ENVVAR_NOT_FOUND)) {
1644 errorlevel = 1;
1645 } else if ((!status)) WCMD_print_error();
1649 /****************************************************************************
1650 * WCMD_setshow_path
1652 * Set/Show the path environment variable
1655 void WCMD_setshow_path (WCHAR *command) {
1657 WCHAR string[1024];
1658 DWORD status;
1659 static const WCHAR pathW[] = {'P','A','T','H','\0'};
1660 static const WCHAR pathEqW[] = {'P','A','T','H','=','\0'};
1662 if (strlenW(param1) == 0) {
1663 status = GetEnvironmentVariable (pathW, string, sizeof(string)/sizeof(WCHAR));
1664 if (status != 0) {
1665 WCMD_output_asis ( pathEqW);
1666 WCMD_output_asis ( string);
1667 WCMD_output_asis ( newline);
1669 else {
1670 WCMD_output (WCMD_LoadMessage(WCMD_NOPATH));
1673 else {
1674 if (*command == '=') command++; /* Skip leading '=' */
1675 status = SetEnvironmentVariable (pathW, command);
1676 if (!status) WCMD_print_error();
1680 /****************************************************************************
1681 * WCMD_setshow_prompt
1683 * Set or show the command prompt.
1686 void WCMD_setshow_prompt (void) {
1688 WCHAR *s;
1689 static const WCHAR promptW[] = {'P','R','O','M','P','T','\0'};
1691 if (strlenW(param1) == 0) {
1692 SetEnvironmentVariable (promptW, NULL);
1694 else {
1695 s = param1;
1696 while ((*s == '=') || (*s == ' ')) s++;
1697 if (strlenW(s) == 0) {
1698 SetEnvironmentVariable (promptW, NULL);
1700 else SetEnvironmentVariable (promptW, s);
1704 /****************************************************************************
1705 * WCMD_setshow_time
1707 * Set/Show the system time
1708 * FIXME: Can't change time yet
1711 void WCMD_setshow_time (void) {
1713 WCHAR curtime[64], buffer[64];
1714 DWORD count;
1715 SYSTEMTIME st;
1716 static const WCHAR parmT[] = {'/','T','\0'};
1718 if (strlenW(param1) == 0) {
1719 GetLocalTime(&st);
1720 if (GetTimeFormat (LOCALE_USER_DEFAULT, 0, &st, NULL,
1721 curtime, sizeof(curtime)/sizeof(WCHAR))) {
1722 WCMD_output (WCMD_LoadMessage(WCMD_CURRENTDATE), curtime);
1723 if (strstrW (quals, parmT) == NULL) {
1724 WCMD_output (WCMD_LoadMessage(WCMD_NEWTIME));
1725 WCMD_ReadFile (GetStdHandle(STD_INPUT_HANDLE), buffer,
1726 sizeof(buffer)/sizeof(WCHAR), &count, NULL);
1727 if (count > 2) {
1728 WCMD_output (WCMD_LoadMessage(WCMD_NYI));
1732 else WCMD_print_error ();
1734 else {
1735 WCMD_output (WCMD_LoadMessage(WCMD_NYI));
1739 /****************************************************************************
1740 * WCMD_shift
1742 * Shift batch parameters.
1743 * Optional /n says where to start shifting (n=0-8)
1746 void WCMD_shift (WCHAR *command) {
1747 int start;
1749 if (context != NULL) {
1750 WCHAR *pos = strchrW(command, '/');
1751 int i;
1753 if (pos == NULL) {
1754 start = 0;
1755 } else if (*(pos+1)>='0' && *(pos+1)<='8') {
1756 start = (*(pos+1) - '0');
1757 } else {
1758 SetLastError(ERROR_INVALID_PARAMETER);
1759 WCMD_print_error();
1760 return;
1763 WINE_TRACE("Shifting variables, starting at %d\n", start);
1764 for (i=start;i<=8;i++) {
1765 context -> shift_count[i] = context -> shift_count[i+1] + 1;
1767 context -> shift_count[9] = context -> shift_count[9] + 1;
1772 /****************************************************************************
1773 * WCMD_title
1775 * Set the console title
1777 void WCMD_title (WCHAR *command) {
1778 SetConsoleTitle(command);
1781 /****************************************************************************
1782 * WCMD_type
1784 * Copy a file to standard output.
1787 void WCMD_type (WCHAR *command) {
1789 int argno = 0;
1790 WCHAR *argN = command;
1791 BOOL writeHeaders = FALSE;
1793 if (param1[0] == 0x00) {
1794 WCMD_output (WCMD_LoadMessage(WCMD_NOARG));
1795 return;
1798 if (param2[0] != 0x00) writeHeaders = TRUE;
1800 /* Loop through all args */
1801 errorlevel = 0;
1802 while (argN) {
1803 WCHAR *thisArg = WCMD_parameter (command, argno++, &argN);
1805 HANDLE h;
1806 WCHAR buffer[512];
1807 DWORD count;
1809 if (!argN) break;
1811 WINE_TRACE("type: Processing arg '%s'\n", wine_dbgstr_w(thisArg));
1812 h = CreateFile (thisArg, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
1813 FILE_ATTRIBUTE_NORMAL, NULL);
1814 if (h == INVALID_HANDLE_VALUE) {
1815 WCMD_print_error ();
1816 WCMD_output (WCMD_LoadMessage(WCMD_READFAIL), thisArg);
1817 errorlevel = 1;
1818 } else {
1819 if (writeHeaders) {
1820 static const WCHAR fmt[] = {'\n','%','s','\n','\n','\0'};
1821 WCMD_output(fmt, thisArg);
1823 while (WCMD_ReadFile (h, buffer, sizeof(buffer)/sizeof(WCHAR), &count, NULL)) {
1824 if (count == 0) break; /* ReadFile reports success on EOF! */
1825 buffer[count] = 0;
1826 WCMD_output_asis (buffer);
1828 CloseHandle (h);
1833 /****************************************************************************
1834 * WCMD_more
1836 * Output either a file or stdin to screen in pages
1839 void WCMD_more (WCHAR *command) {
1841 int argno = 0;
1842 WCHAR *argN = command;
1843 BOOL useinput = FALSE;
1844 WCHAR moreStr[100];
1845 WCHAR moreStrPage[100];
1846 WCHAR buffer[512];
1847 DWORD count;
1848 static const WCHAR moreStart[] = {'-','-',' ','\0'};
1849 static const WCHAR moreFmt[] = {'%','s',' ','-','-','\n','\0'};
1850 static const WCHAR moreFmt2[] = {'%','s',' ','(','%','2','.','2','d','%','%',
1851 ')',' ','-','-','\n','\0'};
1852 static const WCHAR conInW[] = {'C','O','N','I','N','$','\0'};
1854 /* Prefix the NLS more with '-- ', then load the text */
1855 errorlevel = 0;
1856 strcpyW(moreStr, moreStart);
1857 LoadString (hinst, WCMD_MORESTR, &moreStr[3],
1858 (sizeof(moreStr)/sizeof(WCHAR))-3);
1860 if (param1[0] == 0x00) {
1862 /* Wine implements pipes via temporary files, and hence stdin is
1863 effectively reading from the file. This means the prompts for
1864 more are satistied by the next line from the input (file). To
1865 avoid this, ensure stdin is to the console */
1866 HANDLE hstdin = GetStdHandle(STD_INPUT_HANDLE);
1867 HANDLE hConIn = CreateFile(conInW, GENERIC_READ | GENERIC_WRITE,
1868 FILE_SHARE_READ, NULL, OPEN_EXISTING,
1869 FILE_ATTRIBUTE_NORMAL, 0);
1870 SetStdHandle(STD_INPUT_HANDLE, hConIn);
1872 /* Warning: No easy way of ending the stream (ctrl+z on windows) so
1873 once you get in this bit unless due to a pipe, its going to end badly... */
1874 useinput = TRUE;
1875 wsprintf(moreStrPage, moreFmt, moreStr);
1877 WCMD_enter_paged_mode(moreStrPage);
1878 while (WCMD_ReadFile (hstdin, buffer, (sizeof(buffer)/sizeof(WCHAR))-1, &count, NULL)) {
1879 if (count == 0) break; /* ReadFile reports success on EOF! */
1880 buffer[count] = 0;
1881 WCMD_output_asis (buffer);
1883 WCMD_leave_paged_mode();
1885 /* Restore stdin to what it was */
1886 SetStdHandle(STD_INPUT_HANDLE, hstdin);
1887 CloseHandle(hConIn);
1889 return;
1890 } else {
1891 BOOL needsPause = FALSE;
1893 /* Loop through all args */
1894 WCMD_enter_paged_mode(moreStrPage);
1896 while (argN) {
1897 WCHAR *thisArg = WCMD_parameter (command, argno++, &argN);
1898 HANDLE h;
1900 if (!argN) break;
1902 if (needsPause) {
1904 /* Wait */
1905 wsprintf(moreStrPage, moreFmt2, moreStr, 100);
1906 WCMD_leave_paged_mode();
1907 WCMD_output_asis(moreStrPage);
1908 WCMD_ReadFile (GetStdHandle(STD_INPUT_HANDLE), buffer,
1909 sizeof(buffer)/sizeof(WCHAR), &count, NULL);
1910 WCMD_enter_paged_mode(moreStrPage);
1914 WINE_TRACE("more: Processing arg '%s'\n", wine_dbgstr_w(thisArg));
1915 h = CreateFile (thisArg, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
1916 FILE_ATTRIBUTE_NORMAL, NULL);
1917 if (h == INVALID_HANDLE_VALUE) {
1918 WCMD_print_error ();
1919 WCMD_output (WCMD_LoadMessage(WCMD_READFAIL), thisArg);
1920 errorlevel = 1;
1921 } else {
1922 ULONG64 curPos = 0;
1923 ULONG64 fileLen = 0;
1924 WIN32_FILE_ATTRIBUTE_DATA fileInfo;
1926 /* Get the file size */
1927 GetFileAttributesEx(thisArg, GetFileExInfoStandard, (void*)&fileInfo);
1928 fileLen = (((ULONG64)fileInfo.nFileSizeHigh) << 32) + fileInfo.nFileSizeLow;
1930 needsPause = TRUE;
1931 while (WCMD_ReadFile (h, buffer, (sizeof(buffer)/sizeof(WCHAR))-1, &count, NULL)) {
1932 if (count == 0) break; /* ReadFile reports success on EOF! */
1933 buffer[count] = 0;
1934 curPos += count;
1936 /* Update % count (would be used in WCMD_output_asis as prompt) */
1937 wsprintf(moreStrPage, moreFmt2, moreStr, (int) min(99, (curPos * 100)/fileLen));
1939 WCMD_output_asis (buffer);
1941 CloseHandle (h);
1945 WCMD_leave_paged_mode();
1949 /****************************************************************************
1950 * WCMD_verify
1952 * Display verify flag.
1953 * FIXME: We don't actually do anything with the verify flag other than toggle
1954 * it...
1957 void WCMD_verify (WCHAR *command) {
1959 int count;
1961 count = strlenW(command);
1962 if (count == 0) {
1963 if (verify_mode) WCMD_output (WCMD_LoadMessage(WCMD_VERIFYPROMPT), onW);
1964 else WCMD_output (WCMD_LoadMessage(WCMD_VERIFYPROMPT), offW);
1965 return;
1967 if (lstrcmpiW(command, onW) == 0) {
1968 verify_mode = 1;
1969 return;
1971 else if (lstrcmpiW(command, offW) == 0) {
1972 verify_mode = 0;
1973 return;
1975 else WCMD_output (WCMD_LoadMessage(WCMD_VERIFYERR));
1978 /****************************************************************************
1979 * WCMD_version
1981 * Display version info.
1984 void WCMD_version (void) {
1986 WCMD_output (version_string);
1990 /****************************************************************************
1991 * WCMD_volume
1993 * Display volume info and/or set volume label. Returns 0 if error.
1996 int WCMD_volume (int mode, WCHAR *path) {
1998 DWORD count, serial;
1999 WCHAR string[MAX_PATH], label[MAX_PATH], curdir[MAX_PATH];
2000 BOOL status;
2002 if (strlenW(path) == 0) {
2003 status = GetCurrentDirectory (sizeof(curdir)/sizeof(WCHAR), curdir);
2004 if (!status) {
2005 WCMD_print_error ();
2006 return 0;
2008 status = GetVolumeInformation (NULL, label, sizeof(label)/sizeof(WCHAR),
2009 &serial, NULL, NULL, NULL, 0);
2011 else {
2012 static const WCHAR fmt[] = {'%','s','\\','\0'};
2013 if ((path[1] != ':') || (strlenW(path) != 2)) {
2014 WCMD_output (WCMD_LoadMessage(WCMD_SYNTAXERR));
2015 return 0;
2017 wsprintf (curdir, fmt, path);
2018 status = GetVolumeInformation (curdir, label, sizeof(label)/sizeof(WCHAR),
2019 &serial, NULL,
2020 NULL, NULL, 0);
2022 if (!status) {
2023 WCMD_print_error ();
2024 return 0;
2026 WCMD_output (WCMD_LoadMessage(WCMD_VOLUMEDETAIL),
2027 curdir[0], label, HIWORD(serial), LOWORD(serial));
2028 if (mode) {
2029 WCMD_output (WCMD_LoadMessage(WCMD_VOLUMEPROMPT));
2030 WCMD_ReadFile (GetStdHandle(STD_INPUT_HANDLE), string,
2031 sizeof(string)/sizeof(WCHAR), &count, NULL);
2032 if (count > 1) {
2033 string[count-1] = '\0'; /* ReadFile output is not null-terminated! */
2034 if (string[count-2] == '\r') string[count-2] = '\0'; /* Under Windoze we get CRLF! */
2036 if (strlenW(path) != 0) {
2037 if (!SetVolumeLabel (curdir, string)) WCMD_print_error ();
2039 else {
2040 if (!SetVolumeLabel (NULL, string)) WCMD_print_error ();
2043 return 1;
2046 /**************************************************************************
2047 * WCMD_exit
2049 * Exit either the process, or just this batch program
2053 void WCMD_exit (void) {
2055 static const WCHAR parmB[] = {'/','B','\0'};
2056 int rc = atoiW(param1); /* Note: atoi of empty parameter is 0 */
2058 if (context && lstrcmpiW(quals, parmB) == 0) {
2059 errorlevel = rc;
2060 context -> skip_rest = TRUE;
2061 } else {
2062 ExitProcess(rc);
2066 /**************************************************************************
2067 * WCMD_ask_confirm
2069 * Issue a message and ask 'Are you sure (Y/N)', waiting on a valid
2070 * answer.
2072 * Returns True if Y (or A) answer is selected
2073 * If optionAll contains a pointer, ALL is allowed, and if answered
2074 * set to TRUE
2077 BOOL WCMD_ask_confirm (WCHAR *message, BOOL showSureText, BOOL *optionAll) {
2079 WCHAR msgbuffer[MAXSTRING];
2080 WCHAR Ybuffer[MAXSTRING];
2081 WCHAR Nbuffer[MAXSTRING];
2082 WCHAR Abuffer[MAXSTRING];
2083 WCHAR answer[MAX_PATH] = {'\0'};
2084 DWORD count = 0;
2086 /* Load the translated 'Are you sure', plus valid answers */
2087 LoadString (hinst, WCMD_CONFIRM, msgbuffer, sizeof(msgbuffer)/sizeof(WCHAR));
2088 LoadString (hinst, WCMD_YES, Ybuffer, sizeof(Ybuffer)/sizeof(WCHAR));
2089 LoadString (hinst, WCMD_NO, Nbuffer, sizeof(Nbuffer)/sizeof(WCHAR));
2090 LoadString (hinst, WCMD_ALL, Abuffer, sizeof(Abuffer)/sizeof(WCHAR));
2092 /* Loop waiting on a Y or N */
2093 while (answer[0] != Ybuffer[0] && answer[0] != Nbuffer[0]) {
2094 static const WCHAR startBkt[] = {' ','(','\0'};
2095 static const WCHAR endBkt[] = {')','?','\0'};
2097 WCMD_output_asis (message);
2098 if (showSureText) {
2099 WCMD_output_asis (msgbuffer);
2101 WCMD_output_asis (startBkt);
2102 WCMD_output_asis (Ybuffer);
2103 WCMD_output_asis (fslashW);
2104 WCMD_output_asis (Nbuffer);
2105 if (optionAll) {
2106 WCMD_output_asis (fslashW);
2107 WCMD_output_asis (Abuffer);
2109 WCMD_output_asis (endBkt);
2110 WCMD_ReadFile (GetStdHandle(STD_INPUT_HANDLE), answer,
2111 sizeof(answer)/sizeof(WCHAR), &count, NULL);
2112 answer[0] = toupper(answer[0]);
2115 /* Return the answer */
2116 return ((answer[0] == Ybuffer[0]) ||
2117 (optionAll && (answer[0] == Abuffer[0])));
2120 /*****************************************************************************
2121 * WCMD_assoc
2123 * Lists or sets file associations (assoc = TRUE)
2124 * Lists or sets file types (assoc = FALSE)
2126 void WCMD_assoc (WCHAR *command, BOOL assoc) {
2128 HKEY key;
2129 DWORD accessOptions = KEY_READ;
2130 WCHAR *newValue;
2131 LONG rc = ERROR_SUCCESS;
2132 WCHAR keyValue[MAXSTRING];
2133 DWORD valueLen = MAXSTRING;
2134 HKEY readKey;
2135 static const WCHAR shOpCmdW[] = {'\\','S','h','e','l','l','\\',
2136 'O','p','e','n','\\','C','o','m','m','a','n','d','\0'};
2138 /* See if parameter includes '=' */
2139 errorlevel = 0;
2140 newValue = strchrW(command, '=');
2141 if (newValue) accessOptions |= KEY_WRITE;
2143 /* Open a key to HKEY_CLASSES_ROOT for enumerating */
2144 if (RegOpenKeyEx(HKEY_CLASSES_ROOT, nullW, 0,
2145 accessOptions, &key) != ERROR_SUCCESS) {
2146 WINE_FIXME("Unexpected failure opening HKCR key: %d\n", GetLastError());
2147 return;
2150 /* If no parameters then list all associations */
2151 if (*command == 0x00) {
2152 int index = 0;
2154 /* Enumerate all the keys */
2155 while (rc != ERROR_NO_MORE_ITEMS) {
2156 WCHAR keyName[MAXSTRING];
2157 DWORD nameLen;
2159 /* Find the next value */
2160 nameLen = MAXSTRING;
2161 rc = RegEnumKeyEx(key, index++,
2162 keyName, &nameLen,
2163 NULL, NULL, NULL, NULL);
2165 if (rc == ERROR_SUCCESS) {
2167 /* Only interested in extension ones if assoc, or others
2168 if not assoc */
2169 if ((keyName[0] == '.' && assoc) ||
2170 (!(keyName[0] == '.') && (!assoc)))
2172 WCHAR subkey[MAXSTRING];
2173 strcpyW(subkey, keyName);
2174 if (!assoc) strcatW(subkey, shOpCmdW);
2176 if (RegOpenKeyEx(key, subkey, 0,
2177 accessOptions, &readKey) == ERROR_SUCCESS) {
2179 valueLen = sizeof(keyValue)/sizeof(WCHAR);
2180 rc = RegQueryValueEx(readKey, NULL, NULL, NULL,
2181 (LPBYTE)keyValue, &valueLen);
2182 WCMD_output_asis(keyName);
2183 WCMD_output_asis(equalW);
2184 /* If no default value found, leave line empty after '=' */
2185 if (rc == ERROR_SUCCESS) {
2186 WCMD_output_asis(keyValue);
2188 WCMD_output_asis(newline);
2193 RegCloseKey(readKey);
2195 } else {
2197 /* Parameter supplied - if no '=' on command line, its a query */
2198 if (newValue == NULL) {
2199 WCHAR *space;
2200 WCHAR subkey[MAXSTRING];
2202 /* Query terminates the parameter at the first space */
2203 strcpyW(keyValue, command);
2204 space = strchrW(keyValue, ' ');
2205 if (space) *space=0x00;
2207 /* Set up key name */
2208 strcpyW(subkey, keyValue);
2209 if (!assoc) strcatW(subkey, shOpCmdW);
2211 if (RegOpenKeyEx(key, subkey, 0,
2212 accessOptions, &readKey) == ERROR_SUCCESS) {
2214 rc = RegQueryValueEx(readKey, NULL, NULL, NULL,
2215 (LPBYTE)keyValue, &valueLen);
2216 WCMD_output_asis(command);
2217 WCMD_output_asis(equalW);
2218 /* If no default value found, leave line empty after '=' */
2219 if (rc == ERROR_SUCCESS) WCMD_output_asis(keyValue);
2220 WCMD_output_asis(newline);
2221 RegCloseKey(readKey);
2223 } else {
2224 WCHAR msgbuffer[MAXSTRING];
2225 WCHAR outbuffer[MAXSTRING];
2227 /* Load the translated 'File association not found' */
2228 if (assoc) {
2229 LoadString (hinst, WCMD_NOASSOC, msgbuffer, sizeof(msgbuffer)/sizeof(WCHAR));
2230 } else {
2231 LoadString (hinst, WCMD_NOFTYPE, msgbuffer, sizeof(msgbuffer)/sizeof(WCHAR));
2233 wsprintf(outbuffer, msgbuffer, keyValue);
2234 WCMD_output_asis(outbuffer);
2235 errorlevel = 2;
2238 /* Not a query - its a set or clear of a value */
2239 } else {
2241 WCHAR subkey[MAXSTRING];
2243 /* Get pointer to new value */
2244 *newValue = 0x00;
2245 newValue++;
2247 /* Set up key name */
2248 strcpyW(subkey, command);
2249 if (!assoc) strcatW(subkey, shOpCmdW);
2251 /* If nothing after '=' then clear value - only valid for ASSOC */
2252 if (*newValue == 0x00) {
2254 if (assoc) rc = RegDeleteKey(key, command);
2255 if (assoc && rc == ERROR_SUCCESS) {
2256 WINE_TRACE("HKCR Key '%s' deleted\n", wine_dbgstr_w(command));
2258 } else if (assoc && rc != ERROR_FILE_NOT_FOUND) {
2259 WCMD_print_error();
2260 errorlevel = 2;
2262 } else {
2263 WCHAR msgbuffer[MAXSTRING];
2264 WCHAR outbuffer[MAXSTRING];
2266 /* Load the translated 'File association not found' */
2267 if (assoc) {
2268 LoadString (hinst, WCMD_NOASSOC, msgbuffer,
2269 sizeof(msgbuffer)/sizeof(WCHAR));
2270 } else {
2271 LoadString (hinst, WCMD_NOFTYPE, msgbuffer,
2272 sizeof(msgbuffer)/sizeof(WCHAR));
2274 wsprintf(outbuffer, msgbuffer, keyValue);
2275 WCMD_output_asis(outbuffer);
2276 errorlevel = 2;
2279 /* It really is a set value = contents */
2280 } else {
2281 rc = RegCreateKeyEx(key, subkey, 0, NULL, REG_OPTION_NON_VOLATILE,
2282 accessOptions, NULL, &readKey, NULL);
2283 if (rc == ERROR_SUCCESS) {
2284 rc = RegSetValueEx(readKey, NULL, 0, REG_SZ,
2285 (LPBYTE)newValue, strlenW(newValue));
2286 RegCloseKey(readKey);
2289 if (rc != ERROR_SUCCESS) {
2290 WCMD_print_error();
2291 errorlevel = 2;
2292 } else {
2293 WCMD_output_asis(command);
2294 WCMD_output_asis(equalW);
2295 WCMD_output_asis(newValue);
2296 WCMD_output_asis(newline);
2302 /* Clean up */
2303 RegCloseKey(key);
2306 /****************************************************************************
2307 * WCMD_color
2309 * Clear the terminal screen.
2312 void WCMD_color (void) {
2314 /* Emulate by filling the screen from the top left to bottom right with
2315 spaces, then moving the cursor to the top left afterwards */
2316 CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
2317 HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
2319 if (param1[0] != 0x00 && strlenW(param1) > 2) {
2320 WCMD_output (WCMD_LoadMessage(WCMD_ARGERR));
2321 return;
2324 if (GetConsoleScreenBufferInfo(hStdOut, &consoleInfo))
2326 COORD topLeft;
2327 DWORD screenSize;
2328 DWORD color = 0;
2330 screenSize = consoleInfo.dwSize.X * (consoleInfo.dwSize.Y + 1);
2332 topLeft.X = 0;
2333 topLeft.Y = 0;
2335 /* Convert the color hex digits */
2336 if (param1[0] == 0x00) {
2337 color = defaultColor;
2338 } else {
2339 color = strtoulW(param1, NULL, 16);
2342 /* Fail if fg == bg color */
2343 if (((color & 0xF0) >> 4) == (color & 0x0F)) {
2344 errorlevel = 1;
2345 return;
2348 /* Set the current screen contents and ensure all future writes
2349 remain this color */
2350 FillConsoleOutputAttribute(hStdOut, color, screenSize, topLeft, &screenSize);
2351 SetConsoleTextAttribute(hStdOut, color);