/**
 *
 * $Id: Xmos.c,v 1.26 1999/06/03 21:51:56 danny Exp $
 *
 * Copyright (C) 1995 Free Software Foundation, Inc.
 *
 * This file is part of the GNU LessTif Library.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 **/

static const char rcsid[] = "$Id: Xmos.c,v 1.26 1999/06/03 21:51:56 danny Exp $";

#include <LTconfig.h>

#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#ifdef TIME_WITH_SYS_TIME
#include <time.h>
#endif
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#include <ctype.h>
#include <X11/Xfuncproto.h>
#include <XmI/XmI.h>

#include <Xm/XmP.h>
#include <X11/Xfuncs.h>

#include <LTglob.h>
#include <Xm/XmosP.h>

#include <XmI/DebugUtil.h>

#include "LTmisc.h"

/*
 * this is OS dependent, but this should catch most OS's.
 */
#define MAX_PATH_LEN   2048

/*
 * Pauses program execution for a given number of seconds. Although
 * the sillable `Micro' suggest otherwise, the granularity is really
 * only seconds.
 *
 * BTW - Does anyone knows whether we continue sleeping after a
 * signal was raised? I think so, but I'm not sure... --aldi
 *
 * Not so.  There are actually two functions, one for seconds and one
 * for microseconds.  The semantics are identical to the well-known
 * sleep() and usleep().  Neither continues after a signal.  - Jamie
 */

int
_XmSleep(unsigned int secs)
{
    return sleep(secs);
}

int
_XmMicroSleep(long secs)
{
    struct timeval tv;

    /* usleep() isn't as common as it could be,
     * so this isn't quite as pointless as _XmSleep.
     */

    tv.tv_sec = 0;
    tv.tv_usec = secs;
    return select(0, NULL, NULL, NULL, &tv);
}

XmString
_XmOSGetLocalizedString(char *reserved,
			Widget w,
			String resourceName,
			String value)
{
    /* THIS IS A BIG HACK --- FIX ME  --- DON'T KNOW HOW... SORRY */
    return XmStringCreateSimple(value);
}

/*
 * find the pattern part in a fileSpec
 * Rules I've been able to get out of M*tif
 * 1) If a spec ends in '/', return the remaining null string.
 * 2) Ordinarily, return either a) the first path component without
 * an escaped wildcard, or b) the last component that doesn't end in '/'.
 * 3) Escaped wildcards ('\') will be ignored.
 */
String
_XmOSFindPatternPart(String fileSpec)
{
    static char *wildcards = "*?[]+/";
    String ret, tmp;

    /*
     * Wendell Duncan <duncan@mercury.chem.utah.edu> says stripping off the
     * directory information helps : now directories with some of the
     * traditional wildcard characters in their names will behave as expected.
     */
    if (strlen(fileSpec)) {
	if ((ret = strrchr(fileSpec, '/')))
		fileSpec = ret + 1;
    }

    for (;;)
    {
	tmp = fileSpec;
retry:
	if (strlen(tmp) == 0)
	{
	    return fileSpec;
	}
	if ((ret = strpbrk(tmp, wildcards)) == NULL)
	{
	    return fileSpec;
	}
	else
	{
	    if (ret > tmp && ret[-1] == '\\')
	    {
		tmp = ret + 1;
		goto retry;
	    }
	    else if (*ret == '/')
	    {
		fileSpec = ret + 1;
		continue;
	    }

	    while ((ret > fileSpec) && (*ret != '/'))
	    {
		ret--;
	    }

	    if (*ret == '/')
	    {
		ret++;
	    }

	    return ret;
	}
    }

}

String
_XmOSGetHomeDirName(void)
{
    static char *home;

    /* Only actually check for the directory the first time.
     * We just keep it around for future calls.
     */

    if (!home)
    {
	char *estr;
	struct passwd *pw;

	/* First, try the environment.
	 * If that fails, look in the passwd file.
	 * If nothing there, return NULL - the caller must handle this.
	 */

	home = (estr = getenv("HOME"))
	    ? XtNewString(estr)
	    : ((estr = getenv("LOGNAME")) && (pw = getpwuid(getuid()))) ||
	      ((estr = getenv("USER")) && (pw = getpwuid(getuid()))) ||
	      (pw = getpwuid(getuid()))
	    ? XtNewString(pw->pw_dir) : "";
    }
    return home;
}

static Boolean
startsWithTwiddle(String dir)
{
    char *ptr;

    for (ptr = dir; *ptr && isspace(*ptr); ptr++)
    {
    }

    return *ptr == '~';
}

