From e5a96934119ba92a212e87c726d5e9914a9b94cc Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Fri, 28 Oct 2011 01:05:19 +0000 Subject: [PATCH] enhanded qse_dir_read() --- qse/Makefile.in | 2 +- qse/configure | 30 ++++ qse/configure.ac | 3 + qse/include/qse/cmn/time.h | 7 +- qse/include/qse/config.h.in | 9 ++ qse/include/qse/fs/dir.h | 56 +++++-- qse/lib/fs/dir.c | 291 +++++++++++++++++++++++++++++------- qse/samples/fs/dir01.c | 15 +- 8 files changed, 336 insertions(+), 77 deletions(-) diff --git a/qse/Makefile.in b/qse/Makefile.in index ac445d01..fd1fe0a1 100644 --- a/qse/Makefile.in +++ b/qse/Makefile.in @@ -34,7 +34,7 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = . -DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \ +DIST_COMMON = $(am__configure_deps) $(srcdir)/Makefile.am \ $(srcdir)/Makefile.in $(srcdir)/README.in \ $(top_srcdir)/configure ac/config.guess ac/config.sub \ ac/depcomp ac/install-sh ac/ltmain.sh ac/missing diff --git a/qse/configure b/qse/configure index 090e3aaa..b943268b 100755 --- a/qse/configure +++ b/qse/configure @@ -15745,6 +15745,36 @@ _ACEOF fi +ac_fn_c_check_member "$LINENO" "struct stat" "st_birthtime" "ac_cv_member_struct_stat_st_birthtime" "$ac_includes_default" +if test "x$ac_cv_member_struct_stat_st_birthtime" = x""yes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_STAT_ST_BIRTHTIME 1 +_ACEOF + + +fi + +ac_fn_c_check_member "$LINENO" "struct stat" "st_mtim.tv_nsec" "ac_cv_member_struct_stat_st_mtim_tv_nsec" "$ac_includes_default" +if test "x$ac_cv_member_struct_stat_st_mtim_tv_nsec" = x""yes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC 1 +_ACEOF + + +fi + +ac_fn_c_check_member "$LINENO" "struct stat" "st_mtimespec.tv_nsec" "ac_cv_member_struct_stat_st_mtimespec_tv_nsec" "$ac_includes_default" +if test "x$ac_cv_member_struct_stat_st_mtimespec_tv_nsec" = x""yes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1 +_ACEOF + + +fi + # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects diff --git a/qse/configure.ac b/qse/configure.ac index e7a03f8d..2b9575c0 100644 --- a/qse/configure.ac +++ b/qse/configure.ac @@ -109,6 +109,9 @@ AC_CHECK_FUNCS([powf fmodf sinf cosf tanf atanf atan2f logf expf sqrtf]) LIBS="$OLDLIBS" AC_STRUCT_DIRENT_D_TYPE +AC_CHECK_MEMBERS([struct stat.st_birthtime]) +AC_CHECK_MEMBERS([struct stat.st_mtim.tv_nsec]) +AC_CHECK_MEMBERS([struct stat.st_mtimespec.tv_nsec]) dnl check the size of primitive data types AC_CHECK_SIZEOF(char,,[[]]) diff --git a/qse/include/qse/cmn/time.h b/qse/include/qse/cmn/time.h index 5b9d7d52..05fa051f 100644 --- a/qse/include/qse/cmn/time.h +++ b/qse/include/qse/cmn/time.h @@ -53,12 +53,16 @@ #define QSE_USECS_PER_MSEC (1000) #define QSE_NSECS_PER_USEC (1000) +#define QSE_NSECS_PER_MSEC (QSE_NSECS_PER_USEC*QSE_USECS_PER_MSEC) #define QSE_USECS_PER_SEC (QSE_USECS_PER_MSEC*QSE_MSECS_PER_SEC) #define QSE_IS_LEAPYEAR(year) ((!((year)%4) && ((year)%100)) || !((year)%400)) #define QSE_DAYS_PER_YEAR(year) \ (QSE_IS_LEAPYEAR(year)? QSE_DAYS_PER_LEAPYEAR: QSE_DAYS_PER_NORMYEAR) +#define QSE_SECNSEC_TO_MSEC(sec,nsec) \ + (((qse_ntime_t)(sec) * QSE_MSECS_PER_SEC) + ((qse_ntime_t)(nsec) / QSE_NSECS_PER_MSEC)) + /* number of milliseconds since the Epoch (00:00:00 UTC, Jan 1, 1970) */ typedef qse_long_t qse_ntime_t; typedef struct qse_btime_t qse_btime_t; @@ -95,7 +99,6 @@ int qse_gettime ( int qse_settime ( qse_ntime_t nt ); -/******/ /** @@ -137,7 +140,7 @@ int qse_timelocal ( * The qse_strftime() functions formats time. */ qse_size_t qse_strftime ( - qse_char_t* buf, + qse_char_t* buf, qse_size_t size, const qse_char_t* fmt, qse_btime_t* bt diff --git a/qse/include/qse/config.h.in b/qse/include/qse/config.h.in index bbb11e7a..d02d243a 100644 --- a/qse/include/qse/config.h.in +++ b/qse/include/qse/config.h.in @@ -178,6 +178,15 @@ /* Define to 1 if `d_type' is a member of `struct dirent'. */ #undef HAVE_STRUCT_DIRENT_D_TYPE +/* Define to 1 if `st_birthtime' is a member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_BIRTHTIME + +/* Define to 1 if `st_mtimespec.tv_nsec' is a member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC + +/* Define to 1 if `st_mtim.tv_nsec' is a member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC + /* Define to 1 if you have the `sysconf' function. */ #undef HAVE_SYSCONF diff --git a/qse/include/qse/fs/dir.h b/qse/include/qse/fs/dir.h index c78f134b..1b95d53b 100644 --- a/qse/include/qse/fs/dir.h +++ b/qse/include/qse/fs/dir.h @@ -23,6 +23,7 @@ #include #include +#include enum qse_dir_errnum_t { @@ -38,20 +39,46 @@ enum qse_dir_errnum_t }; typedef enum qse_dir_errnum_t qse_dir_errnum_t; +enum qse_dir_ent_flag_t +{ + QSE_DIR_ENT_NAME = (1 << 0), + QSE_DIR_ENT_TYPE = (1 << 1), + QSE_DIR_ENT_SIZE = (1 << 2), + QSE_DIR_ENT_TIME = (1 << 3) +}; + +enum qse_dir_ent_type_t +{ + QSE_DIR_ENT_UNKNOWN, + QSE_DIR_ENT_SUBDIR, + QSE_DIR_ENT_REGULAR, + QSE_DIR_ENT_CHRDEV, + QSE_DIR_ENT_BLKDEV, + QSE_DIR_ENT_SYMLINK, + QSE_DIR_ENT_PIPE +}; + +typedef enum qse_dir_ent_type_t qse_dir_ent_type_t; + struct qse_dir_ent_t { - enum + int flags; + + struct { - QSE_DIR_ENT_UNKNOWN, - QSE_DIR_ENT_DIR, - QSE_DIR_ENT_REG, - QSE_DIR_ENT_FIFO, - QSE_DIR_ENT_CHAR, - QSE_DIR_ENT_BLOCK, - QSE_DIR_ENT_LINK - } type; - qse_char_t* name; - qse_foff_t size; + qse_char_t* base; + qse_char_t* path; + } name; + qse_dir_ent_type_t type; + qse_foff_t size; + + struct + { + qse_ntime_t create; + qse_ntime_t access; + qse_ntime_t modify; + qse_ntime_t change; /* inode status change */ + } time; }; typedef struct qse_dir_ent_t qse_dir_ent_t; @@ -70,10 +97,10 @@ typedef struct qse_dir_t qse_dir_t; enum qse_dir_option_t { /**< don't follow a symbolic link in qse_dir_change() */ - QSE_DIR_NOFOLLOW = (1 << 0), + QSE_DIR_NOFOLLOW = (1 << 1), /**< check directories against file system in qse_dir_change() */ - QSE_DIR_REALPATH = (1 << 1) + QSE_DIR_REALPATH = (1 << 2) }; #ifdef __cplusplus @@ -109,7 +136,8 @@ const qse_char_t* qse_dir_geterrmsg ( ); qse_dir_ent_t* qse_dir_read ( - qse_dir_t* dir + qse_dir_t* dir, + int flags ); int qse_dir_change ( diff --git a/qse/lib/fs/dir.c b/qse/lib/fs/dir.c index 81021b57..faf44b08 100644 --- a/qse/lib/fs/dir.c +++ b/qse/lib/fs/dir.c @@ -415,11 +415,35 @@ static int set_entry_name (qse_dir_t* dir, const qse_mchar_t* name) len++; qse_mbstowcs (name, info->name.ptr, &len); #endif - dir->ent.name = info->name.ptr; + + dir->ent.name.base = info->name.ptr; + dir->ent.flags |= QSE_DIR_ENT_NAME; return 0; } -qse_dir_ent_t* qse_dir_read (qse_dir_t* dir) +#if defined(_WIN32) +static QSE_INLINE qse_ntime_t filetime_to_ntime (const FILETIME* ft) +{ + /* 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; +} +#endif + +qse_dir_ent_t* qse_dir_read (qse_dir_t* dir, int flags) { #if defined(_WIN32) info_t* info; @@ -455,26 +479,93 @@ qse_dir_ent_t* qse_dir_read (qse_dir_t* dir) /* call set_entry_name before changing other fields * in dir->ent not to pollute it in case set_entry_name fails */ + QSE_MEMSET (&dir->ent, 0, QSE_SIZEOF(dir->ent)); if (set_entry_name (dir, info->wfd.cFileName) <= -1) return QSE_NULL; - if (info->wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + if (flags & QSE_DIR_ENT_TYPE) { - dir->ent.size = 0; - dir->ent.type = QSE_DIR_ENT_DIRECTORY; + if (info->wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + dir->ent.type = QSE_DIR_ENT_SUBDIR; + } + else if ((info->wfd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && + (info->wfd.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) + { + dir->ent.type = QSE_DIR_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] = dir->curdir; + tmp_name[1] = QSE_T("\\"); + tmp_name[2] = info->wfd.cFileName; + tmp_name[3] = QSE_NULL; + fname = qse_stradup (tmp_name, dir->mmgr); + if (fname == QSE_NULL) + { + dir->errnum = QSE_DIR_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 (dir->mmgr, fname); + + if (h != INVALID_HANDLE_VALUE) + { + DWORD t = GetFileType (h); + switch (t) + { + case FILE_TYPE_CHAR: + dir->ent.type = QSE_DIR_ENT_CHRDEV; + break; + case FILE_TYPE_DISK: + dir->ent.type = QSE_DIR_ENT_BLKDEV; + break; + case FILE_TYPE_PIPE: + dir->ent.type = QSE_DIR_ENT_PIPE; + break; + default: + dir->ent.type = QSE_DIR_ENT_UNKNOWN; + break; + } + CloseHandle (h); + } + else + { + dir->ent.type = QSE_DIR_ENT_UNKNOWN; + } + } + dir->ent.type |= QSE_DIR_ENT_TYPE; } - else if ((info->wfd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && - (info->wfd.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) + + if (flags & QSE_DIR_ENT_SIZE) { - dir->ent.size = 0; - dir->ent.type = QSE_DIR_ENT_LINK; - } - else - { - LARGE_INTEGER li; + ULARGE_INTEGER li; li.LowPart = info->wfd.nFileSizeLow; li.HighPart = info->wfd.nFileSizeHigh; dir->ent.size = li.QuadPart; - dir->ent.type = QSE_DIR_ENT_UNKNOWN; + dir->ent.type |= QSE_DIR_ENT_SIZE; + } + + if (flags & QSE_DIR_ENT_TIME) + { + dir->ent.time.create = filetime_to_ntime (&info->wfd.ftCreationTime); + dir->ent.time.access = filetime_to_ntime (&info->wfd.ftLastAccessTime); + dir->ent.time.modify = filetime_to_ntime (&info->wfd.ftLastWriteTime); + dir->ent.type |= QSE_DIR_ENT_TIME; } #elif defined(__OS2__) @@ -486,13 +577,13 @@ qse_dir_ent_t* qse_dir_read (qse_dir_t* dir) info_t* info; struct dirent* ent; int x; -#if defined(QSE_LSTAT64) + + int stat_needed; + #if defined(QSE_LSTAT64) struct stat64 st; -#else + #else struct stat st; -#endif - qse_mchar_t* tmp_name[4]; - qse_mchar_t* mfname; + #endif info = dir->info; if (info == QSE_NULL) @@ -509,48 +600,138 @@ qse_dir_ent_t* qse_dir_read (qse_dir_t* dir) return QSE_NULL; } -#if defined(HAVE_STRUCT_DIRENT_D_TYPE) -/* end->d_type */ -#endif - -/* 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, dir->mmgr); - if (mfname == QSE_NULL) - { - dir->errnum = QSE_DIR_ENOMEM; - return QSE_NULL; - } - -#if defined(QSE_LSTAT64) - x = QSE_LSTAT64 (mfname, &st); -#else - x = QSE_LSTAT (mfname, &st); -#endif - - QSE_MMGR_FREE (dir->mmgr, mfname); - - if (x == -1) - { - dir->errnum = syserr_to_errnum (errno); - return QSE_NULL; - } - + QSE_MEMSET (&dir->ent, 0, QSE_SIZEOF(dir->ent)); if (set_entry_name (dir, ent->d_name) <= -1) return QSE_NULL; + stat_needed = + #if !defined(HAVE_STRUCT_DIRENT_D_TYPE) + (flags & QSE_DIR_ENT_TYPE) || + #endif + (flags & QSE_DIR_ENT_SIZE) || + (flags & QSE_DIR_ENT_TIME); + if (stat_needed) + { + qse_mchar_t* tmp_name[4]; + qse_mchar_t* mfname; - dir->ent.size = st.st_size; +/* 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, dir->mmgr); + if (mfname == QSE_NULL) + { + dir->errnum = QSE_DIR_ENOMEM; + return QSE_NULL; + } + + #if defined(QSE_LSTAT64) + x = QSE_LSTAT64 (mfname, &st); + #else + x = QSE_LSTAT (mfname, &st); + #endif + QSE_MMGR_FREE (dir->mmgr, mfname); -#define IS_TYPE(st,type) ((st.st_mode & S_IFMT) == S_IFDIR) - dir->ent.type = IS_TYPE(st,S_IFDIR)? QSE_DIR_ENT_DIR: - IS_TYPE(st,S_IFCHR)? QSE_DIR_ENT_CHAR: - IS_TYPE(st,S_IFBLK)? QSE_DIR_ENT_BLOCK: - QSE_DIR_ENT_UNKNOWN; + if (x == -1) + { + dir->errnum = syserr_to_errnum (errno); + return QSE_NULL; + } + } + if (flags & QSE_DIR_ENT_TYPE) + { + #if defined(HAVE_STRUCT_DIRENT_D_TYPE) + switch (ent->d_type) + { + case DT_DIR: + dir->ent.type = QSE_DIR_ENT_SUBDIR; + break; + case DT_REG: + dir->ent.type = QSE_DIR_ENT_REGULAR; + break; + + case DT_LNK: + dir->ent.type = QSE_DIR_ENT_SYMLINK; + break; + + case DT_BLK: + dir->ent.type = QSE_DIR_ENT_BLKDEV; + break; + + case DT_CHR: + dir->ent.type = QSE_DIR_ENT_CHRDEV; + break; + + case DT_FIFO: + #if defined(DT_SOCK) + case DT_SOCK: + #endif + dir->ent.type = QSE_DIR_ENT_PIPE; + break; + + default: + dir->ent.type = QSE_DIR_ENT_UNKNOWN; + break; + } + + #else + #define IS_TYPE(st,type) ((st.st_mode & S_IFMT) == S_IFDIR) + dir->ent.type = IS_TYPE(st,S_IFDIR)? QSE_DIR_ENT_SUBDIR: + IS_TYPE(st,S_IFREG)? QSE_DIR_ENT_REGULAR: + IS_TYPE(st,S_IFLNK)? QSE_DIR_ENT_SYMLINK: + IS_TYPE(st,S_IFCHR)? QSE_DIR_ENT_CHRDEV: + IS_TYPE(st,S_IFBLK)? QSE_DIR_ENT_BLKDEV: + IS_TYPE(st,S_IFIFO)? QSE_DIR_ENT_PIPE: + IS_TYPE(st,S_IFSOCK)? QSE_DIR_ENT_PIPE: + QSE_DIR_ENT_UNKNOWN; + #undef IS_TYPE + #endif + dir->ent.flags |= QSE_DIR_ENT_TYPE; + } + + if (flags & QSE_DIR_ENT_SIZE) + { + dir->ent.size = st.st_size; + dir->ent.flags |= QSE_DIR_ENT_SIZE; + } + + if (flags & QSE_DIR_ENT_TIME) + { + #if defined(HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC) + #if defined(HAVE_STRUCT_STAT_ST_BIRTHTIME) + dir->ent.time.create = + QSE_SECNSEC_TO_MSEC(st.st_birthtim.tv_sec,st.st_birthtim.tv_nsec); + #endif + dir->ent.time.access = + QSE_SECNSEC_TO_MSEC(st.st_atim.tv_sec,st.st_atim.tv_nsec); + dir->ent.time.modify = + QSE_SECNSEC_TO_MSEC(st.st_mtim.tv_sec,st.st_mtim.tv_nsec); + dir->ent.time.change = + QSE_SECNSEC_TO_MSEC(st.st_ctim.tv_sec,st.st_ctim.tv_nsec); + #elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC) + #if defined(HAVE_STRUCT_STAT_ST_BIRTHTIME) + dir->ent.time.create = st.st_birthtime; + QSE_SECNSEC_TO_MSEC(st.st_birthtimespec.tv_sec,st.st_birthtimespec.tv_nsec); + #endif + dir->ent.time.access = + QSE_SECNSEC_TO_MSEC(st.st_atimespec.tv_sec,st.st_atimespec.tv_nsec); + dir->ent.time.modify = + QSE_SECNSEC_TO_MSEC(st.st_mtimespec.tv_sec,st.st_mtimespec.tv_nsec); + dir->ent.time.change = + QSE_SECNSEC_TO_MSEC(st.st_ctimespec.tv_sec,st.st_ctimespec.tv_nsec); + #else + #if defined(HAVE_STRUCT_STAT_ST_BIRTHTIME) + dir->ent.time.create = st.st_birthtime * QSE_MSEC_PER_SEC; + #endif + dir->ent.time.access = st.st_atime * QSE_MSEC_PER_SEC; + dir->ent.time.modify = st.st_mtime * QSE_MSEC_PER_SEC; + dir->ent.time.change = st.st_ctime * QSE_MSEC_PER_SEC; + #endif + dir->ent.flags |= QSE_DIR_ENT_TIME; + } #endif return &dir->ent; diff --git a/qse/samples/fs/dir01.c b/qse/samples/fs/dir01.c index e479596a..fcbc92b3 100644 --- a/qse/samples/fs/dir01.c +++ b/qse/samples/fs/dir01.c @@ -19,7 +19,9 @@ static void list (qse_dir_t* dir, const qse_char_t* name) do { - ent = qse_dir_read (dir); + qse_btime_t bt; + + ent = qse_dir_read (dir, QSE_DIR_ENT_SIZE | QSE_DIR_ENT_TYPE | QSE_DIR_ENT_TIME); if (ent == QSE_NULL) { qse_dir_errnum_t e = qse_dir_geterrnum(dir); @@ -28,10 +30,13 @@ static void list (qse_dir_t* dir, const qse_char_t* name) break; } - if (ent->type == QSE_DIR_ENT_DIRECTORY) - qse_printf (QSE_T(" %16lu %s\n"), (unsigned long)ent->size, ent->name); - else - qse_printf (QSE_T(" %16lu %s\n"), (unsigned long)ent->size, ent->name); + qse_localtime (ent->time.modify, &bt); + qse_printf (QSE_T("%s %16lu %04d-%02d-%02d %02d:%02d %s\n"), + ((ent->type == QSE_DIR_ENT_SUBDIR)? QSE_T(""): QSE_T(" ")), + (unsigned long)ent->size, + bt.year + 1900, bt.mon+1, bt.mday, bt.hour, bt.min, + ent->name.base + ); } while (1);