OSSP CVS Repository

ossp - ossp-pkg/path/path_canon.c
Not logged in
[Honeypot]  [Browse]  [Directory]  [Home]  [Login
[Reports]  [Search]  [Ticket]  [Timeline
  [Raw

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


CVSTrac 2.0.1