static String
convertTwiddle(char *dir)
{
    String ptr, ptr1;
    String newdir, home;

    for (ptr = dir; *ptr && isspace(*ptr); ptr++)
    {
    }
    ptr++;			/* to get by twiddle */

    ptr1 = strchr(ptr, '/');
    if (ptr == ptr1)
    {
	home = _XmOSGetHomeDirName();
    }
    else
    {
	String name, ptr2;
	struct passwd *pw;

    	name = XtNewString(ptr);
    	ptr2 = strchr(name, '/');
    	*ptr2 = '\0';
    	pw = getpwnam(name);
    	if (pw)
    	{
	    home = XtNewString(pw->pw_dir);
    	}
    	else
    	{
	    home = XtNewString("/");
    	}
    	XtFree(name);
    }
    newdir = XtMalloc(strlen(home) + strlen(ptr1) + 1);
    strcpy(newdir, home);
    strcat(newdir, ptr1);

    return newdir;
}

/*
 * qualify a file/dir spec
 * Rules I've learned from Motif:
 * 1) If dirSpec has wildcards, this routine won't try to remove them.  So
 * wildcards had better not get here.
 * 2) If the dirSpec is all dots, or combinations of dots, the routine will
 * resolve the dots relative to the current directory and return that.
 */
void
_XmOSQualifyFileSpec(String dirSpec,
		     String filterSpec,
		     String *pQualifiedDir,
		     String *pQualifiedPattern)
{
    String dir, filt;
    char *tmp, *tmp2;

    if (!dirSpec) dirSpec = "";
    /* no dir string? get cwd */
    if (strlen(dirSpec) == 0 ||
	strncmp(dirSpec, ".", 1) == 0 || strncmp(dirSpec, "..", 2) == 0)
    {
	if ((tmp = (char *)getcwd(NULL, MAX_PATH_LEN)) == NULL)
	    tmp = XtNewString(_XmOSGetHomeDirName());

	dir = (String)XtMalloc(strlen(tmp) + 1);
	strcpy(dir, tmp);

	XtFree(tmp);

	while (strncmp(dirSpec, "..", 2) == 0)
	{
	    tmp = dir + strlen(dir);
	    while (tmp > dir && *tmp != '/')
	    {
		tmp--;
	    }
	    if (*tmp == '/')
	    {
		*tmp = 0;
	    }
	    dirSpec += 2;
	    if (*dirSpec == '/')
	    {
		dirSpec++;
	    }
	}
	while (strncmp(dirSpec, ".", 2) == 0)
	{
	    dirSpec++;
	    if (*dirSpec == '/')
	    {
		dirSpec++;
	    }
	}
    }
    /* otherwise, we have a dir */
    else
    {
	dir = (String)XtMalloc(strlen(dirSpec) + 1);
	strcpy(dir, dirSpec);
    }

    /* if the dir doesn't terminate in a '/', add one */
    if (dir[strlen(dir) - 1] != '/')
    {
	dir = (String)XtRealloc(dir, strlen(dir) + 2);
	strcat(dir, "/");
    }
    if (startsWithTwiddle(dir))
    {
	char *newdir;

	newdir = convertTwiddle(dir);
	XtFree(dir);
	dir = newdir;
    }
    /* if the dir doesn't start in a '/', add the cwd */
    if (dir[0] != '/')
    {
    String tmp, tmp1;

	if ((tmp = (char *)getcwd(NULL, MAX_PATH_LEN)) == NULL)
	    tmp = XtNewString(_XmOSGetHomeDirName());

	tmp1 = XtMalloc(strlen(tmp) + strlen(dir) + 2);
	strcpy(tmp1, tmp);
	strcat(tmp1,"/");
	strcat(tmp1,dir);
	XtFree(dir);
	dir = tmp1;
    }

    if (!filterSpec) filterSpec = "";
    /* if the filter starts with an absolute pathname, use it */
    if (*filterSpec == '/')
    {
	XtFree(dir);
	dir = XtNewString(filterSpec);
    }
    else if (startsWithTwiddle(dir))
    {
	char *newdir;

	newdir = convertTwiddle(dir);
	XtFree(dir);
	dir = newdir;
	dir = XtRealloc(dir, strlen(dir) + strlen(filterSpec) + 1);
	strcat(dir, filterSpec);
    }
    /* if the filter is empty, make it all files */
    else if (strlen(filterSpec) == 0)
    {
	dir = (String)XtRealloc(dir, strlen(dir) + 2);
	strcat(dir, "*");
    }
    else
    {
	/* now add the filter spec */
	dir = XtRealloc(dir, strlen(dir) + strlen(filterSpec) + 1);
	strcat(dir, filterSpec);
    }

    /* eat any "..", ".", or "//" left in the path */
    for (;;)
    {
	if ((tmp = strstr(dir, "/./")) != NULL)
	{
	    if (tmp == dir || (tmp > dir && tmp[-1] != '\\'))
	    {
		*tmp = 0;
		tmp2 = XtNewString(tmp + 2);

		strcat(dir, tmp2);

		XtFree(tmp2);

		continue;
	    }
	}
	else if ((tmp = strstr(dir, "/../")) != NULL)
	{
	    if (tmp == dir || (tmp > dir && tmp[-1] != '\\'))
	    {
		*tmp = 0;
		if ((tmp2 = strrchr(dir, '/')) != NULL)
		{
		    *tmp2 = 0;
		    tmp2 = XtNewString(tmp + 3);

		    strcat(dir, tmp2);

		    XtFree(tmp2);

		    continue;
		}
		else
		{
		    strcat(dir, tmp + 3);
		}
	    }
	}
	else if ((tmp = strstr(dir, "//")) != NULL)
	{
	    if (tmp == dir || (tmp > dir && tmp[-1] != '\\'))
	    {
		*tmp = 0;
		tmp2 = XtNewString(tmp + 1);

		strcat(dir, tmp2);

		XtFree(tmp2);

		continue;
	    }
	}
	else
	{
	    break;
	}
    }

    /* extract the last component for the filter spec */
    if ((tmp = strrchr(dir, '/')) != NULL)
    {
	if (strlen(tmp) != 0)
	{
	    tmp++;
	    filt = XtNewString(tmp);
	    *tmp = 0;
	}
	else
	    filt = XtNewString("*");
    }
    else
    {
	filt = dir;
	dir = XtNewString("/");
    }

    *pQualifiedDir = dir;
    *pQualifiedPattern = filt;
}

