/*---------------------------------------------------------------------
 * $Date: 2009-08-18 15:23:20 $             $Revision: 1.3 $
 *---------------------------------------------------------------------
 * 
 *
 *             Copyright (c) 1990, Visual Edge Software Ltd.
 *
 * ALL  RIGHTS  RESERVED.  Permission  to  use,  copy,  modify,  and
 * distribute  this  software  and its documentation for any purpose
 * and  without  fee  is  hereby  granted,  provided  that the above
 * copyright  notice  appear  in  all  copies  and  that  both  that
 * copyright  notice and this permission notice appear in supporting
 * documentation,  and that  the name of Visual Edge Software not be
 * used  in advertising  or publicity  pertaining to distribution of
 * the software without specific, written prior permission. The year
 * included in the notice is the year of the creation of the work.

.VERSION
 090813		last modif

 *-------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xos.h>
#include <X11/Intrinsic.h>

#if defined(sco) || defined(motor88) || defined(ncrix86) || defined(linux) || defined(vms)
#	include <unistd.h>
#endif

#if defined(vms)
#	include <limits.h>
#endif

#include "pathlist.h"
#include "valloc.h"
#include "global.h"
#include "vtypes.h"
#ifdef	MOTIF_WIDGETS
#include <sys/param.h>	/* for MAXPATHLEN... */
#ifndef MAXPATHLEN
#define MAXPATHLEN 1024
#endif  /* MAXPATHLEN */
#endif	/* MOTIF_WIDGETS */

/* header files needed for messages */
#include "uimx_cat.h"
#include "misc1_ds.h"

#define CGETS_MISC(ms,ds_ms)    UxCatGets(MC_MISC,ms,ds_ms)
#define GETMSG(ms) UXCATGETS(MC_MISC1,MS_MISC1_,ms)


/**** This define is the maximum number of characters in a filename
 **** BEFORE the name is appended with '.rf' or '.c'
 ****/
#define TRUNC_LENGTH    11
#define MAX_FILE_LEN    14
#define MAX_SUFFIX      10

extern char		*getenv();
extern char		*UxToLowerString();
char 			*UxGetDirName();
char 			*UxTruncateFilename();

extern int UxStrEqual();
extern void UxAppend_dstring();


/*****************************************************************************
NAME:		int	UxCheckForInvalidCharacters (filename)

INPUT:		char		*filename	- filename to check

RETURN:		int		- 0 = ok, 1 = there are invalid characters

DESCRIPTION:	Scans the filename entered by the user for invalid characters;
		also checks for '//', which is illegal. An empty filename is
		also an error.

LAST REV:	10 April 1990
		-- allow $, it's not invalid
-----------------------------------------------------------------------------*/

int	UxCheckForInvalidCharacters (filename)
char	*filename;
{

char *Ux_strpbrk ();


	static	char	bad_set[] = "!@%^&*()=|\\`<>?\'\"[]{}; ";

	/* check for zero length */
	if (strlen (filename) == 0)
		return 1;

	/* look for any occurence of bad characters */
	if (Ux_strpbrk (filename, bad_set) != NULL)
		return 1;


	/* filename is ok (or Ux_strpbrk failed!) */
	return 0;
}

/******************************************************************************
NAME:		UxShouldTruncate ()
INPUT:		
OUTPUT:		---
RETURN:		int	: TRUE / FALSE
DESCRIPTION:	Checks if the resource "truncateFilenames" is set to TRUE
		in which case we turn TRUE.
EXT REFERENCES:	---
EXT EFFECTS:	---
LAST REV:	October 9 1991
-----------------------------------------------------------------------------*/
int UxShouldTruncate()
{
        static  int     initialized = FALSE;
        static  int     truncate = FALSE;

        if ( initialized == FALSE )
        {
        	char	*UxGetUimxDefault(), *UxToLowerString(), *trunc_res;
                /***********************************************/
                /* Find out if filenames need to be truncated  */
                /* by examining the truncateFilenames          */
                /* resource.                                   */
                /***********************************************/

                initialized = TRUE;

		trunc_res = UxGetUimxDefault ("truncateFilenames", "false");
                if (UxStrEqual ( UxToLowerString(trunc_res), "false"))
                        truncate = FALSE;
                else
                        truncate = TRUE;
        }
        return(truncate);
}
/******************************************************************************
NAME:		UxTruncateFilename (fname, suffix_len)
INPUT:		char	*fname			: name to truncate
		int	suffix_len		: length of suffix to be added
OUTPUT:		---
RETURN:		char *				: truncated name
DESCRIPTION:	returns a filename
		with its last component truncated to not more than 14
		characters including the possible suffix.
		NB! This function may modify the passed filename.
EXT REFERENCES:	---
EXT EFFECTS:	---
LAST REV:	5 November 1990 IBM Beta1
		-- made this function available to OLE and UIM/X.
		-- Also made the strcmp case insensitive by
		   calling UxToLowerString()
-----------------------------------------------------------------------------*/

