qse/lib/si/fio.c

1676 lines
36 KiB
C

/*
* $Id$
*
Copyright (c) 2006-2019 Chung, Hyung-Hwan. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <qse/si/fio.h>
#include <qse/cmn/str.h>
#include <qse/cmn/fmt.h>
#include <qse/cmn/alg.h>
#include <qse/cmn/time.h>
#include <qse/cmn/mbwc.h>
#include "../cmn/mem-prv.h"
#if defined(_WIN32)
# include <windows.h>
/*# include <psapi.h>*/ /* for GetMappedFileName(). but dynamically loaded */
# include <tchar.h>
# if !defined(INVALID_SET_FILE_POINTER)
# define INVALID_SET_FILE_POINTER ((DWORD)-1)
# endif
#elif defined(__OS2__)
# define INCL_DOSFILEMGR
# define INCL_DOSMODULEMGR
# define INCL_DOSPROCESS
# define INCL_DOSERRORS
# include <os2.h>
#elif defined(__DOS__)
# include <io.h>
# include <fcntl.h>
# include <errno.h>
#elif defined(vms) || defined(__vms)
# define __NEW_STARLET 1
# include <starlet.h>
# include <rms.h>
#else
# include "../cmn/syscall.h"
#endif
/* internal status codes */
enum
{
STATUS_APPEND = (1 << 0),
STATUS_NOCLOSE = (1 << 1),
STATUS_WIN32_STDIN = (1 << 2)
};
#include "../cmn/syserr.h"
IMPLEMENT_SYSERR_TO_ERRNUM (fio, FIO)
#if defined(_WIN32)
typedef DWORD WINAPI (*getmappedfilename_t) (
HANDLE hProcess,
LPVOID lpv,
LPTSTR lpFilename,
DWORD nSize
);
#elif defined(__OS2__)
#if defined(__WATCOMC__) && (__WATCOMC__ < 1200) && !defined(LONGLONG_INCLUDED)
typedef struct _LONGLONG {
ULONG ulLo;
LONG ulHi;
} LONGLONG, *PLONGLONG;
typedef struct _ULONGLONG {
ULONG ulLo;
ULONG ulHi;
} ULONGLONG, *PULONGLONG;
#endif
typedef APIRET APIENTRY (*dosopenl_t) (
PSZ pszFileName,
PHFILE pHf,
PULONG pulAction,
LONGLONG cbFile,
ULONG ulAttribute,
ULONG fsOpenFlags,
ULONG fsOpenMode,
PEAOP2 peaop2
);
typedef APIRET APIENTRY (*dossetfileptrl_t) (
HFILE hFile,
LONGLONG ib,
ULONG method,
PLONGLONG ibActual
);
typedef APIRET APIENTRY (*dossetfilesizel_t) (
HFILE hFile,
LONGLONG cbSize
);
static int dos_set = 0;
static dosopenl_t dos_open_l = QSE_NULL;
static dossetfileptrl_t dos_set_file_ptr_l = QSE_NULL;
static dossetfilesizel_t dos_set_file_size_l = QSE_NULL;
#endif
qse_fio_t* qse_fio_open (
qse_mmgr_t* mmgr, qse_size_t xtnsize,
const qse_char_t* path, int flags, int mode)
{
qse_fio_t* fio;
fio = QSE_MMGR_ALLOC (mmgr, QSE_SIZEOF(qse_fio_t) + xtnsize);
if (fio)
{
if (qse_fio_init (fio, mmgr, path, flags, mode) <= -1)
{
QSE_MMGR_FREE (mmgr, fio);
return QSE_NULL;
}
else QSE_MEMSET (QSE_XTN(fio), 0, xtnsize);
}
return fio;
}
void qse_fio_close (qse_fio_t* fio)
{
qse_fio_fini (fio);
QSE_MMGR_FREE (fio->mmgr, fio);
}
int qse_fio_init (
qse_fio_t* fio, qse_mmgr_t* mmgr,
const qse_char_t* path, int flags, int mode)
{
qse_fio_hnd_t handle;
qse_uint32_t temp_no;
qse_char_t* temp_ptr;
qse_size_t temp_tries;
#if defined(_WIN32)
int fellback = 0;
#endif
#if defined(__OS2__)
if (!dos_set)
{
DosEnterCritSec ();
if (!dos_set)
{
HMODULE mod;
if (DosLoadModule(NULL, 0, "DOSCALL1", &mod) == NO_ERROR)
{
/* look up routines by ordinal */
DosQueryProcAddr (mod, 981, NULL, (PFN*)&dos_open_l);
DosQueryProcAddr (mod, 988, NULL, (PFN*)&dos_set_file_ptr_l);
DosQueryProcAddr (mod, 989, NULL, (PFN*)&dos_set_file_size_l);
}
dos_set = 1;
}
DosExitCritSec ();
}
#endif
QSE_MEMSET (fio, 0, QSE_SIZEOF(*fio));
fio->mmgr = mmgr;
if (!(flags & (QSE_FIO_READ | QSE_FIO_WRITE | QSE_FIO_APPEND | QSE_FIO_HANDLE)))
{
/* one of QSE_FIO_READ, QSE_FIO_WRITE, QSE_FIO_APPEND,
* and QSE_FIO_HANDLE must be specified */
fio->errnum = QSE_FIO_EINVAL;
return -1;
}
/* Store some flags for later use */
if (flags & QSE_FIO_NOCLOSE)
fio->status |= STATUS_NOCLOSE;
if (flags & QSE_FIO_TEMPORARY)
{
qse_ntime_t now;
if (flags & (QSE_FIO_HANDLE | QSE_FIO_MBSPATH))
{
/* QSE_FIO_TEMPORARY and QSE_FIO_HANDLE/QSE_FIO_MBSPATH
* are mutually exclusive */
fio->errnum = QSE_FIO_EINVAL;
return -1;
}
temp_no = 0;
/* if QSE_FIO_TEMPORARY is used, the path name must
* be writable. */
for (temp_ptr = (qse_char_t*)path; *temp_ptr; temp_ptr++)
temp_no += *temp_ptr;
/* The path name template must be at least 4 characters long
* excluding the terminating null. this function fails if not */
if (temp_ptr - path < 4)
{
fio->errnum = QSE_FIO_EINVAL;
return -1;
}
qse_gettime (&now);
temp_no += (now.sec & 0xFFFFFFFFlu);
temp_tries = 0;
temp_ptr -= 4;
retry_temporary:
temp_tries++;
/* Fails after 5000 tries. 5000 randomly chosen */
if (temp_tries > 5000)
{
fio->errnum = QSE_FIO_EINVAL;
return -1;
}
/* Generate the next random number to use to make a
* new path name */
temp_no = qse_rand31 (temp_no);
/*
* You must not pass a constant string for a path name
* when QSE_FIO_TEMPORARY is set, because it changes
* the path name with a random number generated
*/
qse_fmtuintmax (
temp_ptr,
4,
temp_no % 0x10000,
16 | QSE_FMTUINTMAX_NOTRUNC | QSE_FMTUINTMAX_NONULL,
4,
QSE_T('\0'),
QSE_NULL
);
}
#if defined(_WIN32)
if (flags & QSE_FIO_HANDLE)
{
handle = *(qse_fio_hnd_t*)path;
QSE_ASSERTX (
handle != INVALID_HANDLE_VALUE,
"Do not specify an invalid handle value"
);
if (handle == GetStdHandle (STD_INPUT_HANDLE))
fio->status |= STATUS_WIN32_STDIN;
}
else
{
DWORD desired_access = 0;
DWORD share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
DWORD creation_disposition = 0;
DWORD flag_and_attr = FILE_ATTRIBUTE_NORMAL;
if (fellback) share_mode &= ~FILE_SHARE_DELETE;
if (flags & QSE_FIO_APPEND)
{
if (fellback)
{
desired_access |= GENERIC_WRITE;
}
else
{
/* this is not officially documented for CreateFile.
* ZwCreateFile (kernel) seems to document it */
fio->status &= ~STATUS_APPEND;
desired_access |= FILE_APPEND_DATA;
}
}
else if (flags & QSE_FIO_WRITE)
{
/* In WIN32, FILE_APPEND_DATA and GENERIC_WRITE can't
* be used together */
desired_access |= GENERIC_WRITE;
}
if (flags & QSE_FIO_READ) desired_access |= GENERIC_READ;
if (flags & QSE_FIO_CREATE)
{
creation_disposition =
(flags & QSE_FIO_EXCLUSIVE)? CREATE_NEW:
(flags & QSE_FIO_TRUNCATE)? CREATE_ALWAYS: OPEN_ALWAYS;
}
else if (flags & QSE_FIO_TRUNCATE)
{
creation_disposition = TRUNCATE_EXISTING;
}
else creation_disposition = OPEN_EXISTING;
if (flags & QSE_FIO_NOSHREAD)
share_mode &= ~FILE_SHARE_READ;
if (flags & QSE_FIO_NOSHWRITE)
share_mode &= ~FILE_SHARE_WRITE;
if (flags & QSE_FIO_NOSHDELETE)
share_mode &= ~FILE_SHARE_DELETE;
if (!(mode & QSE_FIO_WUSR))
flag_and_attr = FILE_ATTRIBUTE_READONLY;
if (flags & QSE_FIO_SYNC)
flag_and_attr |= FILE_FLAG_WRITE_THROUGH;
#if defined(FILE_FLAG_OPEN_REPARSE_POINT)
if (flags & QSE_FIO_NOFOLLOW)
flag_and_attr |= FILE_FLAG_OPEN_REPARSE_POINT;
#endif
/* these two are just hints to OS */
if (flags & QSE_FIO_RANDOM)
flag_and_attr |= FILE_FLAG_RANDOM_ACCESS;
if (flags & QSE_FIO_SEQUENTIAL)
flag_and_attr |= FILE_FLAG_SEQUENTIAL_SCAN;
if (flags & QSE_FIO_MBSPATH)
{
handle = CreateFileA (
(const qse_mchar_t*)path, desired_access, share_mode,
QSE_NULL, /* set noinherit by setting no secattr */
creation_disposition, flag_and_attr, 0
);
}
else
{
handle = CreateFile (
path, desired_access, share_mode,
QSE_NULL, /* set noinherit by setting no secattr */
creation_disposition, flag_and_attr, 0
);
}
if (handle == INVALID_HANDLE_VALUE)
{
DWORD e = GetLastError();
if (!fellback && e == ERROR_INVALID_PARAMETER &&
((share_mode & FILE_SHARE_DELETE) || (flags & QSE_FIO_APPEND)))
{
/* old windows fails with ERROR_INVALID_PARAMETER
* when some advanced flags are used. so try again
* with fallback flags */
fellback = 1;
share_mode &= ~FILE_SHARE_DELETE;
if (flags & QSE_FIO_APPEND)
{
fio->status |= STATUS_APPEND;
desired_access &= ~FILE_APPEND_DATA;
desired_access |= GENERIC_WRITE;
}
if (flags & QSE_FIO_MBSPATH)
{
handle = CreateFileA (
(const qse_mchar_t*)path, desired_access, share_mode,
QSE_NULL, /* set noinherit by setting no secattr */
creation_disposition, flag_and_attr, 0
);
}
else
{
handle = CreateFile (
path, desired_access, share_mode,
QSE_NULL, /* set noinherit by setting no secattr */
creation_disposition, flag_and_attr, 0
);
}
if (handle == INVALID_HANDLE_VALUE)
{
if (flags & QSE_FIO_TEMPORARY) goto retry_temporary;
fio->errnum = syserr_to_errnum(GetLastError());
return -1;
}
}
else
{
if (flags & QSE_FIO_TEMPORARY) goto retry_temporary;
fio->errnum = syserr_to_errnum(e);
return -1;
}
}
}
/* some special check */
#if 0
if (GetFileType(handle) == FILE_TYPE_UNKNOWN)
{
fio->errnum = syserr_to_errnum(GetLastError());
CloseHandle (handle);
return -1;
}
#endif
/* TODO: support more features on WIN32 - TEMPORARY, DELETE_ON_CLOSE */
#elif defined(__OS2__)
if (flags & QSE_FIO_HANDLE)
{
handle = *(qse_fio_hnd_t*)path;
}
else
{
APIRET ret;
ULONG action_taken = 0;
ULONG open_action, open_mode, open_attr;
#if defined(QSE_CHAR_IS_MCHAR)
const qse_mchar_t* path_mb = path;
#else
qse_mchar_t path_mb_buf[CCHMAXPATH];
qse_mchar_t* path_mb;
qse_size_t wl, ml;
int px;
if (flags & QSE_FIO_MBSPATH)
{
path_mb = (qse_mchar_t*)path;
}
else
{
path_mb = path_mb_buf;
ml = QSE_COUNTOF(path_mb_buf);
px = qse_wcstombs (path, &wl, path_mb, &ml);
if (px == -2)
{
/* the static buffer is too small.
* dynamically allocate a buffer */
path_mb = qse_wcstombsdup (path, QSE_NULL, mmgr);
if (path_mb == QSE_NULL)
{
fio->errnum = QSE_FIO_ENOMEM;
return -1;
}
}
else if (px <= -1)
{
fio->errnum = QSE_FIO_EINVAL;
return -1;
}
}
#endif
if (flags & QSE_FIO_APPEND)
fio->status |= STATUS_APPEND;
if (flags & QSE_FIO_CREATE)
{
if (flags & QSE_FIO_EXCLUSIVE)
{
open_action = OPEN_ACTION_FAIL_IF_EXISTS |
OPEN_ACTION_CREATE_IF_NEW;
}
else if (flags & QSE_FIO_TRUNCATE)
{
open_action = OPEN_ACTION_REPLACE_IF_EXISTS |
OPEN_ACTION_CREATE_IF_NEW;
}
else
{
open_action = OPEN_ACTION_CREATE_IF_NEW |
OPEN_ACTION_OPEN_IF_EXISTS;
}
}
else if (flags & QSE_FIO_TRUNCATE)
{
open_action = OPEN_ACTION_REPLACE_IF_EXISTS |
OPEN_ACTION_FAIL_IF_NEW;
}
else
{
open_action = OPEN_ACTION_OPEN_IF_EXISTS |
OPEN_ACTION_FAIL_IF_NEW;
}
open_mode = OPEN_FLAGS_NOINHERIT;
if (flags & QSE_FIO_SYNC)
open_mode |= OPEN_FLAGS_WRITE_THROUGH;
if ((flags & QSE_FIO_NOSHREAD) && (flags & QSE_FIO_NOSHWRITE))
open_mode |= OPEN_SHARE_DENYREADWRITE;
else if (flags & QSE_FIO_NOSHREAD)
open_mode |= OPEN_SHARE_DENYREAD;
else if (flags & QSE_FIO_NOSHWRITE)
open_mode |= OPEN_SHARE_DENYWRITE;
else
open_mode |= OPEN_SHARE_DENYNONE;
if ((flags & QSE_FIO_READ) &&
(flags & QSE_FIO_WRITE)) open_mode |= OPEN_ACCESS_READWRITE;
else if (flags & QSE_FIO_READ) open_mode |= OPEN_ACCESS_READONLY;
else if (flags & QSE_FIO_WRITE) open_mode |= OPEN_ACCESS_WRITEONLY;
open_attr = (mode & QSE_FIO_WUSR)? FILE_NORMAL: FILE_READONLY;
#if defined(FIL_STANDARDL)
if (dos_open_l)
{
LONGLONG zero;
zero.ulLo = 0;
zero.ulHi = 0;
ret = dos_open_l (
path_mb, /* file name */
&handle, /* file handle */
&action_taken, /* store action taken */
zero, /* size */
open_attr, /* attribute */
open_action, /* action if it exists */
open_mode, /* open mode */
0L
);
}
else
{
#endif
ret = DosOpen (
path_mb, /* file name */
&handle, /* file handle */
&action_taken, /* store action taken */
0, /* size */
open_attr, /* attribute */
open_action, /* action if it exists */
open_mode, /* open mode */
0L
);
#if defined(FIL_STANDARDL)
}
#endif
#if defined(QSE_CHAR_IS_MCHAR)
/* nothing to do */
#else
if (path_mb != path_mb_buf) QSE_MMGR_FREE (mmgr, path_mb);
#endif
if (ret != NO_ERROR)
{
if (flags & QSE_FIO_TEMPORARY) goto retry_temporary;
fio->errnum = syserr_to_errnum(ret);
return -1;
}
}
#elif defined(__DOS__)
if (flags & QSE_FIO_HANDLE)
{
handle = *(qse_fio_hnd_t*)path;
QSE_ASSERTX (
handle >= 0,
"Do not specify an invalid handle value"
);
}
else
{
int oflags = 0;
int permission = 0;
#if defined(QSE_CHAR_IS_MCHAR)
const qse_mchar_t* path_mb = path;
#else
qse_mchar_t path_mb_buf[_MAX_PATH];
qse_mchar_t* path_mb;
qse_size_t wl, ml;
int px;
if (flags & QSE_FIO_MBSPATH)
{
path_mb = (qse_mchar_t*)path;
}
else
{
path_mb = path_mb_buf;
ml = QSE_COUNTOF(path_mb_buf);
px = qse_wcstombs (path, &wl, path_mb, &ml);
if (px == -2)
{
/* static buffer size not enough.
* switch to dynamic allocation */
path_mb = qse_wcstombsdup (path, QSE_NULL, mmgr);
if (path_mb == QSE_NULL)
{
fio->errnum = QSE_FIO_ENOMEM;
return -1;
}
}
else if (px <= -1)
{
fio->errnum = QSE_FIO_EINVAL;
return -1;
}
}
#endif
if (flags & QSE_FIO_APPEND)
{
if ((flags & QSE_FIO_READ)) oflags |= O_RDWR;
else oflags |= O_WRONLY;
oflags |= O_APPEND;
}
else
{
if ((flags & QSE_FIO_READ) &&
(flags & QSE_FIO_WRITE)) oflags |= O_RDWR;
else if (flags & QSE_FIO_READ) oflags |= O_RDONLY;
else if (flags & QSE_FIO_WRITE) oflags |= O_WRONLY;
}
if (flags & QSE_FIO_CREATE) oflags |= O_CREAT;
if (flags & QSE_FIO_TRUNCATE) oflags |= O_TRUNC;
if (flags & QSE_FIO_EXCLUSIVE) oflags |= O_EXCL;
oflags |= O_BINARY | O_NOINHERIT;
if (mode & QSE_FIO_RUSR) permission |= S_IREAD;
if (mode & QSE_FIO_WUSR) permission |= S_IWRITE;
handle = open (
path_mb,
oflags,
permission
);
#if defined(QSE_CHAR_IS_MCHAR)
/* nothing to do */
#else
if (path_mb != path_mb_buf) QSE_MMGR_FREE (mmgr, path_mb);
#endif
if (handle <= -1)
{
if (flags & QSE_FIO_TEMPORARY) goto retry_temporary;
fio->errnum = syserr_to_errnum (errno);
return -1;
}
}
#elif defined(vms) || defined(__vms)
if (flags & QSE_FIO_HANDLE)
{
/* TODO: implement this */
fio->errnum = QSE_FIO_ENOIMPL;
return -1;
}
else
{
struct FAB* fab;
struct RAB* rab;
unsigned long r0;
#if defined(QSE_CHAR_IS_MCHAR)
const qse_mchar_t* path_mb = path;
#else
qse_mchar_t path_mb_buf[1024];
qse_mchar_t* path_mb;
qse_size_t wl, ml;
int px;
if (flags & QSE_FIO_MBSPATH)
{
path_mb = (qse_mchar_t*)path;
}
else
{
path_mb = path_mb_buf;
ml = QSE_COUNTOF(path_mb_buf);
px = qse_wcstombs (path, &wl, path_mb, &ml);
if (px == -2)
{
/* the static buffer is too small.
* allocate a buffer */
path_mb = qse_wcstombsdup (path, mmgr);
if (path_mb == QSE_NULL)
{
fio->errnum = QSE_FIO_ENOMEM;
return -1;
}
}
else if (px <= -1)
{
fio->errnum = QSE_FIO_EINVAL;
return -1;
}
}
#endif
rab = (struct RAB*)QSE_MMGR_ALLOC (
mmgr, QSE_SIZEOF(*rab) + QSE_SIZEOF(*fab));
if (rab == QSE_NULL)
{
#if defined(QSE_CHAR_IS_MCHAR)
/* nothing to do */
#else
if (path_mb != path_mb_buf) QSE_MMGR_FREE (mmgr, path_mb);
#endif
fio->errnum = QSE_FIO_ENOMEM;
return -1;
}
fab = (struct FAB*)(rab + 1);
*rab = cc$rms_rab;
rab->rab$l_fab = fab;
*fab = cc$rms_fab;
fab->fab$l_fna = path_mb;
fab->fab$b_fns = strlen(path_mb);
fab->fab$b_org = FAB$C_SEQ;
fab->fab$b_rfm = FAB$C_VAR; /* FAB$C_STM, FAB$C_STMLF, FAB$C_VAR, etc... */
fab->fab$b_fac = FAB$M_GET | FAB$M_PUT;
fab->fab$b_fac = FAB$M_NIL;
if (flags & QSE_FIO_READ) fab->fab$b_fac |= FAB$M_GET;
if (flags & (QSE_FIO_WRITE | QSE_FIO_APPEND)) fab->fab$b_fac |= FAB$M_PUT | FAB$M_TRN; /* put, truncate */
fab->fab$b_shr |= FAB$M_SHRPUT | FAB$M_SHRGET; /* FAB$M_NIL */
if (flags & QSE_FIO_NOSHREAD) fab->fab$b_shr &= ~FAB$M_SHRGET;
if (flags & QSE_FIO_NOSHWRITE) fab->fab$b_shr &= ~FAB$M_SHRPUT;
if (flags & QSE_FIO_APPEND) rab->rab$l_rop |= RAB$M_EOF;
if (flags & QSE_FIO_CREATE)
{
if (flags & QSE_FIO_EXCLUSIVE)
fab->fab$l_fop &= ~FAB$M_CIF;
else
fab->fab$l_fop |= FAB$M_CIF;
r0 = sys$create (&fab, 0, 0);
}
else
{
r0 = sys$open (&fab, 0, 0);
}
if (r0 != RMS$_NORMAL && r0 != RMS$_CREATED)
{
#if defined(QSE_CHAR_IS_MCHAR)
/* nothing to do */
#else
if (path_mb != path_mb_buf) QSE_MMGR_FREE (mmgr, path_mb);
#endif
fio->errnum = syserr_to_errnum (r0);
return -1;
}
r0 = sys$connect (&rab, 0, 0);
if (r0 != RMS$_NORMAL)
{
#if defined(QSE_CHAR_IS_MCHAR)
/* nothing to do */
#else
if (path_mb != path_mb_buf) QSE_MMGR_FREE (mmgr, path_mb);
#endif
fio->errnum = syserr_to_errnum (r0);
return -1;
}
#if defined(QSE_CHAR_IS_MCHAR)
/* nothing to do */
#else
if (path_mb != path_mb_buf) QSE_MMGR_FREE (mmgr, path_mb);
#endif
handle = rab;
}
#else
if (flags & QSE_FIO_HANDLE)
{
handle = *(qse_fio_hnd_t*)path;
QSE_ASSERTX (
handle >= 0,
"Do not specify an invalid handle value"
);
}
else
{
int desired_access = 0;
#if defined(QSE_CHAR_IS_MCHAR)
const qse_mchar_t* path_mb = path;
#else
qse_mchar_t path_mb_buf[1024]; /* PATH_MAX instead? */
qse_mchar_t* path_mb;
qse_size_t wl, ml;
int px;
if (flags & QSE_FIO_MBSPATH)
{
path_mb = (qse_mchar_t*)path;
}
else
{
path_mb = path_mb_buf;
ml = QSE_COUNTOF(path_mb_buf);
px = qse_wcstombs (path, &wl, path_mb, &ml);
if (px == -2)
{
/* the static buffer is too small.
* allocate a buffer */
path_mb = qse_wcstombsdup (path, QSE_NULL, mmgr);
if (path_mb == QSE_NULL)
{
fio->errnum = QSE_FIO_ENOMEM;
return -1;
}
}
else if (px <= -1)
{
fio->errnum = QSE_FIO_EINVAL;
return -1;
}
}
#endif
/*
* rwa -> RDWR | APPEND
* ra -> RDWR | APPEND
* wa -> WRONLY | APPEND
* a -> WRONLY | APPEND
*/
if (flags & QSE_FIO_APPEND)
{
if ((flags & QSE_FIO_READ)) desired_access |= O_RDWR;
else desired_access |= O_WRONLY;
desired_access |= O_APPEND;
}
else
{
if ((flags & QSE_FIO_READ) &&
(flags & QSE_FIO_WRITE)) desired_access |= O_RDWR;
else if (flags & QSE_FIO_READ) desired_access |= O_RDONLY;
else if (flags & QSE_FIO_WRITE) desired_access |= O_WRONLY;
}
if (flags & QSE_FIO_CREATE) desired_access |= O_CREAT;
if (flags & QSE_FIO_TRUNCATE) desired_access |= O_TRUNC;
if (flags & QSE_FIO_EXCLUSIVE) desired_access |= O_EXCL;
#if defined(O_SYNC)
if (flags & QSE_FIO_SYNC) desired_access |= O_SYNC;
#endif
#if defined(O_NOFOLLOW)
if (flags & QSE_FIO_NOFOLLOW) desired_access |= O_NOFOLLOW;
#endif
#if defined(O_LARGEFILE)
desired_access |= O_LARGEFILE;
#endif
#if defined(O_CLOEXEC)
desired_access |= O_CLOEXEC; /* no inherit */
#endif
handle = QSE_OPEN (path_mb, desired_access, mode);
#if defined(QSE_CHAR_IS_MCHAR)
/* nothing to do */
#else
if (path_mb != path_mb_buf && path_mb != path)
{
QSE_MMGR_FREE (mmgr, path_mb);
}
#endif
if (handle == -1)
{
if (flags & QSE_FIO_TEMPORARY) goto retry_temporary;
fio->errnum = syserr_to_errnum (errno);
return -1;
}
else
{
#if !defined(O_CLOEXEC) && defined(FD_CLOEXEC)
int flag = fcntl(handle, F_GETFD);
if (flag >= 0) fcntl (handle, F_SETFD, flag | FD_CLOEXEC);
#endif
}
/* set some file access hints */
#if defined(POSIX_FADV_RANDOM)
if (flags & QSE_FIO_RANDOM)
posix_fadvise (handle, 0, 0, POSIX_FADV_RANDOM);
#endif
#if defined(POSIX_FADV_SEQUENTIAL)
if (flags & QSE_FIO_SEQUENTIAL)
posix_fadvise (handle, 0, 0, POSIX_FADV_SEQUENTIAL);
#endif
}
#endif
fio->handle = handle;
return 0;
}
void qse_fio_fini (qse_fio_t* fio)
{
if (!(fio->status & STATUS_NOCLOSE))
{
#if defined(_WIN32)
CloseHandle (fio->handle);
#elif defined(__OS2__)
DosClose (fio->handle);
#elif defined(__DOS__)
close (fio->handle);
#elif defined(vms) || defined(__vms)
struct RAB* rab = (struct RAB*)fio->handle;
sys$disconnect (rab, 0, 0);
sys$close ((struct FAB*)(rab + 1), 0, 0);
QSE_MMGR_FREE (fio->mmgr, fio->handle);
#else
QSE_CLOSE (fio->handle);
#endif
}
}
qse_mmgr_t* qse_fio_getmmgr (qse_fio_t* fio)
{
return fio->mmgr;
}
void* qse_fio_getxtn (qse_fio_t* fio)
{
return QSE_XTN (fio);
}
qse_fio_errnum_t qse_fio_geterrnum (const qse_fio_t* fio)
{
return fio->errnum;
}
qse_fio_hnd_t qse_fio_gethnd (const qse_fio_t* fio)
{
return fio->handle;
}
qse_fio_off_t qse_fio_seek (
qse_fio_t* fio, qse_fio_off_t offset, qse_fio_ori_t origin)
{
#if defined(_WIN32)
static int seek_map[] =
{
FILE_BEGIN,
FILE_CURRENT,
FILE_END
};
LARGE_INTEGER x;
#if defined(_WIN64)
LARGE_INTEGER y;
#endif
QSE_ASSERT (QSE_SIZEOF(offset) <= QSE_SIZEOF(x.QuadPart));
#if defined(_WIN64)
x.QuadPart = offset;
if (SetFilePointerEx (fio->handle, x, &y, seek_map[origin]) == FALSE)
{
return (qse_fio_off_t)-1;
}
return (qse_fio_off_t)y.QuadPart;
#else
/* SetFilePointerEx is not available on Windows NT 4.
* So let's use SetFilePointer */
x.QuadPart = offset;
x.LowPart = SetFilePointer (
fio->handle, x.LowPart, &x.HighPart, seek_map[origin]);
if (x.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
{
fio->errnum = syserr_to_errnum (GetLastError());
return (qse_fio_off_t)-1;
}
return (qse_fio_off_t)x.QuadPart;
#endif
#elif defined(__OS2__)
static int seek_map[] =
{
FILE_BEGIN,
FILE_CURRENT,
FILE_END
};
#if defined(FIL_STANDARDL)
if (dos_set_file_ptr_l)
{
LONGLONG pos, newpos;
APIRET ret;
QSE_ASSERT (QSE_SIZEOF(offset) >= QSE_SIZEOF(pos));
pos.ulLo = (ULONG)(offset&0xFFFFFFFFlu);
pos.ulHi = (ULONG)(offset>>32);
ret = dos_set_file_ptr_l (fio->handle, pos, seek_map[origin], &newpos);
if (ret != NO_ERROR)
{
fio->errnum = syserr_to_errnum (ret);
return (qse_fio_off_t)-1;
}
return ((qse_fio_off_t)newpos.ulHi << 32) | newpos.ulLo;
}
else
{
#endif
ULONG newpos;
APIRET ret;
ret = DosSetFilePtr (fio->handle, offset, seek_map[origin], &newpos);
if (ret != NO_ERROR)
{
fio->errnum = syserr_to_errnum (ret);
return (qse_fio_off_t)-1;
}
return newpos;
#if defined(FIL_STANDARDL)
}
#endif
#elif defined(__DOS__)
static int seek_map[] =
{
SEEK_SET,
SEEK_CUR,
SEEK_END
};
return lseek (fio->handle, offset, seek_map[origin]);
#elif defined(vms) || defined(__vms)
/* TODO: */
fio->errnum = QSE_FIO_ENOIMPL;
return (qse_fio_off_t)-1;
#else
static int seek_map[] =
{
SEEK_SET,
SEEK_CUR,
SEEK_END
};
#if defined(QSE_LLSEEK)
loff_t tmp;
if (QSE_LLSEEK (fio->handle,
(unsigned long)(offset>>32),
(unsigned long)(offset&0xFFFFFFFFlu),
&tmp,
seek_map[origin]) == -1)
{
fio->errnum = syserr_to_errnum (errno);
return (qse_fio_off_t)-1;
}
return (qse_fio_off_t)tmp;
#else
return QSE_LSEEK (fio->handle, offset, seek_map[origin]);
#endif
#endif
}
int qse_fio_truncate (qse_fio_t* fio, qse_fio_off_t size)
{
#if defined(_WIN32)
if (qse_fio_seek (fio, size, QSE_FIO_BEGIN) == (qse_fio_off_t)-1) return -1;
if (SetEndOfFile(fio->handle) == FALSE)
{
fio->errnum = syserr_to_errnum (GetLastError());
return -1;
}
return 0;
#elif defined(__OS2__)
APIRET ret;
#if defined(FIL_STANDARDL)
if (dos_set_file_size_l)
{
LONGLONG sz;
/* the file must have the write access for it to succeed */
sz.ulLo = (ULONG)(size&0xFFFFFFFFlu);
sz.ulHi = (ULONG)(size>>32);
ret = dos_set_file_size_l (fio->handle, sz);
}
else
{
#endif
ret = DosSetFileSize (fio->handle, size);
#if defined(FIL_STANDARDL)
}
#endif
if (ret != NO_ERROR)
{
fio->errnum = syserr_to_errnum (ret);
return -1;
}
return 0;
#elif defined(__DOS__)
int n;
n = chsize (fio->handle, size);
if (n <= -1) fio->errnum = syserr_to_errnum (errno);
return n;
#elif defined(vms) || defined(__vms)
unsigned long r0;
struct RAB* rab = (struct RAB*)fio->handle;
if ((r0 = sys$rewind (rab, 0, 0)) != RMS$_NORMAL ||
(r0 = sys$truncate (rab, 0, 0)) != RMS$_NORMAL)
{
fio->errnum = syserr_to_errnum (r0);
return -1;
}
return 0;
#elif defined(HAVE_FTRUNCATE)
int n;
n = QSE_FTRUNCATE (fio->handle, size);
if (n <= -1) fio->errnum = syserr_to_errnum (errno);
return n;
#else
fio->errnum = QSE_FIO_ENOIMPL;
return -1;
#endif
}
qse_ssize_t qse_fio_read (qse_fio_t* fio, void* buf, qse_size_t size)
{
#if defined(_WIN32)
DWORD count;
if (size > (QSE_TYPE_MAX(qse_ssize_t) & QSE_TYPE_MAX(DWORD)))
size = QSE_TYPE_MAX(qse_ssize_t) & QSE_TYPE_MAX(DWORD);
if (ReadFile (fio->handle, buf, (DWORD)size, &count, QSE_NULL) == FALSE)
{
DWORD e = GetLastError();
/* special case when ReadFile returns failure with ERROR_BROKEN_PIPE.
* this happens when an anonymous pipe is a standard input for redirection.
* assuming that ERROR_BROKEN_PIPE doesn't occur with normal
* input streams, i treat the condition as a normal EOF indicator. */
if ((fio->status & STATUS_WIN32_STDIN) && e == ERROR_BROKEN_PIPE) return 0;
fio->errnum = syserr_to_errnum (e);
return -1;
}
return (qse_ssize_t)count;
#elif defined(__OS2__)
APIRET ret;
ULONG count;
if (size > (QSE_TYPE_MAX(qse_ssize_t) & QSE_TYPE_MAX(ULONG)))
size = QSE_TYPE_MAX(qse_ssize_t) & QSE_TYPE_MAX(ULONG);
ret = DosRead (fio->handle, buf, (ULONG)size, &count);
if (ret != NO_ERROR)
{
fio->errnum = syserr_to_errnum (ret);
return -1;
}
return (qse_ssize_t)count;
#elif defined(__DOS__)
int n;
if (size > (QSE_TYPE_MAX(qse_ssize_t) & QSE_TYPE_MAX(unsigned int)))
size = QSE_TYPE_MAX(qse_ssize_t) & QSE_TYPE_MAX(unsigned int);
n = read (fio->handle, buf, size);
if (n <= -1) fio->errnum = syserr_to_errnum (errno);
return n;
#elif defined(vms) || defined(__vms)
unsigned long r0;
struct RAB* rab = (struct RAB*)fio->handle;
if (size > 32767) size = 32767;
rab->rab$l_ubf = buf;
rab->rab$w_usz = size;
r0 = sys$get (rab, 0, 0);
if (r0 != RMS$_NORMAL)
{
fio->errnum = syserr_to_errnum (r0);
return -1;
}
return rab->rab$w_rsz;
#else
qse_ssize_t n;
if (size > QSE_TYPE_MAX(qse_ssize_t)) size = QSE_TYPE_MAX(qse_ssize_t);
n = QSE_READ (fio->handle, buf, size);
if (n <= -1) fio->errnum = syserr_to_errnum (errno);
return n;
#endif
}
qse_ssize_t qse_fio_write (qse_fio_t* fio, const void* data, qse_size_t size)
{
#if defined(_WIN32)
DWORD count;
if (fio->status & STATUS_APPEND)
{
/* TODO: only when FILE_APPEND_DATA failed??? how do i know this??? */
/* i do this on a best-effort basis */
#if defined(_WIN64)
LARGE_INTEGER x;
x.QuadPart = 0;
SetFilePointerEx (fio->handle, x, QSE_NULL, FILE_END);
#else
SetFilePointer (fio->handle, 0, QSE_NULL, FILE_END);
#endif
}
if (size > (QSE_TYPE_MAX(qse_ssize_t) & QSE_TYPE_MAX(DWORD)))
size = QSE_TYPE_MAX(qse_ssize_t) & QSE_TYPE_MAX(DWORD);
if (WriteFile (fio->handle,
data, (DWORD)size, &count, QSE_NULL) == FALSE)
{
fio->errnum = syserr_to_errnum (GetLastError());
return -1;
}
return (qse_ssize_t)count;
#elif defined(__OS2__)
APIRET ret;
ULONG count;
if (fio->status & STATUS_APPEND)
{
/* i do this on a best-effort basis */
#if defined(FIL_STANDARDL)
if (dos_set_file_ptr_l)
{
LONGLONG pos, newpos;
pos.ulLo = (ULONG)0;
pos.ulHi = (ULONG)0;
dos_set_file_ptr_l (fio->handle, pos, FILE_END, &newpos);
}
else
{
#endif
ULONG newpos;
DosSetFilePtr (fio->handle, 0, FILE_END, &newpos);
#if defined(FIL_STANDARDL)
}
#endif
}
if (size > (QSE_TYPE_MAX(qse_ssize_t) & QSE_TYPE_MAX(ULONG)))
size = QSE_TYPE_MAX(qse_ssize_t) & QSE_TYPE_MAX(ULONG);
ret = DosWrite(fio->handle,
(PVOID)data, (ULONG)size, &count);
if (ret != NO_ERROR)
{
fio->errnum = syserr_to_errnum (ret);
return -1;
}
return (qse_ssize_t)count;
#elif defined(__DOS__)
int n;
if (size > (QSE_TYPE_MAX(qse_ssize_t) & QSE_TYPE_MAX(unsigned int)))
size = QSE_TYPE_MAX(qse_ssize_t) & QSE_TYPE_MAX(unsigned int);
n = write (fio->handle, data, size);
if (n <= -1) fio->errnum = syserr_to_errnum (errno);
return n;
#elif defined(vms) || defined(__vms)
unsigned long r0;
struct RAB* rab = (struct RAB*)fio->handle;
if (size > 32767) size = 32767;
rab->rab$l_rbf = (char*)data;
rab->rab$w_rsz = size;
r0 = sys$put (rab, 0, 0);
if (r0 != RMS$_NORMAL)
{
fio->errnum = syserr_to_errnum (r0);
return -1;
}
return rab->rab$w_rsz;
#else
qse_ssize_t n;
if (size > QSE_TYPE_MAX(qse_ssize_t)) size = QSE_TYPE_MAX(qse_ssize_t);
n = QSE_WRITE (fio->handle, data, size);
if (n <= -1) fio->errnum = syserr_to_errnum (errno);
return n;
#endif
}
#if defined(_WIN32)
static int get_devname_from_handle (
qse_fio_t* fio, qse_char_t* buf, qse_size_t len)
{
HANDLE map = NULL;
void* mem = NULL;
DWORD olen;
HINSTANCE psapi;
getmappedfilename_t getmappedfilename;
/* try to load psapi.dll dynamially for
* systems without it. direct linking to the library
* may end up with dependency failure on such systems.
* this way, the worst case is that this function simply
* fails. */
psapi = LoadLibrary (QSE_T("PSAPI.DLL"));
if (!psapi)
{
fio->errnum = syserr_to_errnum (GetLastError());
return -1;
}
getmappedfilename = (getmappedfilename_t)
GetProcAddress (psapi, QSE_MT("GetMappedFileName"));
if (!getmappedfilename)
{
fio->errnum = syserr_to_errnum (GetLastError());
FreeLibrary (psapi);
return -1;
}
/* create a file mapping object */
map = CreateFileMapping (
fio->handle,
NULL,
PAGE_READONLY,
0,
1,
NULL
);
if (map == NULL)
{
fio->errnum = syserr_to_errnum (GetLastError());
FreeLibrary (psapi);
return -1;
}
/* create a file mapping to get the file name. */
mem = MapViewOfFile (map, FILE_MAP_READ, 0, 0, 1);
if (mem == NULL)
{
fio->errnum = syserr_to_errnum (GetLastError());
CloseHandle (map);
FreeLibrary (psapi);
return -1;
}
olen = getmappedfilename (GetCurrentProcess(), mem, buf, len);
if (olen == 0)
{
fio->errnum = syserr_to_errnum (GetLastError());
UnmapViewOfFile (mem);
CloseHandle (map);
FreeLibrary (psapi);
return -1;
}
UnmapViewOfFile (mem);
CloseHandle (map);
FreeLibrary (psapi);
return 0;
}
static int get_volname_from_handle (
qse_fio_t* fio, qse_char_t* buf, qse_size_t len)
{
if (get_devname_from_handle (fio, buf, len) == -1) return -1;
if (qse_strcasebeg (buf, QSE_T("\\Device\\LanmanRedirector\\")))
{
/*buf[0] = QSE_T('\\');*/
qse_strcpy (&buf[1], &buf[24]);
}
else
{
DWORD n;
qse_char_t drives[128];
n = GetLogicalDriveStrings (QSE_COUNTOF(drives), drives);
if (n == 0 /* error */ ||
n > QSE_COUNTOF(drives) /* buffer small */)
{
fio->errnum = syserr_to_errnum (GetLastError());
return -1;
}
while (n > 0)
{
qse_char_t drv[3];
qse_char_t path[MAX_PATH];
drv[0] = drives[--n];
drv[1] = QSE_T(':');
drv[2] = QSE_T('\0');
if (QueryDosDevice (drv, path, QSE_COUNTOF(path)))
{
qse_size_t pl = qse_strlen(path);
qse_size_t bl = qse_strlen(buf);
if (bl > pl && buf[pl] == QSE_T('\\') &&
qse_strxncasecmp(buf, pl, path, pl) == 0)
{
buf[0] = drv[0];
buf[1] = QSE_T(':');
qse_strcpy (&buf[2], &buf[pl]);
break;
}
}
}
}
/* if the match is not found, the device name is returned
* without translation */
return 0;
}
#endif
int qse_fio_chmod (qse_fio_t* fio, int mode)
{
#if defined(_WIN32)
int flags = FILE_ATTRIBUTE_NORMAL;
qse_char_t name[MAX_PATH];
/* it is a best effort implementation. if the file size is 0,
* it can't even get the file name from the handle and thus fails.
* if GENERIC_READ is not set in CreateFile, CreateFileMapping fails.
* so if this fio is opened without QSE_FIO_READ, this function fails.
*/
if (get_volname_from_handle (fio, name, QSE_COUNTOF(name)) == -1) return -1;
if (!(mode & QSE_FIO_WUSR)) flags = FILE_ATTRIBUTE_READONLY;
if (SetFileAttributes (name, flags) == FALSE)
{
fio->errnum = syserr_to_errnum (GetLastError());
return -1;
}
return 0;
#elif defined(__OS2__)
APIRET n;
int flags = FILE_NORMAL;
#if defined(FIL_STANDARDL)
FILESTATUS3L stat;
#else
FILESTATUS3 stat;
#endif
ULONG size = QSE_SIZEOF(stat);
#if defined(FIL_STANDARDL)
n = DosQueryFileInfo (fio->handle, FIL_STANDARDL, &stat, size);
#else
n = DosQueryFileInfo (fio->handle, FIL_STANDARD, &stat, size);
#endif
if (n != NO_ERROR)
{
fio->errnum = syserr_to_errnum (n);
return -1;
}
if (!(mode & QSE_FIO_WUSR)) flags = FILE_READONLY;
stat.attrFile = flags;
#if defined(FIL_STANDARDL)
n = DosSetFileInfo (fio->handle, FIL_STANDARDL, &stat, size);
#else
n = DosSetFileInfo (fio->handle, FIL_STANDARD, &stat, size);
#endif
if (n != NO_ERROR)
{
fio->errnum = syserr_to_errnum (n);
return -1;
}
return 0;
#elif defined(__DOS__)
int permission = 0;
if (mode & QSE_FIO_RUSR) permission |= S_IREAD;
if (mode & QSE_FIO_WUSR) permission |= S_IWRITE;
/* TODO: fchmod not available. find a way to do this
return fchmod (fio->handle, permission); */
fio->errnum = QSE_FIO_ENOIMPL;
return -1;
#elif defined(vms) || defined(__vms)
/* TODO: */
fio->errnum = QSE_FIO_ENOIMPL;
return (qse_fio_off_t)-1;
#elif defined(HAVE_FCHMOD)
int n;
n = QSE_FCHMOD (fio->handle, mode);
if (n <= -1) fio->errnum = syserr_to_errnum (errno);
return n;
#else
fio->errnum = QSE_FIO_ENOIMPL;
return -1;
#endif
}
int qse_fio_sync (qse_fio_t* fio)
{
#if defined(_WIN32)
if (FlushFileBuffers (fio->handle) == FALSE)
{
fio->errnum = syserr_to_errnum (GetLastError());
return -1;
}
return 0;
#elif defined(__OS2__)
APIRET n;
n = DosResetBuffer (fio->handle);
if (n != NO_ERROR)
{
fio->errnum = syserr_to_errnum (n);
return -1;
}
return 0;
#elif defined(__DOS__)
int n;
n = fsync (fio->handle);
if (n <= -1) fio->errnum = syserr_to_errnum (errno);
return n;
#elif defined(vms) || defined(__vms)
/* TODO: */
fio->errnum = QSE_FIO_ENOIMPL;
return (qse_fio_off_t)-1;
#elif defined(HAVE_FSYNC)
int n;
n = QSE_FSYNC (fio->handle);
if (n <= -1) fio->errnum = syserr_to_errnum (errno);
return n;
#else
fio->errnum = QSE_FIO_ENOIMPL;
return -1;
#endif
}
int qse_fio_lock (qse_fio_t* fio, qse_fio_lck_t* lck, int flags)
{
/* TODO: qse_fio_lock
* struct flock fl;
* fl.l_type = F_RDLCK, F_WRLCK;
* QSE_FCNTL (fio->handle, F_SETLK, &fl);
*/
fio->errnum = QSE_FIO_ENOIMPL;
return -1;
}
int qse_fio_unlock (qse_fio_t* fio, qse_fio_lck_t* lck, int flags)
{
/* TODO: qse_fio_unlock
* struct flock fl;
* fl.l_type = F_UNLCK;
* QSE_FCNTL (fio->handle, F_SETLK, &fl);
*/
fio->errnum = QSE_FIO_ENOIMPL;
return -1;
}
int qse_get_stdfiohandle (qse_fio_std_t std, qse_fio_hnd_t* hnd)
{
#if defined(_WIN32)
static DWORD tab[] =
{
STD_INPUT_HANDLE,
STD_OUTPUT_HANDLE,
STD_ERROR_HANDLE
};
#elif defined(vms) || defined(__vms)
/* TODO */
static int tab[] = { 0, 1, 2 };
#else
static qse_fio_hnd_t tab[] =
{
#if defined(__OS2__)
(HFILE)0, (HFILE)1, (HFILE)2
#elif defined(__DOS__)
0, 1, 2
#else
0, 1, 2
#endif
};
#endif
if (std < 0 || std >= QSE_COUNTOF(tab)) return -1;
#if defined(_WIN32)
{
HANDLE tmp = GetStdHandle (tab[std]);
if (tmp == INVALID_HANDLE_VALUE) return -1;
*hnd = tmp;
}
#elif defined(vms) || defined(__vms)
/* TODO: */
return -1;
#else
*hnd = tab[std];
#endif
return 0;
}