0608f56ba233b7a9ef1ce16055ae5ce789fd5adf
[reactos.git] / reactos / subsystems / mvdm / ntvdm / dos / dem.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: subsystems/mvdm/ntvdm/dos/dem.c
5 * PURPOSE: DOS 32-bit Emulation Support Library -
6 * This library is used by the built-in NTVDM DOS32 and by
7 * the NT 16-bit DOS in Windows (via BOPs). It also exposes
8 * exported functions that can be used by VDDs.
9 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
10 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
11 */
12
13 #include "ntvdm.h"
14
15 #define NDEBUG
16 #include <debug.h>
17
18 /*
19 * EXPERIMENTAL!
20 * Activate this line if you want to have COMMAND.COM completely external.
21 */
22 // #define COMSPEC_FULLY_EXTERNAL
23
24 /* PRIVATE VARIABLES **********************************************************/
25
26 /* PRIVATE FUNCTIONS **********************************************************/
27
28 /* PUBLIC VARIABLES ***********************************************************/
29
30 /* PUBLIC FUNCTIONS ***********************************************************/
31
32
33 /******************************************************************************\
34 |** DOS DEM Kernel helpers **|
35 \******************************************************************************/
36
37
38 VOID BiosCharPrint(CHAR Character)
39 {
40 /* Save AX and BX */
41 USHORT AX = getAX();
42 USHORT BX = getBX();
43
44 /*
45 * Set the parameters:
46 * AL contains the character to print,
47 * BL contains the character attribute,
48 * BH contains the video page to use.
49 */
50 setAL(Character);
51 setBL(DEFAULT_ATTRIBUTE);
52 setBH(Bda->VideoPage);
53
54 /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */
55 setAH(0x0E);
56 Int32Call(&DosContext, BIOS_VIDEO_INTERRUPT);
57
58 /* Restore AX and BX */
59 setBX(BX);
60 setAX(AX);
61 }
62
63 VOID DosCharPrint(CHAR Character)
64 {
65 DosPrintCharacter(DOS_OUTPUT_HANDLE, Character);
66 }
67
68
69 static VOID DemLoadNTDOSKernel(VOID)
70 {
71 BOOLEAN Success = FALSE;
72 LPCSTR DosKernelFileName = "ntdos.sys";
73 HANDLE hDosKernel;
74 ULONG ulDosKernelSize = 0;
75
76 DPRINT1("You are loading Windows NT DOS!\n");
77
78 /* Open the DOS kernel file */
79 hDosKernel = FileOpen(DosKernelFileName, &ulDosKernelSize);
80 if (hDosKernel == NULL) goto Quit;
81
82 /*
83 * Attempt to load the DOS kernel into memory.
84 * The segment where to load the DOS kernel is defined
85 * by the DOS BIOS and is found in DI:0000 .
86 */
87 Success = FileLoadByHandle(hDosKernel,
88 REAL_TO_PHYS(TO_LINEAR(getDI(), 0x0000)),
89 ulDosKernelSize,
90 &ulDosKernelSize);
91
92 DPRINT1("Windows NT DOS file '%s' loading %s at %04X:%04X, size 0x%X (Error: %u).\n",
93 DosKernelFileName,
94 (Success ? "succeeded" : "failed"),
95 getDI(), 0x0000,
96 ulDosKernelSize,
97 GetLastError());
98
99 /* Close the DOS kernel file */
100 FileClose(hDosKernel);
101
102 Quit:
103 if (!Success)
104 {
105 /* We failed everything, stop the VDM */
106 BiosDisplayMessage("Windows NT DOS kernel file '%s' loading failed (Error: %u). The VDM will shut down.\n",
107 DosKernelFileName, GetLastError());
108 EmulatorTerminate();
109 return;
110 }
111 }
112
113 static VOID WINAPI DosSystemBop(LPWORD Stack)
114 {
115 /* Get the Function Number and skip it */
116 BYTE FuncNum = *(PBYTE)SEG_OFF_TO_PTR(getCS(), getIP());
117 setIP(getIP() + 1);
118
119 switch (FuncNum)
120 {
121 /* Load the DOS kernel */
122 case 0x11:
123 {
124 DemLoadNTDOSKernel();
125 break;
126 }
127
128 /* Call 32-bit Driver Strategy Routine */
129 case BOP_DRV_STRATEGY:
130 {
131 DeviceStrategyBop();
132 break;
133 }
134
135 /* Call 32-bit Driver Interrupt Routine */
136 case BOP_DRV_INTERRUPT:
137 {
138 DeviceInterruptBop();
139 break;
140 }
141
142 default:
143 {
144 DPRINT1("Unknown DOS System BOP Function: 0x%02X\n", FuncNum);
145 // setCF(1); // Disable, otherwise we enter an infinite loop
146 break;
147 }
148 }
149 }
150
151
152
153
154 /******************************************************************************\
155 |** DOS Command Process management **|
156 \******************************************************************************/
157
158
159 #ifndef STANDALONE
160 static ULONG SessionId = 0;
161
162 /*
163 * 16-bit Command Interpreter information for DOS reentry
164 */
165 typedef struct _COMSPEC_INFO
166 {
167 LIST_ENTRY Entry;
168 DWORD dwExitCode;
169 WORD ComSpecPsp;
170 BOOLEAN Terminated;
171 } COMSPEC_INFO, *PCOMSPEC_INFO;
172
173 static COMSPEC_INFO RootCmd;
174 static DWORD ReentrancyCount = 0;
175
176 // FIXME: Should we need list locking?
177 static LIST_ENTRY ComSpecInfoList = { &ComSpecInfoList, &ComSpecInfoList };
178
179 static PCOMSPEC_INFO
180 FindComSpecInfoByPsp(WORD Psp)
181 {
182 PLIST_ENTRY Pointer;
183 PCOMSPEC_INFO ComSpecInfo;
184
185 for (Pointer = ComSpecInfoList.Flink; Pointer != &ComSpecInfoList; Pointer = Pointer->Flink)
186 {
187 ComSpecInfo = CONTAINING_RECORD(Pointer, COMSPEC_INFO, Entry);
188 if (ComSpecInfo->ComSpecPsp == Psp) return ComSpecInfo;
189 }
190
191 return NULL;
192 }
193
194 static VOID
195 InsertComSpecInfo(PCOMSPEC_INFO ComSpecInfo)
196 {
197 InsertHeadList(&ComSpecInfoList, &ComSpecInfo->Entry);
198 }
199
200 static VOID
201 RemoveComSpecInfo(PCOMSPEC_INFO ComSpecInfo)
202 {
203 RemoveEntryList(&ComSpecInfo->Entry);
204 if (ComSpecInfo != &RootCmd)
205 RtlFreeHeap(RtlGetProcessHeap(), 0, ComSpecInfo);
206 }
207 #endif
208
209 static VOID DosProcessConsoleAttach(VOID)
210 {
211 /* Attach to the console */
212 ConsoleAttach();
213 VidBiosAttachToConsole();
214 }
215
216 static VOID DosProcessConsoleDetach(VOID)
217 {
218 /* Detach from the console */
219 VidBiosDetachFromConsole();
220 ConsoleDetach();
221 }
222
223 /*
224 * Data for the next DOS command to run
225 */
226 #ifndef STANDALONE
227 static VDM_COMMAND_INFO CommandInfo;
228 static BOOLEAN Repeat = FALSE;
229 static BOOLEAN Reentry = FALSE;
230 #endif
231 static BOOLEAN First = TRUE;
232 static CHAR CmdLine[MAX_PATH] = ""; // DOS_CMDLINE_LENGTH
233 static CHAR AppName[MAX_PATH] = "";
234 #ifndef STANDALONE
235 static CHAR PifFile[MAX_PATH] = "";
236 static CHAR CurDirectory[MAX_PATH] = "";
237 static CHAR Desktop[MAX_PATH] = "";
238 static CHAR Title[MAX_PATH] = "";
239 static ULONG EnvSize = 256;
240 static PVOID Env = NULL;
241 #endif
242
243 #pragma pack(push, 2)
244
245 /*
246 * This structure is compatible with Windows NT DOS
247 */
248 typedef struct _NEXT_CMD
249 {
250 USHORT EnvBlockSeg;
251 USHORT EnvBlockLen;
252 USHORT CurDrive;
253 USHORT NumDrives;
254 USHORT CmdLineSeg;
255 USHORT CmdLineOff;
256 USHORT Unknown0;
257 USHORT ExitCode;
258 USHORT Unknown1;
259 ULONG Unknown2;
260 USHORT CodePage;
261 USHORT Unknown3;
262 USHORT Unknown4;
263 USHORT AppNameSeg;
264 USHORT AppNameOff;
265 USHORT AppNameLen;
266 USHORT Flags;
267 } NEXT_CMD, *PNEXT_CMD;
268
269 #pragma pack(pop)
270
271 static VOID CmdStartProcess(VOID)
272 {
273 #ifndef STANDALONE
274 PCOMSPEC_INFO ComSpecInfo;
275 #endif
276 SIZE_T CmdLen;
277 PNEXT_CMD DataStruct = (PNEXT_CMD)SEG_OFF_TO_PTR(getDS(), getDX());
278
279 DPRINT1("CmdStartProcess -- DS:DX = %04X:%04X (DataStruct = 0x%p)\n",
280 getDS(), getDX(), DataStruct);
281
282 /* Pause the VM */
283 EmulatorPause();
284
285 #ifndef STANDALONE
286 /* Check whether we need to shell out now in case we were started by a 32-bit app */
287 ComSpecInfo = FindComSpecInfoByPsp(Sda->CurrentPsp);
288 if (ComSpecInfo && ComSpecInfo->Terminated)
289 {
290 RemoveComSpecInfo(ComSpecInfo);
291
292 DPRINT1("Exit DOS from start-app BOP\n");
293 setCF(1);
294 goto Quit;
295 }
296
297 /* Clear the structure */
298 RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
299
300 /* Initialize the structure members */
301 CommandInfo.TaskId = SessionId;
302 CommandInfo.VDMState = VDM_FLAG_DOS;
303 CommandInfo.CmdLine = CmdLine;
304 CommandInfo.CmdLen = sizeof(CmdLine);
305 CommandInfo.AppName = AppName;
306 CommandInfo.AppLen = sizeof(AppName);
307 CommandInfo.PifFile = PifFile;
308 CommandInfo.PifLen = sizeof(PifFile);
309 CommandInfo.CurDirectory = CurDirectory;
310 CommandInfo.CurDirectoryLen = sizeof(CurDirectory);
311 CommandInfo.Desktop = Desktop;
312 CommandInfo.DesktopLen = sizeof(Desktop);
313 CommandInfo.Title = Title;
314 CommandInfo.TitleLen = sizeof(Title);
315 CommandInfo.Env = Env;
316 CommandInfo.EnvLen = EnvSize;
317
318 if (First) CommandInfo.VDMState |= VDM_FLAG_FIRST_TASK;
319
320 Command:
321
322 if (Repeat) CommandInfo.VDMState |= VDM_FLAG_RETRY;
323 Repeat = FALSE;
324
325 /* Get the VDM command information */
326 DPRINT1("Calling GetNextVDMCommand in CmdStartProcess: wait for new VDM task...\n");
327 if (!GetNextVDMCommand(&CommandInfo))
328 {
329 DPRINT1("CmdStartProcess - GetNextVDMCommand failed, retrying... last error = %d\n", GetLastError());
330 if (CommandInfo.EnvLen > EnvSize)
331 {
332 /* Expand the environment size */
333 EnvSize = CommandInfo.EnvLen;
334 CommandInfo.Env = Env = RtlReAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, Env, EnvSize);
335
336 /* Repeat the request */
337 Repeat = TRUE;
338 goto Command;
339 }
340
341 /* Shouldn't happen */
342 DisplayMessage(L"An unrecoverable failure happened from start-app BOP; exiting DOS.");
343 setCF(1);
344 goto Quit;
345 }
346
347 // FIXME: What happens if some other 32-bit app is killed while we are waiting there??
348
349 DPRINT1("CmdStartProcess - GetNextVDMCommand succeeded, start app...\n");
350
351 #else
352
353 if (!First)
354 {
355 DPRINT1("Exit DOS from start-app BOP\n");
356 setCF(1);
357 goto Quit;
358 }
359
360 #endif
361
362 CmdLen = strlen(CmdLine);
363 DPRINT1("Starting '%s' ('%.*s')...\n",
364 AppName,
365 /* Display the command line without the terminating 0d 0a (and skip the terminating NULL) */
366 CmdLen >= 2 ? (CmdLine[CmdLen - 2] == '\r' ? CmdLen - 2
367 : CmdLen)
368 : CmdLen,
369 CmdLine);
370
371 /* Start the process */
372 // FIXME: Merge 'Env' with the master environment SEG_OFF_TO_PTR(SYSTEM_ENV_BLOCK, 0)
373 // FIXME: Environment
374 RtlCopyMemory(SEG_OFF_TO_PTR(DataStruct->AppNameSeg, DataStruct->AppNameOff), AppName, MAX_PATH);
375 *(PBYTE)(SEG_OFF_TO_PTR(DataStruct->CmdLineSeg, DataStruct->CmdLineOff)) = (BYTE)(strlen(CmdLine) - 2);
376 RtlCopyMemory(SEG_OFF_TO_PTR(DataStruct->CmdLineSeg, DataStruct->CmdLineOff + 1), CmdLine, DOS_CMDLINE_LENGTH);
377
378 #ifndef STANDALONE
379 /* Update console title if we run in a separate console */
380 if (SessionId != 0)
381 SetConsoleTitleA(AppName);
382 #endif
383
384 First = FALSE;
385 setCF(0);
386
387 DPRINT1("App started!\n");
388
389 Quit:
390 /* Resume the VM */
391 EmulatorResume();
392 }
393
394 static VOID CmdStartExternalCommand(VOID)
395 {
396 DWORD Result;
397
398 // TODO: improve: this code has strong similarities
399 // with the 'default' case of DosCreateProcess.
400
401 LPSTR Command = (LPSTR)SEG_OFF_TO_PTR(getDS(), getSI());
402 CHAR CmdLine[sizeof("cmd.exe /c ") + DOS_CMDLINE_LENGTH + 1] = "";
403 LPSTR CmdLinePtr;
404 ULONG CmdLineLen;
405
406 /* Spawn a user-defined 32-bit command preprocessor */
407
408 // FIXME: Use COMSPEC env var!!
409 CmdLinePtr = CmdLine;
410 strcpy(CmdLinePtr, "cmd.exe /c ");
411 CmdLinePtr += strlen(CmdLinePtr);
412
413 /* Build a Win32-compatible command-line */
414 CmdLineLen = min(strlen(Command), sizeof(CmdLine) - strlen(CmdLinePtr) - 1);
415 RtlCopyMemory(CmdLinePtr, Command, CmdLineLen);
416 CmdLinePtr[CmdLineLen] = '\0';
417
418 /* Remove any trailing return carriage character and NULL-terminate the command line */
419 while (*CmdLinePtr && *CmdLinePtr != '\r' && *CmdLinePtr != '\n') CmdLinePtr++;
420 *CmdLinePtr = '\0';
421
422 DPRINT1("CMD Run Command '%s' ('%s')\n", Command, CmdLine);
423
424 /*
425 * No need to prepare the stack for DosStartComSpec since we won't start it.
426 */
427 Result = DosStartProcess32(Command, CmdLine,
428 SEG_OFF_TO_PTR(getES(), 0) /*Environment*/,
429 MAKELONG(getIP(), getCS()) /*ReturnAddress*/,
430 FALSE);
431 if (Result != ERROR_SUCCESS)
432 {
433 DosDisplayMessage("Failed to start command '%s' ('%s'). Error: %u\n", Command, CmdLine, Result);
434 setCF(0);
435 setAL((UCHAR)Result);
436 }
437 else
438 {
439 DosDisplayMessage("Command '%s' ('%s') started successfully.\n", Command, CmdLine);
440 #ifndef STANDALONE
441 setCF(Repeat); // Set CF if we need to start a 16-bit process
442 #else
443 setCF(0);
444 #endif
445 }
446 }
447
448 static VOID CmdStartComSpec32(VOID)
449 {
450 DWORD Result;
451
452 // TODO: improve: this code has strong similarities with the
453 // 'default' case of DosCreateProcess and with the 'case 0x08'.
454
455 CHAR CmdLine[sizeof("cmd.exe") + 1] = "";
456
457 /* Spawn a user-defined 32-bit command preprocessor */
458
459 // FIXME: Use COMSPEC env var!!
460 strcpy(CmdLine, "cmd.exe");
461
462 DPRINT1("CMD Run 32-bit Command Interpreter '%s'\n", CmdLine);
463
464 /*
465 * No need to prepare the stack for DosStartComSpec since we won't start it.
466 */
467 Result = DosStartProcess32(CmdLine, CmdLine,
468 SEG_OFF_TO_PTR(getES(), 0) /*Environment*/,
469 MAKELONG(getIP(), getCS()) /*ReturnAddress*/,
470 FALSE);
471 if (Result != ERROR_SUCCESS)
472 {
473 DosDisplayMessage("Failed to start 32-bit Command Interpreter '%s'. Error: %u\n", CmdLine, Result);
474 setCF(0);
475 setAL((UCHAR)Result);
476 }
477 else
478 {
479 DosDisplayMessage("32-bit Command Interpreter '%s' started successfully.\n", CmdLine);
480 #ifndef STANDALONE
481 setCF(Repeat); // Set CF if we need to start a 16-bit process
482 #else
483 setCF(0);
484 #endif
485 }
486 }
487
488 static VOID CmdSetExitCode(VOID)
489 {
490 #ifndef STANDALONE
491 BOOL Success;
492 PCOMSPEC_INFO ComSpecInfo;
493 VDM_COMMAND_INFO CommandInfo;
494 #endif
495
496 /* Pause the VM */
497 EmulatorPause();
498
499 #ifndef STANDALONE
500 /*
501 * Check whether we need to shell out now in case we were started by a 32-bit app,
502 * or we were started alone along with the root 32-bit app.
503 */
504 ComSpecInfo = FindComSpecInfoByPsp(Sda->CurrentPsp);
505 if ((ComSpecInfo && ComSpecInfo->Terminated) ||
506 (ComSpecInfo == &RootCmd && SessionId != 0))
507 {
508 RemoveComSpecInfo(ComSpecInfo);
509 #endif
510 DPRINT1("Exit DOS from ExitCode (prologue)!\n");
511 setCF(0);
512 goto Quit;
513 #ifndef STANDALONE
514 }
515
516 /* Clear the VDM structure */
517 RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
518
519 Retry:
520 /* Update the VDM state of the task */
521 // CommandInfo.TaskId = SessionId;
522 CommandInfo.ExitCode = getDX();
523 CommandInfo.VDMState = VDM_FLAG_DONT_WAIT;
524 DPRINT1("Calling GetNextVDMCommand 32bit end of VDM task\n");
525 Success = GetNextVDMCommand(&CommandInfo);
526 DPRINT1("GetNextVDMCommand 32bit end of VDM task success = %s, last error = %d\n", Success ? "true" : "false", GetLastError());
527
528 /*
529 * Check whether we were awaited because the 32-bit process was stopped,
530 * or because it started a new DOS application.
531 */
532 if (CommandInfo.CmdLen != 0 || CommandInfo.AppLen != 0 || CommandInfo.PifLen != 0)
533 {
534 DPRINT1("GetNextVDMCommand end-of-app, this is for a new VDM task - CmdLen = %d, AppLen = %d, PifLen = %d\n",
535 CommandInfo.CmdLen, CommandInfo.AppLen, CommandInfo.PifLen);
536
537 /* Repeat the request */
538 Repeat = TRUE;
539 setCF(1);
540 }
541 else
542 {
543 DPRINT1("GetNextVDMCommand end-of-app, the app stopped\n");
544
545 /* Check whether we need to shell out now in case we were started by a 32-bit app */
546 ComSpecInfo = FindComSpecInfoByPsp(Sda->CurrentPsp);
547 if (!ComSpecInfo || !ComSpecInfo->Terminated)
548 {
549 DPRINT1("Not our 32-bit app, retrying...\n");
550 goto Retry;
551 }
552
553 ASSERT(ComSpecInfo->Terminated == TRUE);
554
555 /* Record found, remove it and exit now */
556 RemoveComSpecInfo(ComSpecInfo);
557
558 DPRINT1("Exit DOS from ExitCode wait!\n");
559 setCF(0);
560 }
561 #endif
562
563 // FIXME: Use the retrieved exit code as the value of our exit code
564 // when COMMAND.COM will shell-out ??
565
566 Quit:
567 /* Resume the VM */
568 EmulatorResume();
569 }
570
571 static VOID WINAPI DosCmdInterpreterBop(LPWORD Stack)
572 {
573 /* Get the Function Number and skip it */
574 BYTE FuncNum = *(PBYTE)SEG_OFF_TO_PTR(getCS(), getIP());
575 setIP(getIP() + 1);
576
577 switch (FuncNum)
578 {
579 /* Kill the VDM */
580 case 0x00:
581 {
582 /* Stop the VDM */
583 EmulatorTerminate();
584 return;
585 }
586
587 /*
588 * Get a new app to start
589 *
590 * Input
591 * DS:DX : Data block.
592 *
593 * Output
594 * CF : 0: Success; 1: Failure.
595 */
596 case 0x01:
597 {
598 CmdStartProcess();
599 break;
600 }
601
602 /*
603 * Check binary format
604 *
605 * Input
606 * DS:DX : Program to check.
607 *
608 * Output
609 * CF : 0: Success; 1: Failure.
610 * AX : Error code.
611 */
612 case 0x07:
613 {
614 DWORD BinaryType;
615 LPSTR ProgramName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX());
616
617 if (!GetBinaryTypeA(ProgramName, &BinaryType))
618 {
619 /* An error happened, bail out */
620 setCF(1);
621 setAX(LOWORD(GetLastError()));
622 break;
623 }
624
625 // FIXME: We only support DOS binaries for now...
626 ASSERT(BinaryType == SCS_DOS_BINARY);
627 if (BinaryType != SCS_DOS_BINARY)
628 {
629 /* An error happened, bail out */
630 setCF(1);
631 setAX(LOWORD(ERROR_BAD_EXE_FORMAT));
632 break;
633 }
634
635 /* Return success: DOS application */
636 setCF(0);
637 break;
638 }
639
640 /*
641 * Start an external command
642 *
643 * Input
644 * DS:SI : Command to start.
645 * ES : Environment block segment.
646 * AL : Current drive number.
647 * AH : 0: Directly start the command;
648 * 1: Use "cmd.exe /c" to start the command.
649 *
650 * Output
651 * CF : 0: Shell-out; 1: Continue.
652 * AL : Error/Exit code.
653 */
654 case 0x08:
655 {
656 CmdStartExternalCommand();
657 break;
658 }
659
660 /*
661 * Start the default 32-bit command interpreter (COMSPEC)
662 *
663 * Input
664 * ES : Environment block segment.
665 * AL : Current drive number.
666 *
667 * Output
668 * CF : 0: Shell-out; 1: Continue.
669 * AL : Error/Exit code.
670 */
671 case 0x0A:
672 {
673 CmdStartComSpec32();
674 break;
675 }
676
677 /*
678 * Set exit code
679 *
680 * Input
681 * DX : Exit code
682 *
683 * Output
684 * CF : 0: Shell-out; 1: Continue.
685 */
686 case 0x0B:
687 {
688 CmdSetExitCode();
689 break;
690 }
691
692 /*
693 * Get start information
694 *
695 * Output
696 * AL : 0 (resp. 1): Started from (resp. without) an existing console.
697 */
698 case 0x10:
699 {
700 #ifndef STANDALONE
701 /*
702 * When a new instance of our (internal) COMMAND.COM is started,
703 * we check whether we need to run a 32-bit COMSPEC. This goes by
704 * checking whether we were started in a new console (no parent
705 * console process) or from an existing one.
706 *
707 * However COMMAND.COM can also be started in the case where a
708 * 32-bit process (started by a 16-bit parent) wants to start a new
709 * 16-bit process: to ensure DOS reentry we need to start a new
710 * instance of COMMAND.COM. On Windows the COMMAND.COM is started
711 * just before the 32-bit process (in fact, it is this COMMAND.COM
712 * which starts the 32-bit process via an undocumented command-line
713 * switch '/z', which syntax is:
714 * COMMAND.COM /z\bAPPNAME.EXE
715 * notice the '\b' character inserted in-between. Then COMMAND.COM
716 * issues a BOP_CMD 08h with AH=00h to start the process).
717 *
718 * Instead, we do the reverse, i.e. we start the 32-bit process,
719 * and *only* if needed, i.e. if this process wants to start a
720 * new 16-bit process, we start our COMMAND.COM.
721 *
722 * The problem we then face is that our COMMAND.COM will possibly
723 * want to start a new COMSPEC, however we do not want this.
724 * The chosen solution is to flag this case -- done with the 'Reentry'
725 * boolean -- so that COMMAND.COM will not attempt to start COMSPEC
726 * but instead will directly try to start the 16-bit process.
727 */
728 // setAL(SessionId != 0);
729 setAL((SessionId != 0) && !Reentry);
730 /* Reset 'Reentry' */
731 Reentry = FALSE;
732 #else
733 setAL(0);
734 #endif
735 break;
736 }
737
738 default:
739 {
740 DPRINT1("Unknown DOS CMD Interpreter BOP Function: 0x%02X\n", FuncNum);
741 // setCF(1); // Disable, otherwise we enter an infinite loop
742 break;
743 }
744 }
745 }
746
747 #ifndef COMSPEC_FULLY_EXTERNAL
748 /*
749 * Internal COMMAND.COM binary data in the CommandCom array.
750 */
751 #include "command_com.h"
752 #endif
753
754 static
755 DWORD DosStartComSpec(IN BOOLEAN Permanent,
756 IN LPCSTR Environment OPTIONAL,
757 IN DWORD ReturnAddress OPTIONAL,
758 OUT PWORD ComSpecPsp OPTIONAL)
759 {
760 /*
761 * BOOLEAN Permanent
762 * TRUE to simulate the /P switch of command.com: starts AUTOEXEC.BAT/NT
763 * and makes the interpreter permanent (cannot exit).
764 */
765
766 DWORD Result;
767
768 if (ComSpecPsp) *ComSpecPsp = 0;
769
770 Result =
771 #ifndef COMSPEC_FULLY_EXTERNAL
772 DosLoadExecutableInternal(DOS_LOAD_AND_EXECUTE,
773 CommandCom,
774 sizeof(CommandCom),
775 "COMMAND.COM",
776 #else
777 DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
778 #ifndef STANDALONE // FIXME: Those values are hardcoded paths on my local test machines!!
779 "C:\\CMDCMD.COM",
780 #else
781 "H:\\DOS_tests\\CMDCMD.COM",
782 #endif // STANDALONE
783 #endif // COMSPEC_FULLY_EXTERNAL
784 NULL,
785 Permanent ? "/P" : "",
786 Environment ? Environment : "", // FIXME: Default environment!
787 ReturnAddress);
788 if (Result != ERROR_SUCCESS) return Result;
789
790 /* TODO: Read AUTOEXEC.NT/BAT */
791
792 /* Retrieve the PSP of the COMSPEC (current PSP set by DosLoadExecutable) */
793 if (ComSpecPsp) *ComSpecPsp = Sda->CurrentPsp;
794
795 return Result;
796 }
797
798 typedef struct _DOS_START_PROC32
799 {
800 LPSTR ExecutablePath;
801 LPSTR CommandLine;
802 LPSTR Environment OPTIONAL;
803 #ifndef STANDALONE
804 PCOMSPEC_INFO ComSpecInfo;
805 HANDLE hEvent;
806 #endif
807 } DOS_START_PROC32, *PDOS_START_PROC32;
808
809 static DWORD
810 WINAPI
811 CommandThreadProc(LPVOID Parameter)
812 {
813 BOOL Success;
814 PROCESS_INFORMATION ProcessInfo;
815 STARTUPINFOA StartupInfo;
816 DWORD dwExitCode;
817 PDOS_START_PROC32 DosStartProc32 = (PDOS_START_PROC32)Parameter;
818 #ifndef STANDALONE
819 VDM_COMMAND_INFO CommandInfo;
820 PCOMSPEC_INFO ComSpecInfo = DosStartProc32->ComSpecInfo;
821 #endif
822
823 /* Set up the VDM, startup and process info structures */
824 #ifndef STANDALONE
825 RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
826 #endif
827 RtlZeroMemory(&ProcessInfo, sizeof(ProcessInfo));
828 RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
829 StartupInfo.cb = sizeof(StartupInfo);
830
831 // FIXME: Build suitable 32-bit environment!!
832
833 #ifndef STANDALONE
834 /*
835 * Wait for signaling a new VDM task and increment the VDM re-entry count so
836 * that we can handle 16-bit apps that may be possibly started by the 32-bit app.
837 */
838 CommandInfo.VDMState = VDM_INC_REENTER_COUNT;
839 DPRINT1("Calling GetNextVDMCommand reenter++\n");
840 Success = GetNextVDMCommand(&CommandInfo);
841 DPRINT1("GetNextVDMCommand reenter++ success = %s, last error = %d\n", Success ? "true" : "false", GetLastError());
842 ++ReentrancyCount;
843 #endif
844
845 /* Start the process */
846 Success = CreateProcessA(NULL, // ProgramName,
847 DosStartProc32->CommandLine,
848 NULL,
849 NULL,
850 TRUE, // Inherit handles
851 CREATE_DEFAULT_ERROR_MODE | CREATE_SUSPENDED,
852 DosStartProc32->Environment,
853 NULL, // lpCurrentDirectory, see "START" command in cmd.exe
854 &StartupInfo,
855 &ProcessInfo);
856
857 #ifndef STANDALONE
858 /* Signal our caller the process was started */
859 SetEvent(DosStartProc32->hEvent);
860 // After this point, 'DosStartProc32' is not valid anymore.
861 #endif
862
863 if (Success)
864 {
865 /* Resume the process */
866 ResumeThread(ProcessInfo.hThread);
867
868 /* Wait for the process to finish running and retrieve its exit code */
869 WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
870 GetExitCodeProcess(ProcessInfo.hProcess, &dwExitCode);
871
872 /* Close the handles */
873 CloseHandle(ProcessInfo.hThread);
874 CloseHandle(ProcessInfo.hProcess);
875 }
876 else
877 {
878 dwExitCode = GetLastError();
879 }
880
881 #ifndef STANDALONE
882 ASSERT(ComSpecInfo);
883 ComSpecInfo->Terminated = TRUE;
884 ComSpecInfo->dwExitCode = dwExitCode;
885
886 /* Decrement the VDM re-entry count */
887 CommandInfo.VDMState = VDM_DEC_REENTER_COUNT;
888 DPRINT1("Calling GetNextVDMCommand reenter--\n");
889 Success = GetNextVDMCommand(&CommandInfo);
890 DPRINT1("GetNextVDMCommand reenter-- success = %s, last error = %d\n", Success ? "true" : "false", GetLastError());
891 --ReentrancyCount;
892
893 return 0;
894 #else
895 return dwExitCode;
896 #endif
897 }
898
899 DWORD DosStartProcess32(IN LPCSTR ExecutablePath,
900 IN LPCSTR CommandLine,
901 IN LPCSTR Environment OPTIONAL,
902 IN DWORD ReturnAddress OPTIONAL,
903 IN BOOLEAN StartComSpec)
904 {
905 DWORD Result = ERROR_SUCCESS;
906 HANDLE CommandThread;
907 DOS_START_PROC32 DosStartProc32;
908 #ifndef STANDALONE
909 BOOL Success;
910 VDM_COMMAND_INFO CommandInfo;
911 #endif
912
913 DosStartProc32.ExecutablePath = (LPSTR)ExecutablePath;
914 DosStartProc32.CommandLine = (LPSTR)CommandLine;
915 DosStartProc32.Environment = (LPSTR)Environment;
916
917 #ifndef STANDALONE
918 DosStartProc32.ComSpecInfo =
919 RtlAllocateHeap(RtlGetProcessHeap(),
920 HEAP_ZERO_MEMORY,
921 sizeof(*DosStartProc32.ComSpecInfo));
922 ASSERT(DosStartProc32.ComSpecInfo);
923
924 DosStartProc32.hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
925 ASSERT(DosStartProc32.hEvent);
926 #endif
927
928 /* Pause the VM and detach from the console */
929 EmulatorPause();
930 DosProcessConsoleDetach();
931
932 /* Start the 32-bit process via another thread */
933 CommandThread = CreateThread(NULL, 0, &CommandThreadProc, &DosStartProc32, 0, NULL);
934 if (CommandThread == NULL)
935 {
936 DisplayMessage(L"FATAL: Failed to create the command processing thread: %d", GetLastError());
937 Result = GetLastError();
938 goto Quit;
939 }
940
941 #ifndef STANDALONE
942 /* Close the thread handle */
943 CloseHandle(CommandThread);
944
945 /* Wait for the process to be ready to start */
946 WaitForSingleObject(DosStartProc32.hEvent, INFINITE);
947
948 /* Wait for any potential new DOS app started by the 32-bit process */
949 RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
950
951 Retry:
952 CommandInfo.VDMState = VDM_FLAG_NESTED_TASK | VDM_FLAG_DONT_WAIT;
953 DPRINT1("Calling GetNextVDMCommand 32bit for possible new VDM task...\n");
954 Success = GetNextVDMCommand(&CommandInfo);
955 DPRINT1("GetNextVDMCommand 32bit awaited, success = %s, last error = %d\n", Success ? "true" : "false", GetLastError());
956
957 /*
958 * Check whether we were awaited because the 32-bit process was stopped,
959 * or because it started a new DOS application.
960 */
961 if (CommandInfo.CmdLen != 0 || CommandInfo.AppLen != 0 || CommandInfo.PifLen != 0)
962 {
963 DPRINT1("GetNextVDMCommand 32bit, this is for a new VDM task - CmdLen = %d, AppLen = %d, PifLen = %d\n",
964 CommandInfo.CmdLen, CommandInfo.AppLen, CommandInfo.PifLen);
965
966 /* Repeat the request */
967 Repeat = TRUE;
968
969 /*
970 * Set 'Reentry' to TRUE or FALSE depending on whether we are going
971 * to reenter with a new COMMAND.COM. See the comment for:
972 * BOP_CMD 0x10 'Get start information'
973 * (dem.c!DosCmdInterpreterBop) for more details.
974 */
975 Reentry = StartComSpec;
976
977 /* If needed, start a new command interpreter to handle the possible incoming DOS commands */
978 if (StartComSpec)
979 {
980 //
981 // DosStartProcess32 was only called by DosCreateProcess, called from INT 21h,
982 // so the caller stack is already prepared for running a new DOS program
983 // (Flags, CS and IP, and the extra interrupt number, are already pushed).
984 //
985 Result = DosStartComSpec(FALSE, Environment, ReturnAddress,
986 &DosStartProc32.ComSpecInfo->ComSpecPsp);
987 if (Result != ERROR_SUCCESS)
988 {
989 DosDisplayMessage("Failed to start a new Command Interpreter (Error: %u).\n", Result);
990 goto Quit;
991 }
992 }
993 else
994 {
995 /* Retrieve the PSP of the COMSPEC (current PSP set by DosLoadExecutable) */
996 DosStartProc32.ComSpecInfo->ComSpecPsp = Sda->CurrentPsp;
997 Result = ERROR_SUCCESS;
998 }
999
1000 /* Insert the new entry in the list; it will be freed when needed by COMMAND.COM */
1001 InsertComSpecInfo(DosStartProc32.ComSpecInfo);
1002 }
1003 else
1004 {
1005 DPRINT1("GetNextVDMCommand 32bit, 32-bit app stopped\n");
1006
1007 /* Check whether this was our 32-bit app which was killed */
1008 if (!DosStartProc32.ComSpecInfo->Terminated)
1009 {
1010 DPRINT1("Not our 32-bit app, retrying...\n");
1011 goto Retry;
1012 }
1013
1014 Result = DosStartProc32.ComSpecInfo->dwExitCode;
1015
1016 /* Delete the entry */
1017 RtlFreeHeap(RtlGetProcessHeap(), 0, DosStartProc32.ComSpecInfo);
1018 }
1019 #else
1020 /* Wait for the thread to finish */
1021 WaitForSingleObject(CommandThread, INFINITE);
1022 GetExitCodeThread(CommandThread, &Result);
1023
1024 /* Close the thread handle */
1025 CloseHandle(CommandThread);
1026
1027 DPRINT1("32-bit app stopped\n");
1028 #endif
1029
1030 Quit:
1031 #ifndef STANDALONE
1032 CloseHandle(DosStartProc32.hEvent);
1033 #endif
1034
1035 /* Attach to the console and resume the VM */
1036 DosProcessConsoleAttach();
1037 EmulatorResume();
1038
1039 return Result;
1040 }
1041
1042
1043
1044
1045 /******************************************************************************\
1046 |** DOS Bootloader emulation, Startup and Shutdown **|
1047 \******************************************************************************/
1048
1049
1050 //
1051 // This function (equivalent of the DOS bootsector) is called by the bootstrap
1052 // loader *BEFORE* jumping at 0000:7C00. What we should do is to write at 0000:7C00
1053 // a BOP call that calls DosInitialize back. Then the bootstrap loader jumps at
1054 // 0000:7C00, our BOP gets called and then we can initialize the 32-bit part of the DOS.
1055 //
1056
1057 /* 16-bit bootstrap code at 0000:7C00 */
1058 /* Of course, this is not in real bootsector format, because we don't care about it for now */
1059 static BYTE Bootsector1[] =
1060 {
1061 LOBYTE(EMULATOR_BOP), HIBYTE(EMULATOR_BOP), BOP_LOAD_DOS
1062 };
1063 /* This portion of code is run if we failed to load the DOS */
1064 // NOTE: This may also be done by the BIOS32.
1065 static BYTE Bootsector2[] =
1066 {
1067 LOBYTE(EMULATOR_BOP), HIBYTE(EMULATOR_BOP), BOP_UNSIMULATE
1068 };
1069
1070 static VOID WINAPI DosInitialize(LPWORD Stack);
1071
1072 VOID DosBootsectorInitialize(VOID)
1073 {
1074 /* We write the bootsector at 0000:7C00 */
1075 ULONG_PTR StartAddress = (ULONG_PTR)SEG_OFF_TO_PTR(0x0000, 0x7C00);
1076 ULONG_PTR Address = StartAddress;
1077 CHAR DosKernelFileName[] = ""; // No DOS BIOS file name, therefore we will load DOS32
1078
1079 DPRINT("DosBootsectorInitialize\n");
1080
1081 /* Write the "bootsector" */
1082 RtlCopyMemory((PVOID)Address, Bootsector1, sizeof(Bootsector1));
1083 Address += sizeof(Bootsector1);
1084 RtlCopyMemory((PVOID)Address, DosKernelFileName, sizeof(DosKernelFileName));
1085 Address += sizeof(DosKernelFileName);
1086 RtlCopyMemory((PVOID)Address, Bootsector2, sizeof(Bootsector2));
1087 Address += sizeof(Bootsector2);
1088
1089 /* Initialize the callback context */
1090 InitializeContext(&DosContext, 0x0000,
1091 (ULONG_PTR)MEM_ALIGN_UP(0x7C00 + Address - StartAddress, sizeof(WORD)));
1092
1093 /* Register the DOS Loading BOP */
1094 RegisterBop(BOP_LOAD_DOS, DosInitialize);
1095 }
1096
1097
1098 //
1099 // This function is called by the DOS bootsector in case we load DOS32.
1100 // It sets up the DOS32 start code then jumps to 0070:0000.
1101 //
1102
1103 /* 16-bit startup code for DOS32 at 0070:0000 */
1104 static BYTE Startup[] =
1105 {
1106 LOBYTE(EMULATOR_BOP), HIBYTE(EMULATOR_BOP), BOP_START_DOS,
1107 LOBYTE(EMULATOR_BOP), HIBYTE(EMULATOR_BOP), BOP_UNSIMULATE
1108 };
1109
1110 static VOID WINAPI DosStart(LPWORD Stack);
1111
1112 static VOID WINAPI DosInitialize(LPWORD Stack)
1113 {
1114 /* Get the DOS BIOS file name (NULL-terminated) */
1115 // FIXME: Isn't it possible to use some DS:SI instead??
1116 LPCSTR DosBiosFileName = (LPCSTR)SEG_OFF_TO_PTR(getCS(), getIP());
1117 setIP(getIP() + strlen(DosBiosFileName) + 1); // Skip it
1118
1119 DPRINT("DosInitialize('%s')\n", DosBiosFileName);
1120
1121 /*
1122 * We succeeded, deregister the DOS Loading BOP
1123 * so that no app will be able to call us back.
1124 */
1125 RegisterBop(BOP_LOAD_DOS, NULL);
1126
1127 /* Register the DOS BOPs */
1128 RegisterBop(BOP_DOS, DosSystemBop );
1129 RegisterBop(BOP_CMD, DosCmdInterpreterBop);
1130
1131 if (DosBiosFileName[0] != '\0')
1132 {
1133 BOOLEAN Success = FALSE;
1134 HANDLE hDosBios;
1135 ULONG ulDosBiosSize = 0;
1136
1137 /* Open the DOS BIOS file */
1138 hDosBios = FileOpen(DosBiosFileName, &ulDosBiosSize);
1139 if (hDosBios == NULL) goto Quit;
1140
1141 /* Attempt to load the DOS BIOS into memory */
1142 Success = FileLoadByHandle(hDosBios,
1143 REAL_TO_PHYS(TO_LINEAR(0x0070, 0x0000)),
1144 ulDosBiosSize,
1145 &ulDosBiosSize);
1146
1147 DPRINT1("DOS BIOS file '%s' loading %s at %04X:%04X, size 0x%X (Error: %u).\n",
1148 DosBiosFileName,
1149 (Success ? "succeeded" : "failed"),
1150 0x0070, 0x0000,
1151 ulDosBiosSize,
1152 GetLastError());
1153
1154 /* Close the DOS BIOS file */
1155 FileClose(hDosBios);
1156
1157 Quit:
1158 if (!Success)
1159 {
1160 BiosDisplayMessage("DOS BIOS file '%s' loading failed (Error: %u). The VDM will shut down.\n",
1161 DosBiosFileName, GetLastError());
1162 return;
1163 }
1164 }
1165 else
1166 {
1167 /* Load the 16-bit startup code for DOS32 and register its Starting BOP */
1168 RtlCopyMemory(SEG_OFF_TO_PTR(0x0070, 0x0000), Startup, sizeof(Startup));
1169
1170 // This is the equivalent of BOP_LOAD_DOS, function 0x11 "Load the DOS kernel"
1171 // for the Windows NT DOS.
1172 RegisterBop(BOP_START_DOS, DosStart);
1173 }
1174
1175 /* Position execution pointers for DOS startup and return */
1176 setCS(0x0070);
1177 setIP(0x0000);
1178 }
1179
1180 static VOID WINAPI DosStart(LPWORD Stack)
1181 {
1182 BOOLEAN Success;
1183 DWORD Result;
1184 #ifndef STANDALONE
1185 INT i;
1186 #endif
1187
1188 DPRINT("DosStart\n");
1189
1190 /*
1191 * We succeeded, deregister the DOS Starting BOP
1192 * so that no app will be able to call us back.
1193 */
1194 RegisterBop(BOP_START_DOS, NULL);
1195
1196 /* Initialize the callback context */
1197 InitializeContext(&DosContext, BIOS_CODE_SEGMENT, 0x0010);
1198
1199 Success = DosBIOSInitialize();
1200 // Success &= DosKRNLInitialize();
1201 if (!Success)
1202 {
1203 BiosDisplayMessage("DOS32 loading failed (Error: %u). The VDM will shut down.\n", GetLastError());
1204 EmulatorTerminate();
1205 return;
1206 }
1207
1208 /* Load the mouse driver */
1209 DosMouseInitialize();
1210
1211 #ifndef STANDALONE
1212
1213 /* Parse the command line arguments */
1214 for (i = 1; i < NtVdmArgc; i++)
1215 {
1216 if (wcsncmp(NtVdmArgv[i], L"-i", 2) == 0)
1217 {
1218 /* This is the session ID (hex format) */
1219 SessionId = wcstoul(NtVdmArgv[i] + 2, NULL, 16);
1220 }
1221 }
1222
1223 /* Initialize Win32-VDM environment */
1224 Env = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, EnvSize);
1225 if (Env == NULL)
1226 {
1227 DosDisplayMessage("Failed to initialize the global environment (Error: %u). The VDM will shut down.\n", GetLastError());
1228 EmulatorTerminate();
1229 return;
1230 }
1231
1232 /* Clear the structure */
1233 RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
1234
1235 /* Get the initial information */
1236 CommandInfo.TaskId = SessionId;
1237 CommandInfo.VDMState = VDM_GET_FIRST_COMMAND | VDM_FLAG_DOS;
1238 GetNextVDMCommand(&CommandInfo);
1239
1240 #else
1241
1242 /* Retrieve the command to start */
1243 if (NtVdmArgc >= 2)
1244 {
1245 WideCharToMultiByte(CP_ACP, 0, NtVdmArgv[1], -1, AppName, sizeof(AppName), NULL, NULL);
1246
1247 if (NtVdmArgc >= 3)
1248 WideCharToMultiByte(CP_ACP, 0, NtVdmArgv[2], -1, CmdLine, sizeof(CmdLine), NULL, NULL);
1249 else
1250 strcpy(CmdLine, "");
1251 }
1252 else
1253 {
1254 DosDisplayMessage("Invalid DOS command line\n");
1255 EmulatorTerminate();
1256 return;
1257 }
1258
1259 #endif
1260
1261 /*
1262 * At this point, CS:IP points to the DOS BIOS exit code. If the
1263 * root command interpreter fails to start (or if it exits), DOS
1264 * exits and the VDM terminates.
1265 */
1266
1267 /* Start the root command interpreter */
1268 // TODO: Really interpret the 'SHELL=' line of CONFIG.NT, and use it!
1269
1270 /*
1271 * Prepare the stack for DosStartComSpec:
1272 * push Flags, CS and IP, and an extra WORD.
1273 */
1274 setSP(getSP() - sizeof(WORD));
1275 *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = (WORD)getEFLAGS();
1276 setSP(getSP() - sizeof(WORD));
1277 *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = getCS();
1278 setSP(getSP() - sizeof(WORD));
1279 *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = getIP();
1280 setSP(getSP() - sizeof(WORD));
1281
1282 Result = DosStartComSpec(TRUE, SEG_OFF_TO_PTR(SYSTEM_ENV_BLOCK, 0),
1283 MAKELONG(getIP(), getCS()),
1284 #ifndef STANDALONE
1285 &RootCmd.ComSpecPsp
1286 #else
1287 NULL
1288 #endif
1289 );
1290 if (Result != ERROR_SUCCESS)
1291 {
1292 /* Unprepare the stack for DosStartComSpec */
1293 setSP(getSP() + 4*sizeof(WORD));
1294
1295 DosDisplayMessage("Failed to start the Command Interpreter (Error: %u). The VDM will shut down.\n", Result);
1296 EmulatorTerminate();
1297 return;
1298 }
1299
1300 #ifndef STANDALONE
1301 RootCmd.Terminated = FALSE;
1302 InsertComSpecInfo(&RootCmd);
1303 #endif
1304
1305 /**/
1306 /* Attach to the console and resume the VM */
1307 DosProcessConsoleAttach();
1308 EmulatorResume();
1309 /**/
1310
1311 return;
1312 }
1313
1314 BOOLEAN DosShutdown(BOOLEAN Immediate)
1315 {
1316 /*
1317 * Immediate = TRUE: Immediate shutdown;
1318 * FALSE: Delayed shutdown (notification).
1319 */
1320
1321 #ifndef STANDALONE
1322 if (Immediate)
1323 {
1324 ExitVDM(FALSE, 0);
1325 return TRUE;
1326 }
1327 else
1328 {
1329 // HACK!
1330 extern HANDLE VdmTaskEvent; // see emulator.c
1331
1332 /*
1333 * Signal the root COMMAND.COM that it should terminate
1334 * when it checks for a new command.
1335 */
1336 RootCmd.Terminated = TRUE;
1337
1338 /* If the list is already empty, or just contains only one element, bail out */
1339 // FIXME: Question: if the list has only one element, is it ALWAYS RootCmd ??
1340 // FIXME: The following is hackish.
1341 if ((IsListEmpty(&ComSpecInfoList) ||
1342 (ComSpecInfoList.Flink == &RootCmd.Entry &&
1343 ComSpecInfoList.Blink == &RootCmd.Entry)) &&
1344 ReentrancyCount == 0 &&
1345 WaitForSingleObject(VdmTaskEvent, 0) == WAIT_TIMEOUT)
1346 {
1347 /* Nothing runs, so exit immediately */
1348 ExitVDM(FALSE, 0);
1349 return TRUE;
1350 }
1351
1352 return FALSE;
1353 }
1354 #else
1355 UNREFERENCED_PARAMETER(Immediate);
1356 return TRUE;
1357 #endif
1358 }
1359
1360
1361 /* PUBLIC EXPORTED APIS *******************************************************/
1362
1363 // demLFNCleanup
1364 // demLFNGetCurrentDirectory
1365
1366 // demGetFileTimeByHandle_WOW
1367 // demWOWLFNAllocateSearchHandle
1368 // demWOWLFNCloseSearchHandle
1369 // demWOWLFNEntry
1370 // demWOWLFNGetSearchHandle
1371 // demWOWLFNInit
1372
1373 DWORD
1374 WINAPI
1375 demClientErrorEx(IN HANDLE FileHandle,
1376 IN CHAR Unknown,
1377 IN BOOL Flag)
1378 {
1379 UNIMPLEMENTED;
1380 return GetLastError();
1381 }
1382
1383 DWORD
1384 WINAPI
1385 demFileDelete(IN LPCSTR FileName)
1386 {
1387 if (DeleteFileA(FileName)) SetLastError(ERROR_SUCCESS);
1388
1389 return GetLastError();
1390 }
1391
1392 DWORD
1393 WINAPI
1394 demFileFindFirst(OUT PVOID lpFindFileData,
1395 IN LPCSTR FileName,
1396 IN WORD AttribMask)
1397 {
1398 BOOLEAN Success = TRUE;
1399 WIN32_FIND_DATAA FindData;
1400 HANDLE SearchHandle;
1401 PDOS_FIND_FILE_BLOCK FindFileBlock = (PDOS_FIND_FILE_BLOCK)lpFindFileData;
1402
1403 /* Start a search */
1404 SearchHandle = FindFirstFileA(FileName, &FindData);
1405 if (SearchHandle == INVALID_HANDLE_VALUE) return GetLastError();
1406
1407 do
1408 {
1409 /* Check the attributes and retry as long as we haven't found a matching file */
1410 if (!((FindData.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN |
1411 FILE_ATTRIBUTE_SYSTEM |
1412 FILE_ATTRIBUTE_DIRECTORY))
1413 & ~AttribMask))
1414 {
1415 break;
1416 }
1417 }
1418 while ((Success = FindNextFileA(SearchHandle, &FindData)));
1419
1420 /* If we failed at some point, close the search and return an error */
1421 if (!Success)
1422 {
1423 FindClose(SearchHandle);
1424 return GetLastError();
1425 }
1426
1427 /* Fill the block */
1428 FindFileBlock->DriveLetter = DosData->Sda.CurrentDrive + 'A';
1429 FindFileBlock->AttribMask = AttribMask;
1430 FindFileBlock->SearchHandle = SearchHandle;
1431 FindFileBlock->Attributes = LOBYTE(FindData.dwFileAttributes);
1432 FileTimeToDosDateTime(&FindData.ftLastWriteTime,
1433 &FindFileBlock->FileDate,
1434 &FindFileBlock->FileTime);
1435 FindFileBlock->FileSize = FindData.nFileSizeHigh ? 0xFFFFFFFF
1436 : FindData.nFileSizeLow;
1437 /* Build a short path name */
1438 if (*FindData.cAlternateFileName)
1439 strncpy(FindFileBlock->FileName, FindData.cAlternateFileName, sizeof(FindFileBlock->FileName));
1440 else
1441 GetShortPathNameA(FindData.cFileName, FindFileBlock->FileName, sizeof(FindFileBlock->FileName));
1442
1443 return ERROR_SUCCESS;
1444 }
1445
1446 DWORD
1447 WINAPI
1448 demFileFindNext(OUT PVOID lpFindFileData)
1449 {
1450 WIN32_FIND_DATAA FindData;
1451 PDOS_FIND_FILE_BLOCK FindFileBlock = (PDOS_FIND_FILE_BLOCK)lpFindFileData;
1452
1453 do
1454 {
1455 /* Continue searching as long as we haven't found a matching file */
1456
1457 /* If we failed at some point, close the search and return an error */
1458 if (!FindNextFileA(FindFileBlock->SearchHandle, &FindData))
1459 {
1460 FindClose(FindFileBlock->SearchHandle);
1461 return GetLastError();
1462 }
1463 }
1464 while ((FindData.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN |
1465 FILE_ATTRIBUTE_SYSTEM |
1466 FILE_ATTRIBUTE_DIRECTORY))
1467 & ~FindFileBlock->AttribMask);
1468
1469 /* Update the block */
1470 FindFileBlock->Attributes = LOBYTE(FindData.dwFileAttributes);
1471 FileTimeToDosDateTime(&FindData.ftLastWriteTime,
1472 &FindFileBlock->FileDate,
1473 &FindFileBlock->FileTime);
1474 FindFileBlock->FileSize = FindData.nFileSizeHigh ? 0xFFFFFFFF
1475 : FindData.nFileSizeLow;
1476 /* Build a short path name */
1477 if (*FindData.cAlternateFileName)
1478 strncpy(FindFileBlock->FileName, FindData.cAlternateFileName, sizeof(FindFileBlock->FileName));
1479 else
1480 GetShortPathNameA(FindData.cFileName, FindFileBlock->FileName, sizeof(FindFileBlock->FileName));
1481
1482 return ERROR_SUCCESS;
1483 }
1484
1485 UCHAR
1486 WINAPI
1487 demGetPhysicalDriveType(IN UCHAR DriveNumber)
1488 {
1489 UNIMPLEMENTED;
1490 return DOSDEVICE_DRIVE_UNKNOWN;
1491 }
1492
1493 BOOL
1494 WINAPI
1495 demIsShortPathName(IN LPCSTR Path,
1496 IN BOOL Unknown)
1497 {
1498 UNIMPLEMENTED;
1499 return FALSE;
1500 }
1501
1502 DWORD
1503 WINAPI
1504 demSetCurrentDirectoryGetDrive(IN LPCSTR CurrentDirectory,
1505 OUT PUCHAR DriveNumber)
1506 {
1507 UNIMPLEMENTED;
1508 return ERROR_SUCCESS;
1509 }
1510
1511 /* EOF */