char* UxTruncateName(fname, slen)
	char* fname;
	int   slen;
{
	char *name;
	char str[MAX_FILE_LEN+1];
	int  len, tail_len, ncaps, ndigits;
	int  max_len, max_tail;
	int  i, j;

	/*-----------------------------------------------------
	 * Get the file name itself, without path.
	 *-----------------------------------------------------*/

	if ((name = strrchr(fname, '/')) != NULL) {	
		name++;
	} else {
		name = fname;
	}

	/*-----------------------------------------------------
	 * Is name part too long?
	 *-----------------------------------------------------*/

        len= strlen(name);
	if ((len + slen) <= MAX_FILE_LEN) {
		return fname;
	}

	/*-------------------------------------------------------
	 * We shorten the name by sliding digits and capitals
	 * from the whole tail of the original name
	 * into the last max_tail characters of the shorter name.
	 * Count digits and caps so we can compute the tail length.
	 * Keep counting until we've found all the digits we
	 * can use; we'll drop caps in favour of digits.
	 * 'max_len-max_tail' is the first char allowed to change.
	 *-------------------------------------------------------*/

	max_tail = (MAX_SUFFIX   - slen); 
	max_len  = (MAX_FILE_LEN - slen);

	ncaps = ndigits = 0;

	for (i = len; i >= max_len-max_tail; i--) 
	{
		if (ndigits >= max_tail)
			break;
		else if (ndigits + ncaps + i < max_len)
			break;
		else if (isdigit(name[i]))
			ndigits++;
		else if (isupper(name[i]))
			ncaps++;
	}
		
	/*-----------------------------------------------------
	 * How long a tail of digits and caps?  
	 * If there are too many, keep more digits.
	 *-----------------------------------------------------*/

	tail_len = ndigits + ncaps;

	if (tail_len > max_tail) {
		tail_len = max_tail;
		ncaps = tail_len - ndigits;
	}

	/*-----------------------------------------------------
	 * Shorten the name by moving up digits and caps.
	 * Note we may skip caps, but copy all digits we meet.
	 *-----------------------------------------------------*/

	strncpy (str, name, sizeof(str));

        for(i = j = (max_len-tail_len); i < len && j < max_len; i++) 
	{
                if (isdigit(name[i]))  {
                	str[j++] = name[i];
		} else if (isupper(name[i]) && ncaps-- > 0) {
                        str[j++]= name[i];
                }
	}
	str[j] = '\0';

	/*-----------------------------------------------------
	 * 'name' is a pointer to where the filename part of
	 * the original path starts.  str is a shortened name,
	 * so it will surely fit into the end of the original.
	 *-----------------------------------------------------*/

	strcpy(name, str);
	return fname;
}
/******************************************************************************
NAME:		UxTruncateFilename (fname, suffix_len)
INPUT:		char	*fname			: name to truncate
		int	suffix_len		: length of suffix to be added
OUTPUT:		---
RETURN:		char *				: truncated name
DESCRIPTION:	If the 'truncateFilenames' resource is true, returns a filename
		with its last component truncated to not more than 14
		characters including the possible suffix.
		NB! This function may modify the passed filename.
		If the 'truncateFilenames' resource is false, just 
		return the given filename unchanged.
EXT REFERENCES:	---
EXT EFFECTS:	---
LAST REV:	5 November 1990 IBM Beta1
		-- made this function available to OLE and UIM/X.
		-- Also made the strcmp case insensitive by
		   calling UxToLowerString()
-----------------------------------------------------------------------------*/

char* UxTruncateFilename(fname, slen)
	char* fname;
	int   slen;
{
        if(UxShouldTruncate() == FALSE) {
                return(fname);
	}

	return UxTruncateName (fname, slen);
}
/*****************************************************************************
NAME:		dstring		gethome(username)

INPUT:		dstring		username	- user name (with ~)

RETURN:		home directory

DESCRIPTION:	return the user 'uname's home directory

		Accepts a dstring parameter, which it frees.
		Creates and returns a dstring which should be freed by
		the calling function.

LAST REV:	10 April 1990
		-- parameter is a dstring
-----------------------------------------------------------------------------*/

