858 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			858 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* 
 | |
|  * $Id$
 | |
|  *
 | |
|     Copyright (c) 2006-2014 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 "fs-prv.h"
 | |
| #include <qse/cmn/str.h>
 | |
| #include <qse/cmn/mbwc.h>
 | |
| #include <qse/cmn/path.h>
 | |
| #include "../cmn/mem-prv.h"
 | |
| 
 | |
| #if defined(_WIN32)
 | |
| 	/* nothing else */
 | |
| #elif defined(__OS2__)
 | |
| 	/* nothing else */
 | |
| #elif defined(__DOS__)
 | |
| 	/* nothing else */
 | |
| #else
 | |
| #	include <dirent.h>
 | |
| #	include <errno.h>
 | |
| #endif
 | |
| 
 | |
| typedef struct info_t info_t;
 | |
| struct info_t
 | |
| {
 | |
| 	qse_cstr_t name;
 | |
| 
 | |
| #if defined(_WIN32)
 | |
| 	HANDLE handle;
 | |
| 	WIN32_FIND_DATA wfd;
 | |
| 	int just_changed_fs;
 | |
| #elif defined(__OS2__)
 | |
| #elif defined(__DOS__)
 | |
| #else
 | |
| 	DIR* handle;
 | |
| 	qse_mchar_t* mcurdir;
 | |
| #endif
 | |
| };
 | |
| 
 | |
| qse_fs_t* qse_fs_open (qse_mmgr_t* mmgr, qse_size_t xtnsize)
 | |
| {
 | |
| 	qse_fs_t* fs;
 | |
| 
 | |
| 	fs = QSE_MMGR_ALLOC (mmgr, QSE_SIZEOF(*fs) + xtnsize);
 | |
| 	if (fs == QSE_NULL) return QSE_NULL;
 | |
| 
 | |
| 	if (qse_fs_init (fs, mmgr) <= -1) 
 | |
| 	{
 | |
| 		QSE_MMGR_FREE (mmgr, fs);
 | |
| 		return QSE_NULL;
 | |
| 	}
 | |
| 
 | |
| 	QSE_MEMSET (fs + 1, 0, xtnsize);
 | |
| 	return fs;
 | |
| }
 | |
| 
 | |
| void qse_fs_close (qse_fs_t* fs)
 | |
| {
 | |
| 	qse_fs_fini (fs);
 | |
| 	QSE_MMGR_FREE (fs->mmgr, fs);
 | |
| }
 | |
| 
 | |
| int qse_fs_init (qse_fs_t* fs, qse_mmgr_t* mmgr)
 | |
