diff --git a/qse/include/qse/fs/path.h b/qse/include/qse/fs/path.h new file mode 100644 index 00000000..5d7e0488 --- /dev/null +++ b/qse/include/qse/fs/path.h @@ -0,0 +1,45 @@ +/* + * $Id + * + Copyright 2006-2011 Chung, Hyung-Hwan. + This file is part of QSE. + + QSE is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + QSE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with QSE. If not, see . + */ + +#ifndef _QSE_FS_PATH_H_ +#define _QSE_FS_PATH_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +qse_size_t qse_canonpath ( + const qse_char_t* path, + qse_char_t* canon +); + +qse_size_t qse_realpath ( + const qse_char_t* path, + qse_char_t* real +); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/qse/lib/fs/path.c b/qse/lib/fs/path.c index 29add38c..b401bb25 100644 --- a/qse/lib/fs/path.c +++ b/qse/lib/fs/path.c @@ -18,9 +18,7 @@ License along with QSE. If not, see . */ -#include -#include -/*#include */ +#include #if defined(_WIN32) || defined(__OS2__) || defined(__DOS__) # define ISSEP(c) ((c) == QSE_T('/') || (c) == QSE_T('\\')) @@ -30,23 +28,37 @@ #define ISSEPNIL(c) (ISSEP(c) || ((c) == QSE_T('\0'))) -int qse_canonpath (const qse_char_t* path) +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* root = QSE_NULL; - -/* TODO: delete this. */ - qse_char_t* buf = malloc (10000); + qse_char_t* non_root_start; + int has_root = 0; ptr = path; - dst = buf; + dst = canon; - if (ISSEP(*ptr)) +#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__) + if (((ptr[0] >= QSE_T('A') && ptr[0] <= QSE_T('Z')) || + (ptr[0] >= QSE_T('a') && ptr[0] <= QSE_T('z'))) && + ptr[1] == QSE_T(':')) { - *dst++ = *ptr++; + /* handle drive letter */ + *dst++ = *ptr++; /* drive letter */ + *dst++ = *ptr++; /* colon */ -#if defined(_WIN32) + if (ISSEP(*ptr)) + { + *dst++ = *ptr++; /* root directory */ + has_root = 1; + } + } + else if (ISSEP(*ptr)) + { + *dst++ = *ptr++; /* root directory */ + has_root = 1; + + #if defined(_WIN32) /* handle UNC path */ if (ISSEP(*ptr)) { @@ -55,18 +67,29 @@ int qse_canonpath (const qse_char_t* path) /* if it starts with \\, process host name */ while (!ISSEPNIL(*ptr)) *dst++ = *ptr++; - /* \ following the host name */ + /* \ following the host name. note that + * \\\ is treated as if the host name is empty. */ + if (ISSEP(*ptr)) *dst++ = *ptr++; } + #endif + } +#else + if (ISSEP(*ptr)) + { + *dst++ = *ptr++; /* root directory */ + has_root = 1; + } #endif - } - root = dst; + /* non_root_start points to the beginning of the canonicalized + * path excluding the root directory part. */ + non_root_start = dst; do { const qse_char_t* seg; - qse_size_t len; + qse_size_t seglen; /* skip duplicate separators */ while (ISSEP(*ptr)) ptr++; @@ -74,54 +97,87 @@ int qse_canonpath (const qse_char_t* path) /* end of path reached */ if (*ptr == QSE_T('\0')) break; + /* find the next segment */ seg = ptr; while (!ISSEPNIL(*ptr)) ptr++; + seglen = ptr - seg; - len = ptr - seg; - if (len == 1 && seg[0] == QSE_T('.')) + /* handle the segment */ + if (seglen == 1 && seg[0] == QSE_T('.')) { /* eat up . */ } - else if (len == 2 && seg[0] == QSE_T('.') && seg[1] == QSE_T('.')) + else if (seglen == 2 && seg[0] == QSE_T('.') && seg[1] == QSE_T('.')) { /* eat up the previous segment */ - /*if (!root) goto normal;*/ - const qse_char_t* tmp; + qse_char_t* tmp; tmp = dst; - if (tmp > root) tmp--; - while (tmp > root) + if (tmp > non_root_start) { - tmp--; - if (ISSEP(*tmp)) break; + /* there is a previous segment. */ + + tmp--; /* skip the separator just before .. */ + + /* find the beginning of the previous segment */ + while (tmp > non_root_start) + { + tmp--; + if (ISSEP(*tmp)) + { + tmp++; /* position it next to the separator */ + break; + } + } } - if (root > buf) + if (has_root) { - /* mean that the path contains the root directory */ + /* + * Eat up the previous segment if it exists. + * + * If it doesn't exist, tmp == dst so dst = tmp keeps dst + * unchanged. If it exists, tmp != dst. so dst = tmp + * changes dst. + * + * path /abc/def/.. + * ^ ^ + * seg ptr + * + * canon /abc/def/ + * ^ ^ + * tmp dst + */ dst = tmp; - if (tmp == root) tmp++; } else { - if (tmp == root) goto normal; + qse_size_t prevlen; - if (dst - tmp == 3 && - tmp[0] == QSE_T('.') && tmp[1] == QSE_T('.')) + prevlen = dst - tmp; + + if (/*tmp == non_root_start &&*/ prevlen == 0) { - /* the previous segment ../ */ - goto normal; + /* there is no previous segment */ + goto normal; } - if (dst - tmp == 4 && - ISSEP(tmp[0]) && - tmp[1] == QSE_T('.') && - tmp[2] == QSE_T('.')) + + if (prevlen == 3 && tmp[0] == QSE_T('.') && tmp[1] == QSE_T('.')) { - /* the previous segment is /../ */ + /* nothing to eat away because the previous segment is ../ + * + * path ../../ + * ^ ^ + * seg ptr + * + * canon ../ + * ^ ^ + * tmp dst + */ goto normal; } - dst = tmp + 1; + dst = tmp; } } else @@ -133,6 +189,32 @@ int qse_canonpath (const qse_char_t* path) } while (1); - *dst++ = QSE_T('\0'); - return buf; + if (dst > non_root_start && ISSEP(dst[-1]) && !ISSEP(ptr[-1])) + { + /* if the canoncal path composed so far ends with a separator + * and the original path didn't end with the separator, delete + * the ending separator. + * + * dst > non_root_start: + * there is at least 1 character after the root directory part. + * ISSEP(dst[-1]): + * the canonical path ends with a separator. + * ISSEP(ptr[-1]): + * the origial path ends with a separator + */ + dst[-1] = QSE_T('\0'); + return dst - canon - 1; + } + else + { + /* just null-terminate the canonical path normally */ + dst[0] = QSE_T('\0'); + return dst - canon; + } +} + +qse_size_t qse_realpath (const qse_char_t* path, qse_char_t* real) +{ +/* TODO: canonicalize path with symbolic links resolved */ + return 0; } diff --git a/qse/samples/fs/path01.c b/qse/samples/fs/path01.c index 006c1b23..f9f522a6 100644 --- a/qse/samples/fs/path01.c +++ b/qse/samples/fs/path01.c @@ -2,16 +2,30 @@ #include #include #include +#include +#include int path_main (int argc, qse_char_t* argv[]) { + qse_char_t* canon; + qse_size_t len; + if (argc != 2) { qse_fprintf (QSE_STDERR, QSE_T("Usage: %s \n"), argv[0]); return -1; } - qse_printf (QSE_T("[%s] => [%s]\n"), argv[1], qse_canonpath (argv[1])); + canon = QSE_MMGR_ALLOC (QSE_MMGR_GETDFL(), (qse_strlen(argv[1]) + 1) * QSE_SIZEOF(*canon)); + if (canon == QSE_NULL) + { + qse_fprintf (QSE_STDERR, QSE_T("Error: out of memory\n")); + return -1; + } + + len = qse_canonpath (argv[1], canon); + qse_printf (QSE_T("[%s] => [%s] %d chars\n"), argv[1], canon, (int)len); + QSE_MMGR_FREE (QSE_MMGR_GETDFL(), canon); return 0; }