static dstring gethome(username)
	dstring 	username;
{
	int		uid;
	char		*uname;
	dstring		home;
	char		*pntr, hold, *end;
	struct passwd	*pw;

	uname = dnstr(username);

	end= uname+1;

	while(isalnum(*end))
		end++;

	hold= *end;
	*end= '\0';

	if(strcmp(uname, "~") == 0)
	{	pntr= getenv("HOME");

		if(pntr != NULL)
		{	home= dcreate(pntr);
			*end= hold;
			dappend(home, end);

			dfree(username);
			return(home);
		}

		if((pntr= getenv("USER")) != NULL)
			pw=  getpwnam(pntr);
		else
		{	uid= getuid();
			pw=  getpwuid(uid);
		}

		if(pw)
		{	home= dcreate(pw->pw_dir);
			*end= hold;
			dappend(home, end);

			dfree(username);
			return(home);
		}

		dfree(username);
		return(dcreate(NULL));
	}

	uname++;

	pw= getpwnam(uname);

	if(pw)
		home= dcreate(pw->pw_dir);
	else
		home= dcreate(NULL);

	*end= hold;
	dappend(home, end);

	dfree(username);
	return(home);
}

/*****************************************************************************
NAME:		dstring		mkname(path, name)

INPUT:		char		*path		- path to file
				*name		- name of file

RETURN:		full filename

DESCRIPTION:	return the full filename of 'file' in the directory 'path'
LAST REV:	Feb 19/1989
-----------------------------------------------------------------------------*/

static dstring mkname(path, name)
	char		*path, *name;
{
	dstring		rtrn;
	int		len;
	
	rtrn= dcreate(path);

	len = strlen(path);
	if ( (len > 0) && (path[len-1] != '/') )
		dappend(rtrn, "/");

	dappend(rtrn, name);

	return(rtrn);
}

/*****************************************************************************
NAME:		dstring		expand_macros(ds)

INPUT:		dstring		ds		- macros to expand

RETURN:		expanded macros

DESCRIPTION:	expand all the macros in 'ds', free it, and return expansion

LAST REV:	Feb 21/1989
-----------------------------------------------------------------------------*/

static dstring expand_macros(ds)
	dstring		ds;
{
	char		*pntr, *start, *end = NULL, endc;
	dstring		rtrn;

	rtrn= dcreate(NULL);

	start= dnstr(ds);
	pntr= strchr(start, '$');

	while(pntr)
	{	*(pntr++)= '\0';
		dappend(rtrn, start);

		end= pntr;

		while(isalnum(*end))
			end++;

		endc= *end;
		*end= '\0';

		if(*pntr != '\0')
			dappend(rtrn, getenv(pntr));

		*end= endc;

		start= end;
		pntr= strchr(start, '$');
	}

	dappend(rtrn, end);
	dfree(ds);

	return(rtrn);
}

/*****************************************************************************
NAME:		dstring		tokenize(ds)

INPUT:		dstring		*ds		- dstring to tokenize

RETURN:		first token in 'ds'

DESCRIPTION:	return the first token in 'ds' and reset 'ds' to point to
		the rest of the string

LAST REV:	July 25 1990
		- handle separators at the end of a path.
		- handle a path consisting of only separators.
-----------------------------------------------------------------------------*/

static dstring tokenize(ds)
	dstring		*ds;
{
	char		*pntr, *end;
	dstring		rtrn, dsrtrn;

	pntr= dgetstr(*ds);

	if(pntr == NULL)
		return(dcreate(NULL));

	while(*pntr && (strchr(": ,\n\t", *pntr) != NULL))
		pntr++;

	end= strpbrk(pntr, ": ,\n\t");

	if(end != NULL)
		*end++ = '\0';

	rtrn= dcreate(pntr);

	if(end != NULL) {
		while (*end && (strchr(": ,\n\t", *end) != NULL))
			end++;
		if (*end)
			dsrtrn= dcreate(end);
		else
			dsrtrn= dcreate(NULL);
	} else {
		dsrtrn= dcreate(NULL);
	}

	dfree(*ds);
	*ds= dsrtrn;

	return(rtrn);
}

/******************************************************************************
NAME:		dstring		expand_name(tok)
INPUT:		dstring		tok		- taken to expand
RETURN:		expanded token (original freed if needed)
DESCRIPTION:	expand the env vars and ~[uname]'s in tok
LAST REV:	January 03 1991		bugfix 1116.
-----------------------------------------------------------------------------*/

/*
 *  An always-true predicate to pass to XtResolvePath.
 */
/*ARGSUSED*/
static  Boolean AlwaysTrue(file)
	String file;
{ return True; }