| {
 | |
| 	QSE_MEMSET (fs, 0, QSE_SIZEOF(*fs));
 | |
| 	fs->mmgr = mmgr;
 | |
| 	fs->cmgr = qse_getdflcmgr();
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| void qse_fs_fini (qse_fs_t* fs)
 | |
| {
 | |
| 	info_t* info;
 | |
| 
 | |
| 	info = fs->info;
 | |
| 	if (info)
 | |
| 	{
 | |
| 		if (info->name.ptr)
 | |
| 		{
 | |
| 			QSE_ASSERT (info->name.len > 0);
 | |
| 			QSE_MMGR_FREE (fs->mmgr, info->name.ptr);
 | |
| 			info->name.ptr = QSE_NULL;
 | |
| 			info->name.len = 0;
 | |
| 		}
 | |
| 
 | |
| #if defined(_WIN32)
 | |
| 		if (info->handle != INVALID_HANDLE_VALUE) 
 | |
| 		{
 | |
| 			FindClose (info->handle);
 | |
| 			info->handle = INVALID_HANDLE_VALUE;
 | |
| 		}
 | |
| #elif defined(__OS2__)
 | |
| 		/* TODO: implement this */
 | |
| #elif defined(__DOS__)
 | |
| 		/* TODO: implement this */
 | |
| #else
 | |
| 		if (info->mcurdir && info->mcurdir != fs->curdir)
 | |
| 			QSE_MMGR_FREE (fs->mmgr, info->mcurdir);
 | |
| 		info->mcurdir = QSE_NULL;
 | |
| 			
 | |
| 		if (info->handle)
 | |
| 		{
 | |
| 			closedir (info->handle);
 | |
| 			info->handle = QSE_NULL;
 | |
| 		}
 | |
| #endif
 | |
| 
 | |
| 		QSE_MMGR_FREE (fs->mmgr, info);
 | |
| 		fs->info = QSE_NULL;
 | |
| 	}
 | |
| 
 | |
| 	if (fs->curdir) 
 | |
| 	{
 | |
| 		QSE_MMGR_FREE (fs->mmgr, fs->curdir);
 | |
| 		fs->curdir = QSE_NULL;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| qse_mmgr_t* qse_fs_getmmgr (qse_fs_t* fs)
 | |
| {
 | |
| 	return fs->mmgr;
 | |
| }
 | |
| 
 | |
| void* qse_fs_getxtn (qse_fs_t* fs)
 | |
| {
 | |
| 	return QSE_XTN (fs);
 | |
| }
 | |
| 
 | |
| int qse_fs_getopt (qse_fs_t* fs, qse_fs_opt_t id, void* value)
 | |
| {
 | |
| 	switch (id)
 | |
| 	{
 | |
| 		case QSE_FS_TRAIT:
 | |
| 			*(int*)value = fs->trait;
 | |
| 			return 0;
 | |
| 
 | |
| 		case QSE_FS_CBS:
 | |
| 			*(qse_fs_cbs_t*)value = fs->cbs;
 | |
| 			return 0;
 | |
| 	}
 | |
| 
 | |
| 	fs->errnum = QSE_FS_EINVAL;
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| int qse_fs_setopt (qse_fs_t* fs, qse_fs_opt_t id, const void* value)
 | |
| {
 | |
| 	switch (id)
 | |
| 	{
 | |
| 		case QSE_FS_TRAIT:
 | |
| 			fs->trait = *(const int*)value;
 | |
| 			return 0;
 | |
| 
 | |
| 
 | |
| 		case QSE_FS_CBS:
 | |
| 			fs->cbs = *(qse_fs_cbs_t*)value;
 | |
| 			return 0;
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 	fs->errnum = QSE_FS_EINVAL;
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| static QSE_INLINE info_t* get_info (qse_fs_t* fs)
 | |
| {
 | |
| 	info_t* info;
 | |
| 
 | |
| 	info = fs->info;
 | |
| 	if (info == QSE_NULL)
 | |
| 	{
 | |
| 		info = QSE_MMGR_ALLOC (fs->mmgr, QSE_SIZEOF(*info));
 | |
| 		if (info == QSE_NULL) 
 | |
| 		{
 | |
| 			fs->errnum = QSE_FS_ENOMEM;
 | |
| 			return QSE_NULL;
 | |
| 		}
 | |
| 
 | |
| 		QSE_MEMSET (info, 0, QSE_SIZEOF(*info));
 | |
| #if defined(_WIN32)
 | |
| 		info->handle = INVALID_HANDLE_VALUE;
 | |
| #endif
 | |
| 		fs->info = info;
 | |
| 	}
 | |
| 
 | |
| 	return info;
 | |
| }
 | |
| 
 | |
| int qse_fs_chdir (qse_fs_t* fs, const qse_char_t* name)
 | |
| {
 | |
| 	qse_char_t* fsname;
 | |
| 	info_t* info;
 | |
| 
 | |
| #if defined(_WIN32)
 | |
| 	HANDLE handle;
 | |
| 	WIN32_FIND_DATA wfd;
 | |
| 	const qse_char_t* tmp_name[4];
 | |
| 	qse_size_t idx;
 | |
| #elif defined(__OS2__)
 | |
| 	/* TODO: implement this */
 | |
| #elif defined(__DOS__)
 | |
| 	/* TODO: implement this */
 | |
| #else
 | |
| 	DIR* handle;
 | |
| 	qse_mchar_t* mfsname;
 | |
| 	const qse_char_t* tmp_name[4];
 | |
| 	qse_size_t idx;
 | |
| #endif
 | |
| 
 | |
| 	if (name[0] == QSE_T('\0'))
 | |
| 	{
 | |
| 		fs->errnum = QSE_FS_EINVAL;
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	info = get_info (fs);
 | |
| 	if (info == QSE_NULL) return -1;
 | |
| 
 | |
| #if defined(_WIN32)
 | |
| 	idx = 0;
 | |
| 	if (!qse_isabspath(name) && fs->curdir)
 | |
| 		tmp_name[idx++] = fs->curdir;
 | |
| 	tmp_name[idx++] = name;
 | |
| 
 | |
| 	if (qse_isdrivecurpath(name)) 
 | |
| 		tmp_name[idx++] = QSE_T(" ");
 | |
| 	else
 | |
| 		tmp_name[idx++] = QSE_T("\\ ");
 | |
| 
 | |
| 	tmp_name[idx] = QSE_NULL;
 | |
| 
 | |
| 	fsname = qse_stradup (tmp_name, QSE_NULL, fs->mmgr);
 | |
| 	if (fsname == QSE_NULL) 
 | |
| 	{
 | |
| 		fs->errnum = QSE_FS_ENOMEM; 
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	idx = qse_canonpath (fsname, fsname, 0);
 | |
| 	/* Put an asterisk after canonicalization to prevent side-effects.
 | |
| 	 * otherwise, .\* would be transformed to * by qse_canonpath() */
 | |
| 	fsname[idx-1] = QSE_T('*'); 
 | |
| 
 | |
| 	/* Using FindExInfoBasic won't resolve cAlternatFileName.
 | |
| 	 * so it can get faster a little bit. The problem is that
 | |
| 	 * it is not supported on old windows. just stick to the
 | |
| 	 * simple API instead. */
 | |
| 	#if 0
 | |
| 	handle = FindFirstFileEx (
 | |
| 		fsname, FindExInfoBasic,
 | |
| 		&wfd, FindExSearchNameMatch, 
 | |
| 		NULL, 0/*FIND_FIRST_EX_CASE_SENSITIVE*/);
 | |
| 	#endif
 | |
| 	handle = FindFirstFile (fsname, &wfd);
 | |
| 	if (handle == INVALID_HANDLE_VALUE) 
 | |
| 	{
 | |
| 		fs->errnum = qse_fs_syserrtoerrnum (fs, GetLastError());
 | |
| 		QSE_MMGR_FREE (fs->mmgr, fsname);
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	if (info->handle != INVALID_HANDLE_VALUE) 
 | |
| 		FindClose (info->handle);
 | |
| 
 | |
| 	QSE_MEMSET (info, 0, QSE_SIZEOF(*info));
 | |
| 	info->handle = handle;
 | |
| 	info->wfd = wfd;
 | |
| 	info->just_changed_fs = 1;
 | |
| 
 | |
| 	if (fs->curdir) QSE_MMGR_FREE (fs->mmgr, fs->curdir);
 | |
| 	fsname[idx-1] = QSE_T('\0'); /* drop the asterisk */
 | |
| 	fs->curdir = fsname;
 | |
| 
 | |
| 	return 0;
 | |
| 
 | |
| #elif defined(__OS2__)
 | |
| 	/* TODO: implement this */
 | |
| 	return 0;
 | |
| #elif defined(__DOS__)
 | |
| 	/* TODO: implement this */
 | |
| 	return 0;
 | |
| #else
 | |
| 
 | |
| 	idx = 0;
 | |
| 	if (!qse_isabspath(name) && fs->curdir)
 | |
| 	{
 | |
| 		tmp_name[idx++] = fs->curdir;
 | |
| 		tmp_name[idx++] = QSE_T("/");
 | |
| 	}
 | |
| 	tmp_name[idx++] = name;
 | |
| 	tmp_name[idx] = QSE_NULL;
 | |
| 
 | |
| 	fsname = qse_stradup (tmp_name, QSE_NULL, fs->mmgr);
 | |
| 	if (fsname == QSE_NULL)
 | |
| 	{	
 | |
| 		fs->errnum = QSE_FS_ENOMEM;
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	qse_canonpath (fsname, fsname, 0);
 | |
| 
 | |
| #if defined(QSE_CHAR_IS_MCHAR)
 | |
| 	mfsname = fsname;
 | |
| #else
 | |
| 	mfsname = qse_wcstombsdup (fsname, QSE_NULL, fs->mmgr);
 | |
| 	if (mfsname == QSE_NULL)
 | |
| 	{
 | |
| 		fs->errnum = QSE_FS_ENOMEM;
 | |
| 		QSE_MMGR_FREE (fs->mmgr, fsname);
 | |
| 		return -1;
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
| 	handle = opendir (mfsname);
 | |
| 
 | |
| 	if (handle == QSE_NULL)
 | |
| 	{
 | |
| 		fs->errnum = qse_fs_syserrtoerrnum (fs, errno);
 | |
| 		if (mfsname != fsname) 
 | |
| 			QSE_MMGR_FREE (fs->mmgr, mfsname);
 | |
| 		QSE_MMGR_FREE (fs->mmgr, fsname);
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	if (info->handle) closedir (info->handle);
 | |
| 	info->handle = handle;
 | |
| 
 | |
| 	if (info->mcurdir && info->mcurdir != fs->curdir)
 | |
| 		QSE_MMGR_FREE (fs->mmgr, info->mcurdir);
 | |
| 	info->mcurdir = mfsname;
 | |
| 
 | |
| 	if (fs->curdir) QSE_MMGR_FREE (fs->mmgr, fs->curdir);
 | |
| 	fs->curdir = fsname;
 | |
| 
 | |
| 	return 0;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| #if defined(QSE_CHAR_IS_MCHAR) || defined(_WIN32)
 | |
| static int set_entry_name (qse_fs_t* fs, const qse_char_t* name)
 | |
| #else
 | |
| static int set_entry_name (qse_fs_t* fs, const qse_mchar_t* name)
 | |
| #endif
 | |
| {
 | |
| 	info_t* info;
 | |
| 	qse_size_t len;
 | |
| 
 | |
| #if defined(QSE_CHAR_IS_MCHAR) || defined(_WIN32)
 | |
| 	/* nothing more to declare */
 | |
| #else
 | |
| 	qse_size_t mlen;
 | |
| #endif
 | |
| 
 | |
| 	info = fs->info;
 | |
| 	QSE_ASSERT (info != QSE_NULL);
 | |
| 
 | |
| #if defined(QSE_CHAR_IS_MCHAR) || defined(_WIN32)
 | |
| 	len = qse_strlen (name);
 | |
| #else
 | |
| 	/* TODO: ignore MBWCERR */
 | |
| 	if (qse_mbstowcs (name, &mlen, QSE_NULL, &len) <= -1)
 | |
| 	{
 | |
| 		/* invalid name ??? */
 | |
| 		return -1;
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
| 	if (len > info->name.len)
 | |
| 	{
 | |
| 		qse_char_t* tmp;
 | |
| 
 | |
| /* TOOD: round up len to the nearlest multiples of something (32, 64, ??)*/
 | |
| 		tmp = QSE_MMGR_REALLOC (
 | |
| 			fs->mmgr, 
 | |
| 			info->name.ptr, 
 | |
| 			(len + 1) * QSE_SIZEOF(*tmp)
 | |
| 		);
 | |
| 		if (tmp == QSE_NULL)
 | |
| 		{
 | |
| 			fs->errnum = QSE_FS_ENOMEM;
 | |
| 			return -1;
 | |
| 		}
 | |
| 
 | |
| 		info->name.len = len;
 | |
| 		info->name.ptr = tmp;
 | |
| 	}
 | |
| 
 | |
| #if defined(QSE_CHAR_IS_MCHAR) || defined(_WIN32)
 | |
| 	qse_strcpy (info->name.ptr, name);
 | |
| #else
 | |
| 	len++; /* for terminating null */
 | |
| 	qse_mbstowcs (name, &mlen, info->name.ptr, &len);
 | |
| #endif
 | |
| 
 | |
| 	fs->ent.name.base = info->name.ptr;
 | |
| 	fs->ent.flags |= QSE_FS_ENT_NAME;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| #if defined(_WIN32)
 | |
| static QSE_INLINE void filetime_to_ntime (const FILETIME* ft, qse_ntime_t* nt)
 | |
| {
 | |
| 	/* reverse of http://support.microsoft.com/kb/167296/en-us */
 | |
| 	ULARGE_INTEGER li;
 | |
| 
 | |
| 	li.LowPart = ft->dwLowDateTime;
 | |
| 	li.HighPart = ft->dwHighDateTime;
 | |
| 
 | |
| #if (QSE_SIZEOF_LONG_LONG>=8)
 | |
| 	li.QuadPart -= 116444736000000000ull;
 | |
| #elif (QSE_SIZEOF___INT64>=8)
 | |
| 	li.QuadPart -= 116444736000000000ui64;
 | |
| #else
 | |
| #	error Unsupported 64bit integer type
 | |
| #endif
 | |
| 	/*li.QuadPart /= 10000000;*/
 | |
| 	/*li.QuadPart /= 10000;
 | |
| 	return li.QuadPart;*/
 | |
| 
 | |
| 	/* li.QuadPart is in the 100-nanosecond intervals */
 | |
| 	nt->sec =  li.QuadPart / (QSE_NSECS_PER_SEC / 100);
 | |
| 	nt->nsec = (li.QuadPart % (QSE_NSECS_PER_SEC / 100)) * 100;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| qse_fs_ent_t* qse_fs_read (qse_fs_t* fs, int flags)
 | |
| {
 | |
| #if defined(_WIN32)
 | |
| 	info_t* info;
 | |
| 
 | |
| 	info = fs->info;
 | |
| 	if (info == QSE_NULL) 
 | |
| 	{
 | |
| 		fs->errnum = QSE_FS_ENOENT; /* TODO: is this correct? */
 | |
| 		return QSE_NULL;
 | |
| 	}
 | |
| 
 | |
| 	if (info->just_changed_fs)
 | |
| 	{
 | |
| 		info->just_changed_fs = 0;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		if (FindNextFile (info->handle, &info->wfd) == FALSE) 
 | |
| 		{
 | |
| 			DWORD e = GetLastError();
 | |
| 			if (e == ERROR_NO_MORE_FILES)
 | |
| 			{
 | |
| 				fs->errnum = QSE_FS_ENOERR;
 | |
| 				return QSE_NULL;
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				fs->errnum = qse_fs_syserrtoerrnum (fs, e);
 | |
| 				return QSE_NULL;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* call set_entry_name before changing other fields
 | |
| 	 * in fs->ent not to pollute it in case set_entry_name fails */
 | |
| 	QSE_MEMSET (&fs->ent, 0, QSE_SIZEOF(fs->ent));
 | |
| 	if (set_entry_name (fs, info->wfd.cFileName) <= -1) return QSE_NULL;
 | |
| 
 | |
| 	if (flags & QSE_FS_ENT_TYPE)
 | |
| 	{
 | |
| #if !defined(IO_REPARSE_TAG_SYMLINK)
 | |
| #	define IO_REPARSE_TAG_SYMLINK 0xA000000C
 | |
| #endif
 | |
| #if !defined(FILE_ATTRIBUTE_REPARSE_POINT)
 | |
| #	define FILE_ATTRIBUTE_REPARSE_POINT 0x00000400
 | |
| #endif
 | |
| 		if (info->wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
 | |
| 		{
 | |
| 			fs->ent.type = QSE_FS_ENT_SUBDIR;
 | |
| 		}
 | |
| 		else if ((info->wfd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
 | |
| 			 (info->wfd.dwReserved0 == IO_REPARSE_TAG_SYMLINK))
 | |
| 		{
 | |
| 			fs->ent.type = QSE_FS_ENT_SYMLINK;
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			HANDLE h;
 | |
| 			qse_char_t* tmp_name[4];
 | |
| 			qse_char_t* fname;
 | |
| 
 | |
| /* TODO: use a buffer in info... instead of allocating an deallocating every time */
 | |
| 			tmp_name[0] = fs->curdir;
 | |
| 			tmp_name[1] = QSE_T("\\");
 | |
| 			tmp_name[2] = info->wfd.cFileName;
 | |
| 			tmp_name[3] = QSE_NULL;
 | |
| 			fname = qse_stradup (tmp_name, QSE_NULL, fs->mmgr);
 | |
| 			if (fname == QSE_NULL)
 | |
| 			{
 | |
| 				fs->errnum = QSE_FS_ENOMEM;
 | |
| 				return QSE_NULL;
 | |
| 			}
 | |
| 
 | |
| 			h = CreateFile (
 | |
| 				fname,
 | |
| 				GENERIC_READ, 
 | |
| 				FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 
 | |
| 				QSE_NULL, 
 | |
| 				OPEN_EXISTING,
 | |
| 				FILE_ATTRIBUTE_NORMAL, 
 | |
| 				0
 | |
| 			);
 | |
| 
 | |
| 			QSE_MMGR_FREE (fs->mmgr, fname);
 | |
| 
 | |
| 			if (h != INVALID_HANDLE_VALUE)
 | |
| 			{
 | |
| 				DWORD t = GetFileType (h);
 | |
| 				switch (t)
 | |
| 				{
 | |
| 					case FILE_TYPE_CHAR:
 | |
| 						fs->ent.type = QSE_FS_ENT_CHRDEV;
 | |
| 						break;
 | |
| 					case FILE_TYPE_DISK:
 | |
| 						fs->ent.type = QSE_FS_ENT_BLKDEV;
 | |
| 						break;
 | |
| 					case FILE_TYPE_PIPE:
 | |
| 						fs->ent.type = QSE_FS_ENT_PIPE;
 | |
| 						break;
 | |
| 					default:
 | |
| 						fs->ent.type = QSE_FS_ENT_UNKNOWN;
 | |
| 						break;
 | |
| 				}
 | |
| 				CloseHandle (h);
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				fs->ent.type = QSE_FS_ENT_UNKNOWN;
 | |
| 			}
 | |
| 		}
 | |
| 		fs->ent.type |= QSE_FS_ENT_TYPE;
 | |
| 	}
 | |
| 
 | |
| 	if (flags & QSE_FS_ENT_SIZE)
 | |
| 	{
 | |
| 		LARGE_INTEGER li;
 | |
| 		li.LowPart = info->wfd.nFileSizeLow;
 | |
| 		li.HighPart = info->wfd.nFileSizeHigh;
 | |
| 		fs->ent.size = li.QuadPart;
 | |
| 		fs->ent.type |= QSE_FS_ENT_SIZE;
 | |
| 	}
 | |
| 
 | |
| 	if (flags & QSE_FS_ENT_TIME)
 | |
| 	{
 | |
| 		filetime_to_ntime (&info->wfd.ftCreationTime, &fs->ent.time.create);
 | |
| 		filetime_to_ntime (&info->wfd.ftLastAccessTime, &fs->ent.time.access);
 | |
| 		filetime_to_ntime (&info->wfd.ftLastWriteTime, &fs->ent.time.modify);
 | |
| 		fs->ent.type |= QSE_FS_ENT_TIME;
 | |
| 	}
 | |
| 
 | |
| #elif defined(__OS2__)
 | |
| 	/* TODO: implement this */
 | |
| #elif defined(__DOS__)
 | |
| 	/* TODO: implement this */
 | |
| #else
 | |
| 
 | |
| 	info_t* info;
 | |
| 	struct dirent* ent;
 | |
| 	int x;
 | |
| 
 | |
| 	int stat_needed;
 | |
| 	qse_lstat_t st;
 | |
| 
 | |
| 	info = fs->info;
 | |
| 	if (info == QSE_NULL) 
 | |
| 	{
 | |
| 		fs->errnum = QSE_FS_ENOTDIR;
 | |
| 		return QSE_NULL;
 | |
| 	}
 | |
| 
 | |
| 	errno = 0;
 | |
| 	ent = readdir (info->handle);
 | |
| 	if (ent == QSE_NULL)
 | |
| 	{
 | |
| 		if (errno != 0) fs->errnum = qse_fs_syserrtoerrnum (fs, errno);
 | |
| 		return QSE_NULL;
 | |
| 	}
 | |
| 
 | |
| 	QSE_MEMSET (&fs->ent, 0, QSE_SIZEOF(fs->ent));
 | |
| 	if (set_entry_name (fs, ent->d_name) <= -1) return QSE_NULL;
 | |
| 
 | |
| 	stat_needed =
 | |
| 	#if !defined(HAVE_STRUCT_DIRENT_D_TYPE)
 | |
| 		(flags & QSE_FS_ENT_TYPE) ||
 | |
| 	#endif
 | |
| 		(flags & QSE_FS_ENT_SIZE) ||
 | |
| 		(flags & QSE_FS_ENT_TIME);
 | |
| 	if (stat_needed)
 | |
| 	{
 | |
| 		const qse_mchar_t* tmp_name[4];
 | |
| 		qse_mchar_t* mfname;
 | |
| 
 | |
| /* TODO: use a buffer in info... instead of allocating an deallocating every time */
 | |
| 		tmp_name[0] = info->mcurdir;
 | |
| 		tmp_name[1] = QSE_MT("/");
 | |
| 		tmp_name[2] = ent->d_name;
 | |
| 		tmp_name[3] = QSE_NULL;
 | |
| 		mfname = qse_mbsadup(tmp_name, QSE_NULL, fs->mmgr);
 | |
| 		if (mfname == QSE_NULL)
 | |
| 		{
 | |
| 			fs->errnum = QSE_FS_ENOMEM;
 | |
| 			return QSE_NULL;
 | |
| 		}
 | |
| 	
 | |
| 	#if defined(HAVE_LSTAT)
 | |
| 		x = QSE_LSTAT (mfname, &st);
 | |
| 	#else
 | |
| 		x = QSE_STAT (mfname, &st);
 | |
| 	#endif
 | |
| 		QSE_MMGR_FREE (fs->mmgr, mfname);
 | |
| 
 | |
| 		if (x == -1)
 | |
| 		{
 | |
| 			fs->errnum = qse_fs_syserrtoerrnum (fs, errno);
 | |
| 			return QSE_NULL;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (flags & QSE_FS_ENT_TYPE)
 | |
| 	{
 | |
| 	#if defined(HAVE_STRUCT_DIRENT_D_TYPE) && defined(DT_DIR) && defined(DT_REG) /* and more */
 | |
| 		switch (ent->d_type)
 | |
| 		{
 | |
| 			case DT_DIR:
 | |
| 				fs->ent.type = QSE_FS_ENT_SUBDIR;
 | |
| 				break;
 | |
| 
 | |
| 			case DT_REG:
 | |
| 				fs->ent.type = QSE_FS_ENT_REGULAR;
 | |
| 				break;
 | |
| 
 | |
| 			case DT_LNK:
 | |
| 				fs->ent.type = QSE_FS_ENT_SYMLINK;
 | |
| 				break;
 | |
| 
 | |
| 			case DT_BLK: 
 | |
| 				fs->ent.type = QSE_FS_ENT_BLKDEV;
 | |
| 				break;
 | |
| 
 | |
| 			case DT_CHR:
 | |
| 				fs->ent.type = QSE_FS_ENT_CHRDEV;
 | |
| 				break;
 | |
| 
 | |
| 			case DT_FIFO:
 | |
| 	#if defined(DT_SOCK)
 | |
| 			case DT_SOCK:
 | |
| 	#endif
 | |
| 				fs->ent.type = QSE_FS_ENT_PIPE;
 | |
| 				break;
 | |
| 
 | |
| 			default:
 | |
| 				fs->ent.type = QSE_FS_ENT_UNKNOWN;
 | |
| 				break;
 | |
| 		}
 | |
| 
 | |
| 	#else
 | |
| 		#if defined(__S_IFMT) && !defined(S_IFMT)
 | |
| 		#	define S_IFMT __S_IFMT
 | |
| 		#endif
 | |
| 		#if defined(__S_IFDIR) && !defined(S_IFDIR)
 | |
| 		#	define S_IFDIR __S_IFDIR
 | |
| 		#endif
 | |
| 		#define IS_TYPE(st,type) ((st.st_mode & S_IFMT) == S_IFDIR)
 | |
| 		fs->ent.type = IS_TYPE(st,S_IFDIR)?  QSE_FS_ENT_SUBDIR:
 | |
| 		               IS_TYPE(st,S_IFREG)?  QSE_FS_ENT_REGULAR:
 | |
| 		               IS_TYPE(st,S_IFLNK)?  QSE_FS_ENT_SYMLINK:
 | |
| 		               IS_TYPE(st,S_IFCHR)?  QSE_FS_ENT_CHRDEV:
 | |
| 		               IS_TYPE(st,S_IFBLK)?  QSE_FS_ENT_BLKDEV:
 | |
| 		               IS_TYPE(st,S_IFIFO)?  QSE_FS_ENT_PIPE:
 | |
| 		#if defined(S_IFSOCK)
 | |
| 		               IS_TYPE(st,S_IFSOCK)? QSE_FS_ENT_PIPE:
 | |
| 		#endif
 | |
| 		                                     QSE_FS_ENT_UNKNOWN;
 | |
| 		#undef IS_TYPE
 | |
| 	#endif
 | |
| 		fs->ent.flags |= QSE_FS_ENT_TYPE;
 | |
| 	}
 | |
| 
 | |
| 	if (flags & QSE_FS_ENT_SIZE)
 | |
| 	{
 | |
| 		fs->ent.size = st.st_size;
 | |
| 		fs->ent.flags |= QSE_FS_ENT_SIZE;
 | |
| 	}
 | |
| 
 | |
| 	if (flags & QSE_FS_ENT_TIME)
 | |
| 	{
 | |
| 	#if defined(HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
 | |
| 		#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIM_TV_NSEC)
 | |
| 		fs->ent.time.create.sec = st.st_birthtim.tv_sec;
 | |
| 		fs->ent.time.create.nsec = st.st_birthtim.tv_nsec;
 | |
| 		#endif
 | |
| 
 | |
| 		fs->ent.time.access.sec = st.st_atim.tv_sec;
 | |
| 		fs->ent.time.access.nsec = st.st_atim.tv_nsec;
 | |
| 		fs->ent.time.modify.sec = st.st_mtim.tv_sec;
 | |
| 		fs->ent.time.modify.nsec = st.st_mtim.tv_nsec;
 | |
| 		fs->ent.time.change.sec = st.st_ctim.tv_sec;
 | |
| 		fs->ent.time.change.nsec = st.st_ctim.tv_nsec;
 | |
| 	#elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC)
 | |
| 		#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC)
 | |
| 		fs->ent.time.create.sec = st.st_birthtimespec.tv_sec;
 | |
| 		fs->ent.time.create.nsec = st.st_birthtimespec.tv_nsec;
 | |
| 		#endif
 | |
| 
 | |
| 		fs->ent.time.access.sec = st.st_atimespec.tv_sec;
 | |
| 		fs->ent.time.access.nsec = st.st_atimespec.tv_nsec;
 | |
| 		fs->ent.time.modify.sec = st.st_mtimespec.tv_sec;
 | |
| 		fs->ent.time.modify.nsec = st.st_mtimespec.tv_nsec;
 | |
| 		fs->ent.time.change.sec = st.st_ctimespec.tv_sec;
 | |
| 		fs->ent.time.change.nsec = st.st_ctimespec.tv_nsec;
 | |
| 	#else
 | |
| 		#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIME)
 | |
| 		fs->ent.time.create.sec = st.st_birthtime;
 | |
| 		#endif
 | |
| 		fs->ent.time.access.sec = st.st_atime;
 | |
| 		fs->ent.time.modify.sec = st.st_mtime;
 | |
| 		fs->ent.time.change.sec = st.st_ctime;
 | |
| 	#endif
 | |
| 		fs->ent.flags |= QSE_FS_ENT_TIME;
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
| 	return &fs->ent;
 | |
| }
 | |
| 
 | |
| int qse_fs_rewind (qse_fs_t* fs)
 | |
| {
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| qse_fs_char_t* qse_fs_makefspathformbs (qse_fs_t* fs, const qse_mchar_t* path)
 | |
| {
 | |
| 	qse_fs_char_t* fspath;
 | |
| 
 | |
| #if defined(QSE_FS_CHAR_IS_MCHAR)
 | |
| 	fspath = (qse_mchar_t*)path;
 | |
| #else
 | |
| 	fspath = qse_mbstowcsdupwithcmgr (path, QSE_NULL, fs->mmgr, fs->cmgr);
 | |
| 	if (!fspath) fs->errnum = QSE_FS_ENOMEM;
 | |
| #endif
 | |
| 
 | |
| 	return fspath;
 | |
| }
 | |
| 
 | |
| qse_fs_char_t* qse_fs_makefspathforwcs (qse_fs_t* fs, const qse_wchar_t* path)
 | |
| {
 | |
| 	qse_fs_char_t* fspath;
 | |
| 
 | |
| #if defined(QSE_FS_CHAR_IS_MCHAR)
 | |
| 	fspath = qse_wcstombsdupwithcmgr (path, QSE_NULL, fs->mmgr, fs->cmgr);
 | |
| 	if (!fspath) fs->errnum = QSE_FS_ENOMEM;
 | |
| #else
 | |
| 	fspath = path;
 | |
| #endif
 | |
| 
 | |
| 	return fspath;
 | |
| }
 | |
| 
 | |
| qse_fs_char_t* qse_fs_dupfspathformbs (qse_fs_t* fs, const qse_mchar_t* path)
 | |
| {
 | |
| 	qse_fs_char_t* fspath;
 | |
| 
 | |
| #if defined(QSE_FS_CHAR_IS_MCHAR)
 | |
| 	fspath = qse_mbsdup (path, fs->mmgr);
 | |
| #else
 | |
| 	fspath = qse_mbstowcsdupwithcmgr (path, QSE_NULL, fs->mmgr, fs->cmgr);
 | |
| 	if (!fspath) fs->errnum = QSE_FS_ENOMEM;
 | |
| #endif
 | |
| 
 | |
| 	return fspath;
 | |
| }
 | |
| 
 | |
| qse_fs_char_t* qse_fs_dupfspathforwcs (qse_fs_t* fs, const qse_wchar_t* path)
 | |
| {
 | |
| 	qse_fs_char_t* fspath;
 | |
| 
 | |
| #if defined(QSE_FS_CHAR_IS_MCHAR)
 | |
| 	fspath = qse_wcstombsdupwithcmgr (path, QSE_NULL, fs->mmgr, fs->cmgr);
 | |
| 	if (!fspath) fs->errnum = QSE_FS_ENOMEM;
 | |
| #else
 | |
| 	fspath = qse_wcsdup (path, fs->mmgr);
 | |
| #endif
 | |
| 
 | |
| 	return fspath;
 | |
| }
 | |
| 
 | |
| void qse_fs_freefspathformbs (qse_fs_t* fs, const qse_mchar_t* path, qse_fs_char_t* fspath)
 | |
| {
 | |
| 	if (path != (const qse_mchar_t*)fspath) QSE_MMGR_FREE (fs->mmgr, fspath);
 | |
| }
 | |
| 
 | |
| void qse_fs_freefspathforwcs (qse_fs_t* fs, const qse_wchar_t* path, qse_fs_char_t* fspath)
 | |
| {
 | |
| 	if (path != (const qse_wchar_t*)fspath) QSE_MMGR_FREE (fs->mmgr, fspath);
 | |
| }
 | |
| 
 | |
| 
 | |
| int qse_fs_invokeactcb (qse_fs_t* fs, qse_fs_action_t action, qse_fs_char_t* src_fspath, qse_fs_char_t* dst_fspath, qse_uintmax_t bytes_total, qse_uintmax_t bytes_done)
 | |
| {
 | |
| 	qse_char_t* srcpath = QSE_NULL, * dstpath = QSE_NULL;
 | |
| 	int x = 1;
 | |
| 
 | |
| 	if (src_fspath) 
 | |
| 	{
 | |
| 		srcpath = (qse_char_t*)make_str_with_fspath (fs, src_fspath);
 | |
| 		if (!srcpath) 
 | |
| 		{
 | |
| 			x = -1;
 | |
| 			goto done;
 | |
| 		}
 | |
| 	}
 | |
| 	if (dst_fspath) 
 | |
| 	{
 | |
| 		dstpath = (qse_char_t*)make_str_with_fspath (fs, dst_fspath);
 | |
| 		if (!dstpath)
 | |
| 		{
 | |
| 			x = -1;
 | |
| 			goto done;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	x = fs->cbs.actcb (fs, action, srcpath, dstpath, bytes_total, bytes_done);
 | |
| 
 | |
| done:
 | |
| 	if (srcpath) free_str_with_fspath (fs, cpfile->src_fspath, srcpath);
 | |
| 	if (dstpath) free_str_with_fspath (fs, cpfile->dst_fspath, dstpath);
 | |
| 
 | |
| 	return x;
 | |
| }
 |