2011-10-14 00:49:54 +00:00
|
|
|
/*
|
|
|
|
* $Id
|
|
|
|
*
|
2019-06-06 05:28:23 +00:00
|
|
|
Copyright (c) 2006-2019 Chung, Hyung-Hwan. All rights reserved.
|
2014-11-19 14:42:24 +00:00
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions
|
|
|
|
are met:
|
|
|
|
1. Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer in the
|
|
|
|
documentation and/or other materials provided with the distribution.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
|
|
|
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
|
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
2011-10-14 00:49:54 +00:00
|
|
|
*/
|
|
|
|
|
2011-10-14 22:57:41 +00:00
|
|
|
#include <qse/cmn/path.h>
|
2011-10-14 00:49:54 +00:00
|
|
|
|
2014-11-27 15:58:51 +00:00
|
|
|
/* TODO: support the \\?\ prefix and the \\.\ prefix on windows
|
|
|
|
* support \\?\UNC\server\path which is equivalent to \\server\path.
|
|
|
|
* */
|
|
|
|
|
2012-09-12 15:47:41 +00:00
|
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
/* MBS IMPLEMENTATION */
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
|
|
|
|
int qse_ismbsabspath (const qse_mchar_t* path)
|
2011-10-18 22:21:36 +00:00
|
|
|
{
|
2014-12-08 15:07:55 +00:00
|
|
|
if (QSE_ISPATHMBSEP(path[0])) return 1;
|
2011-10-18 22:21:36 +00:00
|
|
|
#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
|
|
|
|
/* a drive like c:tmp is absolute in positioning the drive.
|
|
|
|
* but the path within the drive is kind of relative */
|
2014-12-08 15:07:55 +00:00
|
|
|
if (QSE_ISPATHMBDRIVE(path)) return 1;
|
2011-10-18 22:21:36 +00:00
|
|
|
#endif
|
2014-11-27 15:58:51 +00:00
|
|
|
return 0;
|
2012-09-12 15:47:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int qse_ismbsdrivepath (const qse_mchar_t* path)
|
|
|
|
{
|
|
|
|
#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
|
2014-12-08 15:07:55 +00:00
|
|
|
if (QSE_ISPATHMBDRIVE(path)) return 1;
|
2012-09-12 15:47:41 +00:00
|
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-12-10 03:10:44 +00:00
|
|
|
int qse_ismbsdriveabspath (const qse_mchar_t* path)
|
|
|
|
{
|
|
|
|
#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
|
2014-12-08 15:07:55 +00:00
|
|
|
if (QSE_ISPATHMBDRIVE(path) && QSE_ISPATHMBSEP(path[2])) return 1;
|
2012-12-10 03:10:44 +00:00
|
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-09-12 15:47:41 +00:00
|
|
|
int qse_ismbsdrivecurpath (const qse_mchar_t* path)
|
|
|
|
{
|
|
|
|
#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
|
2014-12-08 15:07:55 +00:00
|
|
|
if (QSE_ISPATHMBDRIVE(path) && path[2] == QSE_MT('\0')) return 1;
|
2012-09-12 15:47:41 +00:00
|
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:58:51 +00:00
|
|
|
|
2012-09-12 15:47:41 +00:00
|
|
|
qse_size_t qse_canonmbspath (const qse_mchar_t* path, qse_mchar_t* canon, int flags)
|
|
|
|
{
|
|
|
|
const qse_mchar_t* ptr;
|
|
|
|
qse_mchar_t* dst;
|
|
|
|
qse_mchar_t* non_root_start;
|
|
|
|
int has_root = 0;
|
|
|
|
#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
|
|
|
|
int is_drive = 0;
|
|
|
|
#endif
|
|
|
|
qse_size_t canon_len;
|
|
|
|
|
|
|
|
if (path[0] == QSE_MT('\0'))
|
|
|
|
{
|
|
|
|
/* if the source is empty, no translation is needed */
|
|
|
|
canon[0] = QSE_MT('\0');
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ptr = path;
|
|
|
|
dst = canon;
|
2011-10-18 22:21:36 +00:00
|
|
|
|
2012-09-12 15:47:41 +00:00
|
|
|
#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
|
2014-12-08 15:07:55 +00:00
|
|
|
if (QSE_ISPATHMBDRIVE(ptr))
|
2012-09-12 15:47:41 +00:00
|
|
|
{
|
|
|
|
/* handle drive letter */
|
|
|
|
*dst++ = *ptr++; /* drive letter */
|
|
|
|
*dst++ = *ptr++; /* colon */
|
|
|
|
|
|
|
|
is_drive = 1;
|
2014-12-08 15:07:55 +00:00
|
|
|
if (QSE_ISPATHMBSEP(*ptr))
|
2012-09-12 15:47:41 +00:00
|
|
|
{
|
|
|
|
*dst++ = *ptr++; /* root directory */
|
|
|
|
has_root = 1;
|
|
|
|
}
|
|
|
|
}
|
2014-12-08 15:07:55 +00:00
|
|
|
else if (QSE_ISPATHMBSEP(*ptr))
|
2012-09-12 15:47:41 +00:00
|
|
|
{
|
|
|
|
*dst++ = *ptr++; /* root directory */
|
|
|
|
has_root = 1;
|
|
|
|
|
|
|
|
#if defined(_WIN32)
|
|
|
|
/* handle UNC path for Windows */
|
2014-12-08 15:07:55 +00:00
|
|
|
if (QSE_ISPATHMBSEP(*ptr))
|
2012-09-12 15:47:41 +00:00
|
|
|
{
|
|
|
|
*dst++ = *ptr++;
|
|
|
|
|
2014-12-08 15:07:55 +00:00
|
|
|
if (QSE_ISPATHMBSEPORNIL(*ptr))
|
2012-09-12 15:47:41 +00:00
|
|
|
{
|
|
|
|
/* if there is another separator after \\,
|
|
|
|
* it's not an UNC path. */
|
|
|
|
dst--;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* if it starts with \\, process host name */
|
2014-12-08 15:07:55 +00:00
|
|
|
do { *dst++ = *ptr++; } while (!QSE_ISPATHMBSEPORNIL(*ptr));
|
|
|
|
if (QSE_ISPATHMBSEP(*ptr)) *dst++ = *ptr++;
|
2012-09-12 15:47:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#else
|
2014-12-08 15:07:55 +00:00
|
|
|
if (QSE_ISPATHMBSEP(*ptr))
|
2012-09-12 15:47:41 +00:00
|
|
|
{
|
|
|
|
*dst++ = *ptr++; /* root directory */
|
|
|
|
has_root = 1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* non_root_start points to the beginning of the canonicalized
|
|
|
|
* path excluding the root directory part. */
|
|
|
|
non_root_start = dst;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
const qse_mchar_t* seg;
|
|
|
|
qse_size_t seglen;
|
|
|
|
|
|
|
|
/* skip duplicate separators */
|
2014-12-08 15:07:55 +00:00
|
|
|
while (QSE_ISPATHMBSEP(*ptr)) ptr++;
|
2012-09-12 15:47:41 +00:00
|
|
|
|
|
|
|
/* end of path reached */
|
|
|
|
if (*ptr == QSE_MT('\0')) break;
|
|
|
|
|
|
|
|
/* find the next segment */
|
|
|
|
seg = ptr;
|
2014-12-08 15:07:55 +00:00
|
|
|
while (!QSE_ISPATHMBSEPORNIL(*ptr)) ptr++;
|
2012-09-12 15:47:41 +00:00
|
|
|
seglen = ptr - seg;
|
|
|
|
|
|
|
|
/* handle the segment */
|
|
|
|
if (seglen == 1 && seg[0] == QSE_MT('.'))
|
|
|
|
{
|
|
|
|
/* eat up . */
|
|
|
|
}
|
|
|
|
else if (!(flags & QSE_CANONPATH_KEEPDOUBLEDOTS) &&
|
|
|
|
seglen == 2 && seg[0] == QSE_MT('.') && seg[1] == QSE_MT('.'))
|
|
|
|
{
|
|
|
|
/* eat up the previous segment */
|
|
|
|
qse_mchar_t* tmp;
|
|
|
|
|
|
|
|
tmp = dst;
|
|
|
|
if (tmp > non_root_start)
|
|
|
|
{
|
|
|
|
/* there is a previous segment. */
|
|
|
|
|
|
|
|
tmp--; /* skip the separator just before .. */
|
|
|
|
|
|
|
|
/* find the beginning of the previous segment */
|
|
|
|
while (tmp > non_root_start)
|
|
|
|
{
|
|
|
|
tmp--;
|
2014-12-08 15:07:55 +00:00
|
|
|
if (QSE_ISPATHMBSEP(*tmp))
|
2012-09-12 15:47:41 +00:00
|
|
|
{
|
|
|
|
tmp++; /* position it next to the separator */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (has_root)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
qse_size_t prevlen;
|
|
|
|
|
|
|
|
prevlen = dst - tmp;
|
|
|
|
|
|
|
|
if (/*tmp == non_root_start &&*/ prevlen == 0)
|
|
|
|
{
|
|
|
|
/* there is no previous segment */
|
|
|
|
goto normal;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (prevlen == 3 && tmp[0] == QSE_MT('.') && tmp[1] == QSE_MT('.'))
|
|
|
|
{
|
|
|
|
/* nothing to eat away because the previous segment is ../
|
|
|
|
*
|
|
|
|
* path ../../
|
|
|
|
* ^ ^
|
|
|
|
* seg ptr
|
|
|
|
*
|
|
|
|
* canon ../
|
|
|
|
* ^ ^
|
|
|
|
* tmp dst
|
|
|
|
*/
|
|
|
|
goto normal;
|
|
|
|
}
|
|
|
|
|
|
|
|
dst = tmp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
normal:
|
|
|
|
while (seg < ptr) *dst++ = *seg++;
|
2014-12-08 15:07:55 +00:00
|
|
|
if (QSE_ISPATHMBSEP(*ptr))
|
2012-09-12 15:47:41 +00:00
|
|
|
{
|
|
|
|
/* this segment ended with a separator */
|
|
|
|
*dst++ = *seg++; /* copy the separator */
|
|
|
|
ptr++; /* move forward the pointer */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (1);
|
|
|
|
|
2014-12-08 15:07:55 +00:00
|
|
|
if (dst > non_root_start && QSE_ISPATHMBSEP(dst[-1]) &&
|
|
|
|
((flags & QSE_CANONPATH_DROPTRAILINGSEP) || !QSE_ISPATHMBSEP(ptr[-1])))
|
2012-09-12 15:47:41 +00:00
|
|
|
{
|
|
|
|
/* 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.
|
|
|
|
* also delete it if QSE_CANONPATH_DROPTRAILINGSEP is set.
|
|
|
|
*
|
|
|
|
* dst > non_root_start:
|
|
|
|
* there is at least 1 character after the root directory
|
|
|
|
* part.
|
2014-12-08 15:07:55 +00:00
|
|
|
* QSE_ISPATHMBSEP(dst[-1]):
|
2012-09-12 15:47:41 +00:00
|
|
|
* the canonical path ends with a separator.
|
2014-12-08 15:07:55 +00:00
|
|
|
* QSE_ISPATHMBSEP(ptr[-1]):
|
2012-09-12 15:47:41 +00:00
|
|
|
* the origial path ends with a separator.
|
|
|
|
*/
|
|
|
|
dst[-1] = QSE_MT('\0');
|
|
|
|
canon_len = dst - canon - 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* just null-terminate the canonical path normally */
|
2014-11-28 17:01:29 +00:00
|
|
|
dst[0] = QSE_MT('\0');
|
2012-09-12 15:47:41 +00:00
|
|
|
canon_len = dst - canon;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (canon_len <= 0)
|
|
|
|
{
|
|
|
|
if (!(flags & QSE_CANONPATH_EMPTYSINGLEDOT))
|
|
|
|
{
|
|
|
|
/* when resolving to a single dot, a trailing separator is not
|
|
|
|
* retained though the orignal path name contains it. */
|
|
|
|
canon[0] = QSE_MT('.');
|
|
|
|
canon[1] = QSE_MT('\0');
|
|
|
|
canon_len = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* drop a traling separator if the last segment is
|
|
|
|
* double slashes */
|
|
|
|
|
|
|
|
int adj_base_len = 3;
|
|
|
|
|
|
|
|
#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
|
|
|
|
if (is_drive && !has_root)
|
|
|
|
{
|
|
|
|
/* A path like A:..\\\ need some adjustment for
|
|
|
|
* finalization below. */
|
|
|
|
adj_base_len += 2;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (canon_len == adj_base_len)
|
|
|
|
{
|
|
|
|
/* i don't have to retain a trailing separator
|
|
|
|
* if the last segment is double slashes because
|
|
|
|
* the double slahses indicate a directory obviously */
|
|
|
|
if (canon[canon_len-3] == QSE_MT('.') &&
|
|
|
|
canon[canon_len-2] == QSE_MT('.') &&
|
2014-12-08 15:07:55 +00:00
|
|
|
QSE_ISPATHMBSEP(canon[canon_len-1]))
|
2012-09-12 15:47:41 +00:00
|
|
|
{
|
|
|
|
canon[--canon_len] = QSE_MT('\0');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (canon_len > adj_base_len)
|
|
|
|
{
|
2014-12-08 15:07:55 +00:00
|
|
|
if (QSE_ISPATHMBSEP(canon[canon_len-4]) &&
|
2012-09-12 15:47:41 +00:00
|
|
|
canon[canon_len-3] == QSE_MT('.') &&
|
|
|
|
canon[canon_len-2] == QSE_MT('.') &&
|
2014-12-08 15:07:55 +00:00
|
|
|
QSE_ISPATHMBSEP(canon[canon_len-1]))
|
2012-09-12 15:47:41 +00:00
|
|
|
{
|
|
|
|
canon[--canon_len] = QSE_MT('\0');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return canon_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
/* WCS IMPLEMENTATION */
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
|
|
|
|
int qse_iswcsabspath (const qse_wchar_t* path)
|
|
|
|
{
|
2014-12-08 15:07:55 +00:00
|
|
|
if (QSE_ISPATHWCSEP(path[0])) return 1;
|
2012-09-12 15:47:41 +00:00
|
|
|
#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
|
|
|
|
/* a drive like c:tmp is absolute in positioning the drive.
|
|
|
|
* but the path within the drive is kind of relative */
|
2014-12-08 15:07:55 +00:00
|
|
|
if (QSE_ISPATHWCDRIVE(path)) return 1;
|
2012-09-12 15:47:41 +00:00
|
|
|
#endif
|
|
|
|
return 0;
|
2011-10-18 22:21:36 +00:00
|
|
|
}
|
|
|
|
|
2012-09-12 15:47:41 +00:00
|
|
|
int qse_iswcsdrivepath (const qse_wchar_t* path)
|
2011-10-25 01:48:07 +00:00
|
|
|
{
|
|
|
|
#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
|
2014-12-08 15:07:55 +00:00
|
|
|
if (QSE_ISPATHWCDRIVE(path)) return 1;
|
2011-10-25 01:48:07 +00:00
|
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-12-10 03:10:44 +00:00
|
|
|
int qse_iswcsdriveabspath (const qse_wchar_t* path)
|
|
|
|
{
|
|
|
|
#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
|
2014-12-08 15:07:55 +00:00
|
|
|
if (QSE_ISPATHWCDRIVE(path) && QSE_ISPATHWCSEP(path[2])) return 1;
|
2012-12-10 03:10:44 +00:00
|
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-09-12 15:47:41 +00:00
|
|
|
int qse_iswcsdrivecurpath (const qse_wchar_t* path)
|
2011-10-25 01:48:07 +00:00
|
|
|
{
|
|
|
|
#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
|
2014-12-08 15:07:55 +00:00
|
|
|
if (QSE_ISPATHWCDRIVE(path) && path[2] == QSE_WT('\0')) return 1;
|
2011-10-25 01:48:07 +00:00
|
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-09-12 15:47:41 +00:00
|
|
|
qse_size_t qse_canonwcspath (const qse_wchar_t* path, qse_wchar_t* canon, int flags)
|
2011-10-14 00:49:54 +00:00
|
|
|
{
|
2012-09-12 15:47:41 +00:00
|
|
|
const qse_wchar_t* ptr;
|
|
|
|
qse_wchar_t* dst;
|
|
|
|
qse_wchar_t* non_root_start;
|
2011-10-14 15:51:20 +00:00
|
|
|
int has_root = 0;
|
2011-10-25 17:20:25 +00:00
|
|
|
#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
|
|
|
|
int is_drive = 0;
|
|
|
|
#endif
|
2011-10-25 01:48:07 +00:00
|
|
|
qse_size_t canon_len;
|
2011-10-14 00:49:54 +00:00
|
|
|
|
2012-09-12 15:47:41 +00:00
|
|
|
if (path[0] == QSE_WT('\0'))
|
2011-10-25 17:20:25 +00:00
|
|
|
{
|
|
|
|
/* if the source is empty, no translation is needed */
|
2012-09-12 15:47:41 +00:00
|
|
|
canon[0] = QSE_WT('\0');
|
2011-10-25 17:20:25 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-10-14 00:49:54 +00:00
|
|
|
ptr = path;
|
2011-10-14 15:51:20 +00:00
|
|
|
dst = canon;
|
|
|
|
|
|
|
|
#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
|
2014-12-08 15:07:55 +00:00
|
|
|
if (QSE_ISPATHWCDRIVE(ptr))
|
2011-10-14 15:51:20 +00:00
|
|
|
{
|
|
|
|
/* handle drive letter */
|
|
|
|
*dst++ = *ptr++; /* drive letter */
|
|
|
|
*dst++ = *ptr++; /* colon */
|
2011-10-14 00:49:54 +00:00
|
|
|
|
2011-10-25 17:20:25 +00:00
|
|
|
is_drive = 1;
|
2014-12-08 15:07:55 +00:00
|
|
|
if (QSE_ISPATHWCSEP(*ptr))
|
2011-10-14 15:51:20 +00:00
|
|
|
{
|
|
|
|
*dst++ = *ptr++; /* root directory */
|
|
|
|
has_root = 1;
|
|
|
|
}
|
|
|
|
}
|
2014-12-08 15:07:55 +00:00
|
|
|
else if (QSE_ISPATHWCSEP(*ptr))
|
2011-10-14 00:49:54 +00:00
|
|
|
{
|
2011-10-14 15:51:20 +00:00
|
|
|
*dst++ = *ptr++; /* root directory */
|
|
|
|
has_root = 1;
|
2011-10-14 00:49:54 +00:00
|
|
|
|
2011-10-14 15:51:20 +00:00
|
|
|
#if defined(_WIN32)
|
2011-10-14 22:57:41 +00:00
|
|
|
/* handle UNC path for Windows */
|
2014-12-08 15:07:55 +00:00
|
|
|
if (QSE_ISPATHWCSEP(*ptr))
|
2011-10-14 00:49:54 +00:00
|
|
|
{
|
|
|
|
*dst++ = *ptr++;
|
|
|
|
|
2014-12-08 15:07:55 +00:00
|
|
|
if (QSE_ISPATHWCSEPORNIL(*ptr))
|
2011-10-14 22:57:41 +00:00
|
|
|
{
|
|
|
|
/* if there is another separator after \\,
|
|
|
|
* it's not an UNC path. */
|
|
|
|
dst--;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* if it starts with \\, process host name */
|
2014-12-08 15:07:55 +00:00
|
|
|
do { *dst++ = *ptr++; } while (!QSE_ISPATHWCSEPORNIL(*ptr));
|
|
|
|
if (QSE_ISPATHWCSEP(*ptr)) *dst++ = *ptr++;
|
2011-10-14 22:57:41 +00:00
|
|
|
}
|
2011-10-14 00:49:54 +00:00
|
|
|
}
|
2011-10-14 15:51:20 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#else
|
2014-12-08 15:07:55 +00:00
|
|
|
if (QSE_ISPATHWCSEP(*ptr))
|
2011-10-14 15:51:20 +00:00
|
|
|
{
|
|
|
|
*dst++ = *ptr++; /* root directory */
|
|
|
|
has_root = 1;
|
|
|
|
}
|
2011-10-14 00:49:54 +00:00
|
|
|
#endif
|
|
|
|
|
2011-10-14 15:51:20 +00:00
|
|
|
/* non_root_start points to the beginning of the canonicalized
|
|
|
|
* path excluding the root directory part. */
|
|
|
|
non_root_start = dst;
|
2011-10-14 00:49:54 +00:00
|
|
|
|
|
|
|
do
|
|
|
|
{
|
2012-09-12 15:47:41 +00:00
|
|
|
const qse_wchar_t* seg;
|
2011-10-14 15:51:20 +00:00
|
|
|
qse_size_t seglen;
|
2011-10-14 00:49:54 +00:00
|
|
|
|
|
|
|
/* skip duplicate separators */
|
2014-12-08 15:07:55 +00:00
|
|
|
while (QSE_ISPATHWCSEP(*ptr)) ptr++;
|
2011-10-14 00:49:54 +00:00
|
|
|
|
|
|
|
/* end of path reached */
|
2012-09-12 15:47:41 +00:00
|
|
|
if (*ptr == QSE_WT('\0')) break;
|
2011-10-14 00:49:54 +00:00
|
|
|
|
2011-10-14 15:51:20 +00:00
|
|
|
/* find the next segment */
|
2011-10-14 00:49:54 +00:00
|
|
|
seg = ptr;
|
2014-12-08 15:07:55 +00:00
|
|
|
while (!QSE_ISPATHWCSEPORNIL(*ptr)) ptr++;
|
2011-10-14 15:51:20 +00:00
|
|
|
seglen = ptr - seg;
|
2011-10-14 00:49:54 +00:00
|
|
|
|
2011-10-14 15:51:20 +00:00
|
|
|
/* handle the segment */
|
2012-09-12 15:47:41 +00:00
|
|
|
if (seglen == 1 && seg[0] == QSE_WT('.'))
|
2011-10-14 00:49:54 +00:00
|
|
|
{
|
|
|
|
/* eat up . */
|
|
|
|
}
|
2011-10-26 00:19:20 +00:00
|
|
|
else if (!(flags & QSE_CANONPATH_KEEPDOUBLEDOTS) &&
|
2012-09-12 15:47:41 +00:00
|
|
|
seglen == 2 && seg[0] == QSE_WT('.') && seg[1] == QSE_WT('.'))
|
2011-10-14 00:49:54 +00:00
|
|
|
{
|
|
|
|
/* eat up the previous segment */
|
2012-09-12 15:47:41 +00:00
|
|
|
qse_wchar_t* tmp;
|
2011-10-14 00:49:54 +00:00
|
|
|
|
|
|
|
tmp = dst;
|
2011-10-14 15:51:20 +00:00
|
|
|
if (tmp > non_root_start)
|
2011-10-14 00:49:54 +00:00
|
|
|
{
|
2011-10-14 15:51:20 +00:00
|
|
|
/* there is a previous segment. */
|
|
|
|
|
|
|
|
tmp--; /* skip the separator just before .. */
|
|
|
|
|
|
|
|
/* find the beginning of the previous segment */
|
|
|
|
while (tmp > non_root_start)
|
|
|
|
{
|
|
|
|
tmp--;
|
2014-12-08 15:07:55 +00:00
|
|
|
if (QSE_ISPATHWCSEP(*tmp))
|
2011-10-14 15:51:20 +00:00
|
|
|
{
|
|
|
|
tmp++; /* position it next to the separator */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2011-10-14 00:49:54 +00:00
|
|
|
}
|
|
|
|
|
2011-10-14 15:51:20 +00:00
|
|
|
if (has_root)
|
2011-10-14 00:49:54 +00:00
|
|
|
{
|
2011-10-14 15:51:20 +00:00
|
|
|
/*
|
|
|
|
* Eat up the previous segment if it exists.
|
|
|
|
*
|
2012-04-04 08:18:45 +00:00
|
|
|
* If it doesn't exist, tmp == dst so dst = tmp
|
|
|
|
* keeps dst unchanged. If it exists,
|
|
|
|
* tmp != dst. so dst = tmp changes dst.
|
2011-10-14 15:51:20 +00:00
|
|
|
*
|
|
|
|
* path /abc/def/..
|
|
|
|
* ^ ^
|
|
|
|
* seg ptr
|
|
|
|
*
|
|
|
|
* canon /abc/def/
|
|
|
|
* ^ ^
|
|
|
|
* tmp dst
|
|
|
|
*/
|
2011-10-14 00:49:54 +00:00
|
|
|
dst = tmp;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-10-14 15:51:20 +00:00
|
|
|
qse_size_t prevlen;
|
|
|
|
|
|
|
|
prevlen = dst - tmp;
|
2011-10-14 00:49:54 +00:00
|
|
|
|
2011-10-14 15:51:20 +00:00
|
|
|
if (/*tmp == non_root_start &&*/ prevlen == 0)
|
2011-10-14 00:49:54 +00:00
|
|
|
{
|
2011-10-14 15:51:20 +00:00
|
|
|
/* there is no previous segment */
|
|
|
|
goto normal;
|
2011-10-14 00:49:54 +00:00
|
|
|
}
|
2011-10-14 15:51:20 +00:00
|
|
|
|
2012-09-12 15:47:41 +00:00
|
|
|
if (prevlen == 3 && tmp[0] == QSE_WT('.') && tmp[1] == QSE_WT('.'))
|
2011-10-14 00:49:54 +00:00
|
|
|
{
|
2011-10-14 15:51:20 +00:00
|
|
|
/* nothing to eat away because the previous segment is ../
|
|
|
|
*
|
|
|
|
* path ../../
|
|
|
|
* ^ ^
|
|
|
|
* seg ptr
|
|
|
|
*
|
|
|
|
* canon ../
|
|
|
|
* ^ ^
|
|
|
|
* tmp dst
|
|
|
|
*/
|
2011-10-14 00:49:54 +00:00
|
|
|
goto normal;
|
|
|
|
}
|
|
|
|
|
2011-10-14 15:51:20 +00:00
|
|
|
dst = tmp;
|
2011-10-14 00:49:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
normal:
|
2011-10-14 22:57:41 +00:00
|
|
|
while (seg < ptr) *dst++ = *seg++;
|
2014-12-08 15:07:55 +00:00
|
|
|
if (QSE_ISPATHWCSEP(*ptr))
|
2011-10-14 22:57:41 +00:00
|
|
|
{
|
|
|
|
/* this segment ended with a separator */
|
|
|
|
*dst++ = *seg++; /* copy the separator */
|
|
|
|
ptr++; /* move forward the pointer */
|
|
|
|
}
|
2011-10-14 00:49:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
while (1);
|
|
|
|
|
2014-12-08 15:07:55 +00:00
|
|
|
if (dst > non_root_start && QSE_ISPATHWCSEP(dst[-1]) &&
|
|
|
|
((flags & QSE_CANONPATH_DROPTRAILINGSEP) || !QSE_ISPATHWCSEP(ptr[-1])))
|
2011-10-14 15:51:20 +00:00
|
|
|
{
|
|
|
|
/* if the canoncal path composed so far ends with a separator
|
|
|
|
* and the original path didn't end with the separator, delete
|
2011-10-26 00:19:20 +00:00
|
|
|
* the ending separator.
|
|
|
|
* also delete it if QSE_CANONPATH_DROPTRAILINGSEP is set.
|
2011-10-14 15:51:20 +00:00
|
|
|
*
|
|
|
|
* dst > non_root_start:
|
2012-04-04 08:18:45 +00:00
|
|
|
* there is at least 1 character after the root directory
|
|
|
|
* part.
|
2014-12-08 15:07:55 +00:00
|
|
|
* QSE_ISPATHWCSEP(dst[-1]):
|
2011-10-14 15:51:20 +00:00
|
|
|
* the canonical path ends with a separator.
|
2014-12-08 15:07:55 +00:00
|
|
|
* QSE_ISPATHWCSEP(ptr[-1]):
|
2012-04-04 08:18:45 +00:00
|
|
|
* the origial path ends with a separator.
|
2011-10-14 15:51:20 +00:00
|
|
|
*/
|
2012-09-12 15:47:41 +00:00
|
|
|
dst[-1] = QSE_WT('\0');
|
2011-10-25 01:48:07 +00:00
|
|
|
canon_len = dst - canon - 1;
|
2011-10-14 15:51:20 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* just null-terminate the canonical path normally */
|
2012-09-12 15:47:41 +00:00
|
|
|
dst[0] = QSE_WT('\0');
|
2011-10-25 01:48:07 +00:00
|
|
|
canon_len = dst - canon;
|
2011-10-14 15:51:20 +00:00
|
|
|
}
|
2011-10-25 01:48:07 +00:00
|
|
|
|
2011-10-25 17:20:25 +00:00
|
|
|
if (canon_len <= 0)
|
2011-10-25 01:48:07 +00:00
|
|
|
{
|
2011-10-26 00:19:20 +00:00
|
|
|
if (!(flags & QSE_CANONPATH_EMPTYSINGLEDOT))
|
|
|
|
{
|
|
|
|
/* when resolving to a single dot, a trailing separator is not
|
|
|
|
* retained though the orignal path name contains it. */
|
2012-09-12 15:47:41 +00:00
|
|
|
canon[0] = QSE_WT('.');
|
|
|
|
canon[1] = QSE_WT('\0');
|
2011-10-26 00:19:20 +00:00
|
|
|
canon_len = 1;
|
|
|
|
}
|
2011-10-25 01:48:07 +00:00
|
|
|
}
|
2011-10-25 17:20:25 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/* drop a traling separator if the last segment is
|
|
|
|
* double slashes */
|
|
|
|
|
|
|
|
int adj_base_len = 3;
|
|
|
|
|
|
|
|
#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
|
|
|
|
if (is_drive && !has_root)
|
|
|
|
{
|
|
|
|
/* A path like A:..\\\ need some adjustment for
|
|
|
|
* finalization below. */
|
|
|
|
adj_base_len += 2;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (canon_len == adj_base_len)
|
|
|
|
{
|
2012-04-04 08:18:45 +00:00
|
|
|
/* i don't have to retain a trailing separator
|
2011-10-25 17:20:25 +00:00
|
|
|
* if the last segment is double slashes because
|
|
|
|
* the double slahses indicate a directory obviously */
|
2012-09-12 15:47:41 +00:00
|
|
|
if (canon[canon_len-3] == QSE_WT('.') &&
|
|
|
|
canon[canon_len-2] == QSE_WT('.') &&
|
2014-12-08 15:07:55 +00:00
|
|
|
QSE_ISPATHWCSEP(canon[canon_len-1]))
|
2011-10-25 17:20:25 +00:00
|
|
|
{
|
2012-09-12 15:47:41 +00:00
|
|
|
canon[--canon_len] = QSE_WT('\0');
|
2011-10-25 17:20:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (canon_len > adj_base_len)
|
|
|
|
{
|
2014-12-08 15:07:55 +00:00
|
|
|
if (QSE_ISPATHWCSEP(canon[canon_len-4]) &&
|
2012-09-12 15:47:41 +00:00
|
|
|
canon[canon_len-3] == QSE_WT('.') &&
|
|
|
|
canon[canon_len-2] == QSE_WT('.') &&
|
2014-12-08 15:07:55 +00:00
|
|
|
QSE_ISPATHWCSEP(canon[canon_len-1]))
|
2011-10-25 17:20:25 +00:00
|
|
|
{
|
2012-09-12 15:47:41 +00:00
|
|
|
canon[--canon_len] = QSE_WT('\0');
|
2011-10-25 17:20:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-10-25 01:48:07 +00:00
|
|
|
|
|
|
|
return canon_len;
|
2011-10-14 15:51:20 +00:00
|
|
|
}
|
|
|
|
|