static 	dstring expand_name(tok)
	dstring		tok;
{
	dstring		exp, rtrn;

	if(strchr(dnstr(tok), '$') != NULL)
	{	
	    exp = expand_macros(tok);	/* tok is freed in expand_macros */
	    return(expand_name(exp));	/* exp is freed in expand_name   */
	}
	else if(*(dnstr(tok)) == '~')
	{	
	    exp = gethome(tok);		/* tok is freed in gethome 	 */
	    return(expand_name(exp));	/* exp is freed in expand_name   */
	}
	else

#ifdef MOTIF_WIDGETS

	{
	   /* Expand language path character %L (and other toolkit defaults).
	    * Several arguments to XtResolvePathname are NULL so the toolkit
	    * defaults apply.  We pass AlwyasTrue because UIM/X builds its 
	    * paths piece-by-piece; the partial paths we expand here aren't
	    * meant to name exisiting files.
	    */
	   char *r;

	   r = XtResolvePathname (
		  UxDisplay, 
		  (String) NULL,	 	/* type o%T e    ntion */
		  (String) getenv("UXAPP"),  	/* name (app s)  for %N */
		  (String) NULL,   		/* suffix (none) 	*/
		  (String) dnstr(tok), 		/* path   		*/
		  (Substitution) NULL,   	/* substitutions         */
		  0,      			/* num substitions       */
		  (XtFilePredicate) AlwaysTrue  /* file access predicate */
				);
	  if (r)
	    {
		dfree(tok);
		rtrn = dcreate(r);
		XtFree(r);
		return rtrn;
	    }
	  else
	    rtrn = tok;

	  return rtrn;
	}
#else	
        {
	  return(tok);
	}
#endif	/* MOTIF_WIDGETS */
}
/*****************************************************************************
NAME:		int		UxFileExists(fname)

INPUT:		char		*fname		- filename

RETURN:		1: file or directory exists  0: file not found

DESCRIPTION:	return whether the file or directory 'fname' exists

LAST REV:	March 18, 1991
		Replaced stat call by more efficient access call.
-----------------------------------------------------------------------------*/

int UxFileExists(fname)
	char		*fname;
{
	if (access(fname, 0) < 0)
		return(0);

	return(1);
}

/*****************************************************************************
NAME:		pathlist	UxInitPath(str)

INPUT:		char		*str		- initial path list

RETURN:		pathlist object

DESCRIPTION:	initialize a path list to files.
LAST REV:	Feb 19/1989
-----------------------------------------------------------------------------*/

pathlist UxInitPath(str)
	char		*str;
{
	pathlist	path;

	path= (pathlist) UxMalloc(sizeof(dstring));

	*path= dcreate(NULL);

	UxAddPath(path, str);

	return(path);
}

/*****************************************************************************
NAME:		void		UxAddPath(path, str)

INPUT:		pathlist	path		- pathlist
		char		*str		- list of dirs to add

DESCRIPTION:	add the directories in 'str' to the pathlist 'path'

LAST REV:	Feb 19/1989
-----------------------------------------------------------------------------*/

void UxAddPath(path, str)
	pathlist	path;
	char		*str;
{
	dstring		pcopy, tok;

	if((str == NULL) || (str[0] == '\0'))
		return;

	if(path == NULL)
		return;

	pcopy= dcreate(str);

	tok= tokenize(&pcopy);

	while(dgetstr(tok) != NULL)
	{	tok= expand_name(tok);

		if(strlen(dnstr(tok)) != 0)
		{	if(dlen(*path) != 0)
				dappend(*path, ":");

			dconcat(*path, tok);
		}

		dfree(tok);
		tok= tokenize(&pcopy);
	}

	/* don't need to free tok or pcopy, since they are now null */
}

/*****************************************************************************
NAME:		void		UxResetPath(path, str)

INPUT:		pathlist	path		- pathlist
		char		*str		- new list of paths

DESCRIPTION:	reset the pathlist 'path' to be 'str'

LAST REV:	Feb 19/1989
-----------------------------------------------------------------------------*/

void UxResetPath(path, str)
	pathlist	path;
	char		*str;
{
	dfree(*path);

	UxAddPath(path, str);
}

/*****************************************************************************
NAME:		void		UxFreePath(path)

INPUT:		pathlist	path		- pathlist

DESCRIPTION:	free the pathlist 'path'

LAST REV:	Feb 19/1989
-----------------------------------------------------------------------------*/

void UxFreePath(path)
	pathlist	path;
{
	dfree(*path);
	UxFree(path);
}

/*****************************************************************************
NAME:		char		*UxGetPath(path)

INPUT:		pathlist	path		- pathlist

RETURN:		pathlist in string form

DESCRIPTION:	return the pathlist as a string (not to be freed)

LAST REV:	Feb 19/1989
-----------------------------------------------------------------------------*/