/*
 * Takes a string and converts /this/dir/. into /this/dir/
 * also converts /this/dir/.. into /this/
 */
static void
_XmOSGetDotDot(String s)
{
    int i, j;

    if (s == 0)
    {
	return;
    }

    for (i = 0; s[i]; i++)
    {
    }

    if (s[i - 1] == '.' && s[i - 2] == '.' && s[i - 3] == '/')
    {				/* parent dir */

	for (j = i - 3; j > 0 && s[j] != '/'; j--)
	{
	}

	for (j--; j > 0 && s[j] != '/'; j--)
	{
	}

	if (j >= 0 && s[j] == '/')
	{
	    s[j + 1] = '\0';
	}

    }
    else if (s[i - 1] == '.' && s[i - 2] == '/')
    {				/* this dir */

	for (j = i - 2; j > 0 && s[j] != '/'; j--)
	{
	}

	if (j >= 0 && s[j] == '/')
	{
	    s[j + 1] = '\0';
	}

    }
    /* else no action */
}

/*
 * does this operate on one dir, or many ? (later) One.
 * Motif apparently does it this way.  If someone can prove otherwise,
 * PLEASE send the offending output to me (miers@packet.net), and a
 * description of the problem.
 * FIX ME -- This is a hack.  It should understand locale (for * mapping).
 */
