ossp-pkg/path/path_canon.c
/*
** OSSP path - Filesystem Path Manipulation
** Copyright (c) 2002-2003 Ralf S. Engelschall <rse@engelschall.com>
** Copyright (c) 2002-2003 The OSSP Project <http://www.ossp.org/>
** Copyright (c) 2002-2003 Cable & Wireless Deutschland <http://www.cw.com/de/>
**
** This file is part of OSSP path, a filesystem path manipulation library
** which can be found at http://www.ossp.org/pkg/lib/path/.
**
** Permission to use, copy, modify, and distribute this software for
** any purpose with or without fee is hereby granted, provided that
** the above copyright notice and this permission notice appear in all
** copies.
**
** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 AUTHORS AND COPYRIGHT HOLDERS AND THEIR
** CONTRIBUTORS 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.
**
** src_canon.c: pathname canonification
*/
#include <stdio.h>
#include <string.h>
#include "path.h"
path_rc_t
path_canon(
char *res_buf, size_t res_len,
const char *src_buf, size_t src_len)
{
char *res_ptr;
char *res_end;
const char *src_ptr;
const char *src_end;
size_t l;
/* argument sanity checking */
if (src_buf == NULL)
return PATH_ERR_ARG;
/* argument default provision */
if (src_len == 0)
src_len = strlen(src_buf);
if (res_buf == NULL) {
res_buf = (char *)src_buf;
if (res_len == 0)
res_len = src_len;
}
if (res_len == 0)
res_len = strlen(res_buf);
/* perform processing
(keep in mind that source and target can overlap) */
res_ptr = res_buf;
res_end = res_buf+res_len;
src_ptr = src_buf;
src_end = src_buf+src_len;
while (src_ptr < src_end && res_ptr < res_end) {
/* recognize path separator */
if (*src_ptr == '/') {
/* take over separator character once */
*res_ptr++ = *src_ptr++;
/* skip multiple separator characters */
while (src_ptr < src_end && *src_ptr == '/')
src_ptr++;
continue;
}
/* determine length of path element */
l = 0;
while (src_ptr+l < src_end && src_ptr[l] != '/')
l++;
if (l == 0)
break;
/* handle path element */
if (l == 1 && src_ptr[0] == '.') {
/* path element is current directory ("."),
so just skip over to next path element in source */
src_ptr += 1;
while (src_ptr < src_end && *src_ptr == '/')
src_ptr++;
}
else if (l == 2 && src_ptr[0] == '.' && src_ptr[1] == '.') {
/* path element is parent directory (".."),
so skip over to next path element in source and
skip back last path element in result */
if ( res_ptr == res_buf
|| ( res_ptr >= res_buf+3
&& res_ptr[-1] == '/' && res_ptr[-2] == '.' && res_ptr[-3] == '.')) {
/* path is relative and empty or already explicit directing
to the parent directory, so keep explicit parent specification */
while (src_ptr < src_end && res_ptr < res_end && src_ptr[0] != '/')
*res_ptr++ = *src_ptr++;
}
else {
/* there is already a path element in the
result which can be removed */
src_ptr += 2;
while (res_ptr > res_buf && res_ptr[-1] == '/')
res_ptr--;
while (res_ptr > res_buf && res_ptr[-1] != '/')
res_ptr--;
if (res_ptr == res_buf && res_ptr[0] == '/')
res_ptr++;
while (src_ptr < src_end && src_ptr[0] == '/')
src_ptr++;
}
}
else {
/* path element something else, so copy
it over from source to the result */
while (src_ptr < src_end && res_ptr < res_end && src_ptr[0] != '/')
*res_ptr++ = *src_ptr++;
}
}
/* remove tailing path separators */
if (res_ptr > res_buf+1 && res_ptr[-1] == '/')
res_ptr--;
/* make sure we do not reduce to an empty (invalid) path */
if (res_ptr == res_buf && res_ptr < res_end)
*res_ptr++ = '.';
/* append NUL terminating character */
if (res_ptr >= res_end)
return PATH_ERR_MEM;
*res_ptr++ = '\0';
return PATH_OK;
}