mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-31 00:43:02 +01:00
95f1b36bfc
system (header files).
690 lines
17 KiB
C++
690 lines
17 KiB
C++
#error This is an obsolete port.
|
|
/*
|
|
* PROGRAM: JRD Access Method
|
|
* MODULE: os2.c
|
|
* DESCRIPTION: OS2 specific physical IO
|
|
*
|
|
* The contents of this file are subject to the Interbase Public
|
|
* License Version 1.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy
|
|
* of the License at http://www.Inprise.com/IPL.html
|
|
*
|
|
* Software distributed under the License is distributed on an
|
|
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
|
|
* or implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code was created by Inprise Corporation
|
|
* and its predecessors. Portions created by Inprise Corporation are
|
|
* Copyright (C) Inprise Corporation.
|
|
*
|
|
* All Rights Reserved.
|
|
* Contributor(s): ______________________________________.
|
|
*
|
|
* 2002.02.15 Sean Leyne - obsolete "OS/2" port
|
|
*
|
|
*/
|
|
|
|
#define
|
|
#include "firebird.h"INCL_DOSFILEMGR
|
|
#include <os2.h>
|
|
#include <string.h>
|
|
#include "../jrd/jrd.h"
|
|
#include "../jrd/pio.h"
|
|
#include "../jrd/cch.h"
|
|
#include "../jrd/ods.h"
|
|
#include "../jrd/lck.h"
|
|
#include "gen/codes.h"
|
|
#include "../jrd/all_proto.h"
|
|
#include "../jrd/err_proto.h"
|
|
#include "../jrd/cch_proto.h"
|
|
#include "../jrd/isc_f_proto.h"
|
|
|
|
#include "../jrd/lck_proto.h"
|
|
#include "../jrd/mov_proto.h"
|
|
#include "../jrd/pio_proto.h"
|
|
#include "../jrd/thd_proto.h"
|
|
|
|
|
|
extern "C" {
|
|
|
|
|
|
#define DEFAULTBUFFERSIZE 1024
|
|
|
|
static BOOLEAN check_network(TEXT *);
|
|
static FIL seek_file(FIL, BDB, STATUS *);
|
|
static FIL setup_file(DBB, TEXT *, USHORT, ULONG);
|
|
static BOOLEAN dos_error(TEXT *, FIL, STATUS, ULONG, STATUS *);
|
|
|
|
|
|
int PIO_add_file(DBB dbb, FIL main_file, TEXT * file_name, SLONG start)
|
|
{
|
|
/**************************************
|
|
*
|
|
* P I O _ a d d _ f i l e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Add a file to an existing database. Return the sequence
|
|
* number of the new file. If anything goes wrong, return a
|
|
* sequence of 0.
|
|
* NOTE: This routine does not lock any mutexes on
|
|
* its own behalf. It is assumed that mutexes will
|
|
* have been locked before entry.
|
|
*
|
|
**************************************/
|
|
USHORT sequence;
|
|
FIL file, new_file;
|
|
|
|
if (!(new_file = PIO_create(dbb, file_name, strlen(file_name), FALSE)))
|
|
return 0;
|
|
|
|
new_file->fil_min_page = start;
|
|
sequence = 1;
|
|
|
|
for (file = main_file; file->fil_next; file = file->fil_next)
|
|
++sequence;
|
|
|
|
file->fil_max_page = start - 1;
|
|
file->fil_next = new_file;
|
|
|
|
return sequence;
|
|
}
|
|
|
|
|
|
void PIO_close(FIL main_file)
|
|
{
|
|
/**************************************
|
|
*
|
|
* P I O _ c l o s e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* NOTE: This routine does not lock any mutexes on
|
|
* its own behalf. It is assumed that mutexes will
|
|
* have been locked before entry.
|
|
*
|
|
**************************************/
|
|
FIL file;
|
|
USHORT i;
|
|
|
|
for (file = main_file; file; file = file->fil_next)
|
|
if (file->fil_desc) {
|
|
DosClose((HFILE) file->fil_desc);
|
|
V4_MUTEX_DESTROY(file->fil_mutex);
|
|
}
|
|
}
|
|
|
|
|
|
int PIO_connection(TEXT * file_name, USHORT * file_length)
|
|
{
|
|
/**************************************
|
|
*
|
|
* P I O _ c o n n e c t i o n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Analyze a file specification and determine whether a page/lock
|
|
* server is required and available. If so, return a "connection"
|
|
* block. If not, return NULL.
|
|
*
|
|
* Note: The file name must have been expanded prior to this call.
|
|
*
|
|
**************************************/
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
FIL PIO_create(DBB dbb, TEXT * string, SSHORT length, BOOLEAN overwrite)
|
|
{
|
|
/**************************************
|
|
*
|
|
* P I O _ c r e a t e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Create a new database file.
|
|
* NOTE: This routine does not lock any mutexes on
|
|
* its own behalf. It is assumed that mutexes will
|
|
* have been locked before entry.
|
|
*
|
|
**************************************/
|
|
ULONG desc, status, action, open_mode;
|
|
FIL file;
|
|
TEXT expanded_name[MAXPATHLEN], temp_name[MAXPATHLEN], *file_name;
|
|
|
|
length = PIO_expand(string, length, expanded_name);
|
|
|
|
if (check_network(expanded_name))
|
|
ERR_post(gds__unavailable, 0);
|
|
|
|
open_mode = OPEN_ACCESS_READWRITE | /* Access mode -- read/write */
|
|
OPEN_SHARE_DENYNONE | /* Share mode -- permit read/write */
|
|
OPEN_FLAGS_NOINHERIT | /* Inheritance mode -- don't */
|
|
OPEN_FLAGS_FAIL_ON_ERROR | /* Fail errors -- return code */
|
|
OPEN_FLAGS_WRITE_THROUGH | /* Write thru -- absolutely */
|
|
(0 << 15); /* DASD open -- no */
|
|
|
|
/* take care of non null-terminated case */
|
|
|
|
if (length) {
|
|
strncpy(temp_name, string, length);
|
|
temp_name[length] = '\0';
|
|
string = temp_name;
|
|
}
|
|
|
|
status = DosOpen(string, /* File pathname */
|
|
&desc, /* File handle */
|
|
&action, /* Action taken */
|
|
100, /* Initial size */
|
|
FILE_NORMAL, /* File attributes */
|
|
OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_REPLACE_IF_EXISTS, /* Open Flag */
|
|
open_mode, /* Open mode (see above) */
|
|
0); /* Reserved */
|
|
|
|
if (status)
|
|
ERR_post(isc_io_error,
|
|
gds_arg_string, "DosOpen (create)",
|
|
gds_arg_cstring, length, ERR_string(string, length),
|
|
isc_arg_gds, isc_io_create_err, gds_arg_dos, status, 0);
|
|
|
|
/* File open succeeded. Now expand the file name. */
|
|
|
|
file = setup_file(dbb, expanded_name, length, desc);
|
|
|
|
return file;
|
|
}
|
|
|
|
|
|
int PIO_expand(TEXT * file_name, USHORT file_length, TEXT * expanded_name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* P I O _ e x p a n d
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Fully expand a file name. If the file doesn't exist, do something
|
|
* intelligent.
|
|
*
|
|
**************************************/
|
|
|
|
return ISC_expand_filename(file_name, file_length, expanded_name);
|
|
}
|
|
|
|
|
|
void PIO_flush(FIL file)
|
|
{
|
|
/**************************************
|
|
*
|
|
* P I O _ f l u s h
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Flush the operating system cache back to good, solid oxide.
|
|
*
|
|
**************************************/
|
|
}
|
|
|
|
|
|
void PIO_force_write(FIL file, USHORT flag)
|
|
{
|
|
/**************************************
|
|
*
|
|
* P I O _ f o r c e _ w r i t e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Set (or clear) force write, if possible, for the database.
|
|
*
|
|
**************************************/
|
|
}
|
|
|
|
|
|
void PIO_header(DBB dbb, SCHAR * address, int length)
|
|
{
|
|
/**************************************
|
|
*
|
|
* P I O _ h e a d e r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Read the page header. This assumes that the file has not been
|
|
* repositioned since the file was originally mapped.
|
|
*
|
|
**************************************/
|
|
ULONG status, actual_length;
|
|
ULONG new_offset;
|
|
FIL file;
|
|
|
|
file = dbb->dbb_file;
|
|
|
|
V4_MUTEX_LOCK(file->fil_mutex);
|
|
|
|
if (status = DosSetFilePtr(file->fil_desc, 0, FILE_BEGIN, &new_offset)) {
|
|
V4_MUTEX_UNLOCK(file->fil_mutex);
|
|
dos_error("DosSetFilePtr", file, isc_io_read_err, status, 0);
|
|
}
|
|
|
|
#ifdef ISC_DATABASE_ENCRYPTION
|
|
if (dbb->dbb_encrypt_key) {
|
|
SLONG spare_buffer[MAX_PAGE_SIZE / sizeof(SLONG)];
|
|
|
|
if (
|
|
(status =
|
|
DosRead(file->fil_desc, spare_buffer, length, &actual_length))
|
|
|| actual_length != length) {
|
|
V4_MUTEX_UNLOCK(file->fil_mutex);
|
|
dos_error("DosRead (read header)", file, isc_io_read_err, status,
|
|
0);
|
|
}
|
|
|
|
(*dbb->dbb_decrypt) (dbb->dbb_encrypt_key->str_data,
|
|
spare_buffer, length, address);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (
|
|
(status =
|
|
DosRead(file->fil_desc, address, length, &actual_length))
|
|
|| actual_length != length) {
|
|
V4_MUTEX_UNLOCK(file->fil_mutex);
|
|
dos_error("DosRead (read header)", file, isc_io_read_err, status,
|
|
0);
|
|
}
|
|
}
|
|
|
|
V4_MUTEX_UNLOCK(file->fil_mutex);
|
|
}
|
|
|
|
|
|
SLONG PIO_max_alloc(DBB dbb)
|
|
{
|
|
/**************************************
|
|
*
|
|
* P I O _ m a x _ a l l o c
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Compute last physically allocated page of database.
|
|
*
|
|
**************************************/
|
|
ULONG status;
|
|
FILESTATUS3 buffer;
|
|
ULONG length;
|
|
FIL file;
|
|
|
|
for (file = dbb->dbb_file; file->fil_next; file = file->fil_next);
|
|
|
|
if (status =
|
|
DosQueryFileInfo(file->fil_desc, 1, (PVOID) & buffer,
|
|
sizeof(buffer))) dos_error("DosQueryFileInfo", file,
|
|
isc_io_access_err, status,
|
|
0);
|
|
|
|
length = buffer.cbFile;
|
|
|
|
return file->fil_min_page - file->fil_fudge +
|
|
(length + dbb->dbb_page_size - 1) / dbb->dbb_page_size;
|
|
}
|
|
|
|
|
|
FIL PIO_open(DBB dbb,
|
|
TEXT * string,
|
|
SSHORT length,
|
|
SSHORT trace_flag,
|
|
BLK connection, TEXT * file_name, USHORT file_length)
|
|
{
|
|
/**************************************
|
|
*
|
|
* P I O _ o p e n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Open a database file. If a "connection" block is provided, use
|
|
* the connection to communication with a page/lock server.
|
|
*
|
|
**************************************/
|
|
PAG page;
|
|
ULONG desc, action, status;
|
|
ULONG open_mode;
|
|
TEXT temp_name[MAXPATHLEN];
|
|
|
|
if (check_network(string))
|
|
ERR_post(gds__unavailable, 0);
|
|
|
|
open_mode = OPEN_ACCESS_READWRITE | /* Access mode -- read/write */
|
|
OPEN_SHARE_DENYNONE | /* Share mode -- permit read/write */
|
|
OPEN_FLAGS_NOINHERIT | /* Inheritance mode -- don't */
|
|
OPEN_FLAGS_FAIL_ON_ERROR | /* Fail errors -- return code */
|
|
OPEN_FLAGS_WRITE_THROUGH | /* Write thru -- absolutely */
|
|
(0 << 15); /* DASD open -- no */
|
|
|
|
/* take care of non null-terminated case */
|
|
|
|
if (length) {
|
|
strncpy(temp_name, string, length);
|
|
temp_name[length] = '\0';
|
|
string = temp_name;
|
|
}
|
|
|
|
status = DosOpen(string, /* File pathname */
|
|
&desc, /* File handle */
|
|
&action, /* Action taken */
|
|
100, /* Initial size */
|
|
FILE_NORMAL, /* File attributes */
|
|
OPEN_ACTION_OPEN_IF_EXISTS, /* Open Flag (truncate if file exists) */
|
|
open_mode, /* Open mode (see above) */
|
|
0L); /* Reserved */
|
|
|
|
if (status)
|
|
ERR_post(isc_io_error,
|
|
gds_arg_string, "DosOpen (open)",
|
|
gds_arg_cstring, file_length, ERR_string(file_name,
|
|
file_length),
|
|
isc_arg_gds, isc_io_open_err, gds_arg_dos, status, 0);
|
|
|
|
return setup_file(dbb, string, length, desc);
|
|
}
|
|
|
|
|
|
int PIO_read(FIL file, BDB bdb, PAG page, STATUS * status_vector)
|
|
{
|
|
/**************************************
|
|
*
|
|
* P I O _ r e a d
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Read a data page. Oh wow.
|
|
*
|
|
**************************************/
|
|
ULONG status, actual_length;
|
|
DBB dbb;
|
|
|
|
dbb = bdb->bdb_dbb;
|
|
if (!(file = seek_file(file, bdb, status_vector)))
|
|
return FALSE;
|
|
|
|
#ifdef ISC_DATABASE_ENCRYPTION
|
|
if (dbb->dbb_encrypt_key) {
|
|
SLONG spare_buffer[MAX_PAGE_SIZE / sizeof(SLONG)];
|
|
|
|
if (status =
|
|
DosRead(file->fil_desc, spare_buffer, dbb->dbb_page_size,
|
|
&actual_length)) {
|
|
V4_MUTEX_UNLOCK(file->fil_mutex);
|
|
return dos_error("DosRead", file, isc_io_read_err, status,
|
|
status_vector);
|
|
}
|
|
|
|
(*dbb->dbb_decrypt) (dbb->dbb_encrypt_key->str_data,
|
|
spare_buffer, dbb->dbb_page_size, page);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (status =
|
|
DosRead(file->fil_desc, page, dbb->dbb_page_size,
|
|
&actual_length)) {
|
|
V4_MUTEX_UNLOCK(file->fil_mutex);
|
|
return dos_error("DosRead", file, isc_io_read_err, status,
|
|
status_vector);
|
|
}
|
|
}
|
|
|
|
V4_MUTEX_UNLOCK(file->fil_mutex);
|
|
|
|
if (actual_length != dbb->dbb_page_size)
|
|
return dos_error("DosRead truncated", file, isc_io_read_err, status,
|
|
status_vector);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
int PIO_write(FIL file, BDB bdb, PAG page, STATUS * status_vector)
|
|
{
|
|
/**************************************
|
|
*
|
|
* P I O _ w r i t e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Read a data page. Oh wow.
|
|
*
|
|
**************************************/
|
|
ULONG status, actual_length;
|
|
DBB dbb;
|
|
|
|
dbb = bdb->bdb_dbb;
|
|
if (!(file = seek_file(file, bdb, status_vector)))
|
|
return FALSE;
|
|
|
|
#ifdef ISC_DATABASE_ENCRYPTION
|
|
if (dbb->dbb_encrypt_key) {
|
|
SLONG spare_buffer[MAX_PAGE_SIZE / sizeof(SLONG)];
|
|
|
|
(*dbb->dbb_encrypt) (dbb->dbb_encrypt_key->str_data,
|
|
page, dbb->dbb_page_size, spare_buffer);
|
|
if (status =
|
|
DosWrite(file->fil_desc, spare_buffer, dbb->dbb_page_size,
|
|
&actual_length)) {
|
|
V4_MUTEX_UNLOCK(file->fil_mutex);
|
|
return dos_error("DosWrite", file, isc_io_write_err, status,
|
|
status_vector);
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (status =
|
|
DosWrite(file->fil_desc, page, dbb->dbb_page_size,
|
|
&actual_length)) {
|
|
V4_MUTEX_UNLOCK(file->fil_mutex);
|
|
return dos_error("DosWrite", file, isc_io_write_err, status,
|
|
status_vector);
|
|
}
|
|
}
|
|
|
|
V4_MUTEX_UNLOCK(file->fil_mutex);
|
|
|
|
if (actual_length != dbb->dbb_page_size)
|
|
return dos_error("DosWrite truncated", file, isc_io_write_err, status,
|
|
status_vector);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static BOOLEAN check_network(TEXT * file_name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* c h e c k _ n e t w o r k
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Try to determine whether path name given refers to a remote resource.
|
|
* This is necessary under OS/2 since remote path names (prefixed by two
|
|
* slashes, or higher than physical drive letters) may be used as ordinary
|
|
* file names. If the remote server has failed for any reason to make
|
|
* the connection, it should not be allowed here either.
|
|
* This routine checks for both the slash slash network prefix,
|
|
* and the drive letter redirection to network resources.
|
|
* Returns FALSE if local, TRUE if network.
|
|
*
|
|
**************************************/
|
|
|
|
if (file_name[0] == '\\' && file_name[1] == '\\')
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static FIL seek_file(FIL file, BDB bdb, STATUS * status_vector)
|
|
{
|
|
/**************************************
|
|
*
|
|
* s e e k _ f i l e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Given a buffer descriptor block, find the appropropriate
|
|
* file block and seek to the proper page in that file.
|
|
*
|
|
**************************************/
|
|
ULONG status;
|
|
ULONG page, offset, new_offset;
|
|
DBB dbb;
|
|
|
|
dbb = bdb->bdb_dbb;
|
|
page = bdb->bdb_page;
|
|
|
|
for (;; file = file->fil_next)
|
|
if (!file)
|
|
CORRUPT(158); /* msg 158 cannot sort on a field that does not exist */
|
|
else if (page >= file->fil_min_page && page <= file->fil_max_page)
|
|
break;
|
|
|
|
page -= file->fil_min_page - file->fil_fudge;
|
|
offset = page * dbb->dbb_page_size;
|
|
|
|
V4_MUTEX_LOCK(file->fil_mutex);
|
|
|
|
if (status =
|
|
DosSetFilePtr(file->fil_desc, offset, FILE_BEGIN, &new_offset)) {
|
|
V4_MUTEX_UNLOCK(file->fil_mutex);
|
|
return (FIL) dos_error("DosSetFilePtr", file, isc_io_access_err,
|
|
status, status_vector);
|
|
}
|
|
|
|
return file;
|
|
}
|
|
|
|
|
|
static FIL setup_file(
|
|
DBB dbb,
|
|
TEXT * file_name, USHORT file_length, ULONG desc)
|
|
{
|
|
/**************************************
|
|
*
|
|
* s e t u p _ f i l e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Set up file and lock blocks for a file.
|
|
*
|
|
**************************************/
|
|
FIL file;
|
|
LCK lock;
|
|
TEXT *p, *q;
|
|
|
|
/* Allocate file block and copy file name string */
|
|
|
|
file = FB_NEW_RPT(*dbb->dbb_permanent, file_length + 1) fil();
|
|
file->fil_desc = desc;
|
|
file->fil_length = file_length;
|
|
file->fil_max_page = -1;
|
|
MOVE_FAST(file_name, file->fil_string, file_length);
|
|
file->fil_string[file_length] = 0;
|
|
V4_MUTEX_INIT(file->fil_mutex);
|
|
|
|
/* If this isn't the primary file, we're done */
|
|
|
|
if (dbb->dbb_file)
|
|
return file;
|
|
|
|
/* Build unique lock string for file and construct lock block */
|
|
|
|
dbb->dbb_lock = lock = FB_NEW_RPT(*dbb->dbb_permanent, file_length) lck();
|
|
lock->lck_type = LCK_database;
|
|
lock->lck_owner_handle = LCK_get_owner_handle(NULL_TDBB, lock->lck_type);
|
|
lock->lck_object = reinterpret_cast<blk*>(dbb);
|
|
lock->lck_length = file_length;
|
|
lock->lck_ast = CCH_down_grade_dbb;
|
|
lock->lck_dbb = dbb;
|
|
MOVE_FAST(file_name, lock->lck_key.lck_string, file_length);
|
|
|
|
/* upcase the string to ensure equality on OS/2 */
|
|
|
|
for (p = lock->lck_key.lck_string; file_length--; p++)
|
|
*p = UPPER(*p);
|
|
|
|
/* Try to get an exclusive lock on database. If this fails, insist
|
|
on at least a shared lock */
|
|
|
|
dbb->dbb_flags |= DBB_exclusive;
|
|
if (!LCK_lock(NULL_TDBB, lock, LCK_EX, LCK_NO_WAIT)) {
|
|
dbb->dbb_flags &= ~DBB_exclusive;
|
|
LCK_lock(NULL_TDBB, lock,
|
|
(dbb->dbb_flags & DBB_cache_manager) ? LCK_SR : LCK_SW,
|
|
LCK_WAIT);
|
|
}
|
|
|
|
return file;
|
|
}
|
|
|
|
|
|
static BOOLEAN dos_error(
|
|
TEXT * string,
|
|
FIL file,
|
|
STATUS operation,
|
|
ULONG status, STATUS * status_vector)
|
|
{
|
|
/**************************************
|
|
*
|
|
* d o s _ e r r o r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Somebody has noticed a file system error and expects error
|
|
* to do something about it. Harumph!
|
|
*
|
|
**************************************/
|
|
|
|
if (status_vector) {
|
|
*status_vector++ = isc_arg_gds;
|
|
*status_vector++ = isc_io_error;
|
|
*status_vector++ = gds_arg_string;
|
|
*status_vector++ = (STATUS) string;
|
|
*status_vector++ = gds_arg_string;
|
|
*status_vector++ =
|
|
(STATUS) ERR_string(file->fil_string, file->fil_length);
|
|
*status_vector++ = isc_arg_gds;
|
|
*status_vector++ = operation;
|
|
*status_vector++ = gds_arg_dos;
|
|
*status_vector++ = status;
|
|
*status_vector++ = gds_arg_end;
|
|
return FALSE;
|
|
}
|
|
else
|
|
ERR_post(isc_io_error,
|
|
gds_arg_string, string,
|
|
gds_arg_string, ERR_string(file->fil_string,
|
|
file->fil_length), isc_arg_gds,
|
|
operation, gds_arg_dos, status, 0);
|
|
}
|
|
|
|
|
|
} // extern "C"
|