
/*
 *  findPath.c
 *  
 *
 *  Created by Steven J Abner on Wed Apr 28 2004.
 *  Copyright (c) 2004. Any and All may use as they wish.
 *  No warranties implied.
 *
 */

static void
findPath(const char *orgPath, char **retPath, int *idx) {
    struct stat curStatBuf;
    char *newName = NULL;
    char *pathBuffer = *retPath;
    int pBdex = 0, oPdex = *idx, redex;
    int oPlen = strlen(orgPath);
    
    errno = 0;
    
    if (pathBuffer == NULL) {
        pathBuffer = (char *)malloc(2*MAXPATHLEN + 1);
        bzero(pathBuffer, MAXPATHLEN);
        *retPath = pathBuffer;
    }
    
    if (oPdex == 0 && orgPath[0] != '/') {

        /* home code needed for running cmdAH.test */
        char *home = getenv("HOME");
        int i = strlen(home);
        if (i != oPlen) {
            for (i--; i >= 0; i--) {
                if (home[i + 1] != orgPath[i] && home[i + 1] != '/')
                    break;
            }
            if (i < 0) {
                oPlen = strlen(home);
                do { pathBuffer[pBdex] = home[oPdex]; pBdex++; oPdex++; }
                while (oPdex < oPlen);
                pathBuffer[pBdex] = 0;
                return;
            }
        } else if (orgPath[0] == '~') {
            do { pathBuffer[pBdex] = home[oPdex]; pBdex++; oPdex++; }
            while (oPdex < oPlen);
            pathBuffer[pBdex] = 0;
            return;
        }

        getcwd(pathBuffer, (2*MAXPATHLEN));
        pBdex = *idx = strlen(pathBuffer);
        
        pathBuffer[pBdex++] = '/';
        do { pathBuffer[pBdex] = orgPath[oPdex]; pBdex++; oPdex++; }
        while (oPdex < oPlen);
        pathBuffer[pBdex] = 0;
        findPath(pathBuffer, &newName, idx);
        free(pathBuffer);
        *retPath = newName;
        return;
    }

    while (pBdex < oPdex) pathBuffer[pBdex++] = orgPath[pBdex];
    oPdex = pBdex;
findnext:
    redex = oPdex + 1;
    do { pathBuffer[pBdex] = orgPath[oPdex]; pBdex++; oPdex++; }
    while (orgPath[oPdex] != '/' && oPdex < oPlen);
    if (oPdex == oPlen) {
        *idx = redex - 1;
        return;
    }
    lstat(pathBuffer, &curStatBuf);
    if (S_ISLNK(curStatBuf.st_mode)) {
        int normPathLen;
        
        normPathLen = readlink(pathBuffer, (&pathBuffer[redex]), (2*MAXPATHLEN - redex));
        pathBuffer[redex + normPathLen] = 0;
        if (pathBuffer[redex] == '/') {
            pBdex = 0;
            do { pathBuffer[pBdex] = pathBuffer[redex]; pBdex++; redex++; }
            while (--normPathLen > 0);
        } else {
            pBdex = redex + normPathLen;
        }
        do { pathBuffer[pBdex] = orgPath[oPdex]; pBdex++; oPdex++; }
        while (oPdex < oPlen);
        pathBuffer[pBdex] = 0;
        *idx = 0;
        findPath(pathBuffer, &newName, idx);
        free(pathBuffer);
        *retPath = newName;
        return;
    }
    if (errno) {
        do { pathBuffer[pBdex] = orgPath[oPdex]; pBdex++; oPdex++; }
        while (oPdex < oPlen);
        pathBuffer[pBdex] = 0;
        *idx = redex - 1;
        return;
    }
    goto findnext;
}

/*
 *  TclObjNormalizePath.c
 *
 *  	This function takes a path string and replaces it
 *	with a normalized path. The entering path string
 *	must be void of ../, ./ sequences. The returned path
 *	will be normalized up to nextCheckpoint, which points to
 *	either the last path separator or the offending separator.
 *	The end word of the path will not be normalized, it is
 *	totally ignored.
 *  
 *  Results:
 *	pathPtr altered with result of normalization
 *	nextCheckpoint points to end separator or offending,
 *	ie.. the last good separator prior to problem path
 *
 *  Side effects:
 *	if nextCheckpoint points to end separator, do not
 *	make assumption of a fully qualified path.
 *	Occurance: when "join" adds to a file instead of directory.
 *	The file will be normalized but is not a directory.
 *
 *  Returns:
 *	nextCheckpoint
 *	pathPtr
 */

int
TclpObjNormalizePath(interp, pathPtr, nextCheckpoint)
    Tcl_Interp *interp;
    Tcl_Obj *pathPtr;
    int nextCheckpoint;
{
    int pathLen;
    char *resolvedPath = NULL;
    char *path = Tcl_GetStringFromObj(pathPtr, &pathLen);

    nextCheckpoint = 0;
    findPath(path, &resolvedPath, &nextCheckpoint);
    if (resolvedPath) {
        Tcl_DString ds;
        pathLen = strlen(resolvedPath);
        Tcl_ExternalToUtfDString(NULL, resolvedPath, pathLen, &ds);
        Tcl_SetStringObj(pathPtr, Tcl_DStringValue(&ds), Tcl_DStringLength(&ds));
        Tcl_DStringFree(&ds);
        free(resolvedPath);
    }
    return nextCheckpoint;
}
