From f2d60b93664a0922164353d36d937797a100795a Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Tue, 25 Oct 2011 01:48:07 +0000 Subject: [PATCH] enhanced qse_dir_t --- qse/include/qse/cmn/path.h | 16 ++ qse/include/qse/fs/dir.h | 2 +- qse/lib/cmn/path-canon.c | 43 +++++- qse/lib/fs/dir.c | 298 +++++++++++++++++++++++-------------- qse/samples/cmn/str.c | 7 +- qse/samples/fs/dir01.c | 10 +- 6 files changed, 258 insertions(+), 118 deletions(-) diff --git a/qse/include/qse/cmn/path.h b/qse/include/qse/cmn/path.h index afbdefad..f4204edc 100644 --- a/qse/include/qse/cmn/path.h +++ b/qse/include/qse/cmn/path.h @@ -70,6 +70,22 @@ int qse_isabspath ( const qse_char_t* path ); +/** + * The qse_isdrivepath() function determines if a path name begins with + * a drive letter followed by a colon like A:. + */ +int qse_isdrivepath ( + const qse_char_t* path +); + +/** + * The qse_isdrivecurpath() function determines if a path name is in the form + * of a drive letter followed by a colon like A:, without any trailing path. + */ +int qse_isdrivecurpath ( + const qse_char_t* path +); + /** * The qse_canonpath() function canonicalizes a path name @a path by deleting * unnecessary path segments from it and stores the result to a memory buffer diff --git a/qse/include/qse/fs/dir.h b/qse/include/qse/fs/dir.h index c4a2b84e..94e366d0 100644 --- a/qse/include/qse/fs/dir.h +++ b/qse/include/qse/fs/dir.h @@ -33,7 +33,7 @@ enum qse_dir_errnum_t QSE_DIR_EINVAL, QSE_DIR_EACCES, QSE_DIR_ENOENT, - QSE_DIR_ENOTDIR, + QSE_DIR_ENODIR, QSE_DIR_ESYSTEM }; typedef enum qse_dir_errnum_t qse_dir_errnum_t; diff --git a/qse/lib/cmn/path-canon.c b/qse/lib/cmn/path-canon.c index 0836e6da..211768e3 100644 --- a/qse/lib/cmn/path-canon.c +++ b/qse/lib/cmn/path-canon.c @@ -30,7 +30,7 @@ #define ISDRIVE(s) \ (((s[0] >= QSE_T('A') && s[0] <= QSE_T('Z')) || \ - (s[0] >= QSE_T('a') && s[0] <= QSE_T('a'))) && \ + (s[0] >= QSE_T('a') && s[0] <= QSE_T('z'))) && \ s[1] == QSE_T(':')) int qse_isabspath (const qse_char_t* path) @@ -45,12 +45,30 @@ int qse_isabspath (const qse_char_t* path) } +int qse_isdrivepath (const qse_char_t* path) +{ +#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__) + if (ISDRIVE(path)) return 1; +#endif + return 0; +} + +int qse_isdrivecurpath (const qse_char_t* path) +{ +#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__) + if (ISDRIVE(path) && path[2] == QSE_T('\0')) return 1; +#endif + return 0; +} + qse_size_t qse_canonpath (const qse_char_t* path, qse_char_t* canon) { const qse_char_t* ptr; qse_char_t* dst; qse_char_t* non_root_start; int has_root = 0; + int begins_with_curdir = 0; + qse_size_t canon_len; ptr = path; dst = canon; @@ -94,12 +112,20 @@ qse_size_t qse_canonpath (const qse_char_t* path, qse_char_t* canon) } #endif } + else if (ptr[0] == QSE_T('.') && ISSEPNIL(ptr[1])) + { + begins_with_curdir = 1; + } #else if (ISSEP(*ptr)) { *dst++ = *ptr++; /* root directory */ has_root = 1; } + else if (ptr[0] == QSE_T('.') && ISSEPNIL(ptr[1])) + { + begins_with_curdir = 1; + } #endif /* non_root_start points to the beginning of the canonicalized @@ -228,14 +254,25 @@ qse_size_t qse_canonpath (const qse_char_t* path, qse_char_t* canon) * the origial path ends with a separator */ dst[-1] = QSE_T('\0'); - return dst - canon - 1; + canon_len = dst - canon - 1; } else { /* just null-terminate the canonical path normally */ dst[0] = QSE_T('\0'); - return dst - canon; + canon_len = dst - canon; } + + if (canon_len <= 0 && begins_with_curdir) + { + /* when resolving to a single dot, a trailing separator is not + * retained though the orignal path name contains it */ + dst[0] = QSE_T('.'); + dst[1] = QSE_T('\0'); + canon_len = 1; + } + + return canon_len; } qse_size_t qse_realpath (const qse_char_t* path, qse_char_t* real) diff --git a/qse/lib/fs/dir.c b/qse/lib/fs/dir.c index cf804b38..225aac5a 100644 --- a/qse/lib/fs/dir.c +++ b/qse/lib/fs/dir.c @@ -44,14 +44,63 @@ struct info_t #if defined(_WIN32) HANDLE handle; WIN32_FIND_DATA wfd; - unsigned int first_after_change; + int just_changed_dir; #elif defined(__OS2__) #elif defined(__DOS__) #else DIR* handle; + qse_mchar_t* mcurdir; #endif }; +#if defined(_WIN32) +static QSE_INLINE qse_dir_errnum_t syserr_to_errnum (DWORD e) +{ + switch (e) + { + case ERROR_INVALID_NAME: + case ERROR_DIRECTORY: + return QSE_DIR_EINVAL; + + case ERROR_ACCESS_DENIED: + return QSE_DIR_EACCES; + + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + return QSE_DIR_ENOENT; + + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_OUTOFMEMORY: + return QSE_DIR_ENOMEM; + + default: + return QSE_DIR_ESYSTEM; + } +} +#else +static QSE_INLINE qse_dir_errnum_t syserr_to_errnum (int e) +{ + switch (e) + { + case EINVAL: + return QSE_DIR_EINVAL; + + case EACCES: + return QSE_DIR_EACCES; + + case ENOENT: + case ENOTDIR: + return QSE_DIR_ENOENT; + + case ENOMEM: + return QSE_DIR_ENOMEM; + + default: + return QSE_DIR_ESYSTEM; + } +} +#endif + QSE_IMPLEMENT_COMMON_FUNCTIONS (dir) qse_dir_t* qse_dir_open (qse_mmgr_t* mmgr, qse_size_t xtnsize) @@ -119,6 +168,10 @@ void qse_dir_fini (qse_dir_t* dir) #elif defined(__DOS__) # error NOT IMPLEMENTED #else + if (info->mcurdir && info->mcurdir != dir->curdir) + QSE_MMGR_FREE (dir->mmgr, info->mcurdir); + info->mcurdir = QSE_NULL; + if (info->handle) { closedir (info->handle); @@ -137,6 +190,30 @@ void qse_dir_fini (qse_dir_t* dir) } } +static QSE_INLINE info_t* get_info (qse_dir_t* dir) +{ + info_t* info; + + info = dir->info; + if (info == QSE_NULL) + { + info = QSE_MMGR_ALLOC (dir->mmgr, QSE_SIZEOF(*info)); + if (info == QSE_NULL) + { + dir->errnum = QSE_DIR_ENOMEM; + return QSE_NULL; + } + + QSE_MEMSET (info, 0, QSE_SIZEOF(*info)); +#if defined(_WIN32) + info->handle = INVALID_HANDLE_VALUE; +#endif + dir->info = info; + } + + return info; +} + int qse_dir_change (qse_dir_t* dir, const qse_char_t* name) { qse_char_t* dirname; @@ -153,7 +230,9 @@ int qse_dir_change (qse_dir_t* dir, const qse_char_t* name) # error NOT IMPLEMENTED #else DIR* handle; - qse_mchar_t* mbsdirname; + qse_mchar_t* mdirname; + const qse_char_t* tmp_name[4]; + qse_size_t idx; #endif if (name[0] == QSE_T('\0')) @@ -162,29 +241,20 @@ int qse_dir_change (qse_dir_t* dir, const qse_char_t* name) return -1; } - info = dir->info; - if (info == QSE_NULL) - { - info = QSE_MMGR_ALLOC (dir->mmgr, QSE_SIZEOF(*info)); - if (info == QSE_NULL) - { - dir->errnum = QSE_DIR_ENOMEM; - return -1; - } - - QSE_MEMSET (info, 0, QSE_SIZEOF(*info)); -#if defined(_WIN32) - info->handle = INVALID_HANDLE_VALUE; -#endif - dir->info = info; - } + info = get_info (dir); + if (info == QSE_NULL) return -1; #if defined(_WIN32) idx = 0; if (!qse_isabspath(name) && dir->curdir) tmp_name[idx++] = dir->curdir; tmp_name[idx++] = name; - tmp_name[idx++] = QSE_T("\\ "); + + if (qse_isdrivecurpath(name)) + tmp_name[idx++] = QSE_T(" "); + else + tmp_name[idx++] = QSE_T("\\ "); + tmp_name[idx] = QSE_NULL; dirname = qse_stradup (tmp_name, dir->mmgr); @@ -195,26 +265,24 @@ int qse_dir_change (qse_dir_t* dir, const qse_char_t* name) } idx = qse_canonpath (dirname, dirname); - /* Put the asterisk after canonicalization to prevent side-effects. + /* Put an asterisk after canonicalization to prevent side-effects. * otherwise, .\* would be transformed to * by qse_canonpath() */ dirname[idx-1] = QSE_T('*'); - /* - I don't return a short name so FindExInfoBasic can speed up the function. - FindFirstFileEx ( - dirname, FindExInfoBasic, + /* 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 ( + dirname, FindExInfoBasic, &wfd, FindExSearchNameMatch, - NULL, FIND_FIRST_EX_CASE_SENSITIVE); - */ + NULL, 0/*FIND_FIRST_EX_CASE_SENSITIVE*/); + #endif handle = FindFirstFile (dirname, &wfd); if (handle == INVALID_HANDLE_VALUE) { - DWORD e = GetLastError(); - dir->errnum = (e == ERROR_ACCESS_DENIED)? QSE_DIR_EACCES: - (e == ERROR_FILE_NOT_FOUND)? QSE_DIR_ENOENT: - (e == ERROR_INVALID_NAME)? QSE_DIR_EINVAL: - (e == ERROR_DIRECTORY)? QSE_DIR_EINVAL: - QSE_DIR_ESYSTEM; + dir->errnum = syserr_to_errnum (GetLastError()); QSE_MMGR_FREE (dir->mmgr, dirname); return -1; } @@ -225,11 +293,11 @@ int qse_dir_change (qse_dir_t* dir, const qse_char_t* name) QSE_MEMSET (info, 0, QSE_SIZEOF(*info)); info->handle = handle; info->wfd = wfd; + info->just_changed_dir = 1; if (dir->curdir) QSE_MMGR_FREE (dir->mmgr, dir->curdir); dirname[idx-1] = QSE_T('\0'); /* drop the asterisk */ dir->curdir = dirname; - dir->first_after_change = 1; return 0; @@ -238,7 +306,17 @@ int qse_dir_change (qse_dir_t* dir, const qse_char_t* name) #elif defined(__DOS__) # error NOT IMPLEMENTED #else - dirname = qse_strdup (name, dir->mmgr); + + idx = 0; + if (!qse_isabspath(name) && dir->curdir) + { + tmp_name[idx++] = dir->curdir; + tmp_name[idx++] = QSE_T("/"); + } + tmp_name[idx++] = name; + tmp_name[idx] = QSE_NULL; + + dirname = qse_stradup (tmp_name, dir->mmgr); if (dirname == QSE_NULL) { dir->errnum = QSE_DIR_ENOMEM; @@ -248,10 +326,10 @@ int qse_dir_change (qse_dir_t* dir, const qse_char_t* name) qse_canonpath (dirname, dirname); #if defined(QSE_CHAR_IS_MCHAR) - mbsdirname = dirname; + mdirname = dirname; #else - mbsdirname = qse_wcstombsdup (name, dir->mmgr); - if (mbsdirname == QSE_NULL) + mdirname = qse_wcstombsdup (dirname, dir->mmgr); + if (mdirname == QSE_NULL) { dir->errnum = QSE_DIR_ENOMEM; QSE_MMGR_FREE (dir->mmgr, dirname); @@ -259,22 +337,13 @@ int qse_dir_change (qse_dir_t* dir, const qse_char_t* name) } #endif - handle = opendir (mbsdirname); - -#if defined(QSE_CHAR_IS_MCHAR) - /* do nothing */ -#else - QSE_MMGR_FREE (dir->mmgr, mbsdirname); -#endif + handle = opendir (mdirname); if (handle == QSE_NULL) { - dir->errnum = (errno == EACCES)? QSE_DIR_EACCES: - (errno == ENOENT)? QSE_DIR_ENOENT: - (errno == ENOTDIR)? QSE_DIR_ENOTDIR: - (errno == ENOMEM)? QSE_DIR_ENOMEM: - QSE_DIR_ESYSTEM; - + dir->errnum = syserr_to_errnum (errno); + if (mdirname != dirname) + QSE_MMGR_FREE (dir->mmgr, mdirname); QSE_MMGR_FREE (dir->mmgr, dirname); return -1; } @@ -282,6 +351,10 @@ int qse_dir_change (qse_dir_t* dir, const qse_char_t* name) if (info->handle) closedir (info->handle); info->handle = handle; + if (info->mcurdir && info->mcurdir != dir->curdir) + QSE_MMGR_FREE (dir->mmgr, info->mcurdir); + info->mcurdir = mdirname; + if (dir->curdir) QSE_MMGR_FREE (dir->mmgr, dir->curdir); dir->curdir = dirname; @@ -349,34 +422,19 @@ static int set_entry_name (qse_dir_t* dir, const qse_mchar_t* name) qse_dir_ent_t* qse_dir_read (qse_dir_t* dir) { - +#if defined(_WIN32) info_t* info; info = dir->info; if (info == QSE_NULL) { - /* - dir->errnum = NO CHANGE DIR YET; - */ + dir->errnum = QSE_DIR_ENODIR; return QSE_NULL; } -#if defined(_WIN32) - -#if 0 - if (info->no_more_files) + if (info->just_changed_dir) { - dir->errnum = QSE_DIR_ENOERR; - return QSE_NULL; - } -#endif - if (info->first_after_change) - { - /* call set_entry_name before changing other fields - * in dir->ent not to pollute it in case set_entry_name fails */ - if (set_entry_name (dir, info->wfd.cFileName) <= -1) return QSE_NULL; - - info->first_after_change = 0; + info->just_changed_dir = 0; } else { @@ -385,32 +443,32 @@ qse_dir_ent_t* qse_dir_read (qse_dir_t* dir) DWORD e = GetLastError(); if (e == ERROR_NO_MORE_FILES) { - //info->no_more_files = 1; dir->errnum = QSE_DIR_ENOERR; return QSE_NULL; } else { - dir->errnum = (e == ERROR_ACCESS_DENIED)? QSE_DIR_EACCES: - (e == ERROR_FILE_NOT_FOUND)? QSE_DIR_ENOENT: - (e == ERROR_INVALID_NAME)? QSE_DIR_EINVAL: - (e == ERROR_DIRECTORY)? QSE_DIR_EINVAL: - QSE_DIR_ESYSTEM; + dir->errnum = syserr_to_errnum (GetLastError()); return QSE_NULL; } } - - /* call set_entry_name before changing other fields - * in dir->ent not to pollute it in case set_entry_name fails */ - if (set_entry_name (dir, info->wfd.cFileName) <= -1) return QSE_NULL; } + /* call set_entry_name before changing other fields + * in dir->ent not to pollute it in case set_entry_name fails */ + if (set_entry_name (dir, info->wfd.cFileName) <= -1) return QSE_NULL; if (info->wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { dir->ent.size = 0; dir->ent.type = QSE_DIR_ENT_DIRECTORY; } + else if ((info->wfd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && + (info->wfd.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) + { + dir->ent.size = 0; + dir->ent.type = QSE_DIR_ENT_LINK; + } else { LARGE_INTEGER li; @@ -425,56 +483,78 @@ qse_dir_ent_t* qse_dir_read (qse_dir_t* dir) #elif defined(__DOS__) # error NOT IMPLEMENTED #else - struct dirent* ent; + info_t* info; + struct dirent* ent; + int x; +#if defined(HAVE_LSTAT64) + struct stat64 st; +#else + struct stat st; +#endif + qse_mchar_t* tmp_name[4]; + qse_mchar_t* mfname; + + info = dir->info; + if (info == QSE_NULL) + { + dir->errnum = QSE_DIR_ENODIR; + return QSE_NULL; + } + + errno = 0; ent = readdir (info->handle); if (ent == QSE_NULL) { - /*dir->errnum = QSE_DIR_ENOENT;*/ /* TODO: to be more specific */ + if (errno != 0) dir->errnum = syserr_to_errnum (errno); return QSE_NULL; } #if defined(HAVE_STRUCT_DIRENT_D_TYPE) - if (ent->d_type != DT_DIR) +/* 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) { - int x; + dir->errnum = QSE_DIR_ENOMEM; + return QSE_NULL; + } + #if defined(HAVE_LSTAT64) - struct stat64 st; - x = lstat64 (ent->d_name, &st); + x = lstat64 (mfname, &st); #else - struct stat st; - x = lstat (ent->d_name, &st); + x = lstat (mfname, &st); #endif - if (x == -1) - { - /*TODO: dir->errnum = ... */ - return QSE_NULL; - } + + QSE_MMGR_FREE (dir->mmgr, mfname); + + if (x == -1) + { + dir->errnum = syserr_to_errnum (errno); + return QSE_NULL; } if (set_entry_name (dir, ent->d_name) <= -1) return QSE_NULL; - #if defined(HAVE_STRUCT_DIRENT_D_TYPE) - switch (ent->d_type) + if (S_ISDIR(st.st_mode)) { - case DT_DIR: - dir->ent.size = 0; - dir->ent.type = QSE_DIR_ENT_DIRECTORY; - break; - - case DT_REG: - dir->ent.type = QSE_DIR_ENT_REGULAR; - break; - - default: - dir->ent.size = 0; - dir->ent.type = QSE_DIR_ENT_UNKNOWN; - break; + dir->ent.size = 0; + dir->ent.type = QSE_DIR_ENT_DIRECTORY; + } + else + { + dir->ent.size = st.st_size; + dir->ent.type = QSE_DIR_ENT_UNKNOWN; } - #endif #endif + return &dir->ent; } @@ -499,7 +579,7 @@ const qse_char_t* qse_dir_geterrmsg (qse_dir_t* dir) QSE_T("invalid parameter or data"), QSE_T("permission denined"), QSE_T("no such entry"), - QSE_T("not a directory"), + QSE_T("no working directory set"), QSE_T("system error") }; diff --git a/qse/samples/cmn/str.c b/qse/samples/cmn/str.c index 45e312a6..aa2b20be 100644 --- a/qse/samples/cmn/str.c +++ b/qse/samples/cmn/str.c @@ -369,8 +369,9 @@ static int test8 (void) while (1) { + qse_size_t mlen; memset (buf, 'A', sizeof(buf)); - qse_size_t mlen = sizeof(buf); + mlen = sizeof(buf); n = qse_wcstombs (p, buf, &mlen); if (n == 0) break; @@ -576,9 +577,9 @@ static int test14 (void) { qse_str_cpy (&x, a1); qse_str_del (&x, 10, i); - qse_printf (QSE_T("deleleted %d from 10 => %llu [%s]\n"), + qse_printf (QSE_T("deleleted %d from 10 => %lu [%s]\n"), i, - (unsigned long long)QSE_STR_LEN(&x), + (unsigned long)QSE_STR_LEN(&x), QSE_STR_PTR(&x)); } diff --git a/qse/samples/fs/dir01.c b/qse/samples/fs/dir01.c index faf5d605..62d2cb5d 100644 --- a/qse/samples/fs/dir01.c +++ b/qse/samples/fs/dir01.c @@ -9,7 +9,7 @@ static void list (qse_dir_t* dir, const qse_char_t* name) if (qse_dir_change (dir, name) <= -1) { - qse_fprintf (QSE_STDERR, QSE_T("Error: Cannot change directory to %s\n"), name); + qse_fprintf (QSE_STDERR, QSE_T("Error: Cannot change directory to %s - %s\n"), name, qse_dir_geterrmsg(dir)); return; } @@ -20,7 +20,13 @@ static void list (qse_dir_t* dir, const qse_char_t* name) do { ent = qse_dir_read (dir); - if (ent == QSE_NULL) break; + if (ent == QSE_NULL) + { + qse_dir_errnum_t e = qse_dir_geterrnum(dir); + if (e != QSE_DIR_ENOERR) + qse_fprintf (QSE_STDERR, QSE_T("Error: Read error - %s\n"), qse_dir_geterrmsg(dir)); + break; + } if (ent->type == QSE_DIR_ENT_DIRECTORY) qse_printf (QSE_T(" %16lu %s\n"), (unsigned long)ent->size, ent->name);