wined3d: Remove fake nvidia card in GetAdapterIdentifier.
[wine/dibdrv.git] / programs / cmd / builtins.c
blob4d0fd5e9e4ee3a936a8d5162660ccea2094cb41d
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 (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 /****************************************************************************
58 * WCMD_clear_screen
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))
72 COORD topLeft;
73 DWORD screenSize;
75 screenSize = consoleInfo.dwSize.X * (consoleInfo.dwSize.Y + 1);
77 topLeft.X = 0;
78 topLeft.Y = 0;
79 FillConsoleOutputCharacter(hStdOut, ' ', screenSize, topLeft, &screenSize);
80 SetConsoleCursorPosition(hStdOut, topLeft);
84 /****************************************************************************
85 * WCMD_change_tty
87 * Change the default i/o device (ie redirect STDin/STDout).
90 void WCMD_change_tty (void) {
92 WCMD_output (nyi);
96 /****************************************************************************
97 * WCMD_copy
99 * Copy a file or wildcarded set.
100 * FIXME: No wildcard support
103 void WCMD_copy (void) {
105 DWORD count;
106 WIN32_FIND_DATA fd;
107 HANDLE hff;
108 BOOL force, status;
109 static const char overwrite[] = "Overwrite file (Y/N)?";
110 char string[8], outpath[MAX_PATH], inpath[MAX_PATH], *infile, copycmd[3];
111 DWORD len;
113 if (param1[0] == 0x00) {
114 WCMD_output ("Argument missing\n");
115 return;
118 if ((strchr(param1,'*') != NULL) && (strchr(param1,'%') != NULL)) {
119 WCMD_output ("Wildcards not yet supported\n");
120 return;
123 /* If no destination supplied, assume current directory */
124 if (param2[0] == 0x00) {
125 strcpy(param2, ".");
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);
138 FindClose (hff);
141 /* /-Y has the highest priority, then /Y and finally the COPYCMD env. variable */
142 if (strstr (quals, "/-Y"))
143 force = FALSE;
144 else if (strstr (quals, "/Y"))
145 force = TRUE;
146 else {
147 len = GetEnvironmentVariable ("COPYCMD", copycmd, sizeof(copycmd));
148 force = (len && len < sizeof(copycmd) && ! lstrcmpi (copycmd, "/Y"));
151 if (!force) {
152 hff = FindFirstFile (outpath, &fd);
153 if (hff != INVALID_HANDLE_VALUE) {
154 FindClose (hff);
155 WCMD_output (overwrite);
156 ReadFile (GetStdHandle(STD_INPUT_HANDLE), string, sizeof(string), &count, NULL);
157 if (toupper(string[0]) == 'Y') force = TRUE;
159 else force = TRUE;
161 if (force) {
162 status = CopyFile (param1, outpath, FALSE);
163 if (!status) WCMD_print_error ();
167 /****************************************************************************
168 * WCMD_create_dir
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)
178 int len;
179 CHAR *new_path;
180 BOOL ret = TRUE;
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))
190 CHAR *slash;
191 DWORD last_error = GetLastError();
192 if (last_error == ERROR_ALREADY_EXISTS)
193 break;
195 if (last_error != ERROR_PATH_NOT_FOUND)
197 ret = FALSE;
198 break;
201 if (!(slash = strrchr(new_path,'\\')) && ! (slash = strrchr(new_path,'/')))
203 ret = FALSE;
204 break;
207 len = slash - new_path;
208 new_path[len] = 0;
209 if (!create_full_path(new_path))
211 ret = FALSE;
212 break;
214 new_path[len] = '\\';
216 HeapFree(GetProcessHeap(),0,new_path);
217 return ret;
220 void WCMD_create_dir (void) {
222 if (param1[0] == 0x00) {
223 WCMD_output ("Argument missing\n");
224 return;
226 if (!create_full_path(param1)) WCMD_print_error ();
229 /****************************************************************************
230 * WCMD_delete
232 * Delete a file or wildcarded set.
234 * Note on /A:
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
239 * non-hidden files
242 BOOL WCMD_delete (char *command, BOOL expectDir) {
244 int argno = 0;
245 int argsProcessed = 0;
246 char *argN = command;
247 BOOL foundAny = FALSE;
249 /* If not recursing, clear error flag */
250 if (expectDir) errorlevel = 0;
252 /* Loop through all args */
253 while (argN) {
254 char *thisArg = WCMD_parameter (command, argno++, &argN);
255 char argCopy[MAX_PATH];
257 if (argN && argN[0] != '/') {
259 WIN32_FIND_DATA fd;
260 HANDLE hff;
261 char fpath[MAX_PATH];
262 char *p;
263 BOOL handleParm = TRUE;
264 BOOL found = FALSE;
266 strcpy(argCopy, thisArg);
267 WINE_TRACE("del: Processing arg %s (quals:%s)\n", argCopy, quals);
268 argsProcessed++;
270 /* If filename part of parameter is * or *.*, prompt unless
271 /Q supplied. */
272 if ((strstr (quals, "/Q") == NULL) && (strstr (quals, "/P") == NULL)) {
274 char drive[10];
275 char dir[MAX_PATH];
276 char fname[MAX_PATH];
277 char ext[MAX_PATH];
279 /* Convert path into actual directory spec */
280 GetFullPathName (argCopy, sizeof(fpath), fpath, NULL);
281 WCMD_splitpath(fpath, drive, dir, fname, ext);
283 /* Only prompt for * and *.*, not *a, a*, *.a* etc */
284 if ((strcmp(fname, "*") == 0) &&
285 (*ext == 0x00 || (strcmp(ext, ".*") == 0))) {
286 BOOL ok;
287 char question[MAXSTRING];
289 /* Note: Flag as found, to avoid file not found message */
290 found = TRUE;
292 /* Ask for confirmation */
293 sprintf(question, "%s, ", fpath);
294 ok = WCMD_ask_confirm(question, TRUE);
296 /* Abort if answer is 'N' */
297 if (!ok) continue;
301 /* First, try to delete in the current directory */
302 hff = FindFirstFile (argCopy, &fd);
303 if (hff == INVALID_HANDLE_VALUE) {
304 handleParm = FALSE;
305 } else {
306 found = TRUE;
309 /* Support del <dirname> by just deleting all files dirname\* */
310 if (handleParm && (strchr(argCopy,'*') == NULL) && (strchr(argCopy,'?') == NULL)
311 && (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
312 char modifiedParm[MAX_PATH];
313 strcpy(modifiedParm, argCopy);
314 strcat(modifiedParm, "\\*");
315 FindClose(hff);
316 found = TRUE;
317 WCMD_delete(modifiedParm, FALSE);
319 } else if (handleParm) {
321 /* Build the filename to delete as <supplied directory>\<findfirst filename> */
322 strcpy (fpath, argCopy);
323 do {
324 p = strrchr (fpath, '\\');
325 if (p != NULL) {
326 *++p = '\0';
327 strcat (fpath, fd.cFileName);
329 else strcpy (fpath, fd.cFileName);
330 if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
331 BOOL ok = TRUE;
332 char *nextA = strstr (quals, "/A");
334 /* Handle attribute matching (/A) */
335 if (nextA != NULL) {
336 ok = FALSE;
337 while (nextA != NULL && !ok) {
339 char *thisA = (nextA+2);
340 BOOL stillOK = TRUE;
342 /* Skip optional : */
343 if (*thisA == ':') thisA++;
345 /* Parse each of the /A[:]xxx in turn */
346 while (*thisA && *thisA != '/') {
347 BOOL negate = FALSE;
348 BOOL attribute = FALSE;
350 /* Match negation of attribute first */
351 if (*thisA == '-') {
352 negate=TRUE;
353 thisA++;
356 /* Match attribute */
357 switch (*thisA) {
358 case 'R': attribute = (fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY);
359 break;
360 case 'H': attribute = (fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN);
361 break;
362 case 'S': attribute = (fd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM);
363 break;
364 case 'A': attribute = (fd.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE);
365 break;
366 default:
367 WCMD_output ("Syntax error\n");
370 /* Now check result, keeping a running boolean about whether it
371 matches all parsed attribues so far */
372 if (attribute && !negate) {
373 stillOK = stillOK;
374 } else if (!attribute && negate) {
375 stillOK = stillOK;
376 } else {
377 stillOK = FALSE;
379 thisA++;
382 /* Save the running total as the final result */
383 ok = stillOK;
385 /* Step on to next /A set */
386 nextA = strstr (nextA+1, "/A");
390 /* /P means prompt for each file */
391 if (ok && strstr (quals, "/P") != NULL) {
392 char question[MAXSTRING];
394 /* Ask for confirmation */
395 sprintf(question, "%s, Delete", fpath);
396 ok = WCMD_ask_confirm(question, FALSE);
399 /* Only proceed if ok to */
400 if (ok) {
402 /* If file is read only, and /F supplied, delete it */
403 if (fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY &&
404 strstr (quals, "/F") != NULL) {
405 SetFileAttributes(fpath, fd.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY);
408 /* Now do the delete */
409 if (!DeleteFile (fpath)) WCMD_print_error ();
413 } while (FindNextFile(hff, &fd) != 0);
414 FindClose (hff);
417 /* Now recurse into all subdirectories handling the paramater in the same way */
418 if (strstr (quals, "/S") != NULL) {
420 char thisDir[MAX_PATH];
421 int cPos;
423 char drive[10];
424 char dir[MAX_PATH];
425 char fname[MAX_PATH];
426 char ext[MAX_PATH];
428 /* Convert path into actual directory spec */
429 GetFullPathName (argCopy, sizeof(thisDir), thisDir, NULL);
430 WCMD_splitpath(thisDir, drive, dir, fname, ext);
432 strcpy(thisDir, drive);
433 strcat(thisDir, dir);
434 cPos = strlen(thisDir);
436 WINE_TRACE("Searching recursively in '%s'\n", thisDir);
438 /* Append '*' to the directory */
439 thisDir[cPos] = '*';
440 thisDir[cPos+1] = 0x00;
442 hff = FindFirstFile (thisDir, &fd);
444 /* Remove residual '*' */
445 thisDir[cPos] = 0x00;
447 if (hff != INVALID_HANDLE_VALUE) {
448 DIRECTORY_STACK *allDirs = NULL;
449 DIRECTORY_STACK *lastEntry = NULL;
451 do {
452 if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
453 (strcmp(fd.cFileName, "..") != 0) &&
454 (strcmp(fd.cFileName, ".") != 0)) {
456 DIRECTORY_STACK *nextDir;
457 char subParm[MAX_PATH];
459 /* Work out search parameter in sub dir */
460 strcpy (subParm, thisDir);
461 strcat (subParm, fd.cFileName);
462 strcat (subParm, "\\");
463 strcat (subParm, fname);
464 strcat (subParm, ext);
465 WINE_TRACE("Recursive, Adding to search list '%s'\n", subParm);
467 /* Allocate memory, add to list */
468 nextDir = (DIRECTORY_STACK *) HeapAlloc(GetProcessHeap(),0,sizeof(DIRECTORY_STACK));
469 if (allDirs == NULL) allDirs = nextDir;
470 if (lastEntry != NULL) lastEntry->next = nextDir;
471 lastEntry = nextDir;
472 nextDir->next = NULL;
473 nextDir->dirName = HeapAlloc(GetProcessHeap(),0,(strlen(subParm)+1));
474 strcpy(nextDir->dirName, subParm);
476 } while (FindNextFile(hff, &fd) != 0);
477 FindClose (hff);
479 /* Go through each subdir doing the delete */
480 while (allDirs != NULL) {
481 DIRECTORY_STACK *tempDir;
483 tempDir = allDirs->next;
484 found |= WCMD_delete (allDirs->dirName, FALSE);
486 HeapFree(GetProcessHeap(),0,allDirs->dirName);
487 HeapFree(GetProcessHeap(),0,allDirs);
488 allDirs = tempDir;
492 /* Keep running total to see if any found, and if not recursing
493 issue error message */
494 if (expectDir) {
495 if (!found) {
496 errorlevel = 1;
497 WCMD_output ("%s : File Not Found\n", argCopy);
500 foundAny |= found;
504 /* Handle no valid args */
505 if (argsProcessed == 0) {
506 WCMD_output ("Argument missing\n");
509 return foundAny;
512 /****************************************************************************
513 * WCMD_echo
515 * Echo input to the screen (or not). We don't try to emulate the bugs
516 * in DOS (try typing "ECHO ON AGAIN" for an example).
519 void WCMD_echo (const char *command) {
521 static const char eon[] = "Echo is ON\n", eoff[] = "Echo is OFF\n";
522 int count;
524 if ((command[0] == '.') && (command[1] == 0)) {
525 WCMD_output (newline);
526 return;
528 if (command[0]==' ')
529 command++;
530 count = strlen(command);
531 if (count == 0) {
532 if (echo_mode) WCMD_output (eon);
533 else WCMD_output (eoff);
534 return;
536 if (lstrcmpi(command, "ON") == 0) {
537 echo_mode = 1;
538 return;
540 if (lstrcmpi(command, "OFF") == 0) {
541 echo_mode = 0;
542 return;
544 WCMD_output_asis (command);
545 WCMD_output (newline);
549 /**************************************************************************
550 * WCMD_for
552 * Batch file loop processing.
553 * FIXME: We don't exhaustively check syntax. Any command which works in MessDOS
554 * will probably work here, but the reverse is not necessarily the case...
557 void WCMD_for (char *p) {
559 WIN32_FIND_DATA fd;
560 HANDLE hff;
561 char *cmd, *item;
562 char set[MAX_PATH], param[MAX_PATH];
563 int i;
565 if (lstrcmpi (WCMD_parameter (p, 1, NULL), "in")
566 || lstrcmpi (WCMD_parameter (p, 3, NULL), "do")
567 || (param1[0] != '%')) {
568 WCMD_output ("Syntax error\n");
569 return;
571 lstrcpyn (set, WCMD_parameter (p, 2, NULL), sizeof(set));
572 WCMD_parameter (p, 4, &cmd);
573 lstrcpy (param, param1);
576 * If the parameter within the set has a wildcard then search for matching files
577 * otherwise do a literal substitution.
580 i = 0;
581 while (*(item = WCMD_parameter (set, i, NULL))) {
582 if (strpbrk (item, "*?")) {
583 hff = FindFirstFile (item, &fd);
584 if (hff == INVALID_HANDLE_VALUE) {
585 return;
587 do {
588 WCMD_execute (cmd, param, fd.cFileName);
589 } while (FindNextFile(hff, &fd) != 0);
590 FindClose (hff);
592 else {
593 WCMD_execute (cmd, param, item);
595 i++;
599 /*****************************************************************************
600 * WCMD_Execute
602 * Execute a command after substituting variable text for the supplied parameter
605 void WCMD_execute (char *orig_cmd, char *param, char *subst) {
607 char *new_cmd, *p, *s, *dup;
608 int size;
610 size = lstrlen (orig_cmd);
611 new_cmd = (char *) LocalAlloc (LMEM_FIXED | LMEM_ZEROINIT, size);
612 dup = s = strdup (orig_cmd);
614 while ((p = strstr (s, param))) {
615 *p = '\0';
616 size += lstrlen (subst);
617 new_cmd = (char *) LocalReAlloc ((HANDLE)new_cmd, size, 0);
618 strcat (new_cmd, s);
619 strcat (new_cmd, subst);
620 s = p + lstrlen (param);
622 strcat (new_cmd, s);
623 WCMD_process_command (new_cmd);
624 free (dup);
625 LocalFree ((HANDLE)new_cmd);
629 /**************************************************************************
630 * WCMD_give_help
632 * Simple on-line help. Help text is stored in the resource file.
635 void WCMD_give_help (char *command) {
637 int i;
638 char buffer[2048];
640 command = WCMD_strtrim_leading_spaces(command);
641 if (lstrlen(command) == 0) {
642 LoadString (hinst, 1000, buffer, sizeof(buffer));
643 WCMD_output_asis (buffer);
645 else {
646 for (i=0; i<=WCMD_EXIT; i++) {
647 if (CompareString (LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
648 param1, -1, inbuilt[i], -1) == 2) {
649 LoadString (hinst, i, buffer, sizeof(buffer));
650 WCMD_output_asis (buffer);
651 return;
654 WCMD_output ("No help available for %s\n", param1);
656 return;
659 /****************************************************************************
660 * WCMD_go_to
662 * Batch file jump instruction. Not the most efficient algorithm ;-)
663 * Prints error message if the specified label cannot be found - the file pointer is
664 * then at EOF, effectively stopping the batch file.
665 * FIXME: DOS is supposed to allow labels with spaces - we don't.
668 void WCMD_goto (void) {
670 char string[MAX_PATH];
672 if (param1[0] == 0x00) {
673 WCMD_output ("Argument missing\n");
674 return;
676 if (context != NULL) {
677 char *paramStart = param1;
679 /* Handle special :EOF label */
680 if (lstrcmpi (":eof", param1) == 0) {
681 context -> skip_rest = TRUE;
682 return;
685 /* Support goto :label as well as goto label */
686 if (*paramStart == ':') paramStart++;
688 SetFilePointer (context -> h, 0, NULL, FILE_BEGIN);
689 while (WCMD_fgets (string, sizeof(string), context -> h)) {
690 if ((string[0] == ':') && (lstrcmpi (&string[1], paramStart) == 0)) return;
692 WCMD_output ("Target to GOTO not found\n");
694 return;
697 /*****************************************************************************
698 * WCMD_pushd
700 * Push a directory onto the stack
703 void WCMD_pushd (char *command) {
704 struct env_stack *curdir;
705 WCHAR *thisdir;
707 if (strchr(command, '/') != NULL) {
708 SetLastError(ERROR_INVALID_PARAMETER);
709 WCMD_print_error();
710 return;
713 curdir = LocalAlloc (LMEM_FIXED, sizeof (struct env_stack));
714 thisdir = LocalAlloc (LMEM_FIXED, 1024 * sizeof(WCHAR));
715 if( !curdir || !thisdir ) {
716 LocalFree(curdir);
717 LocalFree(thisdir);
718 WCMD_output ("out of memory\n");
719 return;
722 /* Change directory using CD code with /D parameter */
723 strcpy(quals, "/D");
724 GetCurrentDirectoryW (1024, thisdir);
725 errorlevel = 0;
726 WCMD_setshow_default(command);
727 if (errorlevel) {
728 LocalFree(curdir);
729 LocalFree(thisdir);
730 return;
731 } else {
732 curdir -> next = pushd_directories;
733 curdir -> strings = thisdir;
734 if (pushd_directories == NULL) {
735 curdir -> u.stackdepth = 1;
736 } else {
737 curdir -> u.stackdepth = pushd_directories -> u.stackdepth + 1;
739 pushd_directories = curdir;
744 /*****************************************************************************
745 * WCMD_popd
747 * Pop a directory from the stack
750 void WCMD_popd (void) {
751 struct env_stack *temp = pushd_directories;
753 if (!pushd_directories)
754 return;
756 /* pop the old environment from the stack, and make it the current dir */
757 pushd_directories = temp->next;
758 SetCurrentDirectoryW(temp->strings);
759 LocalFree (temp->strings);
760 LocalFree (temp);
763 /****************************************************************************
764 * WCMD_if
766 * Batch file conditional.
767 * FIXME: Much more syntax checking needed!
770 void WCMD_if (char *p) {
772 int negate = 0, test = 0;
773 char condition[MAX_PATH], *command, *s;
775 if (!lstrcmpi (param1, "not")) {
776 negate = 1;
777 lstrcpy (condition, param2);
779 else {
780 lstrcpy (condition, param1);
782 if (!lstrcmpi (condition, "errorlevel")) {
783 if (errorlevel >= atoi(WCMD_parameter (p, 1+negate, NULL))) test = 1;
784 WCMD_parameter (p, 2+negate, &command);
786 else if (!lstrcmpi (condition, "exist")) {
787 if (GetFileAttributesA(WCMD_parameter (p, 1+negate, NULL)) != INVALID_FILE_ATTRIBUTES) {
788 test = 1;
790 WCMD_parameter (p, 2+negate, &command);
792 else if (!lstrcmpi (condition, "defined")) {
793 if (GetEnvironmentVariableA(WCMD_parameter (p, 1+negate, NULL), NULL, 0) > 0) {
794 test = 1;
796 WCMD_parameter (p, 2+negate, &command);
798 else if ((s = strstr (p, "=="))) {
799 s += 2;
800 if (!lstrcmpi (condition, WCMD_parameter (s, 0, NULL))) test = 1;
801 WCMD_parameter (s, 1, &command);
803 else {
804 WCMD_output ("Syntax error\n");
805 return;
807 if (test != negate) {
808 command = strdup (command);
809 WCMD_process_command (command);
810 free (command);
814 /****************************************************************************
815 * WCMD_move
817 * Move a file, directory tree or wildcarded set of files.
820 void WCMD_move (void) {
822 int status;
823 WIN32_FIND_DATA fd;
824 HANDLE hff;
825 char input[MAX_PATH];
826 char output[MAX_PATH];
827 char drive[10];
828 char dir[MAX_PATH];
829 char fname[MAX_PATH];
830 char ext[MAX_PATH];
832 if (param1[0] == 0x00) {
833 WCMD_output ("Argument missing\n");
834 return;
837 /* If no destination supplied, assume current directory */
838 if (param2[0] == 0x00) {
839 strcpy(param2, ".");
842 /* If 2nd parm is directory, then use original filename */
843 /* Convert partial path to full path */
844 GetFullPathName (param1, sizeof(input), input, NULL);
845 GetFullPathName (param2, sizeof(output), output, NULL);
846 WINE_TRACE("Move from '%s'('%s') to '%s'\n", input, param1, output);
848 /* Split into components */
849 WCMD_splitpath(input, drive, dir, fname, ext);
851 hff = FindFirstFile (input, &fd);
852 while (hff != INVALID_HANDLE_VALUE) {
853 char dest[MAX_PATH];
854 char src[MAX_PATH];
855 DWORD attribs;
857 WINE_TRACE("Processing file '%s'\n", fd.cFileName);
859 /* Build src & dest name */
860 strcpy(src, drive);
861 strcat(src, dir);
863 /* See if dest is an existing directory */
864 attribs = GetFileAttributes(output);
865 if (attribs != INVALID_FILE_ATTRIBUTES &&
866 (attribs & FILE_ATTRIBUTE_DIRECTORY)) {
867 strcpy(dest, output);
868 strcat(dest, "\\");
869 strcat(dest, fd.cFileName);
870 } else {
871 strcpy(dest, output);
874 strcat(src, fd.cFileName);
876 WINE_TRACE("Source '%s'\n", src);
877 WINE_TRACE("Dest '%s'\n", dest);
879 /* Check if file is read only, otherwise move it */
880 attribs = GetFileAttributesA(src);
881 if ((attribs != INVALID_FILE_ATTRIBUTES) &&
882 (attribs & FILE_ATTRIBUTE_READONLY)) {
883 SetLastError(ERROR_ACCESS_DENIED);
884 status = 0;
885 } else {
886 BOOL ok = TRUE;
888 /* If destination exists, prompt unless /Y supplied */
889 if (GetFileAttributesA(dest) != INVALID_FILE_ATTRIBUTES) {
890 BOOL force = FALSE;
891 char copycmd[MAXSTRING];
892 int len;
894 /* /-Y has the highest priority, then /Y and finally the COPYCMD env. variable */
895 if (strstr (quals, "/-Y"))
896 force = FALSE;
897 else if (strstr (quals, "/Y"))
898 force = TRUE;
899 else {
900 len = GetEnvironmentVariable ("COPYCMD", copycmd, sizeof(copycmd));
901 force = (len && len < sizeof(copycmd) && ! lstrcmpi (copycmd, "/Y"));
904 /* Prompt if overwriting */
905 if (!force) {
906 char question[MAXSTRING];
907 char overwrite[MAXSTRING];
909 LoadString (hinst, WCMD_OVERWRITE, overwrite, sizeof(overwrite));
911 /* Ask for confirmation */
912 sprintf(question, "%s %s? ", overwrite, dest);
913 ok = WCMD_ask_confirm(question, TRUE);
915 /* So delete the destination prior to the move */
916 if (ok) {
917 if (!DeleteFile (dest)) {
918 WCMD_print_error ();
919 errorlevel = 1;
920 ok = FALSE;
926 if (ok) {
927 status = MoveFile (src, dest);
928 } else {
929 status = 1; /* Anything other than 0 to prevent error msg below */
933 if (!status) {
934 WCMD_print_error ();
935 errorlevel = 1;
938 /* Step on to next match */
939 if (FindNextFile(hff, &fd) == 0) {
940 FindClose(hff);
941 hff = INVALID_HANDLE_VALUE;
942 break;
947 /****************************************************************************
948 * WCMD_pause
950 * Wait for keyboard input.
953 void WCMD_pause (void) {
955 DWORD count;
956 char string[32];
958 WCMD_output (anykey);
959 ReadFile (GetStdHandle(STD_INPUT_HANDLE), string, sizeof(string), &count, NULL);
962 /****************************************************************************
963 * WCMD_remove_dir
965 * Delete a directory.
968 void WCMD_remove_dir (char *command) {
970 int argno = 0;
971 int argsProcessed = 0;
972 char *argN = command;
974 /* Loop through all args */
975 while (argN) {
976 char *thisArg = WCMD_parameter (command, argno++, &argN);
977 if (argN && argN[0] != '/') {
978 WINE_TRACE("rd: Processing arg %s (quals:%s)\n", thisArg, quals);
979 argsProcessed++;
981 /* If subdirectory search not supplied, just try to remove
982 and report error if it fails (eg if it contains a file) */
983 if (strstr (quals, "/S") == NULL) {
984 if (!RemoveDirectory (thisArg)) WCMD_print_error ();
986 /* Otherwise use ShFileOp to recursively remove a directory */
987 } else {
989 SHFILEOPSTRUCT lpDir;
991 /* Ask first */
992 if (strstr (quals, "/Q") == NULL) {
993 BOOL ok;
994 char question[MAXSTRING];
996 /* Ask for confirmation */
997 sprintf(question, "%s, ", thisArg);
998 ok = WCMD_ask_confirm(question, TRUE);
1000 /* Abort if answer is 'N' */
1001 if (!ok) return;
1004 /* Do the delete */
1005 lpDir.hwnd = NULL;
1006 lpDir.pTo = NULL;
1007 lpDir.pFrom = thisArg;
1008 lpDir.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI;
1009 lpDir.wFunc = FO_DELETE;
1010 if (SHFileOperationA(&lpDir)) WCMD_print_error ();
1015 /* Handle no valid args */
1016 if (argsProcessed == 0) {
1017 WCMD_output ("Argument missing\n");
1018 return;
1023 /****************************************************************************
1024 * WCMD_rename
1026 * Rename a file.
1029 void WCMD_rename (void) {
1031 int status;
1032 HANDLE hff;
1033 WIN32_FIND_DATA fd;
1034 char input[MAX_PATH];
1035 char *dotDst = NULL;
1036 char drive[10];
1037 char dir[MAX_PATH];
1038 char fname[MAX_PATH];
1039 char ext[MAX_PATH];
1040 DWORD attribs;
1042 errorlevel = 0;
1044 /* Must be at least two args */
1045 if (param1[0] == 0x00 || param2[0] == 0x00) {
1046 WCMD_output ("Argument missing\n");
1047 errorlevel = 1;
1048 return;
1051 /* Destination cannot contain a drive letter or directory separator */
1052 if ((strchr(param1,':') != NULL) || (strchr(param1,'\\') != NULL)) {
1053 SetLastError(ERROR_INVALID_PARAMETER);
1054 WCMD_print_error();
1055 errorlevel = 1;
1056 return;
1059 /* Convert partial path to full path */
1060 GetFullPathName (param1, sizeof(input), input, NULL);
1061 WINE_TRACE("Rename from '%s'('%s') to '%s'\n", input, param1, param2);
1062 dotDst = strchr(param2, '.');
1064 /* Split into components */
1065 WCMD_splitpath(input, drive, dir, fname, ext);
1067 hff = FindFirstFile (input, &fd);
1068 while (hff != INVALID_HANDLE_VALUE) {
1069 char dest[MAX_PATH];
1070 char src[MAX_PATH];
1071 char *dotSrc = NULL;
1072 int dirLen;
1074 WINE_TRACE("Processing file '%s'\n", fd.cFileName);
1076 /* FIXME: If dest name or extension is *, replace with filename/ext
1077 part otherwise use supplied name. This supports:
1078 ren *.fred *.jim
1079 ren jim.* fred.* etc
1080 However, windows has a more complex algorithum supporting eg
1081 ?'s and *'s mid name */
1082 dotSrc = strchr(fd.cFileName, '.');
1084 /* Build src & dest name */
1085 strcpy(src, drive);
1086 strcat(src, dir);
1087 strcpy(dest, src);
1088 dirLen = strlen(src);
1089 strcat(src, fd.cFileName);
1091 /* Build name */
1092 if (param2[0] == '*') {
1093 strcat(dest, fd.cFileName);
1094 if (dotSrc) dest[dirLen + (dotSrc - fd.cFileName)] = 0x00;
1095 } else {
1096 strcat(dest, param2);
1097 if (dotDst) dest[dirLen + (dotDst - param2)] = 0x00;
1100 /* Build Extension */
1101 if (dotDst && (*(dotDst+1)=='*')) {
1102 if (dotSrc) strcat(dest, dotSrc);
1103 } else if (dotDst) {
1104 if (dotDst) strcat(dest, dotDst);
1107 WINE_TRACE("Source '%s'\n", src);
1108 WINE_TRACE("Dest '%s'\n", dest);
1110 /* Check if file is read only, otherwise move it */
1111 attribs = GetFileAttributesA(src);
1112 if ((attribs != INVALID_FILE_ATTRIBUTES) &&
1113 (attribs & FILE_ATTRIBUTE_READONLY)) {
1114 SetLastError(ERROR_ACCESS_DENIED);
1115 status = 0;
1116 } else {
1117 status = MoveFile (src, dest);
1120 if (!status) {
1121 WCMD_print_error ();
1122 errorlevel = 1;
1125 /* Step on to next match */
1126 if (FindNextFile(hff, &fd) == 0) {
1127 FindClose(hff);
1128 hff = INVALID_HANDLE_VALUE;
1129 break;
1134 /*****************************************************************************
1135 * WCMD_dupenv
1137 * Make a copy of the environment.
1139 static WCHAR *WCMD_dupenv( const WCHAR *env )
1141 WCHAR *env_copy;
1142 int len;
1144 if( !env )
1145 return NULL;
1147 len = 0;
1148 while ( env[len] )
1149 len += (lstrlenW(&env[len]) + 1);
1151 env_copy = LocalAlloc (LMEM_FIXED, (len+1) * sizeof (WCHAR) );
1152 if (!env_copy)
1154 WCMD_output ("out of memory\n");
1155 return env_copy;
1157 memcpy (env_copy, env, len*sizeof (WCHAR));
1158 env_copy[len] = 0;
1160 return env_copy;
1163 /*****************************************************************************
1164 * WCMD_setlocal
1166 * setlocal pushes the environment onto a stack
1167 * Save the environment as unicode so we don't screw anything up.
1169 void WCMD_setlocal (const char *s) {
1170 WCHAR *env;
1171 struct env_stack *env_copy;
1172 char cwd[MAX_PATH];
1174 /* DISABLEEXTENSIONS ignored */
1176 env_copy = LocalAlloc (LMEM_FIXED, sizeof (struct env_stack));
1177 if( !env_copy )
1179 WCMD_output ("out of memory\n");
1180 return;
1183 env = GetEnvironmentStringsW ();
1185 env_copy->strings = WCMD_dupenv (env);
1186 if (env_copy->strings)
1188 env_copy->next = saved_environment;
1189 saved_environment = env_copy;
1191 /* Save the current drive letter */
1192 GetCurrentDirectory (MAX_PATH, cwd);
1193 env_copy->u.cwd = cwd[0];
1195 else
1196 LocalFree (env_copy);
1198 FreeEnvironmentStringsW (env);
1202 /*****************************************************************************
1203 * WCMD_strchrW
1205 static inline WCHAR *WCMD_strchrW(WCHAR *str, WCHAR ch)
1207 while(*str)
1209 if(*str == ch)
1210 return str;
1211 str++;
1213 return NULL;
1216 /*****************************************************************************
1217 * WCMD_endlocal
1219 * endlocal pops the environment off a stack
1220 * Note: When searching for '=', search from char position 1, to handle
1221 * special internal environment variables =C:, =D: etc
1223 void WCMD_endlocal (void) {
1224 WCHAR *env, *old, *p;
1225 struct env_stack *temp;
1226 int len, n;
1228 if (!saved_environment)
1229 return;
1231 /* pop the old environment from the stack */
1232 temp = saved_environment;
1233 saved_environment = temp->next;
1235 /* delete the current environment, totally */
1236 env = GetEnvironmentStringsW ();
1237 old = WCMD_dupenv (GetEnvironmentStringsW ());
1238 len = 0;
1239 while (old[len]) {
1240 n = lstrlenW(&old[len]) + 1;
1241 p = WCMD_strchrW(&old[len] + 1, '=');
1242 if (p)
1244 *p++ = 0;
1245 SetEnvironmentVariableW (&old[len], NULL);
1247 len += n;
1249 LocalFree (old);
1250 FreeEnvironmentStringsW (env);
1252 /* restore old environment */
1253 env = temp->strings;
1254 len = 0;
1255 while (env[len]) {
1256 n = lstrlenW(&env[len]) + 1;
1257 p = WCMD_strchrW(&env[len] + 1, '=');
1258 if (p)
1260 *p++ = 0;
1261 SetEnvironmentVariableW (&env[len], p);
1263 len += n;
1266 /* Restore current drive letter */
1267 if (IsCharAlpha(temp->u.cwd)) {
1268 char envvar[4];
1269 char cwd[MAX_PATH];
1270 sprintf(envvar, "=%c:", temp->u.cwd);
1271 if (GetEnvironmentVariable(envvar, cwd, MAX_PATH)) {
1272 WINE_TRACE("Resetting cwd to %s\n", cwd);
1273 SetCurrentDirectory(cwd);
1277 LocalFree (env);
1278 LocalFree (temp);
1281 /*****************************************************************************
1282 * WCMD_setshow_attrib
1284 * Display and optionally sets DOS attributes on a file or directory
1286 * FIXME: Wine currently uses the Unix stat() function to get file attributes.
1287 * As a result only the Readonly flag is correctly reported, the Archive bit
1288 * is always set and the rest are not implemented. We do the Right Thing anyway.
1290 * FIXME: No SET functionality.
1294 void WCMD_setshow_attrib (void) {
1296 DWORD count;
1297 HANDLE hff;
1298 WIN32_FIND_DATA fd;
1299 char flags[9] = {" "};
1301 if (param1[0] == '-') {
1302 WCMD_output (nyi);
1303 return;
1306 if (lstrlen(param1) == 0) {
1307 GetCurrentDirectory (sizeof(param1), param1);
1308 strcat (param1, "\\*");
1311 hff = FindFirstFile (param1, &fd);
1312 if (hff == INVALID_HANDLE_VALUE) {
1313 WCMD_output ("%s: File Not Found\n",param1);
1315 else {
1316 do {
1317 if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
1318 if (fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
1319 flags[0] = 'H';
1321 if (fd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) {
1322 flags[1] = 'S';
1324 if (fd.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) {
1325 flags[2] = 'A';
1327 if (fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
1328 flags[3] = 'R';
1330 if (fd.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) {
1331 flags[4] = 'T';
1333 if (fd.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) {
1334 flags[5] = 'C';
1336 WCMD_output ("%s %s\n", flags, fd.cFileName);
1337 for (count=0; count < 8; count++) flags[count] = ' ';
1339 } while (FindNextFile(hff, &fd) != 0);
1341 FindClose (hff);
1344 /*****************************************************************************
1345 * WCMD_setshow_default
1347 * Set/Show the current default directory
1350 void WCMD_setshow_default (char *command) {
1352 BOOL status;
1353 char string[1024];
1354 char cwd[1024];
1355 char *pos;
1356 WIN32_FIND_DATA fd;
1357 HANDLE hff;
1359 WINE_TRACE("Request change to directory '%s'\n", command);
1361 /* Skip /D and trailing whitespace if on the front of the command line */
1362 if (CompareString (LOCALE_USER_DEFAULT,
1363 NORM_IGNORECASE | SORT_STRINGSORT,
1364 command, 2, "/D", -1) == 2) {
1365 command += 2;
1366 while (*command && *command==' ') command++;
1369 GetCurrentDirectory (sizeof(cwd), cwd);
1370 if (strlen(command) == 0) {
1371 strcat (cwd, "\n");
1372 WCMD_output (cwd);
1374 else {
1375 /* Remove any double quotes, which may be in the
1376 middle, eg. cd "C:\Program Files"\Microsoft is ok */
1377 pos = string;
1378 while (*command) {
1379 if (*command != '"') *pos++ = *command;
1380 command++;
1382 *pos = 0x00;
1384 /* Search for approprate directory */
1385 WINE_TRACE("Looking for directory '%s'\n", string);
1386 hff = FindFirstFile (string, &fd);
1387 while (hff != INVALID_HANDLE_VALUE) {
1388 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1389 char fpath[MAX_PATH];
1390 char drive[10];
1391 char dir[MAX_PATH];
1392 char fname[MAX_PATH];
1393 char ext[MAX_PATH];
1395 /* Convert path into actual directory spec */
1396 GetFullPathName (string, sizeof(fpath), fpath, NULL);
1397 WCMD_splitpath(fpath, drive, dir, fname, ext);
1399 /* Rebuild path */
1400 sprintf(string, "%s%s%s", drive, dir, fd.cFileName);
1402 FindClose(hff);
1403 hff = INVALID_HANDLE_VALUE;
1404 break;
1407 /* Step on to next match */
1408 if (FindNextFile(hff, &fd) == 0) {
1409 FindClose(hff);
1410 hff = INVALID_HANDLE_VALUE;
1411 break;
1415 /* Change to that directory */
1416 WINE_TRACE("Really changing to directory '%s'\n", string);
1418 status = SetCurrentDirectory (string);
1419 if (!status) {
1420 errorlevel = 1;
1421 WCMD_print_error ();
1422 return;
1423 } else {
1425 /* Restore old directory if drive letter would change, and
1426 CD x:\directory /D (or pushd c:\directory) not supplied */
1427 if ((strstr(quals, "/D") == NULL) &&
1428 (param1[1] == ':') && (toupper(param1[0]) != toupper(cwd[0]))) {
1429 SetCurrentDirectory(cwd);
1433 /* Set special =C: type environment variable, for drive letter of
1434 change of directory, even if path was restored due to missing
1435 /D (allows changing drive letter when not resident on that
1436 drive */
1437 if ((string[1] == ':') && IsCharAlpha (string[0])) {
1438 char env[4];
1439 strcpy(env, "=");
1440 strncpy(env+1, string, 2);
1441 env[3] = 0x00;
1442 SetEnvironmentVariable(env, string);
1446 return;
1449 /****************************************************************************
1450 * WCMD_setshow_date
1452 * Set/Show the system date
1453 * FIXME: Can't change date yet
1456 void WCMD_setshow_date (void) {
1458 char curdate[64], buffer[64];
1459 DWORD count;
1461 if (lstrlen(param1) == 0) {
1462 if (GetDateFormat (LOCALE_USER_DEFAULT, 0, NULL, NULL,
1463 curdate, sizeof(curdate))) {
1464 WCMD_output ("Current Date is %s\n", curdate);
1465 if (strstr (quals, "/T") == NULL) {
1466 WCMD_output("Enter new date: ");
1467 ReadFile (GetStdHandle(STD_INPUT_HANDLE), buffer, sizeof(buffer), &count, NULL);
1468 if (count > 2) {
1469 WCMD_output (nyi);
1473 else WCMD_print_error ();
1475 else {
1476 WCMD_output (nyi);
1480 /****************************************************************************
1481 * WCMD_compare
1483 static int WCMD_compare( const void *a, const void *b )
1485 int r;
1486 const char * const *str_a = a, * const *str_b = b;
1487 r = CompareString( LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
1488 *str_a, -1, *str_b, -1 );
1489 if( r == CSTR_LESS_THAN ) return -1;
1490 if( r == CSTR_GREATER_THAN ) return 1;
1491 return 0;
1494 /****************************************************************************
1495 * WCMD_setshow_sortenv
1497 * sort variables into order for display
1498 * Optionally only display those who start with a stub
1499 * returns the count displayed
1501 static int WCMD_setshow_sortenv(const char *s, const char *stub)
1503 UINT count=0, len=0, i, displayedcount=0, stublen=0;
1504 const char **str;
1506 if (stub) stublen = strlen(stub);
1508 /* count the number of strings, and the total length */
1509 while ( s[len] ) {
1510 len += (lstrlen(&s[len]) + 1);
1511 count++;
1514 /* add the strings to an array */
1515 str = LocalAlloc (LMEM_FIXED | LMEM_ZEROINIT, count * sizeof (char*) );
1516 if( !str )
1517 return 0;
1518 str[0] = s;
1519 for( i=1; i<count; i++ )
1520 str[i] = str[i-1] + lstrlen(str[i-1]) + 1;
1522 /* sort the array */
1523 qsort( str, count, sizeof (char*), WCMD_compare );
1525 /* print it */
1526 for( i=0; i<count; i++ ) {
1527 if (!stub || CompareString (LOCALE_USER_DEFAULT,
1528 NORM_IGNORECASE | SORT_STRINGSORT,
1529 str[i], stublen, stub, -1) == 2) {
1530 /* Don't display special internal variables */
1531 if (str[i][0] != '=') {
1532 WCMD_output_asis(str[i]);
1533 WCMD_output_asis("\n");
1534 displayedcount++;
1539 LocalFree( str );
1540 return displayedcount;
1543 /****************************************************************************
1544 * WCMD_setshow_env
1546 * Set/Show the environment variables
1549 void WCMD_setshow_env (char *s) {
1551 LPVOID env;
1552 char *p;
1553 int status;
1555 errorlevel = 0;
1556 if (param1[0] == 0x00 && quals[0] == 0x00) {
1557 env = GetEnvironmentStrings ();
1558 WCMD_setshow_sortenv( env, NULL );
1559 return;
1562 /* See if /P supplied, and if so echo the prompt, and read in a reply */
1563 if (CompareString (LOCALE_USER_DEFAULT,
1564 NORM_IGNORECASE | SORT_STRINGSORT,
1565 s, 2, "/P", -1) == 2) {
1566 char string[MAXSTRING];
1567 DWORD count;
1569 s += 2;
1570 while (*s && *s==' ') s++;
1572 /* If no parameter, or no '=' sign, return an error */
1573 if (!(*s) || ((p = strchr (s, '=')) == NULL )) {
1574 WCMD_output ("Argument missing\n");
1575 return;
1578 /* Output the prompt */
1579 *p++ = '\0';
1580 if (strlen(p) != 0) WCMD_output(p);
1582 /* Read the reply */
1583 ReadFile (GetStdHandle(STD_INPUT_HANDLE), string, sizeof(string), &count, NULL);
1584 if (count > 1) {
1585 string[count-1] = '\0'; /* ReadFile output is not null-terminated! */
1586 if (string[count-2] == '\r') string[count-2] = '\0'; /* Under Windoze we get CRLF! */
1587 WINE_TRACE("set /p: Setting var '%s' to '%s'\n", s, string);
1588 status = SetEnvironmentVariable (s, string);
1591 } else {
1592 DWORD gle;
1593 p = strchr (s, '=');
1594 if (p == NULL) {
1595 env = GetEnvironmentStrings ();
1596 if (WCMD_setshow_sortenv( env, s ) == 0) {
1597 WCMD_output ("Environment variable %s not defined\n", s);
1598 errorlevel = 1;
1600 return;
1602 *p++ = '\0';
1604 if (strlen(p) == 0) p = NULL;
1605 status = SetEnvironmentVariable (s, p);
1606 gle = GetLastError();
1607 if ((!status) & (gle == ERROR_ENVVAR_NOT_FOUND)) {
1608 errorlevel = 1;
1609 } else if ((!status)) WCMD_print_error();
1613 /****************************************************************************
1614 * WCMD_setshow_path
1616 * Set/Show the path environment variable
1619 void WCMD_setshow_path (char *command) {
1621 char string[1024];
1622 DWORD status;
1624 if (strlen(param1) == 0) {
1625 status = GetEnvironmentVariable ("PATH", string, sizeof(string));
1626 if (status != 0) {
1627 WCMD_output_asis ( "PATH=");
1628 WCMD_output_asis ( string);
1629 WCMD_output_asis ( "\n");
1631 else {
1632 WCMD_output ("PATH not found\n");
1635 else {
1636 if (*command == '=') command++; /* Skip leading '=' */
1637 status = SetEnvironmentVariable ("PATH", command);
1638 if (!status) WCMD_print_error();
1642 /****************************************************************************
1643 * WCMD_setshow_prompt
1645 * Set or show the command prompt.
1648 void WCMD_setshow_prompt (void) {
1650 char *s;
1652 if (strlen(param1) == 0) {
1653 SetEnvironmentVariable ("PROMPT", NULL);
1655 else {
1656 s = param1;
1657 while ((*s == '=') || (*s == ' ')) s++;
1658 if (strlen(s) == 0) {
1659 SetEnvironmentVariable ("PROMPT", NULL);
1661 else SetEnvironmentVariable ("PROMPT", s);
1665 /****************************************************************************
1666 * WCMD_setshow_time
1668 * Set/Show the system time
1669 * FIXME: Can't change time yet
1672 void WCMD_setshow_time (void) {
1674 char curtime[64], buffer[64];
1675 DWORD count;
1676 SYSTEMTIME st;
1678 if (strlen(param1) == 0) {
1679 GetLocalTime(&st);
1680 if (GetTimeFormat (LOCALE_USER_DEFAULT, 0, &st, NULL,
1681 curtime, sizeof(curtime))) {
1682 WCMD_output ("Current Time is %s\n", curtime);
1683 if (strstr (quals, "/T") == NULL) {
1684 WCMD_output ("Enter new time: ", curtime);
1685 ReadFile (GetStdHandle(STD_INPUT_HANDLE), buffer, sizeof(buffer), &count, NULL);
1686 if (count > 2) {
1687 WCMD_output (nyi);
1691 else WCMD_print_error ();
1693 else {
1694 WCMD_output (nyi);
1698 /****************************************************************************
1699 * WCMD_shift
1701 * Shift batch parameters.
1702 * Optional /n says where to start shifting (n=0-8)
1705 void WCMD_shift (char *command) {
1706 int start;
1708 if (context != NULL) {
1709 char *pos = strchr(command, '/');
1710 int i;
1712 if (pos == NULL) {
1713 start = 0;
1714 } else if (*(pos+1)>='0' && *(pos+1)<='8') {
1715 start = (*(pos+1) - '0');
1716 } else {
1717 SetLastError(ERROR_INVALID_PARAMETER);
1718 WCMD_print_error();
1719 return;
1722 WINE_TRACE("Shifting variables, starting at %d\n", start);
1723 for (i=start;i<=8;i++) {
1724 context -> shift_count[i] = context -> shift_count[i+1] + 1;
1726 context -> shift_count[9] = context -> shift_count[9] + 1;
1731 /****************************************************************************
1732 * WCMD_title
1734 * Set the console title
1736 void WCMD_title (char *command) {
1737 SetConsoleTitle(command);
1740 /****************************************************************************
1741 * WCMD_type
1743 * Copy a file to standard output.
1746 void WCMD_type (char *command) {
1748 int argno = 0;
1749 char *argN = command;
1750 BOOL writeHeaders = FALSE;
1752 if (param1[0] == 0x00) {
1753 WCMD_output ("Argument missing\n");
1754 return;
1757 if (param2[0] != 0x00) writeHeaders = TRUE;
1759 /* Loop through all args */
1760 errorlevel = 0;
1761 while (argN) {
1762 char *thisArg = WCMD_parameter (command, argno++, &argN);
1764 HANDLE h;
1765 char buffer[512];
1766 DWORD count;
1768 if (!argN) break;
1770 WINE_TRACE("type: Processing arg '%s'\n", thisArg);
1771 h = CreateFile (thisArg, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
1772 FILE_ATTRIBUTE_NORMAL, NULL);
1773 if (h == INVALID_HANDLE_VALUE) {
1774 WCMD_print_error ();
1775 WCMD_output ("%s :Failed\n", thisArg);
1776 errorlevel = 1;
1777 } else {
1778 if (writeHeaders) {
1779 WCMD_output("\n%s\n\n", thisArg);
1781 while (ReadFile (h, buffer, sizeof(buffer), &count, NULL)) {
1782 if (count == 0) break; /* ReadFile reports success on EOF! */
1783 buffer[count] = 0;
1784 WCMD_output_asis (buffer);
1786 CloseHandle (h);
1791 /****************************************************************************
1792 * WCMD_more
1794 * Output either a file or stdin to screen in pages
1797 void WCMD_more (char *command) {
1799 int argno = 0;
1800 char *argN = command;
1801 BOOL useinput = FALSE;
1802 char moreStr[100];
1803 char moreStrPage[100];
1804 char buffer[512];
1805 DWORD count;
1807 /* Prefix the NLS more with '-- ', then load the text */
1808 errorlevel = 0;
1809 strcpy(moreStr, "-- ");
1810 LoadString (hinst, WCMD_MORESTR, &moreStr[3], sizeof(moreStr)-3);
1812 if (param1[0] == 0x00) {
1814 /* Wine implements pipes via temporary files, and hence stdin is
1815 effectively reading from the file. This means the prompts for
1816 more are satistied by the next line from the input (file). To
1817 avoid this, ensure stdin is to the console */
1818 HANDLE hstdin = GetStdHandle(STD_INPUT_HANDLE);
1819 HANDLE hConIn = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
1820 FILE_SHARE_READ, NULL, OPEN_EXISTING,
1821 FILE_ATTRIBUTE_NORMAL, 0);
1822 SetStdHandle(STD_INPUT_HANDLE, hConIn);
1824 /* Warning: No easy way of ending the stream (ctrl+z on windows) so
1825 once you get in this bit unless due to a pipe, its going to end badly... */
1826 useinput = TRUE;
1827 sprintf(moreStrPage, "%s --\n", moreStr);
1829 WCMD_enter_paged_mode(moreStrPage);
1830 while (ReadFile (hstdin, buffer, sizeof(buffer)-1, &count, NULL)) {
1831 if (count == 0) break; /* ReadFile reports success on EOF! */
1832 buffer[count] = 0;
1833 WCMD_output_asis (buffer);
1835 WCMD_leave_paged_mode();
1837 /* Restore stdin to what it was */
1838 SetStdHandle(STD_INPUT_HANDLE, hstdin);
1839 CloseHandle(hConIn);
1841 return;
1842 } else {
1843 BOOL needsPause = FALSE;
1845 /* Loop through all args */
1846 WCMD_enter_paged_mode(moreStrPage);
1848 while (argN) {
1849 char *thisArg = WCMD_parameter (command, argno++, &argN);
1850 HANDLE h;
1852 if (!argN) break;
1854 if (needsPause) {
1856 /* Wait */
1857 sprintf(moreStrPage, "%s (100%%) --\n", moreStr);
1858 WCMD_leave_paged_mode();
1859 WCMD_output_asis(moreStrPage);
1860 ReadFile (GetStdHandle(STD_INPUT_HANDLE), buffer, sizeof(buffer), &count, NULL);
1861 WCMD_enter_paged_mode(moreStrPage);
1865 WINE_TRACE("more: Processing arg '%s'\n", thisArg);
1866 h = CreateFile (thisArg, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
1867 FILE_ATTRIBUTE_NORMAL, NULL);
1868 if (h == INVALID_HANDLE_VALUE) {
1869 WCMD_print_error ();
1870 WCMD_output ("%s :Failed\n", thisArg);
1871 errorlevel = 1;
1872 } else {
1873 ULONG64 curPos = 0;
1874 ULONG64 fileLen = 0;
1875 WIN32_FILE_ATTRIBUTE_DATA fileInfo;
1877 /* Get the file size */
1878 GetFileAttributesEx(thisArg, GetFileExInfoStandard, (void*)&fileInfo);
1879 fileLen = (((ULONG64)fileInfo.nFileSizeHigh) << 32) + fileInfo.nFileSizeLow;
1881 needsPause = TRUE;
1882 while (ReadFile (h, buffer, sizeof(buffer), &count, NULL)) {
1883 if (count == 0) break; /* ReadFile reports success on EOF! */
1884 buffer[count] = 0;
1885 curPos += count;
1887 /* Update % count (would be used in WCMD_output_asis as prompt) */
1888 sprintf(moreStrPage, "%s (%2.2d%%) --\n", moreStr, (int) min(99, (curPos * 100)/fileLen));
1890 WCMD_output_asis (buffer);
1892 CloseHandle (h);
1896 WCMD_leave_paged_mode();
1900 /****************************************************************************
1901 * WCMD_verify
1903 * Display verify flag.
1904 * FIXME: We don't actually do anything with the verify flag other than toggle
1905 * it...
1908 void WCMD_verify (char *command) {
1910 static const char von[] = "Verify is ON\n", voff[] = "Verify is OFF\n";
1911 int count;
1913 count = strlen(command);
1914 if (count == 0) {
1915 if (verify_mode) WCMD_output (von);
1916 else WCMD_output (voff);
1917 return;
1919 if (lstrcmpi(command, "ON") == 0) {
1920 verify_mode = 1;
1921 return;
1923 else if (lstrcmpi(command, "OFF") == 0) {
1924 verify_mode = 0;
1925 return;
1927 else WCMD_output ("Verify must be ON or OFF\n");
1930 /****************************************************************************
1931 * WCMD_version
1933 * Display version info.
1936 void WCMD_version (void) {
1938 WCMD_output (version_string);
1942 /****************************************************************************
1943 * WCMD_volume
1945 * Display volume info and/or set volume label. Returns 0 if error.
1948 int WCMD_volume (int mode, char *path) {
1950 DWORD count, serial;
1951 char string[MAX_PATH], label[MAX_PATH], curdir[MAX_PATH];
1952 BOOL status;
1954 if (lstrlen(path) == 0) {
1955 status = GetCurrentDirectory (sizeof(curdir), curdir);
1956 if (!status) {
1957 WCMD_print_error ();
1958 return 0;
1960 status = GetVolumeInformation (NULL, label, sizeof(label), &serial, NULL,
1961 NULL, NULL, 0);
1963 else {
1964 if ((path[1] != ':') || (lstrlen(path) != 2)) {
1965 WCMD_output_asis("Syntax Error\n\n");
1966 return 0;
1968 wsprintf (curdir, "%s\\", path);
1969 status = GetVolumeInformation (curdir, label, sizeof(label), &serial, NULL,
1970 NULL, NULL, 0);
1972 if (!status) {
1973 WCMD_print_error ();
1974 return 0;
1976 WCMD_output ("Volume in drive %c is %s\nVolume Serial Number is %04x-%04x\n\n",
1977 curdir[0], label, HIWORD(serial), LOWORD(serial));
1978 if (mode) {
1979 WCMD_output ("Volume label (11 characters, ENTER for none)?");
1980 ReadFile (GetStdHandle(STD_INPUT_HANDLE), string, sizeof(string), &count, NULL);
1981 if (count > 1) {
1982 string[count-1] = '\0'; /* ReadFile output is not null-terminated! */
1983 if (string[count-2] == '\r') string[count-2] = '\0'; /* Under Windoze we get CRLF! */
1985 if (lstrlen(path) != 0) {
1986 if (!SetVolumeLabel (curdir, string)) WCMD_print_error ();
1988 else {
1989 if (!SetVolumeLabel (NULL, string)) WCMD_print_error ();
1992 return 1;
1995 /**************************************************************************
1996 * WCMD_exit
1998 * Exit either the process, or just this batch program
2002 void WCMD_exit (void) {
2004 int rc = atoi(param1); /* Note: atoi of empty parameter is 0 */
2006 if (context && lstrcmpi(quals, "/B") == 0) {
2007 errorlevel = rc;
2008 context -> skip_rest = TRUE;
2009 } else {
2010 ExitProcess(rc);
2014 /**************************************************************************
2015 * WCMD_ask_confirm
2017 * Issue a message and ask 'Are you sure (Y/N)', waiting on a valid
2018 * answer.
2020 * Returns True if Y answer is selected
2023 BOOL WCMD_ask_confirm (char *message, BOOL showSureText) {
2025 char msgbuffer[MAXSTRING];
2026 char Ybuffer[MAXSTRING];
2027 char Nbuffer[MAXSTRING];
2028 char answer[MAX_PATH] = "";
2029 DWORD count = 0;
2031 /* Load the translated 'Are you sure', plus valid answers */
2032 LoadString (hinst, WCMD_CONFIRM, msgbuffer, sizeof(msgbuffer));
2033 LoadString (hinst, WCMD_YES, Ybuffer, sizeof(Ybuffer));
2034 LoadString (hinst, WCMD_NO, Nbuffer, sizeof(Nbuffer));
2036 /* Loop waiting on a Y or N */
2037 while (answer[0] != Ybuffer[0] && answer[0] != Nbuffer[0]) {
2038 WCMD_output_asis (message);
2039 if (showSureText) {
2040 WCMD_output_asis (msgbuffer);
2042 WCMD_output_asis (" (");
2043 WCMD_output_asis (Ybuffer);
2044 WCMD_output_asis ("/");
2045 WCMD_output_asis (Nbuffer);
2046 WCMD_output_asis (")?");
2047 ReadFile (GetStdHandle(STD_INPUT_HANDLE), answer, sizeof(answer),
2048 &count, NULL);
2049 answer[0] = toupper(answer[0]);
2052 /* Return the answer */
2053 return (answer[0] == Ybuffer[0]);
2056 /*****************************************************************************
2057 * WCMD_assoc
2059 * Lists or sets file associations (assoc = TRUE)
2060 * Lists or sets file types (assoc = FALSE)
2062 void WCMD_assoc (char *command, BOOL assoc) {
2064 HKEY key;
2065 DWORD accessOptions = KEY_READ;
2066 char *newValue;
2067 LONG rc = ERROR_SUCCESS;
2068 char keyValue[MAXSTRING];
2069 DWORD valueLen = MAXSTRING;
2070 HKEY readKey;
2073 /* See if parameter includes '=' */
2074 errorlevel = 0;
2075 newValue = strchr(command, '=');
2076 if (newValue) accessOptions |= KEY_WRITE;
2078 /* Open a key to HKEY_CLASSES_ROOT for enumerating */
2079 if (RegOpenKeyEx(HKEY_CLASSES_ROOT, "", 0,
2080 accessOptions, &key) != ERROR_SUCCESS) {
2081 WINE_FIXME("Unexpected failure opening HKCR key: %d\n", GetLastError());
2082 return;
2085 /* If no parameters then list all associations */
2086 if (*command == 0x00) {
2087 int index = 0;
2089 /* Enumerate all the keys */
2090 while (rc != ERROR_NO_MORE_ITEMS) {
2091 char keyName[MAXSTRING];
2092 DWORD nameLen;
2094 /* Find the next value */
2095 nameLen = MAXSTRING;
2096 rc = RegEnumKeyEx(key, index++,
2097 keyName, &nameLen,
2098 NULL, NULL, NULL, NULL);
2100 if (rc == ERROR_SUCCESS) {
2102 /* Only interested in extension ones if assoc, or others
2103 if not assoc */
2104 if ((keyName[0] == '.' && assoc) ||
2105 (!(keyName[0] == '.') && (!assoc)))
2107 char subkey[MAXSTRING];
2108 strcpy(subkey, keyName);
2109 if (!assoc) strcat(subkey, "\\Shell\\Open\\Command");
2111 if (RegOpenKeyEx(key, subkey, 0,
2112 accessOptions, &readKey) == ERROR_SUCCESS) {
2114 valueLen = sizeof(keyValue);
2115 rc = RegQueryValueEx(readKey, NULL, NULL, NULL,
2116 (LPBYTE)keyValue, &valueLen);
2117 WCMD_output_asis(keyName);
2118 WCMD_output_asis("=");
2119 /* If no default value found, leave line empty after '=' */
2120 if (rc == ERROR_SUCCESS) {
2121 WCMD_output_asis(keyValue);
2123 WCMD_output_asis("\n");
2128 RegCloseKey(readKey);
2130 } else {
2132 /* Parameter supplied - if no '=' on command line, its a query */
2133 if (newValue == NULL) {
2134 char *space;
2135 char subkey[MAXSTRING];
2137 /* Query terminates the parameter at the first space */
2138 strcpy(keyValue, command);
2139 space = strchr(keyValue, ' ');
2140 if (space) *space=0x00;
2142 /* Set up key name */
2143 strcpy(subkey, keyValue);
2144 if (!assoc) strcat(subkey, "\\Shell\\Open\\Command");
2146 if (RegOpenKeyEx(key, subkey, 0,
2147 accessOptions, &readKey) == ERROR_SUCCESS) {
2149 rc = RegQueryValueEx(readKey, NULL, NULL, NULL,
2150 (LPBYTE)keyValue, &valueLen);
2151 WCMD_output_asis(command);
2152 WCMD_output_asis("=");
2153 /* If no default value found, leave line empty after '=' */
2154 if (rc == ERROR_SUCCESS) WCMD_output_asis(keyValue);
2155 WCMD_output_asis("\n");
2156 RegCloseKey(readKey);
2158 } else {
2159 char msgbuffer[MAXSTRING];
2160 char outbuffer[MAXSTRING];
2162 /* Load the translated 'File association not found' */
2163 if (assoc) {
2164 LoadString (hinst, WCMD_NOASSOC, msgbuffer, sizeof(msgbuffer));
2165 } else {
2166 LoadString (hinst, WCMD_NOFTYPE, msgbuffer, sizeof(msgbuffer));
2168 sprintf(outbuffer, msgbuffer, keyValue);
2169 WCMD_output_asis(outbuffer);
2170 errorlevel = 2;
2173 /* Not a query - its a set or clear of a value */
2174 } else {
2176 char subkey[MAXSTRING];
2178 /* Get pointer to new value */
2179 *newValue = 0x00;
2180 newValue++;
2182 /* Set up key name */
2183 strcpy(subkey, command);
2184 if (!assoc) strcat(subkey, "\\Shell\\Open\\Command");
2186 /* If nothing after '=' then clear value - only valid for ASSOC */
2187 if (*newValue == 0x00) {
2189 if (assoc) rc = RegDeleteKey(key, command);
2190 if (assoc && rc == ERROR_SUCCESS) {
2191 WINE_TRACE("HKCR Key '%s' deleted\n", command);
2193 } else if (assoc && rc != ERROR_FILE_NOT_FOUND) {
2194 WCMD_print_error();
2195 errorlevel = 2;
2197 } else {
2198 char msgbuffer[MAXSTRING];
2199 char outbuffer[MAXSTRING];
2201 /* Load the translated 'File association not found' */
2202 if (assoc) {
2203 LoadString (hinst, WCMD_NOASSOC, msgbuffer, sizeof(msgbuffer));
2204 } else {
2205 LoadString (hinst, WCMD_NOFTYPE, msgbuffer, sizeof(msgbuffer));
2207 sprintf(outbuffer, msgbuffer, keyValue);
2208 WCMD_output_asis(outbuffer);
2209 errorlevel = 2;
2212 /* It really is a set value = contents */
2213 } else {
2214 rc = RegCreateKeyEx(key, subkey, 0, NULL, REG_OPTION_NON_VOLATILE,
2215 accessOptions, NULL, &readKey, NULL);
2216 if (rc == ERROR_SUCCESS) {
2217 rc = RegSetValueEx(readKey, NULL, 0, REG_SZ,
2218 (LPBYTE)newValue, strlen(newValue));
2219 RegCloseKey(readKey);
2222 if (rc != ERROR_SUCCESS) {
2223 WCMD_print_error();
2224 errorlevel = 2;
2225 } else {
2226 WCMD_output_asis(command);
2227 WCMD_output_asis("=");
2228 WCMD_output_asis(newValue);
2229 WCMD_output_asis("\n");
2235 /* Clean up */
2236 RegCloseKey(key);
2239 /****************************************************************************
2240 * WCMD_color
2242 * Clear the terminal screen.
2245 void WCMD_color (void) {
2247 /* Emulate by filling the screen from the top left to bottom right with
2248 spaces, then moving the cursor to the top left afterwards */
2249 CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
2250 HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
2252 if (param1[0] != 0x00 && strlen(param1) > 2) {
2253 WCMD_output ("Argument invalid\n");
2254 return;
2257 if (GetConsoleScreenBufferInfo(hStdOut, &consoleInfo))
2259 COORD topLeft;
2260 DWORD screenSize;
2261 DWORD color = 0;
2263 screenSize = consoleInfo.dwSize.X * (consoleInfo.dwSize.Y + 1);
2265 topLeft.X = 0;
2266 topLeft.Y = 0;
2268 /* Convert the color hex digits */
2269 if (param1[0] == 0x00) {
2270 color = defaultColor;
2271 } else {
2272 color = strtoul(param1, NULL, 16);
2275 /* Fail if fg == bg color */
2276 if (((color & 0xF0) >> 4) == (color & 0x0F)) {
2277 errorlevel = 1;
2278 return;
2281 /* Set the current screen contents and ensure all future writes
2282 remain this color */
2283 FillConsoleOutputAttribute(hStdOut, color, screenSize, topLeft, &screenSize);
2284 SetConsoleTextAttribute(hStdOut, color);