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;
}