char *UxGetPath(path)
	pathlist	path;
{
	if(path == NULL)
		return("");

	return(dnstr(*path));
}

/*****************************************************************************
NAME:		char		*UxExpandFilename(path_list, fname)

INPUT:		pathlist	path_list	- pathlist
		char		*fname		- filename

RETURN:		expanded filename

DESCRIPTION:	Find the expanded name of 'fname' using the dirs in 'path'

LAST REV:	April 10, 1990
		-- free filename at the proper place
-----------------------------------------------------------------------------*/

char *UxExpandFilename(path_list, fname)
	pathlist	path_list;
	char		*fname;
{
	static dstring	filename= UxEmptyDstring;
	dstring		path, file;
	char		*pntr;

	file= expand_name(dcreate(fname));	
	fname= dnstr(file);

	if(*fname == '\0')
		return(NULL);

	if(fname[0] == '/')
	{	
		dfree(filename);
		filename= dcreate(fname);

		dfree(file);

		if(UxFileExists(dnstr(filename)))
			return(dnstr(filename));
		else
			return(NULL);
	}

	if(path_list == NULL)
	{	dfree(file);
		return(NULL);
	}

	path= dcopy(*path_list);

	pntr= strtok(dnstr(path), ": ,\n\t");

	while(pntr)
	{	
		dfree(filename);
		filename= mkname(pntr, fname);

		if(UxFileExists(dnstr(filename)))
		{	dfree(path);
			dfree(file);
			return(dnstr(filename));
		}

		pntr= strtok(NULL, ": ,\n\t");
	}

	dfree(file);
	dfree(path);

	return(NULL);
}

/*****************************************************************************
NAME:		char		*UxExpandAllFilenames(path_list, fname)

INPUT:		pathlist	path_list	- pathlist or NULL
		char		*fname		- filename

RETURN:		expanded filename

DESCRIPTION:	Find the expanded name of 'fname' using the dirs in 'path_list'
		Remember position in path_list between calls so that subsequent
		calls can continue in the list where the previous call left off.
		If path_list is NULL then continue from previous path otherwise
		start from given path_list.  Don't return same expanded name
		more than once.  If fname is NULL then use previous fname.

LAST REV:	March 13, 1991		See bugfix 2111
-----------------------------------------------------------------------------*/

char *UxExpandAllFilenames(path_list, fname)
	pathlist	path_list;
	char		*fname;
{
	static dstring	filename= UxEmptyDstring;
	static dstring	path= UxEmptyDstring, file= UxEmptyDstring;
	static dstring	alreadyReturned= UxEmptyDstring;
	static char	*pntr;


	if (fname != NULL) {
		dfree(file);
		file= expand_name(dcreate(fname));
	}

	fname= dnstr(file);

	if(*fname == '\0')
		return(NULL);

	if(fname[0] == '/')
	{	
		dfree(filename);
		filename= dcreate(fname);

		if (strstr(dnstr(alreadyReturned), fname) == NULL &&
					UxFileExists(dnstr(filename))) {
			dappend(alreadyReturned, ":");
			dconcat(alreadyReturned, filename);
			return(dnstr(filename));
		} else
			return(NULL);
	}

	if (path_list != NULL) {
		dfree(path);
		path= dcopy(*path_list);
		dappend(path, ":");		/* Make sure we end with ':' */
		dfree(alreadyReturned);
		alreadyReturned = dcreate("");
		pntr= strtok(dnstr(path), ": ,\n\t");
	}

	/*
	 * Start from where we left off or from beginning if new path_list
	 * was specified.
	 */
	while(pntr)
	{	
		dfree(filename);
		filename= mkname(pntr, fname);

		pntr[strlen(pntr)] = ':';
		pntr= strtok(NULL, ": ,\n\t");

		if (strstr(dnstr(alreadyReturned), dgetstr(filename)) == NULL
					  && UxFileExists(dgetstr(filename))) {
			/*
			 * Restore path string before returning.
			 */	
			dappend(alreadyReturned, ":");

			/*  The UIM/X 2.0 releases mostly have
			 *  'dgetstr(filename)' in the line below,
			 *  which is garbage.
			 */
			dconcat(alreadyReturned, filename);
			return(dgetstr(filename));
		}
	}

	return(NULL);
}

/******************************************************************************
NAME:		char		*UxGetFilename(fname)

INPUT:		char		*fname		- filename

RETURN:		expanded filename

DESCRIPTION:	expand all ~'s and $VAR's in the passed in filename

LAST REV:	15 August 1990		OLE MERGE
		-- removed the call to TruncateFilename now
		   called from cfile.c and save.c
-----------------------------------------------------------------------------*/

