/* Convert relative to absolute pathnames
 * Copyright 1991 Phil Karn, KA9Q
 */

/****************************************************************************
*	09 Jun 92	1.2		GT	MS-DOS: watch out for <drive>:<path>.			*
****************************************************************************/

#include <stdio.h>
#include "global.h"
#include "dirutil.h"

static void crunch __ARGS((char *buf,char *path));

/* Given a working directory and an arbitrary pathname, resolve them into
 * an absolute pathname. Memory is allocated for the result, which
 * the caller must free
 */
char *
pathname(cd,path)
char *cd;	/* Current working directory */
char *path;	/* Pathname argument */
{
	register char *buf;
#if defined(MSDOS) || defined(ATARI)
	char *cp,c;
	char *tbuf;
	int tflag = 0;
#ifdef MSDOS
	char *path_ptr;							/* -> path component			*/
#endif
#endif

	if(cd == NULLCHAR || path == NULLCHAR)
		return NULLCHAR;

#if defined(MSDOS) || defined(ATARI)
	/* If path has any backslashes, make a local copy with them
	 * translated into forward slashes
	 */
	if(strchr(path,'\\') != NULLCHAR){
		tflag = 1;
		cp = tbuf = mallocw(strlen(path));
		while((c = *path++) != '\0'){
			if(c == '\\')
				*cp++ = '/';
			else
				*cp++ = c;
		}
		*cp = '\0';
		path = tbuf;
	}
#endif

	/* Strip any leading white space on args */
	while(*cd == ' ' || *cd == '\t')
		cd++;
	while(*path == ' ' || *path == '\t')
		path++;

	/* Allocate and initialize output buffer; user must free */
	buf = mallocw((unsigned)strlen(cd) + strlen(path) + 10);	/* fudge factor */
	buf[0] = '\0';

/* Interpret path relative to cd only if it doesn't begin with "/" */

#if defined(MSDOS) || defined(ATARI)
	if (path[0] != '/' && path[1] != ':')
#else
	if (path[0] != '/') 
#endif
		crunch(buf,cd);

#if defined(MSDOS) /* || defined(ATARI) */
	if (path[1] == ':')
		path_ptr = &path[2];
	else
		path_ptr = path;

	crunch (buf, path_ptr);
#else
	crunch(buf,path);
#endif

	/* Special case: null final path means the root directory */
	if(buf[0] == '\0'){
		buf[0] = '/';
		buf[1] = '\0';
	}
#if defined(MSDOS) || defined(ATARI)
	if(tflag)
		free(tbuf);
#endif
	return buf;
}

/* Process a path name string, starting with and adding to
 * the existing buffer
 */
static void
crunch(buf,path)
char *buf;
register char *path;
{
	register char *cp;

#if defined(MSDOS) || defined(ATARI)
	/* If <path> starts with "<drive>:", nail that into the buffer first. */

	if (*buf == '\0' && path[1] == ':')
		{
		*buf++ = *path++;
		*buf++ = *path++;
		*buf = '\0';
		}
	else if (path[1] == ':')
		{
		/* Something already in buffer - skip drive. */
		
		path += 2;
		}
#endif

	cp = buf + strlen(buf);	/* Start write at end of current buffer */
	
	/* Now start crunching the pathname argument */
	for(;;){
		/* Strip leading /'s; one will be written later */
		while(*path == '/')
			path++;
		if(*path == '\0')
			break;		/* no more, all done */
		/* Look for parent directory references, either at the end
		 * of the path or imbedded in it
		 */
		if(strcmp(path,"..") == 0 || strncmp(path,"../",3) == 0){
			/* Hop up a level */
			if((cp = strrchr(buf,'/')) == NULLCHAR)
				cp = buf;	/* Don't back up beyond root */
			*cp = '\0';		/* In case there's another .. */
			path += 2;		/* Skip ".." */
			while(*path == '/')	/* Skip one or more slashes */
				path++;
		/* Look for current directory references, either at the end
		 * of the path or imbedded in it
		 */
		} else if(strcmp(path,".") == 0 || strncmp(path,"./",2) == 0){
			/* "no op" */
			path++;			/* Skip "." */
			while(*path == '/')	/* Skip one or more slashes */
				path++;
		} else {
			/* Ordinary name, copy up to next '/' or end of path */
			*cp++ = '/';
			while(*path != '/' && *path != '\0')
				*cp++ = *path++;
		}
	}
	*cp++ = '\0';
}
