blob: cea4d33f2fa126bc5acc0579ffc0c4a9689b723c [file] [log] [blame]
Dave Pickles74f440e1999-06-06 15:24:04 +00001/*
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:
Dave Pickles5f8f4f71999-06-26 10:24:08 +000013 * - No support for redirection, pipes, shell parameters
Dave Pickles74f440e1999-06-06 15:24:04 +000014 * - 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
21extern HANDLE STDin, STDout;
22extern HINSTANCE hinst;
23extern char *inbuilt[];
24extern char nyi[];
25extern char newline[];
26extern char version_string[];
27extern char anykey[];
Dave Pickles5f8f4f71999-06-26 10:24:08 +000028extern int echo_mode, verify_mode;
Dave Pickles74f440e1999-06-06 15:24:04 +000029extern char quals[MAX_PATH], param1[MAX_PATH], param2[MAX_PATH];
Dave Pickles5f8f4f71999-06-26 10:24:08 +000030extern BATCH_CONTEXT *context;
Dave Pickles74f440e1999-06-06 15:24:04 +000031
32
Dave Pickles74f440e1999-06-06 15:24:04 +000033
34/****************************************************************************
35 * WCMD_clear_screen
36 *
37 * Clear the terminal screen.
38 */
39
40void WCMD_clear_screen () {
41
42 WCMD_output (nyi);
43
44}
45
46/****************************************************************************
47 * WCMD_change_tty
48 *
49 * Change the default i/o device (ie redirect STDin/STDout).
50 */
51
52void WCMD_change_tty () {
53
54 WCMD_output (nyi);
55
56}
57
58/****************************************************************************
59 * WCMD_copy
60 *
61 * Copy a file or wildcarded set.
62 * FIXME: No wildcard support
63 * FIXME: Needs output file to be fully specified (can't just enter directory)
64 */
65
66void WCMD_copy () {
67
68DWORD count;
69WIN32_FIND_DATA fd;
70HANDLE hff;
71BOOL force, status;
72static char *overwrite = "Overwrite file (Y/N)?";
73char string[8], outpath[MAX_PATH];
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 force = (strstr (quals, "/Y") != NULL);
81 if (!force) {
82 hff = FindFirstFile (outpath, &fd);
83 if (hff != INVALID_HANDLE_VALUE) {
84 FindClose (hff);
85 WCMD_output (overwrite);
86 ReadFile (STDin, string, sizeof(string), &count, NULL);
87 if (toupper(string[0]) == 'Y') force = TRUE;
88 }
89 else force = TRUE;
90 }
91 if (force) {
92 status = CopyFile (param1, outpath, FALSE);
93 if (!status) WCMD_print_error ();
94 }
95}
96
97/****************************************************************************
98 * WCMD_create_dir
99 *
100 * Create a directory.
101 */
102
103void WCMD_create_dir () {
104
105 if (!CreateDirectory (param1, NULL)) WCMD_print_error ();
106}
107
108/****************************************************************************
109 * WCMD_delete
110 *
111 * Delete a file or wildcarded set.
112 *
113 */
114
115void WCMD_delete (int recurse) {
116
117WIN32_FIND_DATA fd;
118HANDLE hff;
119char fpath[MAX_PATH];
120char *p;
121
122 hff = FindFirstFile (param1, &fd);
123 if (hff == INVALID_HANDLE_VALUE) {
124 WCMD_output ("File Not Found\n");
125 return;
126 }
127 if ((strchr(param1,'*') == NULL) && (strchr(param1,'?') == NULL)
128 && (!recurse) && (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
129 strcat (param1, "\\*");
130 WCMD_delete (1);
131 return;
132 }
133 if ((strchr(param1,'*') != NULL) || (strchr(param1,'?') != NULL)) {
134 strcpy (fpath, param1);
135 do {
136 p = strrchr (fpath, '\\');
137 if (p != NULL) {
138 *++p = '\0';
139 strcat (fpath, fd.cFileName);
140 }
141 else strcpy (fpath, fd.cFileName);
142 if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
143 if (!DeleteFile (fpath)) WCMD_print_error ();
144 }
145 } while (FindNextFile(hff, &fd) != 0);
146 FindClose (hff);
147 }
148 else {
149 if (!DeleteFile (param1)) WCMD_print_error ();
150 }
151}
152
153/****************************************************************************
154 * WCMD_echo
155 *
156 * Echo input to the screen (or not). We don't try to emulate the bugs
157 * in DOS (try typing "ECHO ON AGAIN" for an example).
158 */
159
160void WCMD_echo (char *command) {
161
162static char *eon = "Echo is ON\n", *eoff = "Echo is OFF\n";
163int count;
164
165 count = strlen(command);
166 if (count == 0) {
167 if (echo_mode) WCMD_output (eon);
168 else WCMD_output (eoff);
169 return;
170 }
171 if ((count == 1) && (command[0] == '.')) {
172 WCMD_output (newline);
173 return;
174 }
175 if (lstrcmpi(command, "ON") == 0) {
176 echo_mode = 1;
177 return;
178 }
179 if (lstrcmpi(command, "OFF") == 0) {
180 echo_mode = 0;
181 return;
182 }
183 WCMD_output (command);
184 WCMD_output (newline);
185
186}
187
188/****************************************************************************
189 * WCMD_for
190 *
191 * Batch file loop processing.
192 */
193
Dave Pickles5f8f4f71999-06-26 10:24:08 +0000194void WCMD_for (char *p) {
Dave Pickles74f440e1999-06-06 15:24:04 +0000195
Dave Pickles5f8f4f71999-06-26 10:24:08 +0000196 if (lstrcmpi (WCMD_parameter (p, 1), "in") || lstrcmpi (WCMD_parameter (p, 3), "do")) {
197 WCMD_output ("Syntax error\n");
198 return;
199 }
Dave Pickles74f440e1999-06-06 15:24:04 +0000200 WCMD_output (nyi);
201
202}
203
204/**************************************************************************
205 * WCMD_give_help
206 *
207 * Simple on-line help. Help text is stored in the resource file.
208 */
209
210void WCMD_give_help (char *command) {
211
212int i;
213char buffer[2048];
214
215 command = WCMD_strtrim_leading_spaces(command);
216 if (lstrlen(command) == 0) {
217 LoadString (hinst, 1000, buffer, sizeof(buffer));
218 WCMD_output (buffer);
219 }
220 else {
221 for (i=0; i<=WCMD_EXIT; i++) {
222 if (CompareString (LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
223 param1, -1, inbuilt[i], -1) == 2) {
224 LoadString (hinst, i, buffer, sizeof(buffer));
225 WCMD_output (buffer);
226 return;
227 }
228 }
229 WCMD_output ("No help available for %s\n", param1);
230 }
231 return;
232}
233
234/****************************************************************************
Dave Pickles5f8f4f71999-06-26 10:24:08 +0000235 * WCMD_go_to
236 *
237 * Batch file jump instruction. Not the most efficient algorithm ;-)
238 * Prints error message if the specified label cannot be found - the file pointer is
239 * then at EOF, effectively stopping the batch file.
240 * FIXME: DOS is supposed to allow labels with spaces - we don't.
241 */
242
243void WCMD_goto () {
244
245char string[MAX_PATH];
246
247 if (context != NULL) {
248 SetFilePointer (context -> h, 0, NULL, FILE_BEGIN);
249 while (WCMD_fgets (string, sizeof(string), context -> h)) {
250 if ((string[0] == ':') && (strcmp (&string[1], param1) == 0)) return;
251 }
252 WCMD_output ("Target to GOTO not found\n");
253 }
254 return;
255}
256
257
258/****************************************************************************
Dave Pickles74f440e1999-06-06 15:24:04 +0000259 * WCMD_if
260 *
261 * Batch file conditional.
262 */
263
264void WCMD_if () {
265
266 WCMD_output (nyi);
267
268}
269
270/****************************************************************************
271 * WCMD_move
272 *
273 * Move a file, directory tree or wildcarded set of files.
274 */
275
276void WCMD_move () {
277
278 WCMD_output (nyi);
279}
280
281/****************************************************************************
282 * WCMD_pause
283 *
284 * Wait for keyboard input.
285 */
286
287void WCMD_pause () {
288
289DWORD count;
290char string[32];
291
292 WCMD_output (anykey);
293 ReadFile (STDin, string, sizeof(string), &count, NULL);
294}
295
296/****************************************************************************
297 * WCMD_remove_dir
298 *
299 * Delete a directory.
300 */
301
302void WCMD_remove_dir () {
303
304 if (!RemoveDirectory (param1)) WCMD_print_error ();
305}
306
307/****************************************************************************
308 * WCMD_rename
309 *
310 * Rename a file.
311 * FIXME: Needs input and output files to be fully specified.
312 */
313
314void WCMD_rename () {
315
316int status;
317static char *dirmsg = "Input file is a directory. Use the MOVE command\n\n";
318
319 if ((strchr(param1,'*') != NULL) || (strchr(param1,'%') != NULL)) {
320 WCMD_output ("Wildcards not yet supported\n");
321 return;
322 }
323 status = GetFileAttributes (param1);
324 if ((status != -1) && (status & FILE_ATTRIBUTE_DIRECTORY)) {
325 WCMD_output (dirmsg);
326 return;
327 }
328 status = MoveFile (param1, param2);
329 if (!status) WCMD_print_error ();
330}
331
332/*****************************************************************************
333 * WCMD_setshow_attrib
334 *
335 * Display and optionally sets DOS attributes on a file or directory
336 *
337 * FIXME: Wine currently uses the Unix stat() function to get file attributes.
338 * As a result only the Readonly flag is correctly reported, the Archive bit
339 * is always set and the rest are not implemented. We do the Right Thing anyway.
340 *
341 */
342
343void WCMD_setshow_attrib () {
344
345DWORD count;
346HANDLE hff;
347WIN32_FIND_DATA fd;
348char flags[9] = {" "};
349
350 if (lstrlen(param1) == 0) {
351 GetCurrentDirectory (sizeof(param1), param1);
352 strcat (param1, "\\*");
353 }
354
355 hff = FindFirstFile (param1, &fd);
356 if (hff == INVALID_HANDLE_VALUE) {
357 WCMD_output ("File Not Found\n");
358 }
359 else {
360 do {
361 if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
362 if (fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
363 flags[0] = 'H';
364 }
365 if (fd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) {
366 flags[1] = 'S';
367 }
368 if (fd.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) {
369 flags[2] = 'A';
370 }
371 if (fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
372 flags[3] = 'R';
373 }
374 if (fd.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) {
375 flags[4] = 'T';
376 }
377 if (fd.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) {
378 flags[5] = 'C';
379 }
380 WCMD_output ("%s %s\n", flags, fd.cFileName);
381 for (count=0; count < 8; count++) flags[count] = ' ';
382 }
383 } while (FindNextFile(hff, &fd) != 0);
384 }
385 FindClose (hff);
386}
387
388/*****************************************************************************
389 * WCMD_setshow_default
390 *
391 * Set/Show the current default directory
392 */
393
394void WCMD_setshow_default () {
395
396BOOL status;
397char string[1024];
398
399 if (strlen(param1) == 0) {
400 GetCurrentDirectory (sizeof(string), string);
401 strcat (string, "\n");
402 WCMD_output (string);
403 }
404 else {
405 status = SetCurrentDirectory (param1);
406 if (!status) {
407 WCMD_print_error ();
408 return;
409 }
410 }
411 return;
412}
413
414/****************************************************************************
415 * WCMD_setshow_date
416 *
417 * Set/Show the system date
418 * FIXME: Can't change date yet
419 */
420
421void WCMD_setshow_date () {
422
423char curdate[64], buffer[64];
424DWORD count;
425
426 if (lstrlen(param1) == 0) {
427 if (GetDateFormat (LOCALE_USER_DEFAULT, 0, NULL, NULL,
428 curdate, sizeof(curdate))) {
429 WCMD_output ("Current Date is %s\nEnter new date: ", curdate);
430 ReadFile (STDin, buffer, sizeof(buffer), &count, NULL);
431 if (count > 2) {
432 WCMD_output (nyi);
433 }
434 }
435 else WCMD_print_error ();
436 }
437 else {
438 WCMD_output (nyi);
439 }
440}
441
442/****************************************************************************
443 * WCMD_setshow_env
444 *
445 * Set/Show the environment variables
446 *
447 * FIXME: need to sort variables into order for display?
448 */
449
450void WCMD_setshow_env (char *s) {
451
452LPVOID env;
453char *p;
454int status;
455
456 if (strlen(param1) == 0) {
457 env = GetEnvironmentStrings ();
458 p = (char *) env;
459 while (*p) {
460 WCMD_output ("%s\n", p);
461 p += lstrlen(p) + 1;
462 }
463 }
464 else {
465 p = strchr (s, '=');
466 if (p == NULL) {
467 WCMD_output ("Command Syntax: SET variable=value\n");
468 return;
469 }
470 *p++ = '\0';
471 status = SetEnvironmentVariable (s, p);
472 if (!status) WCMD_print_error();
473 }
474 WCMD_output (newline);
475}
476
477/****************************************************************************
478 * WCMD_setshow_path
479 *
480 * Set/Show the path environment variable
481 */
482
483void WCMD_setshow_path () {
484
485char string[1024];
486DWORD status;
487
488 if (strlen(param1) == 0) {
489 status = GetEnvironmentVariable ("PATH", string, sizeof(string));
490 if (status != 0) {
491 WCMD_output ("PATH=%s\n", string);
492 }
493 else {
494 WCMD_output ("PATH not found\n");
495 }
496 }
497 else {
498 status = SetEnvironmentVariable ("PATH", param1);
499 if (!status) WCMD_print_error();
500 }
501}
502
503/****************************************************************************
504 * WCMD_setshow_prompt
505 *
506 * Set or show the command prompt.
507 */
508
509void WCMD_setshow_prompt () {
510
511char *s;
512
513 if (strlen(param1) == 0) {
514 SetEnvironmentVariable ("PROMPT", NULL);
515 }
516 else {
517 s = param1;
518 while ((*s == '=') || (*s == ' ')) s++;
519 if (strlen(s) == 0) {
520 SetEnvironmentVariable ("PROMPT", NULL);
521 }
522 else SetEnvironmentVariable ("PROMPT", s);
523 }
524}
525
526/****************************************************************************
527 * WCMD_setshow_time
528 *
529 * Set/Show the system time
530 * FIXME: Can't change time yet
531 */
532
533void WCMD_setshow_time () {
534
535char curtime[64], buffer[64];
536DWORD count;
537
538 if (strlen(param1) == 0) {
539 if (GetTimeFormat (LOCALE_USER_DEFAULT, 0, NULL, NULL,
540 curtime, sizeof(curtime))) {
541 WCMD_output ("Current Time is %s\nEnter new time: ", curtime);
542 ReadFile (STDin, buffer, sizeof(buffer), &count, NULL);
543 if (count > 2) {
544 WCMD_output (nyi);
545 }
546 }
547 else WCMD_print_error ();
548 }
549 else {
550 WCMD_output (nyi);
551 }
552}
553
554/****************************************************************************
555 * WCMD_shift
556 *
557 * Shift batch parameters.
558 */
559
560void WCMD_shift () {
561
Dave Pickles5f8f4f71999-06-26 10:24:08 +0000562 if (context != NULL) context -> shift_count++;
Dave Pickles74f440e1999-06-06 15:24:04 +0000563
564}
565
566/****************************************************************************
567 * WCMD_type
568 *
569 * Copy a file to standard output.
570 */
571
572void WCMD_type () {
573
574HANDLE h;
575char buffer[512];
576DWORD count;
577
578 h = CreateFile (param1, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
579 FILE_ATTRIBUTE_NORMAL, 0);
580 if (h == INVALID_HANDLE_VALUE) {
581 WCMD_print_error ();
582 return;
583 }
584 while (ReadFile (h, buffer, sizeof(buffer), &count, NULL)) {
585 if (count == 0) break; /* ReadFile reports success on EOF! */
586 WriteFile (STDout, buffer, count, &count, NULL);
587 }
588 CloseHandle (h);
589}
590
591/****************************************************************************
592 * WCMD_verify
593 *
594 * Display verify flag.
Dave Pickles5f8f4f71999-06-26 10:24:08 +0000595 * FIXME: We don't actually do anything with the verify flag other than toggle
596 * it...
Dave Pickles74f440e1999-06-06 15:24:04 +0000597 */
598
Dave Pickles5f8f4f71999-06-26 10:24:08 +0000599void WCMD_verify (char *command) {
Dave Pickles74f440e1999-06-06 15:24:04 +0000600
Dave Pickles5f8f4f71999-06-26 10:24:08 +0000601static char *von = "Verify is ON\n", *voff = "Verify is OFF\n";
602int count;
Dave Pickles74f440e1999-06-06 15:24:04 +0000603
Dave Pickles5f8f4f71999-06-26 10:24:08 +0000604 count = strlen(command);
605 if (count == 0) {
606 if (verify_mode) WCMD_output (von);
607 else WCMD_output (voff);
608 return;
609 }
610 if (lstrcmpi(command, "ON") == 0) {
611 verify_mode = 1;
612 return;
613 }
614 else if (lstrcmpi(command, "OFF") == 0) {
615 verify_mode = 0;
616 return;
617 }
618 else WCMD_output ("Verify must be ON or OFF\n");
Dave Pickles74f440e1999-06-06 15:24:04 +0000619}
620
621/****************************************************************************
622 * WCMD_version
623 *
624 * Display version info.
625 */
626
627void WCMD_version () {
628
629 WCMD_output (version_string);
630
631}
632
633/****************************************************************************
634 * WCMD_volume
635 *
636 * Display volume info and/or set volume label. Returns 0 if error.
637 */
638
639int WCMD_volume (int mode, char *path) {
640
641DWORD count, serial;
642char string[MAX_PATH], label[MAX_PATH], curdir[MAX_PATH];
643BOOL status;
644static char syntax[] = "Syntax Error\n\n";
645
646 if (lstrlen(path) == 0) {
647 status = GetCurrentDirectory (sizeof(curdir), curdir);
648 if (!status) {
649 WCMD_print_error ();
650 return 0;
651 }
652 status = GetVolumeInformation (NULL, label, sizeof(label), &serial, NULL,
653 NULL, NULL, 0);
654 }
655 else {
656 if ((path[1] != ':') || (lstrlen(path) != 2)) {
657 WriteFile (STDout, syntax, strlen(syntax), &count, NULL);
658 return 0;
659 }
660 wsprintf (curdir, "%s\\", path);
661 status = GetVolumeInformation (curdir, label, sizeof(label), &serial, NULL,
662 NULL, NULL, 0);
663 }
664 if (!status) {
665 WCMD_print_error ();
666 return 0;
667 }
668 WCMD_output ("Volume in drive %c is %s\nVolume Serial Number is %04x-%04x\n\n",
669 curdir[0], label, HIWORD(serial), LOWORD(serial));
670 if (mode) {
671 WCMD_output ("Volume label (11 characters, ENTER for none)?");
672 ReadFile (STDin, string, sizeof(string), &count, NULL);
673 if (lstrlen(path) != 0) {
674 if (!SetVolumeLabel (curdir, string)) WCMD_print_error ();
675 }
676 else {
677 if (!SetVolumeLabel (NULL, string)) WCMD_print_error ();
678 }
679 }
680 return 1;
681}