2014-01-26 21:51:27 +00:00
|
|
|
/*
|
|
|
|
* COPYRIGHT: GPL - See COPYING in the top level directory
|
|
|
|
* PROJECT: ReactOS Virtual DOS Machine
|
2015-09-18 17:01:49 +00:00
|
|
|
* FILE: subsystems/mvdm/ntvdm/dos/dos32krnl/bios.c
|
2014-05-15 23:13:06 +00:00
|
|
|
* PURPOSE: DOS32 Bios
|
2014-01-26 21:51:27 +00:00
|
|
|
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
|
|
|
|
*/
|
|
|
|
|
2015-10-04 11:49:28 +00:00
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
|
2015-10-03 19:17:55 +00:00
|
|
|
#include "ntvdm.h"
|
|
|
|
|
2014-01-26 21:51:27 +00:00
|
|
|
#define NDEBUG
|
2015-10-03 19:17:55 +00:00
|
|
|
#include <debug.h>
|
2014-01-26 21:51:27 +00:00
|
|
|
|
2015-10-04 11:49:28 +00:00
|
|
|
#include "emulator.h"
|
|
|
|
#include "int32.h"
|
|
|
|
|
|
|
|
#include "../dem.h"
|
|
|
|
#include "dos.h"
|
|
|
|
#include "dosfiles.h"
|
|
|
|
#include "handle.h"
|
|
|
|
#include "memory.h"
|
|
|
|
#include "bios/bios.h"
|
|
|
|
|
2014-11-09 00:49:17 +00:00
|
|
|
// This is needed because on UNICODE this symbol is redirected to
|
|
|
|
// GetEnvironmentStringsW whereas on ANSI it corresponds to the real
|
|
|
|
// "ANSI" function (and GetEnvironmentStringsA is aliased to it).
|
|
|
|
#undef GetEnvironmentStrings
|
|
|
|
|
|
|
|
// Symmetrize the dumbness of the previous symbol: on UNICODE
|
|
|
|
// FreeEnvironmentStrings aliases to FreeEnvironmentStringsW but
|
|
|
|
// on "ANSI" FreeEnvironmentStrings aliases to FreeEnvironmentStringsA
|
|
|
|
#undef FreeEnvironmentStrings
|
|
|
|
#define FreeEnvironmentStrings FreeEnvironmentStringsA
|
|
|
|
|
2014-01-26 21:51:27 +00:00
|
|
|
/* PRIVATE VARIABLES **********************************************************/
|
|
|
|
|
2015-08-01 12:41:22 +00:00
|
|
|
/* PUBLIC VARIABLES ***********************************************************/
|
|
|
|
|
|
|
|
/* Global DOS BIOS data area */
|
|
|
|
PBIOS_DATA BiosData;
|
|
|
|
|
2014-01-26 21:51:27 +00:00
|
|
|
/* PRIVATE FUNCTIONS **********************************************************/
|
|
|
|
|
|
|
|
/* PUBLIC FUNCTIONS ***********************************************************/
|
|
|
|
|
2015-09-25 22:09:37 +00:00
|
|
|
VOID DosEchoCharacter(CHAR Character)
|
|
|
|
{
|
|
|
|
switch (Character)
|
|
|
|
{
|
|
|
|
case '\0':
|
|
|
|
{
|
|
|
|
/* Nothing */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case '\b':
|
|
|
|
{
|
|
|
|
/* Erase the character */
|
|
|
|
DosPrintCharacter(DOS_OUTPUT_HANDLE, '\b');
|
|
|
|
DosPrintCharacter(DOS_OUTPUT_HANDLE, ' ');
|
|
|
|
DosPrintCharacter(DOS_OUTPUT_HANDLE, '\b');
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
{
|
2015-09-26 02:51:37 +00:00
|
|
|
/*
|
|
|
|
* Check if this is a special character
|
|
|
|
* NOTE: \r and \n are handled by the underlying driver!
|
|
|
|
*/
|
2015-09-26 03:24:55 +00:00
|
|
|
if (Character < 0x20 && Character != '\r' && Character != '\n')
|
2015-09-25 22:09:37 +00:00
|
|
|
{
|
|
|
|
DosPrintCharacter(DOS_OUTPUT_HANDLE, '^');
|
|
|
|
Character += 'A' - 1;
|
|
|
|
}
|
2015-09-26 00:05:10 +00:00
|
|
|
|
|
|
|
/* Echo the character */
|
|
|
|
DosPrintCharacter(DOS_OUTPUT_HANDLE, Character);
|
2015-09-25 22:09:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-26 00:05:10 +00:00
|
|
|
CHAR DosReadCharacter(WORD FileHandle, BOOLEAN Echo)
|
2014-01-26 21:51:27 +00:00
|
|
|
{
|
|
|
|
WORD BytesRead;
|
2015-09-25 22:00:57 +00:00
|
|
|
PDOS_FILE_DESCRIPTOR Descriptor = NULL;
|
|
|
|
WORD OldDeviceInfo;
|
|
|
|
|
|
|
|
/* Find the standard input descriptor and switch it to binary mode */
|
|
|
|
Descriptor = DosGetHandleFileDescriptor(FileHandle);
|
|
|
|
if (Descriptor)
|
|
|
|
{
|
|
|
|
OldDeviceInfo = Descriptor->DeviceInfo;
|
|
|
|
Descriptor->DeviceInfo |= FILE_INFO_BINARY;
|
|
|
|
}
|
2014-01-26 21:51:27 +00:00
|
|
|
|
2015-05-11 02:54:46 +00:00
|
|
|
Sda->ByteBuffer = '\0';
|
2014-05-15 23:13:06 +00:00
|
|
|
DPRINT("DosReadCharacter\n");
|
|
|
|
|
2014-05-11 19:25:09 +00:00
|
|
|
/* Use the file reading function */
|
2015-05-11 02:54:46 +00:00
|
|
|
DosReadFile(FileHandle,
|
|
|
|
MAKELONG(DOS_DATA_OFFSET(Sda.ByteBuffer), DOS_DATA_SEGMENT),
|
|
|
|
1,
|
|
|
|
&BytesRead);
|
2014-01-26 21:51:27 +00:00
|
|
|
|
2015-09-26 00:05:10 +00:00
|
|
|
/* Check if we should echo and the file is actually the CON device */
|
|
|
|
if (Echo && Descriptor && Descriptor->DeviceInfo & FILE_INFO_DEVICE)
|
2015-09-25 22:09:37 +00:00
|
|
|
{
|
|
|
|
/* Echo the character */
|
|
|
|
DosEchoCharacter(Sda->ByteBuffer);
|
|
|
|
}
|
|
|
|
|
2015-09-25 22:00:57 +00:00
|
|
|
/* Restore the old mode and return the character */
|
|
|
|
if (Descriptor) Descriptor->DeviceInfo = OldDeviceInfo;
|
2015-05-11 02:54:46 +00:00
|
|
|
return Sda->ByteBuffer;
|
2014-01-26 21:51:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN DosCheckInput(VOID)
|
|
|
|
{
|
2015-04-26 18:09:57 +00:00
|
|
|
PDOS_FILE_DESCRIPTOR Descriptor = DosGetHandleFileDescriptor(DOS_INPUT_HANDLE);
|
2014-01-26 21:51:27 +00:00
|
|
|
|
2015-04-26 18:09:57 +00:00
|
|
|
if (Descriptor == NULL)
|
2015-03-26 14:52:16 +00:00
|
|
|
{
|
|
|
|
/* Invalid handle */
|
2015-05-11 02:54:46 +00:00
|
|
|
Sda->LastErrorCode = ERROR_INVALID_HANDLE; // ERROR_FILE_NOT_FOUND
|
2015-03-26 14:52:16 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2015-08-01 12:41:22 +00:00
|
|
|
if (Descriptor->DeviceInfo & FILE_INFO_DEVICE)
|
2014-01-26 21:51:27 +00:00
|
|
|
{
|
2015-04-26 18:09:57 +00:00
|
|
|
WORD Result;
|
|
|
|
PDOS_DEVICE_NODE Node = DosGetDriverNode(Descriptor->DevicePointer);
|
|
|
|
|
|
|
|
if (!Node->InputStatusRoutine) return FALSE;
|
2015-05-15 23:13:40 +00:00
|
|
|
|
2015-04-26 18:09:57 +00:00
|
|
|
Result = Node->InputStatusRoutine(Node);
|
|
|
|
return !(Result & DOS_DEVSTAT_BUSY);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DWORD FileSizeHigh;
|
|
|
|
DWORD FileSize = GetFileSize(Descriptor->Win32Handle, &FileSizeHigh);
|
|
|
|
LONG LocationHigh = 0;
|
|
|
|
DWORD Location = SetFilePointer(Descriptor->Win32Handle, 0, &LocationHigh, FILE_CURRENT);
|
|
|
|
|
|
|
|
return ((Location != FileSize) || (LocationHigh != FileSizeHigh));
|
2014-01-26 21:51:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-11 19:25:09 +00:00
|
|
|
VOID DosPrintCharacter(WORD FileHandle, CHAR Character)
|
2014-01-26 21:51:27 +00:00
|
|
|
{
|
|
|
|
WORD BytesWritten;
|
2015-05-11 02:54:46 +00:00
|
|
|
|
|
|
|
Sda->ByteBuffer = Character;
|
2014-01-26 21:51:27 +00:00
|
|
|
|
|
|
|
/* Use the file writing function */
|
2015-05-11 02:54:46 +00:00
|
|
|
DosWriteFile(FileHandle,
|
|
|
|
MAKELONG(DOS_DATA_OFFSET(Sda.ByteBuffer), DOS_DATA_SEGMENT),
|
|
|
|
1,
|
|
|
|
&BytesWritten);
|
2014-01-26 21:51:27 +00:00
|
|
|
}
|
|
|
|
|
2015-06-15 23:43:16 +00:00
|
|
|
BOOLEAN DosBuildSysEnvBlock(VOID)
|
2014-01-26 21:51:27 +00:00
|
|
|
{
|
2014-11-09 00:49:17 +00:00
|
|
|
LPSTR SourcePtr, Environment;
|
2014-01-26 21:51:27 +00:00
|
|
|
LPSTR DestPtr = (LPSTR)SEG_OFF_TO_PTR(SYSTEM_ENV_BLOCK, 0);
|
|
|
|
|
2015-07-13 01:21:46 +00:00
|
|
|
/*
|
|
|
|
* Get the environment strings
|
|
|
|
*
|
|
|
|
* NOTE: On non-STANDALONE builds, this corresponds to the VDM environment
|
|
|
|
* as created by BaseVDM for NTVDM. On STANDALONE builds this is the Win32
|
|
|
|
* environment. In this last case we need to convert it to a proper VDM env.
|
|
|
|
*/
|
2014-11-09 00:49:17 +00:00
|
|
|
SourcePtr = Environment = GetEnvironmentStrings();
|
2014-01-26 21:51:27 +00:00
|
|
|
if (Environment == NULL) return FALSE;
|
|
|
|
|
|
|
|
/* Fill the DOS system environment block */
|
|
|
|
while (*SourcePtr)
|
|
|
|
{
|
2014-11-09 00:49:17 +00:00
|
|
|
/*
|
|
|
|
* - Ignore environment strings starting with a '=',
|
|
|
|
* they describe current directories.
|
|
|
|
* - Ignore also the WINDIR environment variable since
|
|
|
|
* DOS apps should ignore that we started from ReactOS.
|
|
|
|
* - Upper-case the environment names, not their values.
|
|
|
|
*/
|
|
|
|
if (*SourcePtr != '=' && _strnicmp(SourcePtr, "WINDIR", 6) != 0)
|
2014-01-26 21:51:27 +00:00
|
|
|
{
|
2014-11-09 00:49:17 +00:00
|
|
|
PCHAR Delim = NULL;
|
2014-01-26 21:51:27 +00:00
|
|
|
|
2014-11-09 00:49:17 +00:00
|
|
|
/* Copy the environment string */
|
|
|
|
strcpy(DestPtr, SourcePtr);
|
2014-01-26 21:51:27 +00:00
|
|
|
|
2014-11-09 00:49:17 +00:00
|
|
|
/* Upper-case the environment name */
|
|
|
|
Delim = strchr(DestPtr, '='); // Find the '=' delimiter
|
|
|
|
if (Delim) *Delim = '\0'; // Temporarily replace it by NULL
|
|
|
|
_strupr(DestPtr); // Upper-case
|
|
|
|
if (Delim) *Delim = '='; // Restore the delimiter
|
2014-01-26 21:51:27 +00:00
|
|
|
|
2014-11-09 00:49:17 +00:00
|
|
|
DestPtr += strlen(SourcePtr);
|
|
|
|
|
|
|
|
/* NULL-terminate the environment string */
|
|
|
|
*(DestPtr++) = '\0';
|
|
|
|
}
|
2014-01-26 21:51:27 +00:00
|
|
|
|
2014-11-09 00:49:17 +00:00
|
|
|
/* Move to the next string */
|
|
|
|
SourcePtr += strlen(SourcePtr) + 1;
|
2014-01-26 21:51:27 +00:00
|
|
|
}
|
2014-11-09 00:49:17 +00:00
|
|
|
/* NULL-terminate the environment block */
|
|
|
|
*DestPtr = '\0';
|
2014-01-26 21:51:27 +00:00
|
|
|
|
|
|
|
/* Free the memory allocated for environment strings */
|
2014-11-09 00:49:17 +00:00
|
|
|
FreeEnvironmentStrings(Environment);
|
2014-01-26 21:51:27 +00:00
|
|
|
|
2015-06-15 23:43:16 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN DosBIOSInitialize(VOID)
|
|
|
|
{
|
|
|
|
FILE *Stream;
|
|
|
|
WCHAR Buffer[256];
|
|
|
|
|
|
|
|
/* Set the data segment */
|
2015-08-01 12:41:22 +00:00
|
|
|
setDS(BIOS_DATA_SEGMENT);
|
2015-06-15 23:43:16 +00:00
|
|
|
|
2015-08-01 12:41:22 +00:00
|
|
|
/* Initialize the global DOS BIOS data area */
|
|
|
|
BiosData = (PBIOS_DATA)SEG_OFF_TO_PTR(BIOS_DATA_SEGMENT, 0x0000);
|
|
|
|
|
|
|
|
/* Initialize the DOS BIOS stack */
|
|
|
|
// FIXME: Add a block of fixed size for the stack in BIOS/DOS_DATA instead!
|
2015-06-15 23:43:16 +00:00
|
|
|
setSS(0x0F00);
|
|
|
|
setSP(0x0FF0);
|
[NTVDM]
- Add support for DOS VDM reentry, i.e. the fact that a DOS app can start a 32-bit app, which in turn can start another DOS app, ad infinitum...
CORE-9711 CORE-9773 #resolve
- Add a small COMMAND.COM which is, for now, completely included in NTVDM. This COMMAND.COM is needed in order to support reentrancy. The fact that I chose to put it inside NTVDM is that any user can use instead his/her own COMMAND.COM, while retaining the possibility to perform VDM reentrancy (on Windows, if you remove the COMMAND.COM in windows\system32 and replace it with your own, you will break VDM reentrancy on windows' ntvdm).
CORE-5221 #resolve #comment I choose for the moment an internal COMMAND.COM, but you can recompile NTVDM to use it externally instead.
Global remarks:
- Quite a few DPRINTs were added for diagnostic purposes (since DOS reentrancy is a new feature), to be sure everything behaves as expected when being used with a large panel of applications. They will be removed when everything is OK.
- Support for current directories and 16/32-bit environment translation (in ntvdm + basevdm-side) remain to be implemented.
Other changes:
- Improve a bit the VDM shutdown code by gathering it at one place (there's still room for other improvements).
- Add suppport for properly pausing/resuming NTVDM.
- Bufferize some console events before dispatching.
Have fun ;^)
svn path=/trunk/; revision=69204
2015-09-12 20:09:25 +00:00
|
|
|
/// setBP(0x091E); // DOS base stack pointer relic value
|
2015-06-15 23:43:16 +00:00
|
|
|
|
2015-08-01 12:41:22 +00:00
|
|
|
/*
|
|
|
|
* Initialize the INT 13h (BIOS Disk Services) handler chain support.
|
|
|
|
*
|
|
|
|
* The INT 13h handler chain is some functionality that allows DOS
|
|
|
|
* to insert disk filter drivers in between the (hooked) INT 13h handler
|
|
|
|
* and its original handler.
|
|
|
|
* Typically, those are:
|
|
|
|
* - filter for detecting disk changes (for floppy disks),
|
|
|
|
* - filter for tracking formatting calls and correcting DMA boundary errors,
|
|
|
|
* - a possible filter to work around a bug in a particular version of PC-AT's
|
|
|
|
* IBM's ROM BIOS (on systems with model byte FCh and BIOS date "01/10/84" only)
|
|
|
|
* (see http://www.ctyme.com/intr/rb-4453.htm for more details).
|
|
|
|
*
|
|
|
|
* This functionality is known to be used by some legitimate programs,
|
|
|
|
* by Windows 3.x, as well as some illegitimate ones (aka. virii).
|
|
|
|
*
|
|
|
|
* See extra information about this support in dos.h
|
|
|
|
*/
|
|
|
|
// FIXME: Should be done by the DOS BIOS
|
|
|
|
BiosData->RomBiosInt13 = ((PULONG)BaseAddress)[0x13];
|
|
|
|
BiosData->PrevInt13 = BiosData->RomBiosInt13;
|
|
|
|
// RegisterDosInt32(0x13, DosInt13h); // Unused at the moment!
|
2014-01-26 21:51:27 +00:00
|
|
|
|
2015-08-01 12:41:22 +00:00
|
|
|
//
|
|
|
|
// HERE: Do all hardware initialization needed for DOS
|
|
|
|
//
|
2014-01-26 21:51:27 +00:00
|
|
|
|
2015-08-01 12:41:22 +00:00
|
|
|
/*
|
|
|
|
* SysInit part...
|
|
|
|
*/
|
2014-01-26 21:51:27 +00:00
|
|
|
|
2015-08-01 12:41:22 +00:00
|
|
|
/* Initialize the DOS kernel (DosInit) */
|
|
|
|
if (!DosKRNLInitialize())
|
2014-01-26 21:51:27 +00:00
|
|
|
{
|
[NTVDM]
- Add support for DOS VDM reentry, i.e. the fact that a DOS app can start a 32-bit app, which in turn can start another DOS app, ad infinitum...
CORE-9711 CORE-9773 #resolve
- Add a small COMMAND.COM which is, for now, completely included in NTVDM. This COMMAND.COM is needed in order to support reentrancy. The fact that I chose to put it inside NTVDM is that any user can use instead his/her own COMMAND.COM, while retaining the possibility to perform VDM reentrancy (on Windows, if you remove the COMMAND.COM in windows\system32 and replace it with your own, you will break VDM reentrancy on windows' ntvdm).
CORE-5221 #resolve #comment I choose for the moment an internal COMMAND.COM, but you can recompile NTVDM to use it externally instead.
Global remarks:
- Quite a few DPRINTs were added for diagnostic purposes (since DOS reentrancy is a new feature), to be sure everything behaves as expected when being used with a large panel of applications. They will be removed when everything is OK.
- Support for current directories and 16/32-bit environment translation (in ntvdm + basevdm-side) remain to be implemented.
Other changes:
- Improve a bit the VDM shutdown code by gathering it at one place (there's still room for other improvements).
- Add suppport for properly pausing/resuming NTVDM.
- Bufferize some console events before dispatching.
Have fun ;^)
svn path=/trunk/; revision=69204
2015-09-12 20:09:25 +00:00
|
|
|
BiosDisplayMessage("Failed to load the DOS kernel! Exiting...\n");
|
2014-01-26 21:51:27 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2015-08-01 12:41:22 +00:00
|
|
|
/* DOS kernel loading succeeded, we can finish the initialization */
|
2014-01-26 21:51:27 +00:00
|
|
|
|
[NTVDM]
- Add support for DOS VDM reentry, i.e. the fact that a DOS app can start a 32-bit app, which in turn can start another DOS app, ad infinitum...
CORE-9711 CORE-9773 #resolve
- Add a small COMMAND.COM which is, for now, completely included in NTVDM. This COMMAND.COM is needed in order to support reentrancy. The fact that I chose to put it inside NTVDM is that any user can use instead his/her own COMMAND.COM, while retaining the possibility to perform VDM reentrancy (on Windows, if you remove the COMMAND.COM in windows\system32 and replace it with your own, you will break VDM reentrancy on windows' ntvdm).
CORE-5221 #resolve #comment I choose for the moment an internal COMMAND.COM, but you can recompile NTVDM to use it externally instead.
Global remarks:
- Quite a few DPRINTs were added for diagnostic purposes (since DOS reentrancy is a new feature), to be sure everything behaves as expected when being used with a large panel of applications. They will be removed when everything is OK.
- Support for current directories and 16/32-bit environment translation (in ntvdm + basevdm-side) remain to be implemented.
Other changes:
- Improve a bit the VDM shutdown code by gathering it at one place (there's still room for other improvements).
- Add suppport for properly pausing/resuming NTVDM.
- Bufferize some console events before dispatching.
Have fun ;^)
svn path=/trunk/; revision=69204
2015-09-12 20:09:25 +00:00
|
|
|
/* Build the system master (pre-) environment block (inherited by the shell) */
|
2015-08-01 12:41:22 +00:00
|
|
|
if (!DosBuildSysEnvBlock())
|
2014-01-26 21:51:27 +00:00
|
|
|
{
|
[NTVDM]
- Add support for DOS VDM reentry, i.e. the fact that a DOS app can start a 32-bit app, which in turn can start another DOS app, ad infinitum...
CORE-9711 CORE-9773 #resolve
- Add a small COMMAND.COM which is, for now, completely included in NTVDM. This COMMAND.COM is needed in order to support reentrancy. The fact that I chose to put it inside NTVDM is that any user can use instead his/her own COMMAND.COM, while retaining the possibility to perform VDM reentrancy (on Windows, if you remove the COMMAND.COM in windows\system32 and replace it with your own, you will break VDM reentrancy on windows' ntvdm).
CORE-5221 #resolve #comment I choose for the moment an internal COMMAND.COM, but you can recompile NTVDM to use it externally instead.
Global remarks:
- Quite a few DPRINTs were added for diagnostic purposes (since DOS reentrancy is a new feature), to be sure everything behaves as expected when being used with a large panel of applications. They will be removed when everything is OK.
- Support for current directories and 16/32-bit environment translation (in ntvdm + basevdm-side) remain to be implemented.
Other changes:
- Improve a bit the VDM shutdown code by gathering it at one place (there's still room for other improvements).
- Add suppport for properly pausing/resuming NTVDM.
- Bufferize some console events before dispatching.
Have fun ;^)
svn path=/trunk/; revision=69204
2015-09-12 20:09:25 +00:00
|
|
|
DosDisplayMessage("An error occurred when setting up the system environment block.\n");
|
2014-01-26 21:51:27 +00:00
|
|
|
}
|
|
|
|
|
2015-08-01 12:41:22 +00:00
|
|
|
/* TODO: Read CONFIG.NT/SYS */
|
2014-01-26 21:51:27 +00:00
|
|
|
Stream = _wfopen(DOS_CONFIG_PATH, L"r");
|
|
|
|
if (Stream != NULL)
|
|
|
|
{
|
2015-04-19 00:01:03 +00:00
|
|
|
while (fgetws(Buffer, ARRAYSIZE(Buffer), Stream))
|
2014-01-26 21:51:27 +00:00
|
|
|
{
|
|
|
|
// TODO: Parse the line
|
|
|
|
}
|
|
|
|
fclose(Stream);
|
|
|
|
}
|
|
|
|
|
2015-08-01 12:41:22 +00:00
|
|
|
return TRUE;
|
2014-01-26 21:51:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* EOF */
|