Restructure DirectSound. Remove dsound thread, use MM timers
[wine/testsucceed.git] / programs / wcmd / wcmdmain.c
blob23f4aa94f4c6ffcf8d1ec1f55dc62700bbabdc30
1 /*
2 * WCMD - Wine-compatible command line interface.
4 * (C) 1999 D A Pickles
5 */
7 /*
8 * FIXME:
9 * - No support for pipes
10 * - 32-bit limit on file sizes in DIR command
11 * - Cannot handle parameters in quotes
12 * - Lots of functionality missing from builtins
15 #include "wcmd.h"
17 char *inbuilt[] = {"ATTRIB", "CALL", "CD", "CHDIR", "CLS", "COPY", "CTTY",
18 "DATE", "DEL", "DIR", "ECHO", "ERASE", "FOR", "GOTO",
19 "HELP", "IF", "LABEL", "MD", "MKDIR", "MOVE", "PATH", "PAUSE",
20 "PROMPT", "REM", "REN", "RENAME", "RD", "RMDIR", "SET", "SHIFT",
21 "TIME", "TYPE", "VERIFY", "VER", "VOL", "EXIT"};
23 int echo_mode = 1, verify_mode = 0;
24 char nyi[] = "Not Yet Implemented\n\n";
25 char newline[] = "\n";
26 char version_string[] = "WCMD Version 0.12\n\n";
27 char anykey[] = "Press any key to continue: ";
28 char quals[MAX_PATH], param1[MAX_PATH], param2[MAX_PATH];
29 BATCH_CONTEXT *context = NULL;
31 /*****************************************************************************
32 * Main entry point. This is a console application so we have a main() not a
33 * winmain().
37 int wine_main (int argc, char *argv[]) {
39 char string[1024], args[MAX_PATH], param[MAX_PATH];
40 int status, i;
41 DWORD count;
42 HANDLE h;
44 args[0] = param[0] = '\0';
45 if (argc > 1) {
46 for (i=1; i<argc; i++) {
47 if (argv[i][0] == '/') {
48 strcat (args, argv[i]);
50 else {
51 strcat (param, argv[i]);
52 strcat (param, " ");
58 * Allocate a console and set it up.
61 status = FreeConsole ();
62 if (!status) WCMD_print_error();
63 status = AllocConsole();
64 if (!status) WCMD_print_error();
65 SetConsoleMode (GetStdHandle(STD_INPUT_HANDLE), ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT |
66 ENABLE_PROCESSED_INPUT);
69 * Execute any command-line options.
72 if (strstr(args, "/q") != NULL) {
73 WCMD_echo ("OFF");
76 if (strstr(args, "/c") != NULL) {
77 WCMD_process_command (param);
78 return 0;
81 if (strstr(args, "/k") != NULL) {
82 WCMD_process_command (param);
86 * If there is an AUTOEXEC.BAT file, try to execute it.
89 GetFullPathName ("\\autoexec.bat", sizeof(string), string, NULL);
90 h = CreateFile (string, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
91 if (h != INVALID_HANDLE_VALUE) {
92 CloseHandle (h);
93 #if 0
94 WCMD_batch (string, " ");
95 #endif
99 * Loop forever getting commands and executing them.
102 WCMD_version ();
103 while (TRUE) {
104 WCMD_show_prompt ();
105 ReadFile (GetStdHandle(STD_INPUT_HANDLE), string, sizeof(string), &count, NULL);
106 if (count > 1) {
107 string[count-1] = '\0'; /* ReadFile output is not null-terminated! */
108 if (string[count-2] == '\r') string[count-2] = '\0'; /* Under Windoze we get CRLF! */
109 if (lstrlen (string) != 0) {
110 WCMD_process_command (string);
117 /*****************************************************************************
118 * Process one command. If the command is EXIT this routine does not return.
119 * We will recurse through here executing batch files.
123 void WCMD_process_command (char *command) {
125 char cmd[1024];
126 char *p;
127 int status, i;
128 DWORD count;
129 HANDLE old_stdin = 0, old_stdout = 0, h;
132 * Throw away constructs we don't support yet
135 if (strchr(command,'|') != NULL) {
136 WCMD_output ("Pipes not yet implemented\n");
137 return;
141 * Expand up environment variables.
144 status = ExpandEnvironmentStrings (command, cmd, sizeof(cmd));
145 if (!status) {
146 WCMD_print_error ();
147 return;
151 * Changing default drive has to be handled as a special case.
154 if ((cmd[1] == ':') && IsCharAlpha (cmd[0]) && (strlen(cmd) == 2)) {
155 status = SetCurrentDirectory (cmd);
156 if (!status) WCMD_print_error ();
157 return;
159 WCMD_output (newline);
162 * Redirect stdin and/or stdout if required.
165 if ((p = strchr(cmd,'<')) != NULL) {
166 h = CreateFile (WCMD_parameter (++p, 0, NULL), GENERIC_READ, 0, NULL, OPEN_EXISTING,
167 FILE_ATTRIBUTE_NORMAL, 0);
168 if (h == INVALID_HANDLE_VALUE) {
169 WCMD_print_error ();
170 return;
172 old_stdin = GetStdHandle (STD_INPUT_HANDLE);
173 SetStdHandle (STD_INPUT_HANDLE, h);
175 if ((p = strchr(cmd,'>')) != NULL) {
176 h = CreateFile (WCMD_parameter (++p, 0, NULL), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
177 FILE_ATTRIBUTE_NORMAL, 0);
178 if (h == INVALID_HANDLE_VALUE) {
179 WCMD_print_error ();
180 return;
182 old_stdout = GetStdHandle (STD_OUTPUT_HANDLE);
183 SetStdHandle (STD_OUTPUT_HANDLE, h);
184 *--p = '\0';
186 if ((p = strchr(cmd,'<')) != NULL) *p = '\0';
189 * Check if the command entered is internal. If it is, pass the rest of the
190 * line down to the command. If not try to run a program.
193 count = 0;
194 while (IsCharAlphaNumeric(cmd[count])) {
195 count++;
197 for (i=0; i<=WCMD_EXIT; i++) {
198 if (CompareString (LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
199 cmd, count, inbuilt[i], -1) == 2) break;
201 p = WCMD_strtrim_leading_spaces (&cmd[count]);
202 WCMD_parse (p, quals, param1, param2);
203 switch (i) {
205 case WCMD_ATTRIB:
206 WCMD_setshow_attrib ();
207 break;
208 case WCMD_CALL:
209 WCMD_batch (param1, p, 1);
210 break;
211 case WCMD_CD:
212 case WCMD_CHDIR:
213 WCMD_setshow_default ();
214 break;
215 case WCMD_CLS:
216 WCMD_clear_screen ();
217 break;
218 case WCMD_COPY:
219 WCMD_copy ();
220 break;
221 case WCMD_CTTY:
222 WCMD_change_tty ();
223 break;
224 case WCMD_DATE:
225 WCMD_setshow_date ();
226 break;
227 case WCMD_DEL:
228 case WCMD_ERASE:
229 WCMD_delete (0);
230 break;
231 case WCMD_DIR:
232 WCMD_directory ();
233 break;
234 case WCMD_ECHO:
235 WCMD_echo (p);
236 break;
237 case WCMD_FOR:
238 WCMD_for (p);
239 break;
240 case WCMD_GOTO:
241 WCMD_goto ();
242 break;
243 case WCMD_HELP:
244 WCMD_give_help (p);
245 break;
246 case WCMD_IF:
247 WCMD_if (p);
248 break;
249 case WCMD_LABEL:
250 WCMD_volume (1, p);
251 break;
252 case WCMD_MD:
253 case WCMD_MKDIR:
254 WCMD_create_dir ();
255 break;
256 case WCMD_MOVE:
257 WCMD_move ();
258 break;
259 case WCMD_PATH:
260 WCMD_setshow_path ();
261 break;
262 case WCMD_PAUSE:
263 WCMD_pause ();
264 break;
265 case WCMD_PROMPT:
266 WCMD_setshow_prompt ();
267 break;
268 case WCMD_REM:
269 break;
270 case WCMD_REN:
271 case WCMD_RENAME:
272 WCMD_rename ();
273 break;
274 case WCMD_RD:
275 case WCMD_RMDIR:
276 WCMD_remove_dir ();
277 break;
278 case WCMD_SET:
279 WCMD_setshow_env (p);
280 break;
281 case WCMD_SHIFT:
282 WCMD_shift ();
283 break;
284 case WCMD_TIME:
285 WCMD_setshow_time ();
286 break;
287 case WCMD_TYPE:
288 WCMD_type ();
289 break;
290 case WCMD_VER:
291 WCMD_version ();
292 break;
293 case WCMD_VERIFY:
294 WCMD_verify (p);
295 break;
296 case WCMD_VOL:
297 WCMD_volume (0, p);
298 break;
299 case WCMD_EXIT:
300 ExitProcess (0);
301 default:
302 WCMD_run_program (cmd);
304 if (old_stdin) {
305 CloseHandle (GetStdHandle (STD_INPUT_HANDLE));
306 SetStdHandle (STD_INPUT_HANDLE, old_stdin);
308 if (old_stdout) {
309 CloseHandle (GetStdHandle (STD_OUTPUT_HANDLE));
310 SetStdHandle (STD_OUTPUT_HANDLE, old_stdout);
314 /******************************************************************************
315 * WCMD_run_program
317 * Execute a command line as an external program. If no extension given then
318 * precedence is given to .BAT files. Must allow recursion.
320 * FIXME: Case sensitivity in suffixes!
323 void WCMD_run_program (char *command) {
325 STARTUPINFO st;
326 PROCESS_INFORMATION pe;
327 BOOL status;
328 HANDLE h;
329 char filetorun[MAX_PATH];
331 WCMD_parse (command, quals, param1, param2); /* Quick way to get the filename */
332 if (strpbrk (param1, "\\:") == NULL) { /* No explicit path given */
333 if ((strchr (param1, '.') == NULL) || (strstr (param1, ".bat") != NULL)) {
334 if (SearchPath (NULL, param1, ".bat", sizeof(filetorun), filetorun, NULL)) {
335 WCMD_batch (filetorun, command, 0);
336 return;
340 else { /* Explicit path given */
341 if (strstr (param1, ".bat") != NULL) {
342 WCMD_batch (param1, command, 0);
343 return;
345 if (strchr (param1, '.') == NULL) {
346 strcpy (filetorun, param1);
347 strcat (filetorun, ".bat");
348 h = CreateFile (filetorun, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
349 if (h != INVALID_HANDLE_VALUE) {
350 CloseHandle (h);
351 WCMD_batch (param1, command, 0);
352 return;
357 /* No batch file found, assume executable */
359 ZeroMemory (&st, sizeof(STARTUPINFO));
360 st.cb = sizeof(STARTUPINFO);
361 status = CreateProcess (NULL, command, NULL, NULL, FALSE,
362 0, NULL, NULL, &st, &pe);
363 if (!status) {
364 WCMD_print_error ();
368 /******************************************************************************
369 * WCMD_show_prompt
371 * Display the prompt on STDout
375 void WCMD_show_prompt () {
377 int status;
378 char out_string[MAX_PATH], curdir[MAX_PATH], prompt_string[MAX_PATH];
379 char *p, *q;
381 status = GetEnvironmentVariable ("PROMPT", prompt_string, sizeof(prompt_string));
382 if ((status == 0) || (status > sizeof(prompt_string))) {
383 lstrcpy (prompt_string, "$N$G");
385 p = prompt_string;
386 q = out_string;
387 *q = '\0';
388 while (*p != '\0') {
389 if (*p != '$') {
390 *q++ = *p++;
391 *q = '\0';
393 else {
394 p++;
395 switch (toupper(*p)) {
396 case '$':
397 *q++ = '$';
398 break;
399 case 'B':
400 *q++ = '|';
401 break;
402 case 'D':
403 GetDateFormat (LOCALE_USER_DEFAULT, DATE_SHORTDATE, NULL, NULL, q, MAX_PATH);
404 while (*q) q++;
405 break;
406 case 'E':
407 *q++ = '\E';
408 break;
409 case 'G':
410 *q++ = '>';
411 break;
412 case 'L':
413 *q++ = '<';
414 break;
415 case 'N':
416 status = GetCurrentDirectory (sizeof(curdir), curdir);
417 if (status) {
418 *q++ = curdir[0];
420 break;
421 case 'P':
422 status = GetCurrentDirectory (sizeof(curdir), curdir);
423 if (status) {
424 lstrcat (q, curdir);
425 while (*q) q++;
427 break;
428 case 'Q':
429 *q++ = '=';
430 break;
431 case 'T':
432 GetTimeFormat (LOCALE_USER_DEFAULT, 0, NULL, NULL, q, MAX_PATH);
433 while (*q) q++;
434 break;
435 case '_':
436 *q++ = '\n';
437 break;
439 p++;
440 *q = '\0';
443 WCMD_output (out_string);
446 /****************************************************************************
447 * WCMD_print_error
449 * Print the message for GetLastError - not much use yet as Wine doesn't have
450 * the messages available, so we show meaningful messages for the most likely.
453 void WCMD_print_error () {
454 LPVOID lpMsgBuf;
455 DWORD error_code;
457 error_code = GetLastError ();
458 switch (error_code) {
459 case ERROR_FILE_NOT_FOUND:
460 WCMD_output ("File Not Found\n");
461 break;
462 case ERROR_PATH_NOT_FOUND:
463 WCMD_output ("Path Not Found\n");
464 break;
465 default:
466 FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
467 NULL, error_code,
468 MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
469 (LPTSTR) &lpMsgBuf, 0, NULL);
470 WCMD_output (lpMsgBuf);
471 LocalFree ((HLOCAL)lpMsgBuf);
473 return;
476 /*******************************************************************
477 * WCMD_parse - parse a command into parameters and qualifiers.
479 * On exit, all qualifiers are concatenated into q, the first string
480 * not beginning with "/" is in p1 and the
481 * second in p2. Any subsequent non-qualifier strings are lost.
482 * Parameters in quotes are handled.
485 void WCMD_parse (char *s, char *q, char *p1, char *p2) {
487 int p = 0;
489 *q = *p1 = *p2 = '\0';
490 while (TRUE) {
491 switch (*s) {
492 case '/':
493 *q++ = *s++;
494 while ((*s != '\0') && (*s != ' ') && *s != '/') {
495 *q++ = toupper (*s++);
497 *q = '\0';
498 break;
499 case ' ':
500 s++;
501 break;
502 case '"':
503 s++;
504 while ((*s != '\0') && (*s != '"')) {
505 if (p == 0) *p1++ = *s++;
506 else if (p == 1) *p2++ = *s++;
507 else s++;
509 if (p == 0) *p1 = '\0';
510 if (p == 1) *p2 = '\0';
511 p++;
512 if (*s == '"') s++;
513 break;
514 case '\0':
515 return;
516 default:
517 while ((*s != '\0') && (*s != ' ') && (*s != '/')) {
518 if (p == 0) *p1++ = *s++;
519 else if (p == 1) *p2++ = *s++;
520 else s++;
522 if (p == 0) *p1 = '\0';
523 if (p == 1) *p2 = '\0';
524 p++;
529 /*******************************************************************
530 * WCMD_output - send output to current standard output device.
534 void WCMD_output (char *format, ...) {
536 va_list ap;
537 char string[1024];
538 DWORD count;
540 va_start(ap,format);
541 vsprintf (string, format, ap);
542 WriteFile (GetStdHandle(STD_OUTPUT_HANDLE), string, lstrlen(string), &count, NULL);
543 va_end(ap);
547 /* Remove leading spaces from a string. Return a pointer to the first
548 * non-space character. Does not modify the input string
551 char *WCMD_strtrim_leading_spaces (char *string) {
553 char *ptr;
555 ptr = string;
556 while (*ptr == ' ') ptr++;
557 return ptr;
560 /* Remove trailing spaces from a string. This routine modifies the input
561 * string by placing a null after the last non-space character
564 void WCMD_strtrim_trailing_spaces (char *string) {
566 char *ptr;
568 ptr = string + lstrlen (string) - 1;
569 while ((*ptr == ' ') && (ptr >= string)) {
570 *ptr = '\0';
571 ptr--;