void
_XmOSGetDirEntries(String qualifiedDir,
		   String matchPattern,
		   unsigned char fileType,
		   Boolean matchDotsLiterally,
		   Boolean listWithFullPath,
		   String **pEntries,
		   unsigned int *pNumEntries,
		   unsigned int *pNumAlloc)
{
    glob_t result;
    char buf[2048];
    int i, cnt, max;
    String slash, tmp, *ret = NULL;
    int flags = GLOB_MARK | (matchDotsLiterally ? 0 : GLOB_PERIOD);

    DEBUGOUT(XdbDebug(__FILE__, NULL,
		   "_XmOSGetDirEntries(%s,%s)\n", qualifiedDir, matchPattern));

    _XmOSGetDotDot(qualifiedDir);
    _XmOSGetDotDot(matchPattern);

    if (strlen(matchPattern) == 0)
    {
	qualifiedDir = "*";
    }
    else if ((slash = strstr(matchPattern, "/")) != NULL)
    {
	if (slash > matchPattern && slash[-1] != '\\')
	{
	    tmp = XtMalloc(slash - qualifiedDir + 1);
	    bcopy(qualifiedDir, tmp, slash - qualifiedDir);
	    tmp[slash - qualifiedDir] = 0;
	    matchPattern = tmp;
	}
    }

    if (matchPattern[0] == '/')
    {
	strcpy(buf, matchPattern);
    }
    else
    {
	strcpy(buf, qualifiedDir);

	for (i = 0; buf[i]; i++)
	{
	}

	i--;

	if (buf[i] != '/')
	{
	    strcat(buf, "/");
	}

	strcat(buf, matchPattern);
    }

    DEBUGOUT(XdbDebug(__FILE__, NULL,
		      "_XmOSGetDirEntries -> work on '%s'\n", buf));

    bzero((void *)&result, sizeof(result));

    i = _Lesstif_glob(buf, flags, NULL, &result);
    if (i)
    {
	return;
    }

    max = *pNumAlloc;
    if (!max)
    {
	max = 64;
	ret = (String *)XtCalloc(sizeof(String *), max);
    }
    for (i = 0, cnt = *pNumEntries; i < result.gl_pathc; i++)
    {

	if (cnt == max)
	{
	    max *= 2;
	    ret = (String *)XtRealloc((char *)ret, max * sizeof(String *));
	}

	if (fileType == XmFILE_ANY_TYPE)
	{

	    if (result.gl_pathv[i][strlen(result.gl_pathv[i]) - 1] == '/')
	    {
		result.gl_pathv[i][strlen(result.gl_pathv[i]) - 1] = 0;
	    }

	    if (listWithFullPath)
	    {
		if ((ret[cnt] = XtNewString(result.gl_pathv[i])) == NULL)
		{
		    _XmError(NULL, "Out of memory in _XmOSGetDirEntries.");
		}
	    }
	    else
	    {
		if ((tmp = strrchr(result.gl_pathv[i], '/')) == NULL)
		{
		    _XmError(NULL, "No '/' in path!\n");
		}
		if ((ret[cnt] = XtNewString(tmp + 1)) == NULL)
		{
		    _XmError(NULL, "Out of memory in _XmOSGetDirEntries.");
		}
	    }

	    cnt++;
	}
	else if (fileType == XmFILE_REGULAR &&
		 result.gl_pathv[i][strlen(result.gl_pathv[i]) - 1] != '/')
	{

	    if (listWithFullPath)
	    {
		if ((ret[cnt] = XtNewString(result.gl_pathv[i])) == NULL)
		{
		    _XmError(NULL, "Out of memory in _XmOSGetDirEntries.");
		}
	    }
	    else
	    {
		if ((tmp = strrchr(result.gl_pathv[i], '/')) == NULL)
		{
		    _XmError(NULL, "No '/' in path!\n");
		}
		if ((ret[cnt] = XtNewString(tmp + 1)) == NULL)
		{
		    _XmError(NULL, "Out of memory in _XmOSGetDirEntries.");
		}
	    }
	    cnt++;
	}
	else if (fileType == XmFILE_DIRECTORY &&
		 result.gl_pathv[i][strlen(result.gl_pathv[i]) - 1] == '/')
	{

	    if (result.gl_pathv[i][strlen(result.gl_pathv[i]) - 1] == '/')
	    {
		result.gl_pathv[i][strlen(result.gl_pathv[i]) - 1] = 0;
	    }

	    if (listWithFullPath)
	    {
		if ((ret[cnt] = XtNewString(result.gl_pathv[i])) == NULL)
		{
		    _XmError(NULL, "Out of memory in _XmOSGetDirEntries.");
		}
	    }
	    else
	    {
		if ((tmp = strrchr(result.gl_pathv[i], '/')) == NULL)
		{
		    _XmError(NULL, "No '/' in path!\n");
		}
		if ((ret[cnt] = XtNewString(tmp + 1)) == NULL)
		{
		    _XmError(NULL, "Out of memory in _XmOSGetDirEntries.");
		}
	    }

	    cnt++;
	}
    }

    _Lesstif_globfree(&result);
    *pNumAlloc = max;

    if (cnt == 0)
    {
	XtFree((char *)ret);
	*pEntries = NULL;
	*pNumEntries = 0;
    }
    else
    {
	*pNumEntries = cnt;
	*pEntries = ret;
    }

    if (XdbInDebug(__FILE__, NULL))
    {
	int i;

	DEBUGOUT(XdbDebug(__FILE__, NULL,
			  "_XmOSGetDirEntries: %d results\n", cnt));
	for (i = 0; i < cnt; i++)
	{
	    DEBUGOUT(XdbDebug(__FILE__, NULL, "\t[%d] - %s\n", i, ret[i]));
	}
    }
}