char *UxGetFilename(fname)
	char		*fname;
{
	static dstring	filename= UxEmptyDstring;

	/* check for invalid characters in supplied filename */
	if (UxCheckForInvalidCharacters (fname))
		return NULL;

	dfree(filename);

	filename= expand_name(dcreate(fname));

	if (dnstr (filename)) {
		char *xname, *CondenseDots();

		if ((xname = CondenseDots (dnstr(filename)))) {
			dfree (filename);
			filename = dcreate (xname);
			UxFree (xname);
		}
	}

	return(dnstr(filename));
}

/*****************************************************************************
NAME:		char		*UxExpandEnv(str)

INPUT:		char		*str		- string to expand

RETURN:		expanded string

DESCRIPTION:	Expand all the environment variables in 'str' until there are
		no more '$'s in the string:

LAST REV:	Feb 22/1989
-----------------------------------------------------------------------------*/

char *UxExpandEnv(str)
	char		*str;
{
	static dstring	rtrn= UxEmptyDstring;

	dfree(rtrn);

	rtrn= dcreate(str);

	while(strchr(dnstr(rtrn), '$') != NULL)
		rtrn= expand_macros(rtrn);

	return(dnstr(rtrn));
}
/******************************************************************************
NAME:		char *		UxGetDirName(path)

INPUT:		char *path;	-- path to extract the directory from
RETURN:		directory name if the path is not NULL, must be freed.
		otherwise, returns NULL, nothing should be freed..

DESCRIPTION:	Strips the filename from the path.
		If the path is not valid or is just a filename,
		the function sets the path to the current directory
		The return value must be freed.

LAST REV:	Feb 28 1991		
-----------------------------------------------------------------------------*/
char *UxGetDirName(path)
char *path;
{
	int len;
	char *pntr, *dirname;

	if (path == (char *) NULL) {
		return (char *) NULL;
	}
	
	len = strlen(path);
	dirname = (char *) UxMalloc((len + 1) * sizeof(char));

	strcpy(dirname, path);

	pntr = strrchr(dirname, '/');
	if (pntr != NULL) {
		if (pntr == dirname)
			strcpy(dirname, "/");
		*pntr = '\0';
	}
	else {
		strcpy(dirname, ".");
	}

	return dirname;
		
}

/******************************************************************************
NAME:		int		UxWriteAccess(path, error)

INPUT:		char *path;		-- the path to access
		int  *error;		-- a return error flag
RETURN:		Checks if a path can be accessed.

		The calling function should check the return value from 
		UxWriteAccess(). 
		If it is -1, then it should check for the following error flags: 
		error = EACCES  -> file or directory is not writable.
		error = ENOENT  -> file or directory do not exist
		error = EISDIR  -> path is a directory

		If it is 0, then it should check for the following error flags: 

		error = EEXIST  -> file exists already
		error = ENOENT  -> file does not exist

DESCRIPTION:	Checks for the write permission on path. 
		7 cases could arise: 

		(a) path is NULL --> return -1.
		(b) path is a directory --> return -1.
		(c) directory specified in path is not writable 
			   and file does not exist --> return -1.
		(d) directory specified in path is not writable 
			    and file does not exist --> return -1.
		(e) dir is valid, and file does not exist. --> return 0.
		(f) path is valid, and file is not writable --> return -1.
		(g) path is valid, and file is writable --> return 0.

LAST REV:	Feb 28 1991
-----------------------------------------------------------------------------*/
int UxWriteAccess (path,error)
char *path;
int *error;
{
	int result;
	char *dirname;
	struct stat buf;


	if ((path == NULL) || (*path == '\0')) {
		*error = ENOENT;
		return -1;
	}

	/* check if the path points to a filename, or a directory */
	result = stat(path, &buf);


	/* path is not valid, find out why */
	if (result == -1) {
		dirname = UxGetDirName (path);
		result = access(dirname, W_OK);
		free (dirname);

		/* directory is not writable, and file does not exist */
		if (result == -1) {
			*error = EACCES;
			return result;
		}
		/* directory is writable, file does not exist, this is OK */
		else {
			*error = ENOENT;
		}
	}
	/* path is valid */
	else {

		/* path points to a directory  */
		if (buf.st_mode & S_IFDIR) {
			*error = EISDIR;
			return -1;
		}

		/* file is not writable */
		if (!(buf.st_mode & S_IWRITE)) {
			*error = EACCES;
			return -1;
		}
		/* file exists already and is writable */
		else {
			*error = EEXIST;
		}
	}
			

	return 0;

}
/******************************************************************************
NAME:		char	*UxGetCurrentDir ()
INPUT:		
RETURN:		char * 		-current directory
OUTPUT		
DESCRIPTION:	gets the current directory. Returns allocated memory, therefore
		calling function should free memory.
CREATED:	Oct 29/91	fix3099
LAST REV:	
-----------------------------------------------------------------------------*/
char	*UxGetCurrentDir ()
{
	char *getcwd (), *cwd;
			
	if ((cwd = (char *) UxMalloc (MAXPATHLEN +1))) {
		return getcwd (cwd, MAXPATHLEN + 1);
	}
	return (char *)NULL;
}
/******************************************************************************
NAME:		char	CondenseDots (fname)

INPUT:		char *fname;	-- path to extract the directory from
RETURN:		expanded path;
OUTPUT		

DESCRIPTION:	Expands dots in specified file path. Allocates
		memory for buffer, therefore, calling function
		should free return value.  The argument is expanded
		to an absolute name in the process.

CREATED:	July 25/91
LAST REV:	
-----------------------------------------------------------------------------*/

