/* ** OSSP path - Filesystem Path Manipulation ** Copyright (c) 2002-2003 Ralf S. Engelschall ** Copyright (c) 2002-2003 The OSSP Project ** Copyright (c) 2002-2003 Cable & Wireless Deutschland ** ** 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 #include #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; }