fixed bugs in qse_canonpath()

This commit is contained in:
hyung-hwan 2011-10-14 15:51:20 +00:00
parent 68537ad16a
commit b5b23cbc36
3 changed files with 183 additions and 42 deletions

45
qse/include/qse/fs/path.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef _QSE_FS_PATH_H_
#define _QSE_FS_PATH_H_
#include <qse/types.h>
#include <qse/macros.h>
#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

View File

@ -18,9 +18,7 @@
License along with QSE. If not, see <http://www.gnu.org/licenses/>. License along with QSE. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <qse/types.h> #include <qse/fs/path.h>
#include <qse/macros.h>
/*#include <qse/fs/path.h>*/
#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__) #if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
# define ISSEP(c) ((c) == QSE_T('/') || (c) == QSE_T('\\')) # define ISSEP(c) ((c) == QSE_T('/') || (c) == QSE_T('\\'))
@ -30,23 +28,37 @@
#define ISSEPNIL(c) (ISSEP(c) || ((c) == QSE_T('\0'))) #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; const qse_char_t* ptr;
qse_char_t* dst; qse_char_t* dst;
qse_char_t* root = QSE_NULL; qse_char_t* non_root_start;
int has_root = 0;
/* TODO: delete this. */
qse_char_t* buf = malloc (10000);
ptr = path; 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 */ /* handle UNC path */
if (ISSEP(*ptr)) if (ISSEP(*ptr))
{ {
@ -55,18 +67,29 @@ int qse_canonpath (const qse_char_t* path)
/* if it starts with \\, process host name */ /* if it starts with \\, process host name */
while (!ISSEPNIL(*ptr)) *dst++ = *ptr++; 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++; if (ISSEP(*ptr)) *dst++ = *ptr++;
} }
#endif
}
#else
if (ISSEP(*ptr))
{
*dst++ = *ptr++; /* root directory */
has_root = 1;
}
#endif #endif
}
root = dst; /* non_root_start points to the beginning of the canonicalized
* path excluding the root directory part. */
non_root_start = dst;
do do
{ {
const qse_char_t* seg; const qse_char_t* seg;
qse_size_t len; qse_size_t seglen;
/* skip duplicate separators */ /* skip duplicate separators */
while (ISSEP(*ptr)) ptr++; while (ISSEP(*ptr)) ptr++;
@ -74,54 +97,87 @@ int qse_canonpath (const qse_char_t* path)
/* end of path reached */ /* end of path reached */
if (*ptr == QSE_T('\0')) break; if (*ptr == QSE_T('\0')) break;
/* find the next segment */
seg = ptr; seg = ptr;
while (!ISSEPNIL(*ptr)) ptr++; while (!ISSEPNIL(*ptr)) ptr++;
seglen = ptr - seg;
len = ptr - seg; /* handle the segment */
if (len == 1 && seg[0] == QSE_T('.')) if (seglen == 1 && seg[0] == QSE_T('.'))
{ {
/* eat up . */ /* 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 */ /* eat up the previous segment */
/*if (!root) goto normal;*/ qse_char_t* tmp;
const qse_char_t* tmp;
tmp = dst; tmp = dst;
if (tmp > root) tmp--; if (tmp > non_root_start)
while (tmp > root)
{ {
tmp--; /* there is a previous segment. */
if (ISSEP(*tmp)) break;
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; dst = tmp;
if (tmp == root) tmp++;
} }
else else
{ {
if (tmp == root) goto normal; qse_size_t prevlen;
if (dst - tmp == 3 && prevlen = dst - tmp;
tmp[0] == QSE_T('.') && tmp[1] == QSE_T('.'))
if (/*tmp == non_root_start &&*/ prevlen == 0)
{ {
/* the previous segment ../ */ /* there is no previous segment */
goto normal; goto normal;
} }
if (dst - tmp == 4 &&
ISSEP(tmp[0]) && if (prevlen == 3 && tmp[0] == QSE_T('.') && tmp[1] == QSE_T('.'))
tmp[1] == QSE_T('.') &&
tmp[2] == QSE_T('.'))
{ {
/* the previous segment is /../ */ /* nothing to eat away because the previous segment is ../
*
* path ../../
* ^ ^
* seg ptr
*
* canon ../
* ^ ^
* tmp dst
*/
goto normal; goto normal;
} }
dst = tmp + 1; dst = tmp;
} }
} }
else else
@ -133,6 +189,32 @@ int qse_canonpath (const qse_char_t* path)
} }
while (1); while (1);
*dst++ = QSE_T('\0'); if (dst > non_root_start && ISSEP(dst[-1]) && !ISSEP(ptr[-1]))
return buf; {
/* 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;
} }

View File

@ -2,16 +2,30 @@
#include <qse/fs/dir.h> #include <qse/fs/dir.h>
#include <qse/cmn/stdio.h> #include <qse/cmn/stdio.h>
#include <qse/cmn/main.h> #include <qse/cmn/main.h>
#include <qse/cmn/str.h>
#include <qse/cmn/mem.h>
int path_main (int argc, qse_char_t* argv[]) int path_main (int argc, qse_char_t* argv[])
{ {
qse_char_t* canon;
qse_size_t len;
if (argc != 2) if (argc != 2)
{ {
qse_fprintf (QSE_STDERR, QSE_T("Usage: %s <directory>\n"), argv[0]); qse_fprintf (QSE_STDERR, QSE_T("Usage: %s <directory>\n"), argv[0]);
return -1; 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; return 0;
} }