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