diff --git a/lib/fio.c b/lib/fio.c new file mode 100644 index 0000000..877f083 --- /dev/null +++ b/lib/fio.c @@ -0,0 +1,1635 @@ +/* + Copyright (c) 2010-2020 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 +#include "hio-prv.h" + +#if defined(_WIN32) +# include +/*# include */ /* for GetMappedFileName(). but dynamically loaded */ +# include +# 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 +#elif defined(__DOS__) +# include +# include +# include +#elif defined(vms) || defined(__vms) +# define __NEW_STARLET 1 +# include +# include +#else +# include +# include +# include +#endif + +/* internal status codes */ +enum +{ + STATUS_APPEND = (1 << 0), + STATUS_NOCLOSE = (1 << 1), + STATUS_WIN32_STDIN = (1 << 2) +}; + +#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 = HIO_NULL; +static dossetfileptrl_t dos_set_file_ptr_l = HIO_NULL; +static dossetfilesizel_t dos_set_file_size_l = HIO_NULL; + +#endif + +hio_fio_t* hio_fio_open (hio_gem_t* gem, hio_oow_t xtnsize, const hio_ooch_t* path, int flags, int mode) +{ + hio_fio_t* fio; + + fio = (hio_fio_t*)hio_gem_allocmem(gem, HIO_SIZEOF(hio_fio_t) + xtnsize); + if (fio) + { + if (hio_fio_init(fio, gem, path, flags, mode) <= -1) + { + hio_gem_freemem (gem, fio); + return HIO_NULL; + } + else HIO_MEMSET (fio + 1, 0, xtnsize); + } + return fio; +} + +void hio_fio_close (hio_fio_t* fio) +{ + hio_fio_fini (fio); + hio_gem_freemem (fio->gem, fio); +} + +int hio_fio_init (hio_fio_t* fio, hio_gem_t* gem, const hio_ooch_t* path, int flags, int mode) +{ + hio_fio_hnd_t handle; + + hio_uint32_t temp_no; + hio_ooch_t* temp_ptr; + hio_bch_t* temp_ptr_b; + hio_oow_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 + + HIO_MEMSET (fio, 0, HIO_SIZEOF(*fio)); + fio->gem = gem; + + if (!(flags & (HIO_FIO_READ | HIO_FIO_WRITE | HIO_FIO_APPEND | HIO_FIO_HANDLE))) + { + /* one of HIO_FIO_READ, HIO_FIO_WRITE, HIO_FIO_APPEND, + * and HIO_FIO_HANDLE must be specified */ + hio_gem_seterrnum (fio->gem, HIO_NULL, HIO_EINVAL); + return -1; + } + + /* Store some flags for later use */ + if (flags & HIO_FIO_NOCLOSE) + fio->status |= STATUS_NOCLOSE; + + if (flags & HIO_FIO_TEMPORARY) + { + hio_ntime_t now; + + /*if (flags & (HIO_FIO_HANDLE | HIO_FIO_BCSTRPATH))*/ + if (flags & HIO_FIO_HANDLE) + { + /* HIO_FIO_TEMPORARY and HIO_FIO_HANDLE/HIO_FIO_BCSTRPATH + * are mutually exclusive */ + hio_gem_seterrnum (fio->gem, HIO_NULL, HIO_EINVAL); + return -1; + } + + temp_no = 0; + + if (flags & HIO_FIO_BCSTRPATH) + { + + for (temp_ptr_b = (hio_bch_t*)path; *temp_ptr_b; temp_ptr_b++) + temp_no += *temp_ptr_b; + + if (temp_ptr_b - (hio_bch_t*)path < 4) + { + hio_gem_seterrnum (fio->gem, HIO_NULL, HIO_EINVAL); + return -1; + } + + temp_ptr_b -= 4; + } + else + { + /* if HIO_FIO_TEMPORARY is used, the path name must be writable. */ + for (temp_ptr = (hio_ooch_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) + { + hio_gem_seterrnum (fio->gem, HIO_NULL, HIO_EINVAL); + return -1; + } + + temp_ptr -= 4; + } + + hio_get_ntime (&now); + temp_no += (now.sec & 0xFFFFFFFFlu); + + temp_tries = 0; + + retry_temporary: + temp_tries++; + + /* Fails after 5000 tries. 5000 randomly chosen */ + if (temp_tries > 5000) + { + hio_gem_seterrnum (fio->gem, HIO_NULL, HIO_EINVAL); + return -1; + } + + /* Generate the next random number to use to make a + * new path name */ + temp_no = hio_rand31(temp_no); + + /* + * You must not pass a constant string for a path name + * when HIO_FIO_TEMPORARY is set, because it changes + * the path name with a random number generated + */ + if (flags & HIO_FIO_BCSTRPATH) + { + hio_fmt_uintmax_to_bcstr ( + temp_ptr_b, + 4, + temp_no % 0x10000, + 16 | HIO_FMT_UINTMAX_NOTRUNC | HIO_FMT_UINTMAX_NONULL, + 4, + '\0', + HIO_NULL + ); + } + else + { + hio_fmt_uintmax_to_oocstr ( + temp_ptr, + 4, + temp_no % 0x10000, + 16 | HIO_FMT_UINTMAX_NOTRUNC | HIO_FMT_UINTMAX_NONULL, + 4, + HIO_T('\0'), + HIO_NULL + ); + } + } + +#if defined(_WIN32) + if (flags & HIO_FIO_HANDLE) + { + handle = *(hio_fio_hnd_t*)path; + /* do not specify an invalid handle value */ + /*HIO_ASSERT (hio, handle != 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 & HIO_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 & HIO_FIO_WRITE) + { + /* In WIN32, FILE_APPEND_DATA and GENERIC_WRITE can't + * be used together */ + desired_access |= GENERIC_WRITE; + } + if (flags & HIO_FIO_READ) desired_access |= GENERIC_READ; + + if (flags & HIO_FIO_CREATE) + { + creation_disposition = + (flags & HIO_FIO_EXCLUSIVE)? CREATE_NEW: + (flags & HIO_FIO_TRUNCATE)? CREATE_ALWAYS: OPEN_ALWAYS; + } + else if (flags & HIO_FIO_TRUNCATE) + { + creation_disposition = TRUNCATE_EXISTING; + } + else creation_disposition = OPEN_EXISTING; + + if (flags & HIO_FIO_NOSHREAD) + share_mode &= ~FILE_SHARE_READ; + if (flags & HIO_FIO_NOSHWRITE) + share_mode &= ~FILE_SHARE_WRITE; + if (flags & HIO_FIO_NOSHDELETE) + share_mode &= ~FILE_SHARE_DELETE; + + if (!(mode & HIO_FIO_WUSR)) + flag_and_attr = FILE_ATTRIBUTE_READONLY; + if (flags & HIO_FIO_SYNC) + flag_and_attr |= FILE_FLAG_WRITE_THROUGH; + + #if defined(FILE_FLAG_OPEN_REPARSE_POINT) + if (flags & HIO_FIO_NOFOLLOW) + flag_and_attr |= FILE_FLAG_OPEN_REPARSE_POINT; + #endif + + /* these two are just hints to OS */ + if (flags & HIO_FIO_RANDOM) + flag_and_attr |= FILE_FLAG_RANDOM_ACCESS; + if (flags & HIO_FIO_SEQUENTIAL) + flag_and_attr |= FILE_FLAG_SEQUENTIAL_SCAN; + + if (flags & HIO_FIO_BCSTRPATH) + { + handle = CreateFileA( + (const hio_bch_t*)path, desired_access, share_mode, + HIO_NULL, /* set noinherit by setting no secattr */ + creation_disposition, flag_and_attr, 0 + ); + } + else + { + handle = CreateFile( + path, desired_access, share_mode, + HIO_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 & HIO_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 & HIO_FIO_APPEND) + { + fio->status |= STATUS_APPEND; + desired_access &= ~FILE_APPEND_DATA; + desired_access |= GENERIC_WRITE; + } + + if (flags & HIO_FIO_BCSTRPATH) + { + handle = CreateFileA( + (const hio_bch_t*)path, desired_access, share_mode, + HIO_NULL, /* set noinherit by setting no secattr */ + creation_disposition, flag_and_attr, 0 + ); + } + else + { + handle = CreateFile( + path, desired_access, share_mode, + HIO_NULL, /* set noinherit by setting no secattr */ + creation_disposition, flag_and_attr, 0 + ); + } + if (handle == INVALID_HANDLE_VALUE) + { + i if (flags & HIO_FIO_TEMPORARY) goto retry_temporary; + hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(GetLastError())); + return -1; + } + } + else + { + if (flags & HIO_FIO_TEMPORARY) goto retry_temporary; + hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(e)); + return -1; + } + } + } + + /* some special check */ +#if 0 + if (GetFileType(handle) == FILE_TYPE_UNKNOWN) + { + hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(GetLastError())); + CloseHandle (handle); + return -1; + } +#endif + + /* TODO: support more features on WIN32 - TEMPORARY, DELETE_ON_CLOSE */ + +#elif defined(__OS2__) + + if (flags & HIO_FIO_HANDLE) + { + handle = *(hio_fio_hnd_t*)path; + } + else + { + APIRET ret; + ULONG action_taken = 0; + ULONG open_action, open_mode, open_attr; + + #if defined(HIO_OOCH_IS_BCH) + const hio_bch_t* path_mb = path; + #else + hio_bch_t path_mb_buf[CCHMAXPATH]; + hio_bch_t* path_mb; + hio_oow_t wl, ml; + int px; + + if (flags & HIO_FIO_BCSTRPATH) + { + path_mb = (hio_bch_t*)path; + } + else + { + path_mb = path_mb_buf; + ml = HIO_COUNTOF(path_mb_buf); + px = hio_gem_convutobcstr(fio->gem, path, &wl, path_mb, &ml); + if (px == -2) + { + /* the static buffer is too small. + * dynamically allocate a buffer */ + path_mb = hio_gem_duputobcstr(fio->gem, path, HIO_NUL); + if (path_mb == HIO_NULL) return -1; + } + else if (px <= -1) + { + return -1; + } + } + #endif + + if (flags & HIO_FIO_APPEND) fio->status |= STATUS_APPEND; + + if (flags & HIO_FIO_CREATE) + { + if (flags & HIO_FIO_EXCLUSIVE) + { + open_action = OPEN_ACTION_FAIL_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW; + } + else if (flags & HIO_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 & HIO_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 & HIO_FIO_SYNC) open_mode |= OPEN_FLAGS_WRITE_THROUGH; + + if ((flags & HIO_FIO_NOSHREAD) && (flags & HIO_FIO_NOSHWRITE)) open_mode |= OPEN_SHARE_DENYREADWRITE; + else if (flags & HIO_FIO_NOSHREAD) open_mode |= OPEN_SHARE_DENYREAD; + else if (flags & HIO_FIO_NOSHWRITE) open_mode |= OPEN_SHARE_DENYWRITE; + else open_mode |= OPEN_SHARE_DENYNONE; + + if ((flags & HIO_FIO_READ) && (flags & HIO_FIO_WRITE)) open_mode |= OPEN_ACCESS_READWRITE; + else if (flags & HIO_FIO_READ) open_mode |= OPEN_ACCESS_READONLY; + else if (flags & HIO_FIO_WRITE) open_mode |= OPEN_ACCESS_WRITEONLY; + + open_attr = (mode & HIO_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(HIO_OOCH_IS_BCH) + /* nothing to do */ + #else + if (path_mb != path_mb_buf) hio_gem_freemem (fio->gem, path_mb); + #endif + + if (ret != NO_ERROR) + { + if (flags & HIO_FIO_TEMPORARY) goto retry_temporary; + hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(ret)); + return -1; + } + } + +#elif defined(__DOS__) + + if (flags & HIO_FIO_HANDLE) + { + handle = *(hio_fio_hnd_t*)path; + /* do not specify an invalid handle value */ + /*HIO_ASSERT (hio, handle >= 0);*/ + } + else + { + int oflags = 0; + int permission = 0; + + #if defined(HIO_OOCH_IS_BCH) + const hio_bch_t* path_mb = path; + #else + hio_bch_t path_mb_buf[_MAX_PATH]; + hio_bch_t* path_mb; + hio_oow_t wl, ml; + int px; + + if (flags & HIO_FIO_BCSTRPATH) + { + path_mb = (hio_bch_t*)path; + } + else + { + path_mb = path_mb_buf; + ml = HIO_COUNTOF(path_mb_buf); + px = hio_gem_convutobcstr(fio->gem, path, &wl, path_mb, &ml); + if (px == -2) + { + /* static buffer size not enough. + * switch to dynamic allocation */ + path_mb = hio_gem_duputobcstr(fio->gem, path, HIO_NULL); + if (path_mb == HIO_NULL) return -1; + } + else if (px <= -1) + { + return -1; + } + } + #endif + + if (flags & HIO_FIO_APPEND) + { + if ((flags & HIO_FIO_READ)) oflags |= O_RDWR; + else oflags |= O_WRONLY; + oflags |= O_APPEND; + } + else + { + if ((flags & HIO_FIO_READ) && + (flags & HIO_FIO_WRITE)) oflags |= O_RDWR; + else if (flags & HIO_FIO_READ) oflags |= O_RDONLY; + else if (flags & HIO_FIO_WRITE) oflags |= O_WRONLY; + } + + if (flags & HIO_FIO_CREATE) oflags |= O_CREAT; + if (flags & HIO_FIO_TRUNCATE) oflags |= O_TRUNC; + if (flags & HIO_FIO_EXCLUSIVE) oflags |= O_EXCL; + + oflags |= O_BINARY | O_NOINHERIT; + + if (mode & HIO_FIO_RUSR) permission |= S_IREAD; + if (mode & HIO_FIO_WUSR) permission |= S_IWRITE; + + handle = open ( + path_mb, + oflags, + permission + ); + + #if defined(HIO_OOCH_IS_BCH) + /* nothing to do */ + #else + if (path_mb != path_mb_buf) hio_gem_freemem (fio->gem, path_mb); + #endif + + if (handle <= -1) + { + if (flags & HIO_FIO_TEMPORARY) goto retry_temporary; + hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(errno)); + return -1; + } + } + +#elif defined(vms) || defined(__vms) + + if (flags & HIO_FIO_HANDLE) + { + /* TODO: implement this */ + hio_gem_seterrnum (fio->gem, HIO_NULL, HIO_ENOIMPL); + return -1; + } + else + { + struct FAB* fab; + struct RAB* rab; + unsigned long r0; + + #if defined(HIO_OOCH_IS_BCH) + const hio_bch_t* path_mb = path; + #else + hio_bch_t path_mb_buf[1024]; + hio_bch_t* path_mb; + hio_oow_t wl, ml; + int px; + + if (flags & HIO_FIO_BCSTRPATH) + { + path_mb = (hio_bch_t*)path; + } + else + { + path_mb = path_mb_buf; + ml = HIO_COUNTOF(path_mb_buf); + px = hio_convutobcstr(fio->gem, path, &wl, path_mb, &ml); + if (px == -2) + { + /* the static buffer is too small. + * allocate a buffer */ + path_mb = hio_duputobcstr(fio->gem, path, mmgr); + if (path_mb == HIO_NULL) return -1; + } + else if (px <= -1) + { + return -1; + } + } + #endif + + rab = (struct RAB*)hio_gem_allocmem(fio->gem, HIO_SIZEOF(*rab) + HIO_SIZEOF(*fab)); + if (rab == HIO_NULL) + { + #if defined(HIO_OOCH_IS_BCH) + /* nothing to do */ + #else + if (path_mb != path_mb_buf) hio_gem_freemem (fio->gem, path_mb); + #endif + 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 & HIO_FIO_READ) fab->fab$b_fac |= FAB$M_GET; + if (flags & (HIO_FIO_WRITE | HIO_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 & HIO_FIO_NOSHREAD) fab->fab$b_shr &= ~FAB$M_SHRGET; + if (flags & HIO_FIO_NOSHWRITE) fab->fab$b_shr &= ~FAB$M_SHRPUT; + + if (flags & HIO_FIO_APPEND) rab->rab$l_rop |= RAB$M_EOF; + + if (flags & HIO_FIO_CREATE) + { + if (flags & HIO_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(HIO_OOCH_IS_BCH) + /* nothing to do */ + #else + if (path_mb != path_mb_buf) hio_gem_freemem (fio->gem, path_mb); + #endif + hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(r0)); + return -1; + } + + + r0 = sys$connect (&rab, 0, 0); + if (r0 != RMS$_NORMAL) + { + #if defined(HIO_OOCH_IS_BCH) + /* nothing to do */ + #else + if (path_mb != path_mb_buf) hio_gem_freemem (fio->gem, path_mb); + #endif + hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(r0)); + return -1; + } + + #if defined(HIO_OOCH_IS_BCH) + /* nothing to do */ + #else + if (path_mb != path_mb_buf) hio_gem_freemem (fio->gem, path_mb); + #endif + + handle = rab; + } + +#else + + if (flags & HIO_FIO_HANDLE) + { + handle = *(hio_fio_hnd_t*)path; + /* do not specify an invalid handle value */ + /*HIO_ASSERT (hio, handle >= 0);*/ + } + else + { + int desired_access = 0; + + #if defined(HIO_OOCH_IS_BCH) + const hio_bch_t* path_mb = path; + #else + hio_bch_t path_mb_buf[1024]; /* PATH_MAX instead? */ + hio_bch_t* path_mb; + hio_oow_t wl, ml; + int px; + + if (flags & HIO_FIO_BCSTRPATH) + { + path_mb = (hio_bch_t*)path; + } + else + { + path_mb = path_mb_buf; + ml = HIO_COUNTOF(path_mb_buf); + px = hio_conv_ucstr_to_bcstr_with_cmgr(path, &wl, path_mb, &ml, fio->gem->cmgr); + if (px == -2) + { + /* the static buffer is too small. + * allocate a buffer */ + path_mb = hio_gem_duputobcstr(fio->gem, path, HIO_NULL); + if (path_mb == HIO_NULL) return -1; + } + else if (px <= -1) + { + hio_gem_seterrnum (fio->gem, HIO_NULL, HIO_EINVAL); + return -1; + } + } + #endif + /* + * rwa -> RDWR | APPEND + * ra -> RDWR | APPEND + * wa -> WRONLY | APPEND + * a -> WRONLY | APPEND + */ + if (flags & HIO_FIO_APPEND) + { + if ((flags & HIO_FIO_READ)) desired_access |= O_RDWR; + else desired_access |= O_WRONLY; + desired_access |= O_APPEND; + } + else + { + if ((flags & HIO_FIO_READ) && + (flags & HIO_FIO_WRITE)) desired_access |= O_RDWR; + else if (flags & HIO_FIO_READ) desired_access |= O_RDONLY; + else if (flags & HIO_FIO_WRITE) desired_access |= O_WRONLY; + } + + if (flags & HIO_FIO_CREATE) desired_access |= O_CREAT; + if (flags & HIO_FIO_TRUNCATE) desired_access |= O_TRUNC; + if (flags & HIO_FIO_EXCLUSIVE) desired_access |= O_EXCL; + #if defined(O_SYNC) + if (flags & HIO_FIO_SYNC) desired_access |= O_SYNC; + #endif + + #if defined(O_NOFOLLOW) + if (flags & HIO_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 = HIO_OPEN(path_mb, desired_access, mode); + + #if defined(HIO_OOCH_IS_BCH) + /* nothing to do */ + #else + if (path_mb != path_mb_buf && path_mb != (hio_bch_t*)path) + { + hio_gem_freemem (fio->gem, path_mb); + } + #endif + if (handle == -1) + { + if (flags & HIO_FIO_TEMPORARY) goto retry_temporary; + hio_gem_seterrnum (fio->gem, HIO_NULL, hio_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 & HIO_FIO_RANDOM) + posix_fadvise (handle, 0, 0, POSIX_FADV_RANDOM); + #endif + #if defined(POSIX_FADV_SEQUENTIAL) + if (flags & HIO_FIO_SEQUENTIAL) + posix_fadvise (handle, 0, 0, POSIX_FADV_SEQUENTIAL); + #endif + } +#endif + + fio->handle = handle; + return 0; +} + +void hio_fio_fini (hio_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); + hio_gem_freemem (fio->gem, fio->handle); +#else + close (fio->handle); +#endif + } +} + +hio_fio_hnd_t hio_fio_gethnd (const hio_fio_t* fio) +{ + return fio->handle; +} + +hio_fio_off_t hio_fio_seek (hio_fio_t* fio, hio_fio_off_t offset, hio_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 + + /* HIO_ASSERT (fio->hio, HIO_SIZEOF(offset) <= HIO_SIZEOF(x.QuadPart));*/ + + #if defined(_WIN64) + x.QuadPart = offset; + if (SetFilePointerEx (fio->handle, x, &y, seek_map[origin]) == FALSE) + { + return (hio_fio_off_t)-1; + } + return (hio_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) + { + hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(GetLastError())); + return (hio_fio_off_t)-1; + } + return (hio_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; + + /*HIO_ASSERT (fio->hio, HIO_SIZEOF(offset) >= HIO_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) + { + hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(ret)); + return (hio_fio_off_t)-1; + } + + return ((hio_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) + { + hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(ret)); + return (hio_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: */ + hio_gem_seterrnum (fio->gem, HIO_NULL, HIO_ENOIMPL); + return (hio_fio_off_t)-1; +#else + static int seek_map[] = + { + SEEK_SET, + SEEK_CUR, + SEEK_END + }; + +#if defined(HIO_LLSEEK) + loff_t tmp; + + if (HIO_LLSEEK(fio->handle, + (unsigned long)(offset>>32), + (unsigned long)(offset&0xFFFFFFFFlu), + &tmp, + seek_map[origin]) == -1) + { + hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(errno)); + return (hio_fio_off_t)-1; + } + + return (hio_fio_off_t)tmp; +#else + return lseek(fio->handle, offset, seek_map[origin]); +#endif + +#endif +} + +int hio_fio_truncate (hio_fio_t* fio, hio_fio_off_t size) +{ +#if defined(_WIN32) + if (hio_fio_seek (fio, size, HIO_FIO_BEGIN) == (hio_fio_off_t)-1) return -1; + if (SetEndOfFile(fio->handle) == FALSE) + { + hio_gem_seterrnum (fio->gem, HIO_NULL, hio_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) + { + hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(ret)); + return -1; + } + return 0; + +#elif defined(__DOS__) + + int n; + n = chsize (fio->handle, size); + if (n <= -1) hio_gem_seterrnum (fio->gem, HIO_NULL, hio_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) + { + hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(r0)); + return -1; + } + + return 0; + +#else + + int n; + n = ftruncate(fio->handle, size); + if (n <= -1) hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(errno)); + return n; + +#endif +} + +hio_ooi_t hio_fio_read (hio_fio_t* fio, void* buf, hio_oow_t size) +{ +#if defined(_WIN32) + + DWORD count; + + if (size > (HIO_TYPE_MAX(hio_ooi_t) & HIO_TYPE_MAX(DWORD))) + size = HIO_TYPE_MAX(hio_ooi_t) & HIO_TYPE_MAX(DWORD); + if (ReadFile (fio->handle, buf, (DWORD)size, &count, HIO_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; + hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(e)); + return -1; + } + return (hio_ooi_t)count; + +#elif defined(__OS2__) + + APIRET ret; + ULONG count; + if (size > (HIO_TYPE_MAX(hio_ooi_t) & HIO_TYPE_MAX(ULONG))) + size = HIO_TYPE_MAX(hio_ooi_t) & HIO_TYPE_MAX(ULONG); + ret = DosRead (fio->handle, buf, (ULONG)size, &count); + if (ret != NO_ERROR) + { + hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(ret)); + return -1; + } + return (hio_ooi_t)count; + +#elif defined(__DOS__) + + int n; + if (size > (HIO_TYPE_MAX(hio_ooi_t) & HIO_TYPE_MAX(unsigned int))) + size = HIO_TYPE_MAX(hio_ooi_t) & HIO_TYPE_MAX(unsigned int); + n = read (fio->handle, buf, size); + if (n <= -1) hio_gem_seterrnum (fio->gem, HIO_NULL, hio_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) + { + hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(r0)); + return -1; + } + + return rab->rab$w_rsz; +#else + + hio_ooi_t n; + if (size > HIO_TYPE_MAX(hio_ooi_t)) size = HIO_TYPE_MAX(hio_ooi_t); + n = read(fio->handle, buf, size); + if (n <= -1) hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(errno)); + return n; +#endif +} + +hio_ooi_t hio_fio_write (hio_fio_t* fio, const void* data, hio_oow_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, HIO_NULL, FILE_END); + #else + SetFilePointer (fio->handle, 0, HIO_NULL, FILE_END); + #endif + } + + if (size > (HIO_TYPE_MAX(hio_ooi_t) & HIO_TYPE_MAX(DWORD))) + size = HIO_TYPE_MAX(hio_ooi_t) & HIO_TYPE_MAX(DWORD); + if (WriteFile(fio->handle, data, (DWORD)size, &count, HIO_NULL) == FALSE) + { + hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(GetLastError())); + return -1; + } + return (hio_ooi_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 > (HIO_TYPE_MAX(hio_ooi_t) & HIO_TYPE_MAX(ULONG))) + size = HIO_TYPE_MAX(hio_ooi_t) & HIO_TYPE_MAX(ULONG); + ret = DosWrite(fio->handle, (PVOID)data, (ULONG)size, &count); + if (ret != NO_ERROR) + { + hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(ret)); + return -1; + } + return (hio_ooi_t)count; + +#elif defined(__DOS__) + + int n; + if (size > (HIO_TYPE_MAX(hio_ooi_t) & HIO_TYPE_MAX(unsigned int))) + size = HIO_TYPE_MAX(hio_ooi_t) & HIO_TYPE_MAX(unsigned int); + n = write(fio->handle, data, size); + if (n <= -1) hio_gem_seterrnum (fio->gem, HIO_NULL, hio_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) + { + hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(r0)); + return -1; + } + + return rab->rab$w_rsz; + +#else + + hio_ooi_t n; + if (size > HIO_TYPE_MAX(hio_ooi_t)) size = HIO_TYPE_MAX(hio_ooi_t); + n = write(fio->handle, data, size); + if (n <= -1) hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(errno)); + return n; +#endif +} + +#if defined(_WIN32) + +static int get_devname_from_handle ( + hio_fio_t* fio, hio_ooch_t* buf, hio_oow_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 (HIO_T("PSAPI.DLL")); + if (!psapi) + { + hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(GetLastError())); + return -1; + } + + getmappedfilename = (getmappedfilename_t) + GetProcAddress (psapi, HIO_BT("GetMappedFileName")); + if (!getmappedfilename) + { + hio_gem_seterrnum (fio->gem, HIO_NULL, hio_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) + { + hio_gem_seterrnum (fio->gem, HIO_NULL, hio_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) + { + hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(GetLastError())); + CloseHandle (map); + FreeLibrary (psapi); + return -1; + } + + olen = getmappedfilename (GetCurrentProcess(), mem, buf, len); + if (olen == 0) + { + hio_gem_seterrnum (fio->gem, HIO_NULL, hio_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 (hio_fio_t* fio, hio_ooch_t* buf, hio_oow_t len) +{ + if (get_devname_from_handle (fio, buf, len) == -1) return -1; + + if (hio_comp_oocstr_limited(buf, HIO_T("\\Device\\LanmanRedirector\\"), 25, 1) == 0) + { + /*buf[0] = HIO_T('\\');*/ + hio_copy_oocstr_unlimited (&buf[1], &buf[24]); + } + else + { + DWORD n; + hio_ooch_t drives[128]; + + n = GetLogicalDriveStrings(HIO_COUNTOF(drives), drives); + + if (n == 0 /* error */ || + n > HIO_COUNTOF(drives) /* buffer small */) + { + hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(GetLastError())); + return -1; + } + + while (n > 0) + { + hio_ooch_t drv[3]; + hio_ooch_t path[MAX_PATH]; + + drv[0] = drives[--n]; + drv[1] = HIO_T(':'); + drv[2] = HIO_T('\0'); + if (QueryDosDevice (drv, path, HIO_COUNTOF(path))) + { + hio_oow_t pl = hio_count_oocstr(path); + hio_oow_t bl = hio_count_oocstr(buf); + if (bl > pl && buf[pl] == HIO_T('\\') && + hio_comp_oochars(buf, pl, path, pl, 1) == 0) + { + buf[0] = drv[0]; + buf[1] = HIO_T(':'); + hio_copy_oocstr_unlimited (&buf[2], &buf[pl]); + break; + } + } + } + } + + /* if the match is not found, the device name is returned + * without translation */ + return 0; +} +#endif + +int hio_fio_chmod (hio_fio_t* fio, int mode) +{ +#if defined(_WIN32) + + int flags = FILE_ATTRIBUTE_NORMAL; + hio_ooch_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 HIO_FIO_READ, this function fails. + */ + if (get_volname_from_handle(fio, name, HIO_COUNTOF(name)) == -1) return -1; + + if (!(mode & HIO_FIO_WUSR)) flags = FILE_ATTRIBUTE_READONLY; + if (SetFileAttributes(name, flags) == FALSE) + { + hio_gem_seterrnum (fio->gem, HIO_NULL, hio_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 = HIO_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) + { + hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(n)); + return -1; + } + + if (!(mode & HIO_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) + { + hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(n)); + return -1; + } + + return 0; + +#elif defined(__DOS__) + + int permission = 0; + + if (mode & HIO_FIO_RUSR) permission |= S_IREAD; + if (mode & HIO_FIO_WUSR) permission |= S_IWRITE; + + /* TODO: fchmod not available. find a way to do this + return fchmod (fio->handle, permission); */ + + hio_gem_seterrnum (fio->gem, HIO_NULL, HIO_ENOIMPL); + return -1; + +#elif defined(vms) || defined(__vms) + + /* TODO: */ + hio_gem_seterrnum (fio->gem, HIO_NULL, HIO_ENOIMPL); + return (hio_fio_off_t)-1; + +#elif defined(HAVE_FCHMOD) + int n; + n = fchmod(fio->handle, mode); + if (n <= -1) hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(errno)); + return n; + +#else + hio_gem_seterrnum (fio->gem, HIO_NULL, HIO_ENOIMPL); + return -1; + +#endif +} + +int hio_fio_sync (hio_fio_t* fio) +{ +#if defined(_WIN32) + + if (FlushFileBuffers(fio->handle) == FALSE) + { + hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(GetLastError())); + return -1; + } + return 0; + +#elif defined(__OS2__) + + APIRET n; + n = DosResetBuffer(fio->handle); + if (n != NO_ERROR) + { + hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(n)); + return -1; + } + return 0; + +#elif defined(__DOS__) + + int n; + n = fsync(fio->handle); + if (n <= -1) hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(errno)); + return n; + +#elif defined(vms) || defined(__vms) + + /* TODO: */ + hio_gem_seterrnum (fio->gem, HIO_NULL, HIO_ENOIMPL); + return (hio_fio_off_t)-1; + +#elif defined(HAVE_FSYNC) + + int n; + n = fsync(fio->handle); + if (n <= -1) hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(errno)); + return n; +#else + hio_gem_seterrnum (fio->gem, HIO_NULL, HIO_ENOIMPL); + return -1; +#endif +} + +int hio_fio_lock (hio_fio_t* fio, hio_fio_lck_t* lck, int flags) +{ + /* TODO: hio_fio_lock + * struct flock fl; + * fl.l_type = F_RDLCK, F_WRLCK; + * HIO_FCNTL (fio->handle, F_SETLK, &fl); + */ + hio_gem_seterrnum (fio->gem, HIO_NULL, HIO_ENOIMPL); + return -1; +} + +int hio_fio_unlock (hio_fio_t* fio, hio_fio_lck_t* lck, int flags) +{ + /* TODO: hio_fio_unlock + * struct flock fl; + * fl.l_type = F_UNLCK; + * HIO_FCNTL (fio->handle, F_SETLK, &fl); + */ + hio_gem_seterrnum (fio->gem, HIO_NULL, HIO_ENOIMPL); + return -1; +} + +int hio_get_std_fio_handle (hio_fio_std_t std, hio_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 hio_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 >= HIO_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; +} diff --git a/lib/hio-fio.h b/lib/hio-fio.h new file mode 100644 index 0000000..7baa59c --- /dev/null +++ b/lib/hio-fio.h @@ -0,0 +1,293 @@ +/* + Copyright (c) 2016-2020 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. + */ + +#ifndef _HIO_FIO_H_ +#define _HIO_FIO_H_ + +#include + +enum hio_fio_flag_t +{ + /* (1 << 0) to (1 << 7) reserved for hio_sio_flag_t. + * see . nerver use this value. */ + HIO_FIO_RESERVED = 0xFF, + + /** treat the file name pointer as a handle pointer */ + HIO_FIO_HANDLE = (1 << 8), + + /** treat the file name pointer as a pointer to file name + * template to use when making a temporary file name */ + HIO_FIO_TEMPORARY = (1 << 9), + + /** don't close an I/O handle in hio_fio_fini() and hio_fio_close() */ + HIO_FIO_NOCLOSE = (1 << 10), + + /** treat the path name as a multi-byte string */ + HIO_FIO_BCSTRPATH = (1 << 11), + + /* normal open flags */ + HIO_FIO_READ = (1 << 14), + HIO_FIO_WRITE = (1 << 15), + HIO_FIO_APPEND = (1 << 16), + + HIO_FIO_CREATE = (1 << 17), + HIO_FIO_TRUNCATE = (1 << 18), + HIO_FIO_EXCLUSIVE = (1 << 19), + HIO_FIO_SYNC = (1 << 20), + + /* do not follow a symbolic link, only on a supported platform */ + HIO_FIO_NOFOLLOW = (1 << 23), + + /* for WIN32 only. harmless(no effect) when used on other platforms */ + HIO_FIO_NOSHREAD = (1 << 24), + HIO_FIO_NOSHWRITE = (1 << 25), + HIO_FIO_NOSHDELETE = (1 << 26), + + /* hints to OS. harmless(no effect) when used on unsupported platforms */ + HIO_FIO_RANDOM = (1 << 27), /* hint that access be random */ + HIO_FIO_SEQUENTIAL = (1 << 28) /* hint that access is sequential */ +}; + +enum hio_fio_std_t +{ + HIO_FIO_STDIN = 0, + HIO_FIO_STDOUT = 1, + HIO_FIO_STDERR = 2 +}; +typedef enum hio_fio_std_t hio_fio_std_t; + +/* seek origin */ +enum hio_fio_ori_t +{ + HIO_FIO_BEGIN = 0, + HIO_FIO_CURRENT = 1, + HIO_FIO_END = 2 +}; +/* file origin for seek */ +typedef enum hio_fio_ori_t hio_fio_ori_t; + +enum hio_fio_mode_t +{ + HIO_FIO_SUID = 04000, /* set UID */ + HIO_FIO_SGID = 02000, /* set GID */ + HIO_FIO_SVTX = 01000, /* sticky bit */ + HIO_FIO_RUSR = 00400, /* can be read by owner */ + HIO_FIO_WUSR = 00200, /* can be written by owner */ + HIO_FIO_XUSR = 00100, /* can be executed by owner */ + HIO_FIO_RGRP = 00040, /* can be read by group */ + HIO_FIO_WGRP = 00020, /* can be written by group */ + HIO_FIO_XGRP = 00010, /* can be executed by group */ + HIO_FIO_ROTH = 00004, /* can be read by others */ + HIO_FIO_WOTH = 00002, /* can be written by others */ + HIO_FIO_XOTH = 00001 /* can be executed by others */ +}; + +#if defined(_WIN32) + /* => typedef PVOID HANDLE; */ + typedef void* hio_fio_hnd_t; +#elif defined(__OS2__) + /* => typedef LHANDLE HFILE; + typedef unsigned long LHANDLE; */ + typedef unsigned long hio_fio_hnd_t; +#elif defined(__DOS__) + typedef int hio_fio_hnd_t; +#elif defined(vms) || defined(__vms) + typedef void* hio_fio_hnd_t; /* struct FAB*, struct RAB* */ +#else + typedef int hio_fio_hnd_t; +#endif + +/* file offset */ +typedef hio_foff_t hio_fio_off_t; + +typedef struct hio_fio_t hio_fio_t; +typedef struct hio_fio_lck_t hio_fio_lck_t; + +struct hio_fio_t +{ + hio_t* gem; + hio_fio_hnd_t handle; + int status; +}; + +struct hio_fio_lck_t +{ + int type; /* READ, WRITE */ + hio_fio_off_t offset; /* starting offset */ + hio_fio_off_t length; /* length */ + hio_fio_ori_t origin; /* origin */ +}; + +#define HIO_FIO_HANDLE(fio) ((fio)->handle) + +#if defined(__cplusplus) +extern "C" { +#endif + +/** + * The hio_fio_open() function opens a file. + * To open a file, you should set the flags with at least one of + * HIO_FIO_READ, HIO_FIO_WRITE, HIO_FIO_APPEND. + * + * If the #HIO_FIO_HANDLE flag is set, the \a path parameter is interpreted + * as a pointer to hio_fio_hnd_t. + * + * If the #HIO_FIO_TEMPORARY flag is set, the \a path parameter is + * interpreted as a path name template and an actual file name to open + * is internally generated using the template. The \a path parameter + * is filled with the last actual path name attempted when the function + * returns. So, you must not pass a constant string to the \a path + * parameter when #HIO_FIO_TEMPORARY is set. + */ +HIO_EXPORT hio_fio_t* hio_fio_open ( + hio_gem_t* gem, + hio_oow_t xtnsize, + const hio_ooch_t* path, + int flags, + int mode +); + +/** + * The hio_fio_close() function closes a file. + */ +HIO_EXPORT void hio_fio_close ( + hio_fio_t* fio +); + +/** + * The hio_fio_close() function opens a file into \a fio. + */ +HIO_EXPORT int hio_fio_init ( + hio_fio_t* fio, + hio_gem_t* gem, + const hio_ooch_t* path, + int flags, + int mode +); + +/** + * The hio_fio_close() function finalizes a file by closing the handle + * stored in @a fio. + */ +HIO_EXPORT void hio_fio_fini ( + hio_fio_t* fio +); + +#if defined(HIO_HAVE_INLINE) +static HIO_INLINE void* hio_fio_getxtn (hio_fio_t* fio) { return (void*)(fio + 1); } +#else +#define hio_fio_getxtn(fio) ((void*)((hio_fio_t*)(fio) + 1)) +#endif + +/** + * The hio_fio_gethnd() function returns the native file handle. + */ +HIO_EXPORT hio_fio_hnd_t hio_fio_gethnd ( + const hio_fio_t* fio +); + +/** + * The hio_fio_seek() function changes the current file position. + */ +HIO_EXPORT hio_fio_off_t hio_fio_seek ( + hio_fio_t* fio, + hio_fio_off_t offset, + hio_fio_ori_t origin +); + +/** + * The hio_fio_truncate() function truncates a file to @a size. + */ +HIO_EXPORT int hio_fio_truncate ( + hio_fio_t* fio, + hio_fio_off_t size +); + +/** + * The hio_fio_read() function reads data. + */ +HIO_EXPORT hio_ooi_t hio_fio_read ( + hio_fio_t* fio, + void* buf, + hio_oow_t size +); + +/** + * The hio_fio_write() function writes data. + */ +HIO_EXPORT hio_ooi_t hio_fio_write ( + hio_fio_t* fio, + const void* data, + hio_oow_t size +); + +/** + * The hio_fio_chmod() function changes the file mode. + * + * \note + * On _WIN32, this function is implemented on the best-effort basis and + * returns an error on the following conditions: + * - The file size is 0. + * - The file is opened without #HIO_FIO_READ. + */ +HIO_EXPORT int hio_fio_chmod ( + hio_fio_t* fio, + int mode +); + +/** + * The hio_fio_sync() function synchronizes file contents into storage media + * It is useful in determining the media error, without which hio_fio_close() + * may succeed despite such an error. + */ +HIO_EXPORT int hio_fio_sync ( + hio_fio_t* fio +); + +HIO_EXPORT int hio_fio_lock ( + hio_fio_t* fio, + hio_fio_lck_t* lck, + int flags +); + +HIO_EXPORT int hio_fio_unlock ( + hio_fio_t* fio, + hio_fio_lck_t* lck, + int flags +); + +/** + * The hio_get_std_fio_handle() returns a low-level system handle to + * commonly used I/O channels. + */ +HIO_EXPORT int hio_get_std_fio_handle ( + hio_fio_std_t std, + hio_fio_hnd_t* hnd +); + +#if defined(__cplusplus) +} +#endif + +#endif