void
_XmOSBuildFileList(String dirPath,
		   String pattern,
		   unsigned char typeMask,
		   String **pEntries,
		   unsigned int *pNumEntries,
		   unsigned int *pNumAlloc)
{
    glob_t result;
    char buf[2048];
    int i, cnt, max;
    String *ret = NULL;
    int flags = GLOB_MARK | GLOB_PERIOD | GLOB_NOSORT;

    DEBUGOUT(XdbDebug(__FILE__, NULL,
		      "_XmOSBuildFileList(%s,%s)\n", dirPath, pattern));

    *pEntries = NULL;
    *pNumEntries = 0;
    *pNumAlloc = 0;

    _XmOSGetDotDot(dirPath);
    _XmOSGetDotDot(pattern);

    if (strlen(dirPath) == 0)
	dirPath = "*";

    if (pattern[0] == '/')
	strcpy(buf, pattern);
    else
    {
	strcpy(buf, dirPath);
	for (i = 0; buf[i]; i++);
	i--;
	if (buf[i] != '/')
	    strcat(buf, "/");
	strcat(buf, pattern);
    }

    i = _Lesstif_glob(buf, flags, NULL, &result);
    if (i)
	return;

    DEBUGOUT(XdbDebug0(__FILE__, NULL,
	"WARNING: _XmOSBuildFileList: Resetting initial list\n"));
    *pNumAlloc = *pNumEntries = 0;
    *pEntries = NULL;

    max = *pNumAlloc;
    if (!max)
    {
	max = 64;
	ret = (String *)XtCalloc(sizeof(String *), max);
    }
    else
    {
    	ret = *pEntries;
    }

    for (i = 0, cnt = *pNumEntries; i < result.gl_pathc; i++)
    {

	if (cnt == max)
	{
	    max *= 2;
	    ret = (String *)XtRealloc((char *)ret, max * sizeof(String *));
	}

	if (typeMask & XmFILE_REGULAR &&
	    result.gl_pathv[i][strlen(result.gl_pathv[i]) - 1] != '/')
	{
	    if ((ret[cnt] = XtNewString(result.gl_pathv[i])) == NULL)
	    {
		_XmError(NULL, "Out of memory in _XmOSGetDirEntries.");
	    }
	    cnt++;
	}

	if (typeMask & XmFILE_DIRECTORY &&
	    result.gl_pathv[i][strlen(result.gl_pathv[i]) - 1] == '/')
	{
	    if (result.gl_pathv[i][strlen(result.gl_pathv[i]) - 1] == '/')
	    {
		result.gl_pathv[i][strlen(result.gl_pathv[i]) - 1] = 0;
	    }
	    if ((ret[cnt] = XtNewString(result.gl_pathv[i])) == NULL)
	    {
		_XmError(NULL, "Out of memory in _XmOSGetDirEntries.");
	    }
	    cnt++;
	}
    }

    _Lesstif_globfree(&result);

    *pNumAlloc = max;

    if (cnt == 0)
    {
	XtFree((char *)ret);
	*pEntries = NULL;
	*pNumEntries = 0;
    }
    else
    {
	*pNumEntries = cnt;
	*pEntries = ret;
	/*
	qsort((void *)ret, cnt, sizeof(String), vstrcmp);
	*/
    }

    if (XdbInDebug(__FILE__, NULL))
    {
	int i;

	DEBUGOUT(XdbDebug(__FILE__, NULL,
			  "_XmOSBuildFileList: %d results\n", cnt));
	for (i = 0; i < cnt; i++)
	{
	    DEBUGOUT(XdbDebug(__FILE__, NULL, "\t[%d] - %s\n", i, ret[i]));
	}
    }
}

/*
 * a sort function, perhaps?
 */
/* rws 3 Jul 1998
   Mozilla shows us what this is.  It is the comparison function used in
   qsort.
 */

int
_XmOSFileCompare(XmConst void *sp1, XmConst void *sp2)
{
    return strcmp(*((char **)sp1), *((char **)sp2));
}

char _XmSDEFAULT_FONT[] = "fixed";
char _XmSDEFAULT_BACKGROUND[] = "Blue";

/*
 * Not sure what this thing does but it's rumoured to exist
 */
void
XmOSInitPath()
{
}