char 	*CondenseDots (fname)
char	*fname;
{
	char	*fstr, *xname = (char *)NULL;

	if (fname == NULL) {
		return NULL;
	} else if (*fname == 0) {
		return UxCopyString(fname);
	}

	/*-------------------------------------------------------------
	 * If fname does not begin with a slash, prepend current path.
	 *------------------------------------------------------------*/ 
	if (fname [0] != '/') {
		char *cwd;
			
		cwd = UxGetCurrentDir ();
		if (cwd) {
		    xname = (char *) UxMalloc (strlen(cwd) + strlen(fname) + 3);
		    sprintf (xname, "%s/%s/", cwd, fname);
		    UxFree (cwd);
		}
		else {
		    xname = (char *) UxMalloc (strlen(fname) + 2);
		    sprintf (xname, "%s/", fname);
		}
	} else {
       		/*----------------------------------------------------
		 * Append a slash to the path, needed for below.
		 *----------------------------------------------------*/
		xname = UxMalloc (strlen (fname) + 2);
		sprintf (xname, "%s/", fname);
	}

       /*-----------------------------------------------
	* Loop until all dots have been condensed out
	*----------------------------------------------*/
	while (1) 
	{
		char *dots;	
		fstr = UxCopyString (xname);

		if ((dots = strstr(fstr, "/./"))) 
		{
	       		/*-----------------------------
			 * "/./" -- remove "./"
			 *-----------------------------*/
			dots [1] = '\0';
			sprintf (xname, "%s%s", fstr, dots +3);
		}
		else if ((dots = strstr(fstr, "/../"))) 
		{
	       		/*-------------------------------------
			 * remove /../ and preceding directory
			 *-------------------------------------*/
			if (dots == fstr) {
			    	/* Path starts in "/../", not much to do.
			     	 */
			    	break; 
			} else {
			    	char *tail = dots + 4;
	
			    	while (*(--dots) != '/') {
					if (dots == fstr) {
						break;
					}
			    	}
				if (*dots == '/') {
				    	dots [1] = '\0';
					sprintf (xname, "%s%s", fstr, tail);
				} else {
					break;	 /* No leading /name/../  */
				}
			}
		} else {
			/*-----------------------------------------------
			 * No more dots. Remove the '/' we appended.
			 *-----------------------------------------------*/
			int len;

			if ((len = strlen (xname)) > 1) 
				xname [len - 1] = '\0';
			UxFree (fstr);
			return xname;
		}
		UxFree (fstr);
	}	

	/*------------------------------------------------------------
	 * If we failed the normal loop, just use the original.
	 *------------------------------------------------------------*/
	UxFree (fstr);
	UxFree (xname);
	return UxCopyString(fname);
}

/******************************************************************************
NAME:		int		CreateDirectory(path, mode)

INPUT:		char *path;	-- directory to create.
		ushort  mode;	-- protection mode.
RETURN:		error status.
DESCRIPTION:	Creates a directory with the specified mode,
		if the directory does not exist, and the parent directory
		is has write permission.

LAST REV:	July/31/91
		Oct/23/91	fix3074
-----------------------------------------------------------------------------*/
static int CreateDirectory (dir, mode)
char	*dir;
ushort	mode;
{
	int status;

	status = access (dir, 2);	

	if (status == -1) {
		if (errno == ENOENT) {
			status = mkdir (dir, mode);
		}
		else if (errno ==  EISDIR) {
			status = 0;
		}
	}
	return (status == 0 ? NO_ERROR : ERROR);
}
/******************************************************************************
NAME:		int		UxGetDirMode(path, mode)

INPUT:		char *path;	-- directory to create.
		ushort  *mode;	-- protection mode.
RETURN:		error status.
OUTPUT:		protection mode
DESCRIPTION:	gets the protection mode of the specified directory.

LAST REV:	July/31/91
		Oct 7/91	fix3031
-----------------------------------------------------------------------------*/
int	UxGetDirMode (dir, mode)
char	*dir;
ushort  *mode;
{
	struct stat  out;
	int	status;

	status = stat (dir, &out);

	if (status == 0) {
		*mode = out.st_mode;
		return NO_ERROR;
	}

	return ERROR;
}

