Fixed color definition (bg and fg were swapped).
[wine] / programs / wcmd / builtins.c
1 /*
2  * WCMD - Wine-compatible command line interface - built-in functions.
3  *
4  * (C) 1999 D A Pickles
5  *
6  * On entry to each function, global variables quals, param1, param2 contain
7  * the qualifiers (uppercased and concatenated) and parameters entered, with
8  * environment-variable and batch parameter substitution already done.
9  */
10
11 /*
12  * FIXME:
13  * - No support for pipes, shell parameters
14  * - Lots of functionality missing from builtins
15  * - Messages etc need international support
16  */
17
18 #include "wcmd.h"
19
20 void WCMD_execute (char *orig_command, char *parameter, char *substitution);
21
22 extern HINSTANCE hinst;
23 extern char *inbuilt[];
24 extern char nyi[];
25 extern char newline[];
26 extern char version_string[];
27 extern char anykey[];
28 extern int echo_mode, verify_mode;
29 extern char quals[MAX_PATH], param1[MAX_PATH], param2[MAX_PATH];
30 extern BATCH_CONTEXT *context;
31 extern DWORD errorlevel;
32
33
34
35 /****************************************************************************
36  * WCMD_clear_screen
37  *
38  * Clear the terminal screen.
39  */
40
41 void WCMD_clear_screen () {
42
43   WCMD_output (nyi);
44
45 }
46
47 /****************************************************************************
48  * WCMD_change_tty
49  *
50  * Change the default i/o device (ie redirect STDin/STDout).
51  */
52
53 void WCMD_change_tty () {
54
55   WCMD_output (nyi);
56
57 }
58
59 /****************************************************************************
60  * WCMD_copy
61  *
62  * Copy a file or wildcarded set.
63  * FIXME: No wildcard support
64  */
65
66 void WCMD_copy () {
67
68 DWORD count;
69 WIN32_FIND_DATA fd;
70 HANDLE hff;
71 BOOL force, status;
72 static char *overwrite = "Overwrite file (Y/N)?";
73 char string[8], outpath[MAX_PATH], inpath[MAX_PATH], *infile;
74
75   if ((strchr(param1,'*') != NULL) && (strchr(param1,'%') != NULL)) {
76     WCMD_output ("Wildcards not yet supported\n");
77     return;
78   }
79   GetFullPathName (param2, sizeof(outpath), outpath, NULL);
80   hff = FindFirstFile (outpath, &fd);
81   if (hff != INVALID_HANDLE_VALUE) {
82     if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
83       GetFullPathName (param1, sizeof(inpath), inpath, &infile);
84       strcat (outpath, "\\");
85       strcat (outpath, infile);
86     }
87     FindClose (hff);
88   }
89   force = (strstr (quals, "/Y") != NULL);
90   if (!force) {
91     hff = FindFirstFile (outpath, &fd);
92     if (hff != INVALID_HANDLE_VALUE) {
93       FindClose (hff);
94       WCMD_output (overwrite);
95       ReadFile (GetStdHandle(STD_INPUT_HANDLE), string, sizeof(string), &count, NULL);
96       if (toupper(string[0]) == 'Y') force = TRUE;
97     }
98     else force = TRUE;
99   }
100   if (force) {
101     status = CopyFile (param1, outpath, FALSE);
102     if (!status) WCMD_print_error ();
103   }
104 }
105
106 /****************************************************************************
107  * WCMD_create_dir
108  *
109  * Create a directory.
110  */
111
112 void WCMD_create_dir () {
113
114   if (!CreateDirectory (param1, NULL)) WCMD_print_error ();
115 }
116
117 /****************************************************************************
118  * WCMD_delete
119  *
120  * Delete a file or wildcarded set.
121  *
122  */
123
124 void WCMD_delete (int recurse) {
125
126 WIN32_FIND_DATA fd;
127 HANDLE hff;
128 char fpath[MAX_PATH];
129 char *p;
130
131   hff = FindFirstFile (param1, &fd);
132   if (hff == INVALID_HANDLE_VALUE) {
133     WCMD_output ("File Not Found\n");
134     return;
135   }
136   if ((strchr(param1,'*') == NULL) && (strchr(param1,'?') == NULL)
137         && (!recurse) && (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
138     strcat (param1, "\\*");
139     WCMD_delete (1);
140     return;
141   }
142   if ((strchr(param1,'*') != NULL) || (strchr(param1,'?') != NULL)) {
143     strcpy (fpath, param1);
144     do {
145       p = strrchr (fpath, '\\');
146       if (p != NULL) {
147         *++p = '\0';
148         strcat (fpath, fd.cFileName);
149       }
150       else strcpy (fpath, fd.cFileName);
151       if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
152         if (!DeleteFile (fpath)) WCMD_print_error ();
153       }
154     } while (FindNextFile(hff, &fd) != 0);
155     FindClose (hff);
156   }
157   else {
158     if (!DeleteFile (param1)) WCMD_print_error ();
159   }
160 }
161
162 /****************************************************************************
163  * WCMD_echo
164  *
165  * Echo input to the screen (or not). We don't try to emulate the bugs
166  * in DOS (try typing "ECHO ON AGAIN" for an example).
167  */
168
169 void WCMD_echo (char *command) {
170
171 static char *eon = "Echo is ON\n", *eoff = "Echo is OFF\n";
172 int count;
173
174   count = strlen(command);
175   if (count == 0) {
176     if (echo_mode) WCMD_output (eon);
177     else WCMD_output (eoff);
178     return;
179   }
180   if ((count == 1) && (command[0] == '.')) {
181     WCMD_output (newline);
182     return;
183   }
184   if (lstrcmpi(command, "ON") == 0) {
185     echo_mode = 1;
186     return;
187   }
188   if (lstrcmpi(command, "OFF") == 0) {
189     echo_mode = 0;
190     return;
191   }
192   WCMD_output_asis (command);    
193   WCMD_output (newline);
194
195 }
196
197 /**************************************************************************
198  * WCMD_for
199  *
200  * Batch file loop processing.
201  * FIXME: We don't exhaustively check syntax. Any command which works in MessDOS
202  * will probably work here, but the reverse is not necessarily the case...
203  */
204
205 void WCMD_for (char *p) {
206
207 WIN32_FIND_DATA fd;
208 HANDLE hff;
209 char *cmd, *item;
210 char set[MAX_PATH], param[MAX_PATH];
211 int i;
212
213   if (lstrcmpi (WCMD_parameter (p, 1, NULL), "in")
214         || lstrcmpi (WCMD_parameter (p, 3, NULL), "do")
215         || (param1[0] != '%')) {
216     WCMD_output ("Syntax error\n");
217     return;
218   }
219   lstrcpyn (set, WCMD_parameter (p, 2, NULL), sizeof(set));
220   WCMD_parameter (p, 4, &cmd);
221   lstrcpy (param, param1);
222
223 /*
224  *      If the parameter within the set has a wildcard then search for matching files
225  *      otherwise do a literal substitution.
226  */
227
228   i = 0;
229   while (*(item = WCMD_parameter (set, i, NULL))) {
230     if (strpbrk (item, "*?")) {
231       hff = FindFirstFile (item, &fd);
232       if (hff == INVALID_HANDLE_VALUE) {
233         return;
234       }
235       do {
236         WCMD_execute (cmd, param, fd.cFileName);
237       } while (FindNextFile(hff, &fd) != 0);
238       FindClose (hff);
239 }
240     else {
241       WCMD_execute (cmd, param, item);
242     }
243     i++;
244   }
245 }
246
247 /*****************************************************************************
248  * WCMD_Execute
249  *
250  *      Execute a command after substituting variable text for the supplied parameter
251  */
252
253 void WCMD_execute (char *orig_cmd, char *param, char *subst) {
254
255 char *new_cmd, *p, *s, *dup;
256 int size;
257
258   size = lstrlen (orig_cmd);
259   new_cmd = (char *) LocalAlloc (LMEM_FIXED | LMEM_ZEROINIT, size);
260   dup = s = strdup (orig_cmd);
261
262   while ((p = strstr (s, param))) {
263     *p = '\0';
264     size += lstrlen (subst);
265     new_cmd = (char *) LocalReAlloc ((HANDLE)new_cmd, size, 0);
266     strcat (new_cmd, s);
267     strcat (new_cmd, subst);
268     s = p + lstrlen (param);
269   }
270   strcat (new_cmd, s);
271   WCMD_process_command (new_cmd);
272   free (dup);
273   LocalFree ((HANDLE)new_cmd);
274 }
275
276
277 /**************************************************************************
278  * WCMD_give_help
279  *
280  *      Simple on-line help. Help text is stored in the resource file.
281  */
282
283 void WCMD_give_help (char *command) {
284
285 int i;
286 char buffer[2048];
287
288   command = WCMD_strtrim_leading_spaces(command);
289   if (lstrlen(command) == 0) {
290     LoadString (0, 1000, buffer, sizeof(buffer));
291     WCMD_output (buffer);
292   }
293   else {
294     for (i=0; i<=WCMD_EXIT; i++) {
295       if (CompareString (LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
296           param1, -1, inbuilt[i], -1) == 2) {
297         LoadString (hinst, i, buffer, sizeof(buffer));
298         WCMD_output (buffer);
299         return;
300       }
301     }
302     WCMD_output ("No help available for %s\n", param1);
303   }
304   return;
305 }
306
307 /****************************************************************************
308  * WCMD_go_to
309  *
310  * Batch file jump instruction. Not the most efficient algorithm ;-)
311  * Prints error message if the specified label cannot be found - the file pointer is
312  * then at EOF, effectively stopping the batch file.
313  * FIXME: DOS is supposed to allow labels with spaces - we don't.
314  */
315
316 void WCMD_goto () {
317
318 char string[MAX_PATH];
319
320   if (context != NULL) {
321     SetFilePointer (context -> h, 0, NULL, FILE_BEGIN);
322     while (WCMD_fgets (string, sizeof(string), context -> h)) {
323       if ((string[0] == ':') && (strcmp (&string[1], param1) == 0)) return;
324     }
325     WCMD_output ("Target to GOTO not found\n");
326   }
327   return;
328 }
329
330
331 /****************************************************************************
332  * WCMD_if
333  *
334  * Batch file conditional.
335  * FIXME: Much more syntax checking needed!
336  */
337
338 void WCMD_if (char *p) {
339
340 HANDLE h;
341 int negate = 0, test = 0;
342 char condition[MAX_PATH], *command, *s;
343
344   if (!lstrcmpi (param1, "not")) {
345     negate = 1;
346     lstrcpy (condition, param2);
347 }
348   else {
349     lstrcpy (condition, param1);
350   }
351   if (!lstrcmpi (condition, "errorlevel")) {
352     if (errorlevel >= atoi(WCMD_parameter (p, 1+negate, NULL))) test = 1;
353     return;
354   }
355   else if (!lstrcmpi (condition, "exist")) {
356     if ((h = CreateFile (WCMD_parameter (p, 1+negate, NULL), GENERIC_READ, 0, NULL,
357         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE) {
358       CloseHandle (h);
359       test = 1;
360     }
361   }
362   else if ((s = strstr (p, "=="))) {
363     s += 2;
364     if (!lstrcmpi (condition, WCMD_parameter (s, 0, NULL))) test = 1;
365   }
366   else {
367     WCMD_output ("Syntax error\n");
368     return;
369   }
370   if (test != negate) {
371     WCMD_parameter (p, 2+negate, &s);
372     command = strdup (s);
373     WCMD_process_command (command);
374     free (command);
375   }
376 }
377
378 /****************************************************************************
379  * WCMD_move
380  *
381  * Move a file, directory tree or wildcarded set of files.
382  * FIXME: Needs input and output files to be fully specified.
383  */
384
385 void WCMD_move () {
386
387 int status;
388
389   if ((strchr(param1,'*') != NULL) || (strchr(param1,'%') != NULL)) {
390     WCMD_output ("Wildcards not yet supported\n");
391     return;
392 }
393   status = MoveFile (param1, param2);
394   if (!status) WCMD_print_error ();
395 }
396
397 /****************************************************************************
398  * WCMD_pause
399  *
400  * Wait for keyboard input.
401  */
402
403 void WCMD_pause () {
404
405 DWORD count;
406 char string[32];
407
408   WCMD_output (anykey);
409   ReadFile (GetStdHandle(STD_INPUT_HANDLE), string, sizeof(string), &count, NULL);
410 }
411
412 /****************************************************************************
413  * WCMD_remove_dir
414  *
415  * Delete a directory.
416  */
417
418 void WCMD_remove_dir () {
419
420   if (!RemoveDirectory (param1)) WCMD_print_error ();
421 }
422
423 /****************************************************************************
424  * WCMD_rename
425  *
426  * Rename a file.
427  * FIXME: Needs input and output files to be fully specified.
428  */
429
430 void WCMD_rename () {
431
432 int status;
433
434   if ((strchr(param1,'*') != NULL) || (strchr(param1,'%') != NULL)) {
435     WCMD_output ("Wildcards not yet supported\n");
436     return;
437   }
438   status = MoveFile (param1, param2);
439   if (!status) WCMD_print_error ();
440 }
441
442 /*****************************************************************************
443  * WCMD_setshow_attrib
444  *
445  * Display and optionally sets DOS attributes on a file or directory
446  *
447  * FIXME: Wine currently uses the Unix stat() function to get file attributes.
448  * As a result only the Readonly flag is correctly reported, the Archive bit
449  * is always set and the rest are not implemented. We do the Right Thing anyway.
450  *
451  * FIXME: No SET functionality.
452  *
453  */
454
455 void WCMD_setshow_attrib () {
456
457 DWORD count;
458 HANDLE hff;
459 WIN32_FIND_DATA fd;
460 char flags[9] = {"        "};
461
462   if (param1[0] == '-') {
463     WCMD_output (nyi);
464     return;
465   }
466
467   if (lstrlen(param1) == 0) {
468     GetCurrentDirectory (sizeof(param1), param1);
469     strcat (param1, "\\*");
470   }
471
472   hff = FindFirstFile (param1, &fd);
473   if (hff == INVALID_HANDLE_VALUE) {
474     WCMD_output ("File Not Found\n");
475   }
476   else {
477     do {
478       if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
479         if (fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
480           flags[0] = 'H';
481         }
482         if (fd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) {
483           flags[1] = 'S';
484         }
485         if (fd.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) {
486           flags[2] = 'A';
487         }
488         if (fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
489           flags[3] = 'R';
490         }
491         if (fd.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) {
492           flags[4] = 'T';
493         }
494         if (fd.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) {
495           flags[5] = 'C';
496         }
497         WCMD_output ("%s   %s\n", flags, fd.cFileName);
498         for (count=0; count < 8; count++) flags[count] = ' ';
499       }
500     } while (FindNextFile(hff, &fd) != 0);
501   }
502   FindClose (hff);
503 }
504
505 /*****************************************************************************
506  * WCMD_setshow_default
507  *
508  *      Set/Show the current default directory
509  */
510
511 void WCMD_setshow_default () {
512
513 BOOL status;
514 char string[1024];
515
516   if (strlen(param1) == 0) {
517     GetCurrentDirectory (sizeof(string), string);
518     strcat (string, "\n");
519     WCMD_output (string);
520   }
521   else {
522     status = SetCurrentDirectory (param1);
523     if (!status) {
524       WCMD_print_error ();
525       return;
526     }
527    }
528   return;
529 }
530
531 /****************************************************************************
532  * WCMD_setshow_date
533  *
534  * Set/Show the system date
535  * FIXME: Can't change date yet
536  */
537
538 void WCMD_setshow_date () {
539
540 char curdate[64], buffer[64];
541 DWORD count;
542
543   if (lstrlen(param1) == 0) {
544     if (GetDateFormat (LOCALE_USER_DEFAULT, 0, NULL, NULL,
545                 curdate, sizeof(curdate))) {
546       WCMD_output ("Current Date is %s\nEnter new date: ", curdate);
547       ReadFile (GetStdHandle(STD_INPUT_HANDLE), buffer, sizeof(buffer), &count, NULL);
548       if (count > 2) {
549         WCMD_output (nyi);
550       }
551     }
552     else WCMD_print_error ();
553   }
554   else {
555     WCMD_output (nyi);
556   }
557 }
558
559 /****************************************************************************
560  * WCMD_setshow_env
561  *
562  * Set/Show the environment variables
563  *
564  * FIXME: need to sort variables into order for display?
565  */
566
567 void WCMD_setshow_env (char *s) {
568
569 LPVOID env;
570 char *p;
571 int status;
572 char buffer[1048];                                                   
573
574   if (strlen(param1) == 0) {
575     env = GetEnvironmentStrings ();
576     p = (char *) env;
577     while (*p) {
578       WCMD_output ("%s\n", p);
579       p += lstrlen(p) + 1;
580     }
581   }
582   else {
583     p = strchr (s, '=');
584     if (p == NULL) {
585
586       /* FIXME: Emulate Win98 for now, ie "SET C" looks ONLY for an    
587          environment variable C, whereas on NT it shows ALL variables  
588          starting with C.                                              
589        */                                                            
590       status = GetEnvironmentVariable(s, buffer, sizeof(buffer));    
591       if (status) {                                                  
592         WCMD_output("%s=%s\n", s, buffer);                           
593       } else {                                                       
594         WCMD_output ("Environment variable %s not defined\n", s);    
595       }                                                              
596       return;
597     }
598     *p++ = '\0';
599
600     if (strlen(p) == 0) p = 0x00;                                    
601     status = SetEnvironmentVariable (s, p);   
602     if (!status) WCMD_print_error();
603   }
604   /* WCMD_output (newline);   @JED*/
605 }
606
607 /****************************************************************************
608  * WCMD_setshow_path
609  *
610  * Set/Show the path environment variable
611  */
612
613 void WCMD_setshow_path () {
614
615 char string[1024];
616 DWORD status;
617
618   if (strlen(param1) == 0) {
619     status = GetEnvironmentVariable ("PATH", string, sizeof(string));
620     if (status != 0) {
621       WCMD_output ("PATH=%s\n", string);
622     }
623     else {
624       WCMD_output ("PATH not found\n");
625     }
626   }
627   else {
628     status = SetEnvironmentVariable ("PATH", param1);
629     if (!status) WCMD_print_error();
630   }
631 }
632
633 /****************************************************************************
634  * WCMD_setshow_prompt
635  *
636  * Set or show the command prompt.
637  */
638
639 void WCMD_setshow_prompt () {
640
641 char *s;
642
643   if (strlen(param1) == 0) {
644     SetEnvironmentVariable ("PROMPT", NULL);
645   }
646   else {
647     s = param1;
648     while ((*s == '=') || (*s == ' ')) s++;
649     if (strlen(s) == 0) {
650       SetEnvironmentVariable ("PROMPT", NULL);
651     }
652     else SetEnvironmentVariable ("PROMPT", s);
653   }
654 }
655
656 /****************************************************************************
657  * WCMD_setshow_time
658  *
659  * Set/Show the system time
660  * FIXME: Can't change time yet
661  */
662
663 void WCMD_setshow_time () {
664
665 char curtime[64], buffer[64];
666 DWORD count;
667 SYSTEMTIME st;
668
669   if (strlen(param1) == 0) {
670     GetLocalTime(&st);
671     if (GetTimeFormat (LOCALE_USER_DEFAULT, 0, &st, NULL,
672                 curtime, sizeof(curtime))) {
673       WCMD_output ("Current Time is %s\nEnter new time: ", curtime);
674       ReadFile (GetStdHandle(STD_INPUT_HANDLE), buffer, sizeof(buffer), &count, NULL);
675       if (count > 2) {
676         WCMD_output (nyi);
677       }
678     }
679     else WCMD_print_error ();
680   }
681   else {
682     WCMD_output (nyi);
683   }
684 }
685
686 /****************************************************************************
687  * WCMD_shift
688  *
689  * Shift batch parameters.
690  */
691
692 void WCMD_shift () {
693
694   if (context != NULL) context -> shift_count++;
695
696 }
697
698 /****************************************************************************
699  * WCMD_type
700  *
701  * Copy a file to standard output.
702  */
703
704 void WCMD_type () {
705
706 HANDLE h;
707 char buffer[512];
708 DWORD count;
709
710   h = CreateFile (param1, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
711                 FILE_ATTRIBUTE_NORMAL, NULL);
712   if (h == INVALID_HANDLE_VALUE) {
713     WCMD_print_error ();
714     return;
715   }
716   while (ReadFile (h, buffer, sizeof(buffer), &count, NULL)) {
717     if (count == 0) break;      /* ReadFile reports success on EOF! */
718     WriteFile (GetStdHandle(STD_OUTPUT_HANDLE), buffer, count, &count, NULL);
719   }
720   CloseHandle (h);
721 }
722
723 /****************************************************************************
724  * WCMD_verify
725  *
726  * Display verify flag.
727  * FIXME: We don't actually do anything with the verify flag other than toggle
728  * it...
729  */
730
731 void WCMD_verify (char *command) {
732
733 static char *von = "Verify is ON\n", *voff = "Verify is OFF\n";
734 int count;
735
736   count = strlen(command);
737   if (count == 0) {
738     if (verify_mode) WCMD_output (von);
739     else WCMD_output (voff);
740     return;
741   }
742   if (lstrcmpi(command, "ON") == 0) {
743     verify_mode = 1;
744     return;
745   }
746   else if (lstrcmpi(command, "OFF") == 0) {
747     verify_mode = 0;
748     return;
749   }
750   else WCMD_output ("Verify must be ON or OFF\n");
751 }
752
753 /****************************************************************************
754  * WCMD_version
755  *
756  * Display version info.
757  */
758
759 void WCMD_version () {
760
761   WCMD_output (version_string);
762
763 }
764
765 /****************************************************************************
766  * WCMD_volume
767  *
768  * Display volume info and/or set volume label. Returns 0 if error.
769  */
770
771 int WCMD_volume (int mode, char *path) {
772
773 DWORD count, serial;
774 char string[MAX_PATH], label[MAX_PATH], curdir[MAX_PATH];
775 BOOL status;
776 static char syntax[] = "Syntax Error\n\n";
777
778   if (lstrlen(path) == 0) {
779     status = GetCurrentDirectory (sizeof(curdir), curdir);
780     if (!status) {
781       WCMD_print_error ();
782       return 0;
783     }
784     status = GetVolumeInformation (NULL, label, sizeof(label), &serial, NULL,
785         NULL, NULL, 0);
786   }
787   else {
788     if ((path[1] != ':') || (lstrlen(path) != 2)) {
789       WriteFile (GetStdHandle(STD_OUTPUT_HANDLE), syntax, strlen(syntax), &count, NULL);
790       return 0;
791     }
792     wsprintf (curdir, "%s\\", path);
793     status = GetVolumeInformation (curdir, label, sizeof(label), &serial, NULL,
794         NULL, NULL, 0);
795   }
796   if (!status) {
797     WCMD_print_error ();
798     return 0;
799   }
800   WCMD_output ("Volume in drive %c is %s\nVolume Serial Number is %04x-%04x\n\n",
801         curdir[0], label, HIWORD(serial), LOWORD(serial));
802   if (mode) {
803     WCMD_output ("Volume label (11 characters, ENTER for none)?");
804     ReadFile (GetStdHandle(STD_INPUT_HANDLE), string, sizeof(string), &count, NULL);
805     if (count > 1) {
806       string[count-1] = '\0';           /* ReadFile output is not null-terminated! */
807       if (string[count-2] == '\r') string[count-2] = '\0'; /* Under Windoze we get CRLF! */
808     }
809     if (lstrlen(path) != 0) {
810       if (!SetVolumeLabel (curdir, string)) WCMD_print_error ();
811     }
812     else {
813       if (!SetVolumeLabel (NULL, string)) WCMD_print_error ();
814     }
815   }
816   return 1;
817 }