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
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.
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
39 #include "wine/debug.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(cmd);
43 void WCMD_execute (char *orig_command, char *parameter, char *substitution);
45 struct env_stack *saved_environment;
46 struct env_stack *pushd_directories;
48 extern HINSTANCE hinst;
49 extern char *inbuilt[];
50 extern int echo_mode, verify_mode, defaultColor;
51 extern char quals[MAX_PATH], param1[MAX_PATH], param2[MAX_PATH];
52 extern BATCH_CONTEXT *context;
53 extern DWORD errorlevel;
57 /****************************************************************************
60 * Clear the terminal screen.
63 void WCMD_clear_screen (void) {
65 /* Emulate by filling the screen from the top left to bottom right with
66 spaces, then moving the cursor to the top left afterwards */
67 CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
68 HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
70 if (GetConsoleScreenBufferInfo(hStdOut, &consoleInfo))
75 screenSize = consoleInfo.dwSize.X * (consoleInfo.dwSize.Y + 1);
79 FillConsoleOutputCharacter(hStdOut, ' ', screenSize, topLeft, &screenSize);
80 SetConsoleCursorPosition(hStdOut, topLeft);
84 /****************************************************************************
87 * Change the default i/o device (ie redirect STDin/STDout).
90 void WCMD_change_tty (void) {
96 /****************************************************************************
99 * Copy a file or wildcarded set.
100 * FIXME: No wildcard support
103 void WCMD_copy (void) {
109 static const char overwrite[] = "Overwrite file (Y/N)?";
110 char string[8], outpath[MAX_PATH], inpath[MAX_PATH], *infile, copycmd[3];
113 if (param1[0] == 0x00) {
114 WCMD_output ("Argument missing\n");
118 if ((strchr(param1,'*') != NULL) && (strchr(param1,'%') != NULL)) {
119 WCMD_output ("Wildcards not yet supported\n");
123 /* If no destination supplied, assume current directory */
124 if (param2[0] == 0x00) {
128 GetFullPathName (param2, sizeof(outpath), outpath, NULL);
129 if (outpath[strlen(outpath) - 1] == '\\')
130 outpath[strlen(outpath) - 1] = '\0';
131 hff = FindFirstFile (outpath, &fd);
132 if (hff != INVALID_HANDLE_VALUE) {
133 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
134 GetFullPathName (param1, sizeof(inpath), inpath, &infile);
135 strcat (outpath, "\\");
136 strcat (outpath, infile);
141 /* /-Y has the highest priority, then /Y and finally the COPYCMD env. variable */
142 if (strstr (quals, "/-Y"))
144 else if (strstr (quals, "/Y"))
147 len = GetEnvironmentVariable ("COPYCMD", copycmd, sizeof(copycmd));
148 force = (len && len < sizeof(copycmd) && ! lstrcmpi (copycmd, "/Y"));
152 hff = FindFirstFile (outpath, &fd);
153 if (hff != INVALID_HANDLE_VALUE) {
155 WCMD_output (overwrite);
156 ReadFile (GetStdHandle(STD_INPUT_HANDLE), string, sizeof(string), &count, NULL);
157 if (toupper(string[0]) == 'Y') force = TRUE;
162 status = CopyFile (param1, outpath, FALSE);
163 if (!status) WCMD_print_error ();
167 /****************************************************************************
170 * Create a directory.
172 * this works recursivly. so mkdir dir1\dir2\dir3 will create dir1 and dir2 if
173 * they do not already exist.
176 static BOOL create_full_path(CHAR* path)
182 new_path = HeapAlloc(GetProcessHeap(),0,strlen(path)+1);
183 strcpy(new_path,path);
185 while ((len = strlen(new_path)) && new_path[len - 1] == '\\')
186 new_path[len - 1] = 0;
188 while (!CreateDirectory(new_path,NULL))
191 DWORD last_error = GetLastError();
192 if (last_error == ERROR_ALREADY_EXISTS)
195 if (last_error != ERROR_PATH_NOT_FOUND)
201 if (!(slash = strrchr(new_path,'\\')) && ! (slash = strrchr(new_path,'/')))
207 len = slash - new_path;
209 if (!create_full_path(new_path))
214 new_path[len] = '\\';
216 HeapFree(GetProcessHeap(),0,new_path);
220 void WCMD_create_dir (void) {
222 if (param1[0] == 0x00) {
223 WCMD_output ("Argument missing\n");
226 if (!create_full_path(param1)) WCMD_print_error ();
229 /****************************************************************************
232 * Delete a file or wildcarded set.
235 * - Testing shows /A is repeatable, eg. /a-r /ar matches all files
236 * - Each set is a pattern, eg /ahr /as-r means
237 * readonly+hidden OR nonreadonly system files
238 * - The '-' applies to a single field, ie /a:-hr means read only
242 void WCMD_delete (char *command) {
245 int argsProcessed = 0;
246 char *argN = command;
248 /* Loop through all args */
250 char *thisArg = WCMD_parameter (command, argno++, &argN);
251 if (argN && argN[0] != '/') {
255 char fpath[MAX_PATH];
259 WINE_TRACE("del: Processing arg %s (quals:%s)\n", thisArg, quals);
262 /* If filename part of parameter is * or *.*, prompt unless
264 if ((strstr (quals, "/Q") == NULL) && (strstr (quals, "/P") == NULL)) {
268 char fname[MAX_PATH];
271 /* Convert path into actual directory spec */
272 GetFullPathName (thisArg, sizeof(fpath), fpath, NULL);
273 WCMD_splitpath(fpath, drive, dir, fname, ext);
275 /* Only prompt for * and *.*, not *a, a*, *.a* etc */
276 if ((strcmp(fname, "*") == 0) &&
277 (*ext == 0x00 || (strcmp(ext, ".*") == 0))) {
279 char question[MAXSTRING];
281 /* Ask for confirmation */
282 sprintf(question, "%s, ", fpath);
283 ok = WCMD_ask_confirm(question, TRUE);
285 /* Abort if answer is 'N' */
290 hff = FindFirstFile (thisArg, &fd);
291 if (hff == INVALID_HANDLE_VALUE) {
292 WCMD_output ("%s :File Not Found\n", thisArg);
295 /* Support del <dirname> by just deleting all files dirname\* */
296 if ((strchr(thisArg,'*') == NULL) && (strchr(thisArg,'?') == NULL)
297 && (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
298 char modifiedParm[MAX_PATH];
299 strcpy(modifiedParm, thisArg);
300 strcat(modifiedParm, "\\*");
302 WCMD_delete(modifiedParm);
307 /* Build the filename to delete as <supplied directory>\<findfirst filename> */
308 strcpy (fpath, thisArg);
310 p = strrchr (fpath, '\\');
313 strcat (fpath, fd.cFileName);
315 else strcpy (fpath, fd.cFileName);
316 if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
318 char *nextA = strstr (quals, "/A");
320 /* Handle attribute matching (/A) */
323 while (nextA != NULL && !ok) {
325 char *thisA = (nextA+2);
328 /* Skip optional : */
329 if (*thisA == ':') thisA++;
331 /* Parse each of the /A[:]xxx in turn */
332 while (*thisA && *thisA != '/') {
334 BOOL attribute = FALSE;
336 /* Match negation of attribute first */
342 /* Match attribute */
344 case 'R': attribute = (fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY);
346 case 'H': attribute = (fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN);
348 case 'S': attribute = (fd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM);
350 case 'A': attribute = (fd.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE);
353 WCMD_output ("Syntax error\n");
356 /* Now check result, keeping a running boolean about whether it
357 matches all parsed attribues so far */
358 if (attribute && !negate) {
360 } else if (!attribute && negate) {
368 /* Save the running total as the final result */
371 /* Step on to next /A set */
372 nextA = strstr (nextA+1, "/A");
376 /* /P means prompt for each file */
377 if (ok && strstr (quals, "/P") != NULL) {
378 char question[MAXSTRING];
380 /* Ask for confirmation */
381 sprintf(question, "%s, Delete", fpath);
382 ok = WCMD_ask_confirm(question, FALSE);
385 /* Only proceed if ok to */
388 /* If file is read only, and /F supplied, delete it */
389 if (fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY &&
390 strstr (quals, "/F") != NULL) {
391 SetFileAttributes(fpath, fd.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY);
394 /* Now do the delete */
395 if (!DeleteFile (fpath)) WCMD_print_error ();
399 } while (FindNextFile(hff, &fd) != 0);
405 /* Handle no valid args */
406 if (argsProcessed == 0) {
407 WCMD_output ("Argument missing\n");
412 /****************************************************************************
415 * Echo input to the screen (or not). We don't try to emulate the bugs
416 * in DOS (try typing "ECHO ON AGAIN" for an example).
419 void WCMD_echo (const char *command) {
421 static const char eon[] = "Echo is ON\n", eoff[] = "Echo is OFF\n";
424 if ((command[0] == '.') && (command[1] == 0)) {
425 WCMD_output (newline);
430 count = strlen(command);
432 if (echo_mode) WCMD_output (eon);
433 else WCMD_output (eoff);
436 if (lstrcmpi(command, "ON") == 0) {
440 if (lstrcmpi(command, "OFF") == 0) {
444 WCMD_output_asis (command);
445 WCMD_output (newline);
449 /**************************************************************************
452 * Batch file loop processing.
453 * FIXME: We don't exhaustively check syntax. Any command which works in MessDOS
454 * will probably work here, but the reverse is not necessarily the case...
457 void WCMD_for (char *p) {
462 char set[MAX_PATH], param[MAX_PATH];
465 if (lstrcmpi (WCMD_parameter (p, 1, NULL), "in")
466 || lstrcmpi (WCMD_parameter (p, 3, NULL), "do")
467 || (param1[0] != '%')) {
468 WCMD_output ("Syntax error\n");
471 lstrcpyn (set, WCMD_parameter (p, 2, NULL), sizeof(set));
472 WCMD_parameter (p, 4, &cmd);
473 lstrcpy (param, param1);
476 * If the parameter within the set has a wildcard then search for matching files
477 * otherwise do a literal substitution.
481 while (*(item = WCMD_parameter (set, i, NULL))) {
482 if (strpbrk (item, "*?")) {
483 hff = FindFirstFile (item, &fd);
484 if (hff == INVALID_HANDLE_VALUE) {
488 WCMD_execute (cmd, param, fd.cFileName);
489 } while (FindNextFile(hff, &fd) != 0);
493 WCMD_execute (cmd, param, item);
499 /*****************************************************************************
502 * Execute a command after substituting variable text for the supplied parameter
505 void WCMD_execute (char *orig_cmd, char *param, char *subst) {
507 char *new_cmd, *p, *s, *dup;
510 size = lstrlen (orig_cmd);
511 new_cmd = (char *) LocalAlloc (LMEM_FIXED | LMEM_ZEROINIT, size);
512 dup = s = strdup (orig_cmd);
514 while ((p = strstr (s, param))) {
516 size += lstrlen (subst);
517 new_cmd = (char *) LocalReAlloc ((HANDLE)new_cmd, size, 0);
519 strcat (new_cmd, subst);
520 s = p + lstrlen (param);
523 WCMD_process_command (new_cmd);
525 LocalFree ((HANDLE)new_cmd);
529 /**************************************************************************
532 * Simple on-line help. Help text is stored in the resource file.
535 void WCMD_give_help (char *command) {
540 command = WCMD_strtrim_leading_spaces(command);
541 if (lstrlen(command) == 0) {
542 LoadString (hinst, 1000, buffer, sizeof(buffer));
543 WCMD_output_asis (buffer);
546 for (i=0; i<=WCMD_EXIT; i++) {
547 if (CompareString (LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
548 param1, -1, inbuilt[i], -1) == 2) {
549 LoadString (hinst, i, buffer, sizeof(buffer));
550 WCMD_output_asis (buffer);
554 WCMD_output ("No help available for %s\n", param1);
559 /****************************************************************************
562 * Batch file jump instruction. Not the most efficient algorithm ;-)
563 * Prints error message if the specified label cannot be found - the file pointer is
564 * then at EOF, effectively stopping the batch file.
565 * FIXME: DOS is supposed to allow labels with spaces - we don't.
568 void WCMD_goto (void) {
570 char string[MAX_PATH];
572 if (param1[0] == 0x00) {
573 WCMD_output ("Argument missing\n");
576 if (context != NULL) {
577 char *paramStart = param1;
579 /* Handle special :EOF label */
580 if (lstrcmpi (":eof", param1) == 0) {
581 context -> skip_rest = TRUE;
585 /* Support goto :label as well as goto label */
586 if (*paramStart == ':') paramStart++;
588 SetFilePointer (context -> h, 0, NULL, FILE_BEGIN);
589 while (WCMD_fgets (string, sizeof(string), context -> h)) {
590 if ((string[0] == ':') && (lstrcmpi (&string[1], paramStart) == 0)) return;
592 WCMD_output ("Target to GOTO not found\n");
597 /*****************************************************************************
600 * Push a directory onto the stack
603 void WCMD_pushd (char *command) {
604 struct env_stack *curdir;
607 if (strchr(command, '/') != NULL) {
608 SetLastError(ERROR_INVALID_PARAMETER);
613 curdir = LocalAlloc (LMEM_FIXED, sizeof (struct env_stack));
614 thisdir = LocalAlloc (LMEM_FIXED, 1024 * sizeof(WCHAR));
615 if( !curdir || !thisdir ) {
618 WCMD_output ("out of memory\n");
622 /* Change directory using CD code with /D parameter */
624 GetCurrentDirectoryW (1024, thisdir);
626 WCMD_setshow_default(command);
632 curdir -> next = pushd_directories;
633 curdir -> strings = thisdir;
634 if (pushd_directories == NULL) {
635 curdir -> u.stackdepth = 1;
637 curdir -> u.stackdepth = pushd_directories -> u.stackdepth + 1;
639 pushd_directories = curdir;
644 /*****************************************************************************
647 * Pop a directory from the stack
650 void WCMD_popd (void) {
651 struct env_stack *temp = pushd_directories;
653 if (!pushd_directories)
656 /* pop the old environment from the stack, and make it the current dir */
657 pushd_directories = temp->next;
658 SetCurrentDirectoryW(temp->strings);
659 LocalFree (temp->strings);
663 /****************************************************************************
666 * Batch file conditional.
667 * FIXME: Much more syntax checking needed!
670 void WCMD_if (char *p) {
672 int negate = 0, test = 0;
673 char condition[MAX_PATH], *command, *s;
675 if (!lstrcmpi (param1, "not")) {
677 lstrcpy (condition, param2);
680 lstrcpy (condition, param1);
682 if (!lstrcmpi (condition, "errorlevel")) {
683 if (errorlevel >= atoi(WCMD_parameter (p, 1+negate, NULL))) test = 1;
684 WCMD_parameter (p, 2+negate, &command);
686 else if (!lstrcmpi (condition, "exist")) {
687 if (GetFileAttributesA(WCMD_parameter (p, 1+negate, NULL)) != INVALID_FILE_ATTRIBUTES) {
690 WCMD_parameter (p, 2+negate, &command);
692 else if (!lstrcmpi (condition, "defined")) {
693 if (GetEnvironmentVariableA(WCMD_parameter (p, 1+negate, NULL), NULL, 0) > 0) {
696 WCMD_parameter (p, 2+negate, &command);
698 else if ((s = strstr (p, "=="))) {
700 if (!lstrcmpi (condition, WCMD_parameter (s, 0, NULL))) test = 1;
701 WCMD_parameter (s, 1, &command);
704 WCMD_output ("Syntax error\n");
707 if (test != negate) {
708 command = strdup (command);
709 WCMD_process_command (command);
714 /****************************************************************************
717 * Move a file, directory tree or wildcarded set of files.
720 void WCMD_move (void) {
725 char input[MAX_PATH];
726 char output[MAX_PATH];
729 char fname[MAX_PATH];
732 if (param1[0] == 0x00) {
733 WCMD_output ("Argument missing\n");
737 /* If no destination supplied, assume current directory */
738 if (param2[0] == 0x00) {
742 /* If 2nd parm is directory, then use original filename */
743 /* Convert partial path to full path */
744 GetFullPathName (param1, sizeof(input), input, NULL);
745 GetFullPathName (param2, sizeof(output), output, NULL);
746 WINE_TRACE("Move from '%s'('%s') to '%s'\n", input, param1, output);
748 /* Split into components */
749 WCMD_splitpath(input, drive, dir, fname, ext);
751 hff = FindFirstFile (input, &fd);
752 while (hff != INVALID_HANDLE_VALUE) {
757 WINE_TRACE("Processing file '%s'\n", fd.cFileName);
759 /* Build src & dest name */
763 /* See if dest is an existing directory */
764 attribs = GetFileAttributes(output);
765 if (attribs != INVALID_FILE_ATTRIBUTES &&
766 (attribs & FILE_ATTRIBUTE_DIRECTORY)) {
767 strcpy(dest, output);
769 strcat(dest, fd.cFileName);
771 strcpy(dest, output);
774 strcat(src, fd.cFileName);
776 WINE_TRACE("Source '%s'\n", src);
777 WINE_TRACE("Dest '%s'\n", dest);
779 /* Check if file is read only, otherwise move it */
780 attribs = GetFileAttributesA(src);
781 if ((attribs != INVALID_FILE_ATTRIBUTES) &&
782 (attribs & FILE_ATTRIBUTE_READONLY)) {
783 SetLastError(ERROR_ACCESS_DENIED);
788 /* If destination exists, prompt unless /Y supplied */
789 if (GetFileAttributesA(dest) != INVALID_FILE_ATTRIBUTES) {
791 char copycmd[MAXSTRING];
794 /* /-Y has the highest priority, then /Y and finally the COPYCMD env. variable */
795 if (strstr (quals, "/-Y"))
797 else if (strstr (quals, "/Y"))
800 len = GetEnvironmentVariable ("COPYCMD", copycmd, sizeof(copycmd));
801 force = (len && len < sizeof(copycmd) && ! lstrcmpi (copycmd, "/Y"));
804 /* Prompt if overwriting */
806 char question[MAXSTRING];
807 char overwrite[MAXSTRING];
809 LoadString (hinst, WCMD_OVERWRITE, overwrite, sizeof(overwrite));
811 /* Ask for confirmation */
812 sprintf(question, "%s %s? ", overwrite, dest);
813 ok = WCMD_ask_confirm(question, TRUE);
815 /* So delete the destination prior to the move */
817 if (!DeleteFile (dest)) {
827 status = MoveFile (src, dest);
829 status = 1; /* Anything other than 0 to prevent error msg below */
838 /* Step on to next match */
839 if (FindNextFile(hff, &fd) == 0) {
841 hff = INVALID_HANDLE_VALUE;
847 /****************************************************************************
850 * Wait for keyboard input.
853 void WCMD_pause (void) {
858 WCMD_output (anykey);
859 ReadFile (GetStdHandle(STD_INPUT_HANDLE), string, sizeof(string), &count, NULL);
862 /****************************************************************************
865 * Delete a directory.
868 void WCMD_remove_dir (char *command) {
871 int argsProcessed = 0;
872 char *argN = command;
874 /* Loop through all args */
876 char *thisArg = WCMD_parameter (command, argno++, &argN);
877 if (argN && argN[0] != '/') {
878 WINE_TRACE("rd: Processing arg %s (quals:%s)\n", thisArg, quals);
881 /* If subdirectory search not supplied, just try to remove
882 and report error if it fails (eg if it contains a file) */
883 if (strstr (quals, "/S") == NULL) {
884 if (!RemoveDirectory (thisArg)) WCMD_print_error ();
886 /* Otherwise use ShFileOp to recursively remove a directory */
889 SHFILEOPSTRUCT lpDir;
892 if (strstr (quals, "/Q") == NULL) {
894 char question[MAXSTRING];
896 /* Ask for confirmation */
897 sprintf(question, "%s, ", thisArg);
898 ok = WCMD_ask_confirm(question, TRUE);
900 /* Abort if answer is 'N' */
907 lpDir.pFrom = thisArg;
908 lpDir.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI;
909 lpDir.wFunc = FO_DELETE;
910 if (SHFileOperationA(&lpDir)) WCMD_print_error ();
915 /* Handle no valid args */
916 if (argsProcessed == 0) {
917 WCMD_output ("Argument missing\n");
923 /****************************************************************************
929 void WCMD_rename (void) {
934 char input[MAX_PATH];
938 char fname[MAX_PATH];
944 /* Must be at least two args */
945 if (param1[0] == 0x00 || param2[0] == 0x00) {
946 WCMD_output ("Argument missing\n");
951 /* Destination cannot contain a drive letter or directory separator */
952 if ((strchr(param1,':') != NULL) || (strchr(param1,'\\') != NULL)) {
953 SetLastError(ERROR_INVALID_PARAMETER);
959 /* Convert partial path to full path */
960 GetFullPathName (param1, sizeof(input), input, NULL);
961 WINE_TRACE("Rename from '%s'('%s') to '%s'\n", input, param1, param2);
962 dotDst = strchr(param2, '.');
964 /* Split into components */
965 WCMD_splitpath(input, drive, dir, fname, ext);
967 hff = FindFirstFile (input, &fd);
968 while (hff != INVALID_HANDLE_VALUE) {
974 WINE_TRACE("Processing file '%s'\n", fd.cFileName);
976 /* FIXME: If dest name or extension is *, replace with filename/ext
977 part otherwise use supplied name. This supports:
980 However, windows has a more complex algorithum supporting eg
981 ?'s and *'s mid name */
982 dotSrc = strchr(fd.cFileName, '.');
984 /* Build src & dest name */
988 dirLen = strlen(src);
989 strcat(src, fd.cFileName);
992 if (param2[0] == '*') {
993 strcat(dest, fd.cFileName);
994 if (dotSrc) dest[dirLen + (dotSrc - fd.cFileName)] = 0x00;
996 strcat(dest, param2);
997 if (dotDst) dest[dirLen + (dotDst - param2)] = 0x00;
1000 /* Build Extension */
1001 if (dotDst && (*(dotDst+1)=='*')) {
1002 if (dotSrc) strcat(dest, dotSrc);
1003 } else if (dotDst) {
1004 if (dotDst) strcat(dest, dotDst);
1007 WINE_TRACE("Source '%s'\n", src);
1008 WINE_TRACE("Dest '%s'\n", dest);
1010 /* Check if file is read only, otherwise move it */
1011 attribs = GetFileAttributesA(src);
1012 if ((attribs != INVALID_FILE_ATTRIBUTES) &&
1013 (attribs & FILE_ATTRIBUTE_READONLY)) {
1014 SetLastError(ERROR_ACCESS_DENIED);
1017 status = MoveFile (src, dest);
1021 WCMD_print_error ();
1025 /* Step on to next match */
1026 if (FindNextFile(hff, &fd) == 0) {
1028 hff = INVALID_HANDLE_VALUE;
1034 /*****************************************************************************
1037 * Make a copy of the environment.
1039 static WCHAR *WCMD_dupenv( const WCHAR *env )
1049 len += (lstrlenW(&env[len]) + 1);
1051 env_copy = LocalAlloc (LMEM_FIXED, (len+1) * sizeof (WCHAR) );
1054 WCMD_output ("out of memory\n");
1057 memcpy (env_copy, env, len*sizeof (WCHAR));
1063 /*****************************************************************************
1066 * setlocal pushes the environment onto a stack
1067 * Save the environment as unicode so we don't screw anything up.
1069 void WCMD_setlocal (const char *s) {
1071 struct env_stack *env_copy;
1074 /* DISABLEEXTENSIONS ignored */
1076 env_copy = LocalAlloc (LMEM_FIXED, sizeof (struct env_stack));
1079 WCMD_output ("out of memory\n");
1083 env = GetEnvironmentStringsW ();
1085 env_copy->strings = WCMD_dupenv (env);
1086 if (env_copy->strings)
1088 env_copy->next = saved_environment;
1089 saved_environment = env_copy;
1091 /* Save the current drive letter */
1092 GetCurrentDirectory (MAX_PATH, cwd);
1093 env_copy->u.cwd = cwd[0];
1096 LocalFree (env_copy);
1098 FreeEnvironmentStringsW (env);
1102 /*****************************************************************************
1105 static inline WCHAR *WCMD_strchrW(WCHAR *str, WCHAR ch)
1116 /*****************************************************************************
1119 * endlocal pops the environment off a stack
1120 * Note: When searching for '=', search from char position 1, to handle
1121 * special internal environment variables =C:, =D: etc
1123 void WCMD_endlocal (void) {
1124 WCHAR *env, *old, *p;
1125 struct env_stack *temp;
1128 if (!saved_environment)
1131 /* pop the old environment from the stack */
1132 temp = saved_environment;
1133 saved_environment = temp->next;
1135 /* delete the current environment, totally */
1136 env = GetEnvironmentStringsW ();
1137 old = WCMD_dupenv (GetEnvironmentStringsW ());
1140 n = lstrlenW(&old[len]) + 1;
1141 p = WCMD_strchrW(&old[len] + 1, '=');
1145 SetEnvironmentVariableW (&old[len], NULL);
1150 FreeEnvironmentStringsW (env);
1152 /* restore old environment */
1153 env = temp->strings;
1156 n = lstrlenW(&env[len]) + 1;
1157 p = WCMD_strchrW(&env[len] + 1, '=');
1161 SetEnvironmentVariableW (&env[len], p);
1166 /* Restore current drive letter */
1167 if (IsCharAlpha(temp->u.cwd)) {
1170 sprintf(envvar, "=%c:", temp->u.cwd);
1171 if (GetEnvironmentVariable(envvar, cwd, MAX_PATH)) {
1172 WINE_TRACE("Resetting cwd to %s\n", cwd);
1173 SetCurrentDirectory(cwd);
1181 /*****************************************************************************
1182 * WCMD_setshow_attrib
1184 * Display and optionally sets DOS attributes on a file or directory
1186 * FIXME: Wine currently uses the Unix stat() function to get file attributes.
1187 * As a result only the Readonly flag is correctly reported, the Archive bit
1188 * is always set and the rest are not implemented. We do the Right Thing anyway.
1190 * FIXME: No SET functionality.
1194 void WCMD_setshow_attrib (void) {
1199 char flags[9] = {" "};
1201 if (param1[0] == '-') {
1206 if (lstrlen(param1) == 0) {
1207 GetCurrentDirectory (sizeof(param1), param1);
1208 strcat (param1, "\\*");
1211 hff = FindFirstFile (param1, &fd);
1212 if (hff == INVALID_HANDLE_VALUE) {
1213 WCMD_output ("%s: File Not Found\n",param1);
1217 if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
1218 if (fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
1221 if (fd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) {
1224 if (fd.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) {
1227 if (fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
1230 if (fd.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) {
1233 if (fd.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) {
1236 WCMD_output ("%s %s\n", flags, fd.cFileName);
1237 for (count=0; count < 8; count++) flags[count] = ' ';
1239 } while (FindNextFile(hff, &fd) != 0);
1244 /*****************************************************************************
1245 * WCMD_setshow_default
1247 * Set/Show the current default directory
1250 void WCMD_setshow_default (char *command) {
1259 WINE_TRACE("Request change to directory '%s'\n", command);
1261 /* Skip /D and trailing whitespace if on the front of the command line */
1262 if (CompareString (LOCALE_USER_DEFAULT,
1263 NORM_IGNORECASE | SORT_STRINGSORT,
1264 command, 2, "/D", -1) == 2) {
1266 while (*command && *command==' ') command++;
1269 GetCurrentDirectory (sizeof(cwd), cwd);
1270 if (strlen(command) == 0) {
1275 /* Remove any double quotes, which may be in the
1276 middle, eg. cd "C:\Program Files"\Microsoft is ok */
1279 if (*command != '"') *pos++ = *command;
1284 /* Search for approprate directory */
1285 WINE_TRACE("Looking for directory '%s'\n", string);
1286 hff = FindFirstFile (string, &fd);
1287 while (hff != INVALID_HANDLE_VALUE) {
1288 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1289 char fpath[MAX_PATH];
1292 char fname[MAX_PATH];
1295 /* Convert path into actual directory spec */
1296 GetFullPathName (string, sizeof(fpath), fpath, NULL);
1297 WCMD_splitpath(fpath, drive, dir, fname, ext);
1300 sprintf(string, "%s%s%s", drive, dir, fd.cFileName);
1303 hff = INVALID_HANDLE_VALUE;
1307 /* Step on to next match */
1308 if (FindNextFile(hff, &fd) == 0) {
1310 hff = INVALID_HANDLE_VALUE;
1315 /* Change to that directory */
1316 WINE_TRACE("Really changing to directory '%s'\n", string);
1318 status = SetCurrentDirectory (string);
1321 WCMD_print_error ();
1325 /* Restore old directory if drive letter would change, and
1326 CD x:\directory /D (or pushd c:\directory) not supplied */
1327 if ((strstr(quals, "/D") == NULL) &&
1328 (param1[1] == ':') && (toupper(param1[0]) != toupper(cwd[0]))) {
1329 SetCurrentDirectory(cwd);
1333 /* Set special =C: type environment variable, for drive letter of
1334 change of directory, even if path was restored due to missing
1335 /D (allows changing drive letter when not resident on that
1337 if ((string[1] == ':') && IsCharAlpha (string[0])) {
1340 strncpy(env+1, string, 2);
1342 SetEnvironmentVariable(env, string);
1349 /****************************************************************************
1352 * Set/Show the system date
1353 * FIXME: Can't change date yet
1356 void WCMD_setshow_date (void) {
1358 char curdate[64], buffer[64];
1361 if (lstrlen(param1) == 0) {
1362 if (GetDateFormat (LOCALE_USER_DEFAULT, 0, NULL, NULL,
1363 curdate, sizeof(curdate))) {
1364 WCMD_output ("Current Date is %s\n", curdate);
1365 if (strstr (quals, "/T") == NULL) {
1366 WCMD_output("Enter new date: ");
1367 ReadFile (GetStdHandle(STD_INPUT_HANDLE), buffer, sizeof(buffer), &count, NULL);
1373 else WCMD_print_error ();
1380 /****************************************************************************
1383 static int WCMD_compare( const void *a, const void *b )
1386 const char * const *str_a = a, * const *str_b = b;
1387 r = CompareString( LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
1388 *str_a, -1, *str_b, -1 );
1389 if( r == CSTR_LESS_THAN ) return -1;
1390 if( r == CSTR_GREATER_THAN ) return 1;
1394 /****************************************************************************
1395 * WCMD_setshow_sortenv
1397 * sort variables into order for display
1398 * Optionally only display those who start with a stub
1399 * returns the count displayed
1401 static int WCMD_setshow_sortenv(const char *s, const char *stub)
1403 UINT count=0, len=0, i, displayedcount=0, stublen=0;
1406 if (stub) stublen = strlen(stub);
1408 /* count the number of strings, and the total length */
1410 len += (lstrlen(&s[len]) + 1);
1414 /* add the strings to an array */
1415 str = LocalAlloc (LMEM_FIXED | LMEM_ZEROINIT, count * sizeof (char*) );
1419 for( i=1; i<count; i++ )
1420 str[i] = str[i-1] + lstrlen(str[i-1]) + 1;
1422 /* sort the array */
1423 qsort( str, count, sizeof (char*), WCMD_compare );
1426 for( i=0; i<count; i++ ) {
1427 if (!stub || CompareString (LOCALE_USER_DEFAULT,
1428 NORM_IGNORECASE | SORT_STRINGSORT,
1429 str[i], stublen, stub, -1) == 2) {
1430 /* Don't display special internal variables */
1431 if (str[i][0] != '=') {
1432 WCMD_output_asis(str[i]);
1433 WCMD_output_asis("\n");
1440 return displayedcount;
1443 /****************************************************************************
1446 * Set/Show the environment variables
1449 void WCMD_setshow_env (char *s) {
1456 if (param1[0] == 0x00 && quals[0] == 0x00) {
1457 env = GetEnvironmentStrings ();
1458 WCMD_setshow_sortenv( env, NULL );
1462 /* See if /P supplied, and if so echo the prompt, and read in a reply */
1463 if (CompareString (LOCALE_USER_DEFAULT,
1464 NORM_IGNORECASE | SORT_STRINGSORT,
1465 s, 2, "/P", -1) == 2) {
1466 char string[MAXSTRING];
1470 while (*s && *s==' ') s++;
1472 /* If no parameter, or no '=' sign, return an error */
1473 if (!(*s) || ((p = strchr (s, '=')) == NULL )) {
1474 WCMD_output ("Argument missing\n");
1478 /* Output the prompt */
1480 if (strlen(p) != 0) WCMD_output(p);
1482 /* Read the reply */
1483 ReadFile (GetStdHandle(STD_INPUT_HANDLE), string, sizeof(string), &count, NULL);
1485 string[count-1] = '\0'; /* ReadFile output is not null-terminated! */
1486 if (string[count-2] == '\r') string[count-2] = '\0'; /* Under Windoze we get CRLF! */
1487 WINE_TRACE("set /p: Setting var '%s' to '%s'\n", s, string);
1488 status = SetEnvironmentVariable (s, string);
1493 p = strchr (s, '=');
1495 env = GetEnvironmentStrings ();
1496 if (WCMD_setshow_sortenv( env, s ) == 0) {
1497 WCMD_output ("Environment variable %s not defined\n", s);
1504 if (strlen(p) == 0) p = NULL;
1505 status = SetEnvironmentVariable (s, p);
1506 gle = GetLastError();
1507 if ((!status) & (gle == ERROR_ENVVAR_NOT_FOUND)) {
1509 } else if ((!status)) WCMD_print_error();
1513 /****************************************************************************
1516 * Set/Show the path environment variable
1519 void WCMD_setshow_path (char *command) {
1524 if (strlen(param1) == 0) {
1525 status = GetEnvironmentVariable ("PATH", string, sizeof(string));
1527 WCMD_output_asis ( "PATH=");
1528 WCMD_output_asis ( string);
1529 WCMD_output_asis ( "\n");
1532 WCMD_output ("PATH not found\n");
1536 if (*command == '=') command++; /* Skip leading '=' */
1537 status = SetEnvironmentVariable ("PATH", command);
1538 if (!status) WCMD_print_error();
1542 /****************************************************************************
1543 * WCMD_setshow_prompt
1545 * Set or show the command prompt.
1548 void WCMD_setshow_prompt (void) {
1552 if (strlen(param1) == 0) {
1553 SetEnvironmentVariable ("PROMPT", NULL);
1557 while ((*s == '=') || (*s == ' ')) s++;
1558 if (strlen(s) == 0) {
1559 SetEnvironmentVariable ("PROMPT", NULL);
1561 else SetEnvironmentVariable ("PROMPT", s);
1565 /****************************************************************************
1568 * Set/Show the system time
1569 * FIXME: Can't change time yet
1572 void WCMD_setshow_time (void) {
1574 char curtime[64], buffer[64];
1578 if (strlen(param1) == 0) {
1580 if (GetTimeFormat (LOCALE_USER_DEFAULT, 0, &st, NULL,
1581 curtime, sizeof(curtime))) {
1582 WCMD_output ("Current Time is %s\n", curtime);
1583 if (strstr (quals, "/T") == NULL) {
1584 WCMD_output ("Enter new time: ", curtime);
1585 ReadFile (GetStdHandle(STD_INPUT_HANDLE), buffer, sizeof(buffer), &count, NULL);
1591 else WCMD_print_error ();
1598 /****************************************************************************
1601 * Shift batch parameters.
1602 * Optional /n says where to start shifting (n=0-8)
1605 void WCMD_shift (char *command) {
1608 if (context != NULL) {
1609 char *pos = strchr(command, '/');
1614 } else if (*(pos+1)>='0' && *(pos+1)<='8') {
1615 start = (*(pos+1) - '0');
1617 SetLastError(ERROR_INVALID_PARAMETER);
1622 WINE_TRACE("Shifting variables, starting at %d\n", start);
1623 for (i=start;i<=8;i++) {
1624 context -> shift_count[i] = context -> shift_count[i+1] + 1;
1626 context -> shift_count[9] = context -> shift_count[9] + 1;
1631 /****************************************************************************
1634 * Set the console title
1636 void WCMD_title (char *command) {
1637 SetConsoleTitle(command);
1640 /****************************************************************************
1643 * Copy a file to standard output.
1646 void WCMD_type (char *command) {
1649 char *argN = command;
1650 BOOL writeHeaders = FALSE;
1652 if (param1[0] == 0x00) {
1653 WCMD_output ("Argument missing\n");
1657 if (param2[0] != 0x00) writeHeaders = TRUE;
1659 /* Loop through all args */
1662 char *thisArg = WCMD_parameter (command, argno++, &argN);
1670 WINE_TRACE("type: Processing arg '%s'\n", thisArg);
1671 h = CreateFile (thisArg, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
1672 FILE_ATTRIBUTE_NORMAL, NULL);
1673 if (h == INVALID_HANDLE_VALUE) {
1674 WCMD_print_error ();
1675 WCMD_output ("%s :Failed\n", thisArg);
1679 WCMD_output("\n%s\n\n", thisArg);
1681 while (ReadFile (h, buffer, sizeof(buffer), &count, NULL)) {
1682 if (count == 0) break; /* ReadFile reports success on EOF! */
1684 WCMD_output_asis (buffer);
1691 /****************************************************************************
1694 * Output either a file or stdin to screen in pages
1697 void WCMD_more (char *command) {
1700 char *argN = command;
1701 BOOL useinput = FALSE;
1703 char moreStrPage[100];
1707 /* Prefix the NLS more with '-- ', then load the text */
1709 strcpy(moreStr, "-- ");
1710 LoadString (hinst, WCMD_MORESTR, &moreStr[3], sizeof(moreStr)-3);
1712 if (param1[0] == 0x00) {
1714 /* Wine implements pipes via temporary files, and hence stdin is
1715 effectively reading from the file. This means the prompts for
1716 more are satistied by the next line from the input (file). To
1717 avoid this, ensure stdin is to the console */
1718 HANDLE hstdin = GetStdHandle(STD_INPUT_HANDLE);
1719 HANDLE hConIn = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
1720 FILE_SHARE_READ, NULL, OPEN_EXISTING,
1721 FILE_ATTRIBUTE_NORMAL, 0);
1722 SetStdHandle(STD_INPUT_HANDLE, hConIn);
1724 /* Warning: No easy way of ending the stream (ctrl+z on windows) so
1725 once you get in this bit unless due to a pipe, its going to end badly... */
1727 sprintf(moreStrPage, "%s --\n", moreStr);
1729 WCMD_enter_paged_mode(moreStrPage);
1730 while (ReadFile (hstdin, buffer, sizeof(buffer)-1, &count, NULL)) {
1731 if (count == 0) break; /* ReadFile reports success on EOF! */
1733 WCMD_output_asis (buffer);
1735 WCMD_leave_paged_mode();
1737 /* Restore stdin to what it was */
1738 SetStdHandle(STD_INPUT_HANDLE, hstdin);
1739 CloseHandle(hConIn);
1743 BOOL needsPause = FALSE;
1745 /* Loop through all args */
1746 WCMD_enter_paged_mode(moreStrPage);
1749 char *thisArg = WCMD_parameter (command, argno++, &argN);
1757 sprintf(moreStrPage, "%s (100%%) --\n", moreStr);
1758 WCMD_leave_paged_mode();
1759 WCMD_output_asis(moreStrPage);
1760 ReadFile (GetStdHandle(STD_INPUT_HANDLE), buffer, sizeof(buffer), &count, NULL);
1761 WCMD_enter_paged_mode(moreStrPage);
1765 WINE_TRACE("more: Processing arg '%s'\n", thisArg);
1766 h = CreateFile (thisArg, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
1767 FILE_ATTRIBUTE_NORMAL, NULL);
1768 if (h == INVALID_HANDLE_VALUE) {
1769 WCMD_print_error ();
1770 WCMD_output ("%s :Failed\n", thisArg);
1774 ULONG64 fileLen = 0;
1775 WIN32_FILE_ATTRIBUTE_DATA fileInfo;
1777 /* Get the file size */
1778 GetFileAttributesEx(thisArg, GetFileExInfoStandard, (void*)&fileInfo);
1779 fileLen = (((ULONG64)fileInfo.nFileSizeHigh) << 32) + fileInfo.nFileSizeLow;
1782 while (ReadFile (h, buffer, sizeof(buffer), &count, NULL)) {
1783 if (count == 0) break; /* ReadFile reports success on EOF! */
1787 /* Update % count (would be used in WCMD_output_asis as prompt) */
1788 sprintf(moreStrPage, "%s (%2.2d%%) --\n", moreStr, (int) min(99, (curPos * 100)/fileLen));
1790 WCMD_output_asis (buffer);
1796 WCMD_leave_paged_mode();
1800 /****************************************************************************
1803 * Display verify flag.
1804 * FIXME: We don't actually do anything with the verify flag other than toggle
1808 void WCMD_verify (char *command) {
1810 static const char von[] = "Verify is ON\n", voff[] = "Verify is OFF\n";
1813 count = strlen(command);
1815 if (verify_mode) WCMD_output (von);
1816 else WCMD_output (voff);
1819 if (lstrcmpi(command, "ON") == 0) {
1823 else if (lstrcmpi(command, "OFF") == 0) {
1827 else WCMD_output ("Verify must be ON or OFF\n");
1830 /****************************************************************************
1833 * Display version info.
1836 void WCMD_version (void) {
1838 WCMD_output (version_string);
1842 /****************************************************************************
1845 * Display volume info and/or set volume label. Returns 0 if error.
1848 int WCMD_volume (int mode, char *path) {
1850 DWORD count, serial;
1851 char string[MAX_PATH], label[MAX_PATH], curdir[MAX_PATH];
1854 if (lstrlen(path) == 0) {
1855 status = GetCurrentDirectory (sizeof(curdir), curdir);
1857 WCMD_print_error ();
1860 status = GetVolumeInformation (NULL, label, sizeof(label), &serial, NULL,
1864 if ((path[1] != ':') || (lstrlen(path) != 2)) {
1865 WCMD_output_asis("Syntax Error\n\n");
1868 wsprintf (curdir, "%s\\", path);
1869 status = GetVolumeInformation (curdir, label, sizeof(label), &serial, NULL,
1873 WCMD_print_error ();
1876 WCMD_output ("Volume in drive %c is %s\nVolume Serial Number is %04x-%04x\n\n",
1877 curdir[0], label, HIWORD(serial), LOWORD(serial));
1879 WCMD_output ("Volume label (11 characters, ENTER for none)?");
1880 ReadFile (GetStdHandle(STD_INPUT_HANDLE), string, sizeof(string), &count, NULL);
1882 string[count-1] = '\0'; /* ReadFile output is not null-terminated! */
1883 if (string[count-2] == '\r') string[count-2] = '\0'; /* Under Windoze we get CRLF! */
1885 if (lstrlen(path) != 0) {
1886 if (!SetVolumeLabel (curdir, string)) WCMD_print_error ();
1889 if (!SetVolumeLabel (NULL, string)) WCMD_print_error ();
1895 /**************************************************************************
1898 * Exit either the process, or just this batch program
1902 void WCMD_exit (void) {
1904 int rc = atoi(param1); /* Note: atoi of empty parameter is 0 */
1906 if (context && lstrcmpi(quals, "/B") == 0) {
1908 context -> skip_rest = TRUE;
1914 /**************************************************************************
1917 * Issue a message and ask 'Are you sure (Y/N)', waiting on a valid
1920 * Returns True if Y answer is selected
1923 BOOL WCMD_ask_confirm (char *message, BOOL showSureText) {
1925 char msgbuffer[MAXSTRING];
1926 char Ybuffer[MAXSTRING];
1927 char Nbuffer[MAXSTRING];
1928 char answer[MAX_PATH] = "";
1931 /* Load the translated 'Are you sure', plus valid answers */
1932 LoadString (hinst, WCMD_CONFIRM, msgbuffer, sizeof(msgbuffer));
1933 LoadString (hinst, WCMD_YES, Ybuffer, sizeof(Ybuffer));
1934 LoadString (hinst, WCMD_NO, Nbuffer, sizeof(Nbuffer));
1936 /* Loop waiting on a Y or N */
1937 while (answer[0] != Ybuffer[0] && answer[0] != Nbuffer[0]) {
1938 WCMD_output_asis (message);
1940 WCMD_output_asis (msgbuffer);
1942 WCMD_output_asis (" (");
1943 WCMD_output_asis (Ybuffer);
1944 WCMD_output_asis ("/");
1945 WCMD_output_asis (Nbuffer);
1946 WCMD_output_asis (")?");
1947 ReadFile (GetStdHandle(STD_INPUT_HANDLE), answer, sizeof(answer),
1949 answer[0] = toupper(answer[0]);
1952 /* Return the answer */
1953 return (answer[0] == Ybuffer[0]);
1956 /*****************************************************************************
1959 * Lists or sets file associations (assoc = TRUE)
1960 * Lists or sets file types (assoc = FALSE)
1962 void WCMD_assoc (char *command, BOOL assoc) {
1965 DWORD accessOptions = KEY_READ;
1967 LONG rc = ERROR_SUCCESS;
1968 char keyValue[MAXSTRING];
1969 DWORD valueLen = MAXSTRING;
1973 /* See if parameter includes '=' */
1975 newValue = strchr(command, '=');
1976 if (newValue) accessOptions |= KEY_WRITE;
1978 /* Open a key to HKEY_CLASSES_ROOT for enumerating */
1979 if (RegOpenKeyEx(HKEY_CLASSES_ROOT, "", 0,
1980 accessOptions, &key) != ERROR_SUCCESS) {
1981 WINE_FIXME("Unexpected failure opening HKCR key: %d\n", GetLastError());
1985 /* If no parameters then list all associations */
1986 if (*command == 0x00) {
1989 /* Enumerate all the keys */
1990 while (rc != ERROR_NO_MORE_ITEMS) {
1991 char keyName[MAXSTRING];
1994 /* Find the next value */
1995 nameLen = MAXSTRING;
1996 rc = RegEnumKeyEx(key, index++,
1998 NULL, NULL, NULL, NULL);
2000 if (rc == ERROR_SUCCESS) {
2002 /* Only interested in extension ones if assoc, or others
2004 if ((keyName[0] == '.' && assoc) ||
2005 (!(keyName[0] == '.') && (!assoc)))
2007 char subkey[MAXSTRING];
2008 strcpy(subkey, keyName);
2009 if (!assoc) strcat(subkey, "\\Shell\\Open\\Command");
2011 if (RegOpenKeyEx(key, subkey, 0,
2012 accessOptions, &readKey) == ERROR_SUCCESS) {
2014 valueLen = sizeof(keyValue);
2015 rc = RegQueryValueEx(readKey, NULL, NULL, NULL,
2016 (LPBYTE)keyValue, &valueLen);
2017 WCMD_output_asis(keyName);
2018 WCMD_output_asis("=");
2019 /* If no default value found, leave line empty after '=' */
2020 if (rc == ERROR_SUCCESS) {
2021 WCMD_output_asis(keyValue);
2023 WCMD_output_asis("\n");
2028 RegCloseKey(readKey);
2032 /* Parameter supplied - if no '=' on command line, its a query */
2033 if (newValue == NULL) {
2035 char subkey[MAXSTRING];
2037 /* Query terminates the parameter at the first space */
2038 strcpy(keyValue, command);
2039 space = strchr(keyValue, ' ');
2040 if (space) *space=0x00;
2042 /* Set up key name */
2043 strcpy(subkey, keyValue);
2044 if (!assoc) strcat(subkey, "\\Shell\\Open\\Command");
2046 if (RegOpenKeyEx(key, subkey, 0,
2047 accessOptions, &readKey) == ERROR_SUCCESS) {
2049 rc = RegQueryValueEx(readKey, NULL, NULL, NULL,
2050 (LPBYTE)keyValue, &valueLen);
2051 WCMD_output_asis(command);
2052 WCMD_output_asis("=");
2053 /* If no default value found, leave line empty after '=' */
2054 if (rc == ERROR_SUCCESS) WCMD_output_asis(keyValue);
2055 WCMD_output_asis("\n");
2056 RegCloseKey(readKey);
2059 char msgbuffer[MAXSTRING];
2060 char outbuffer[MAXSTRING];
2062 /* Load the translated 'File association not found' */
2064 LoadString (hinst, WCMD_NOASSOC, msgbuffer, sizeof(msgbuffer));
2066 LoadString (hinst, WCMD_NOFTYPE, msgbuffer, sizeof(msgbuffer));
2068 sprintf(outbuffer, msgbuffer, keyValue);
2069 WCMD_output_asis(outbuffer);
2073 /* Not a query - its a set or clear of a value */
2076 char subkey[MAXSTRING];
2078 /* Get pointer to new value */
2082 /* Set up key name */
2083 strcpy(subkey, command);
2084 if (!assoc) strcat(subkey, "\\Shell\\Open\\Command");
2086 /* If nothing after '=' then clear value - only valid for ASSOC */
2087 if (*newValue == 0x00) {
2089 if (assoc) rc = RegDeleteKey(key, command);
2090 if (assoc && rc == ERROR_SUCCESS) {
2091 WINE_TRACE("HKCR Key '%s' deleted\n", command);
2093 } else if (assoc && rc != ERROR_FILE_NOT_FOUND) {
2098 char msgbuffer[MAXSTRING];
2099 char outbuffer[MAXSTRING];
2101 /* Load the translated 'File association not found' */
2103 LoadString (hinst, WCMD_NOASSOC, msgbuffer, sizeof(msgbuffer));
2105 LoadString (hinst, WCMD_NOFTYPE, msgbuffer, sizeof(msgbuffer));
2107 sprintf(outbuffer, msgbuffer, keyValue);
2108 WCMD_output_asis(outbuffer);
2112 /* It really is a set value = contents */
2114 rc = RegCreateKeyEx(key, subkey, 0, NULL, REG_OPTION_NON_VOLATILE,
2115 accessOptions, NULL, &readKey, NULL);
2116 if (rc == ERROR_SUCCESS) {
2117 rc = RegSetValueEx(readKey, NULL, 0, REG_SZ,
2118 (LPBYTE)newValue, strlen(newValue));
2119 RegCloseKey(readKey);
2122 if (rc != ERROR_SUCCESS) {
2126 WCMD_output_asis(command);
2127 WCMD_output_asis("=");
2128 WCMD_output_asis(newValue);
2129 WCMD_output_asis("\n");
2139 /****************************************************************************
2142 * Clear the terminal screen.
2145 void WCMD_color (void) {
2147 /* Emulate by filling the screen from the top left to bottom right with
2148 spaces, then moving the cursor to the top left afterwards */
2149 CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
2150 HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
2152 if (param1[0] != 0x00 && strlen(param1) > 2) {
2153 WCMD_output ("Argument invalid\n");
2157 if (GetConsoleScreenBufferInfo(hStdOut, &consoleInfo))
2163 screenSize = consoleInfo.dwSize.X * (consoleInfo.dwSize.Y + 1);
2168 /* Convert the color hex digits */
2169 if (param1[0] == 0x00) {
2170 color = defaultColor;
2172 color = strtoul(param1, NULL, 16);
2175 /* Fail if fg == bg color */
2176 if (((color & 0xF0) >> 4) == (color & 0x0F)) {
2181 /* Set the current screen contents and ensure all future writes
2182 remain this color */
2183 FillConsoleOutputAttribute(hStdOut, color, screenSize, topLeft, &screenSize);
2184 SetConsoleTextAttribute(hStdOut, color);