/******************************************************************************
NAME:		int	UxMakeRelativeDirs (absp, relp)
INPUT:		char *absp;	-- absolute path where to create relative dirs.
		char *relp;	-- relative directories to be created.
RETURN:		error status.
DESCRIPTION:	Create all directories in relp if they do not exist, and
		if their parent directory has write access.
		Each directory is built with the same access flags
		as its parent directory.

LAST REV:	July/31/91	fix2845
		Oct 7/91	fix3031
		Oct/23/91	fix3074
-----------------------------------------------------------------------------*/
	
int	UxMakeRelativeDirs (absp, relp)
char	*absp, *relp;
{
	char    *token;
	char	*dir;
	int	status = NO_ERROR;

	if (!absp || !*absp)
		return ERROR;

	if (!relp || !*relp)
		return ERROR;

	dir = (char *)UxMalloc (strlen (absp) + 1);
	strcpy (dir, absp);

	token = strtok (relp, "/");

	while (token != NULL) {
		char *tmp;
		ushort   mode;

		status = UxGetDirMode (dir, &mode);
		tmp = UxCopyString (dir);
		dir = UxRealloc (dir, strlen (tmp) + strlen (token) + 2);
		sprintf (dir, "%s/%s", tmp, token);
			
		UxFree (tmp);

		if (status == NO_ERROR)
			status = CreateDirectory (dir, mode);
		if (status == ERROR) {
			return ERROR;
		}

		token = strtok (NULL, "/");

	}
	return status;
}	

/******************************************************************************
NAME:		UxValidateFilenameLength (fname)
INPUT:		char	*fname			: name to truncate
		int	suffix_len		: length of suffix to be added
OUTPUT:		---
RETURN:		char *				: truncated name
DESCRIPTION:	Check if the passed filename satisfies the length requirements.
		If the filename is too long, return an error message. Otherwise
		return NULL. If filenameTruncation is not turned on, then the
		filename is always valid.	
EXT REFERENCES:	---
EXT EFFECTS:	---
LAST REV:	
-----------------------------------------------------------------------------*/
char	*UxValidateFilenameLength (fname)
	char	*fname;
{
	int		len;
	char		*name;

	if (!UxShouldTruncate())
		return NULL;

	/* If filename truncation is turned on, and the file exists,
	 * we will assume that the system can read the file and 
	 * the validation is therefore successfull.
	 */
	if (UxFileExists(fname))
		return NULL;

	name= strrchr(fname, '/');

        if(name == NULL) {
                name= fname;
        } else {
                name++;
	}

	len = strlen(name);
	if (len > MAX_FILE_LEN) {
		return (GETMSG (LONG_FNAME));
	} else {
		return NULL;
	}
}



/*------------------------------------------------------------------------
 * NAME: UxAbbreviateFileName 
 *
 * INPUT:	char* fname;
 * RETURNS:		a pointer to some point in 'fname',
 *			possibly fname itself.
 * DESCRIPTION:
 *	If 'fname' is a full path to somewhere beneath
 *	the current directory, then return a pointer 
 *	to the significant portion of the path :
 *
 *	if pwd is /usr/bill/foo/proj:
 *
 *	For:	/usr/bill/foo/proj/sub/test.i
 *				   | return ptr to here
 *
 *	For:	/usr/bill/foo/proj/a.i
 *				   | return ptr to here
 *
 *	For:	/usr/bill/foo/other/
 *		|		     return whole thing
 *
 *	This is used to shorten expanded names when we emit messages.
 *	It does nothing fancy, only affecting paths that point inside 
 *	the current directory.
 *
 * LAST REV:	fix3553		Created.
 *------------------------------------------------------------------------*/

char* UxAbbreviateFileName  (fname)
	char* fname;
{
	char* pwd = UxGetCurrentDir();
	int   len = pwd ? strlen(pwd) : 0;

	/*-------------------------------------------------------
	 * Short-circuit of 'if' prevents filename[len] crashing.
	 *-------------------------------------------------------*/

	if (pwd && 
	    fname && 
	    (strncmp(pwd, fname, len) == 0) &&
	    fname[len] == '/')
	{
		UxFree(pwd);
		return fname + len + 1;
	}
	UxFree(pwd);
	return fname;
}
