/**
 *
 * $Id: Xmos.c,v 1.11 1997/08/19 19:29:27 miers 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 char rcsid[] = "$Id: Xmos.c,v 1.11 1997/08/19 19:29:27 miers Exp $";

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <LTconfig.h>
#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>

/*
 * 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
 */
int
_XmMicroSleep(long secs)
{
    unsigned int zzz;

    if (secs > 0)
    {
	zzz = (unsigned int)secs;
	while ((zzz = AMIGALT_sleep(zzz)) > 0)
	{
	    /* continue sleeping */
	}
    }
    return 0;
}

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;

    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 == '/')||(*ret==':'))
	    {
		fileSpec = ret + 1;
		continue;
	    }

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

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

	    return ret;
	}
    }

}

 /*
  * this is used if the cwd is unreadable
  */
String
_XmOSGetHomeDirName(void)
{
    char *ret;

    if ((ret = getenv("HOME")) != NULL)
    {
	return ret;
    }

    /* FIX ME -- check password file */
    return NULL;
}

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

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

    return *ptr == '~';
}

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

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

    if ((home = _XmOSGetHomeDirName()) == NULL)
    {
	return NULL;
    }

    newdir = XtMalloc(strlen(home) + strlen(ptr) + 1);
    strcpy(newdir, home);
    strcat(newdir, ptr);

    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;
DEBUGOUT(printf("_XmOSQualifyFileSpec------------------0\n"));

    /* no dir string? get cwd */
    if (strlen(dirSpec) == 0 ||
	strncmp(dirSpec, ".", 1) == 0 || strncmp(dirSpec, "..", 2) == 0)
    {
DEBUGOUT(printf("_XmOSQualifyFileSpec------------------0-0\n"));
	if ((tmp = (char *)getcwd(NULL, MAX_PATH_LEN)) == NULL)
	{
DEBUGOUT(printf("_XmOSQualifyFileSpec------------------0-1\n"));
	    if ((tmp = _XmOSGetHomeDirName()) == NULL)
	    {
		abort();	/* we've gotta give up sometime */
	    }
	    if ((tmp = XtNewString(tmp)) == NULL)
	    {
		abort();	/* we've gotta give up sometime */
	    }
	}
DEBUGOUT(printf("_XmOSQualifyFileSpec-tmp:%08x---%s--------------0-2\n",tmp,tmp));

	dir = (String)XtMalloc(strlen(tmp) + 1);
        if(dir==0) exit(1);
	strcpy(dir, tmp);
DEBUGOUT(printf("_XmOSQualifyFileSpec-%08x<%08x-----------------0-3\n",dir,tmp));

//removed because of strange AMIGA-crash//	XtFree(tmp);

	while (strncmp(dirSpec, "..", 2) == 0)
	{
DEBUGOUT(printf("_XmOSQualifyFileSpec------------------0-4\n"));
	    tmp = dir + strlen(dir);
	    while (tmp > dir && *tmp != '/' && *tmp!=':')
	    {
		tmp--;
	    }
	    if ((*tmp == '/')||(*tmp==':'))
	    {
		*tmp = 0;
	    }
	    dirSpec += 2;
	    if ((*dirSpec == '/')||(*dirSpec==':'))
	    {
		dirSpec++;
	    }
	}
DEBUGOUT(printf("_XmOSQualifyFileSpec------------------0-5\n"));
	while (strncmp(dirSpec, ".", 2) == 0)
	{
	    dirSpec++;
	    if ((*dirSpec == '/')||(*dirSpec==':'))
	    {
		dirSpec++;
	    }
	}
    }
    /* otherwise, we have a dir */
    else
    {
DEBUGOUT(printf("_XmOSQualifyFileSpec------------------0-6\n"));
	dir = (String)XtMalloc(strlen(dirSpec) + 1);
	strcpy(dir, dirSpec);
    }
DEBUGOUT(printf("_XmOSQualifyFileSpec------------------1\n"));

    /* if the dir doesn't terminate in a '/', add one */
    if ((dir[strlen(dir) - 1] != '/')&&(dir[strlen(dir) - 1] != ':'))
    {
	dir = (String)XtRealloc(dir, strlen(dir) + 2);
	strcat(dir, "/");
    }
DEBUGOUT(printf("_XmOSQualifyFileSpec------------------2\n"));

    /* if the filter starts with an absolute pathname, use it */
    if (*filterSpec == '/')
    {
//removed because of strange AMIGA-crash//	XtFree(dir);
	if ((dir = XtNewString(filterSpec)) == NULL)
	{
	    abort();		/* oh, well */
	}
    }
    else if (startsWithTwiddle(dir))
    {
	char *newdir;

	if ((newdir = convertTwiddle(dir)) == NULL)
	{
	    abort();		/* oh, well */
	}
//removed because of strange AMIGA-crash//	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);
    }
DEBUGOUT(printf("_XmOSQualifyFileSpec------------------3\n"));

    /* 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;
	}
    }
DEBUGOUT(printf("_XmOSQualifyFileSpec------------------4\n"));

    /* extract the last component for the filter spec */
    if(((tmp = strrchr(dir, '/')) != NULL)||
       ((tmp = strrchr(dir, ':')) != NULL))
    {
	if (strlen(tmp) != 0)
	{
	    tmp++;
	    if ((filt = XtNewString(tmp)) == NULL)
	    {
		abort();
	    }
	    *tmp = 0;
	}
	else if ((filt = XtNewString("*")) == NULL)
	{
	    abort();
	}
    }
    else
    {
	filt = dir;
	if ((dir = XtNewString("/")) == NULL)
	{
	    abort();
	}
    }
DEBUGOUT(printf("_XmOSQualifyFileSpec------------------5\n"));

    *pQualifiedDir = dir;
    *pQualifiedPattern = filt;
DEBUGOUT(printf("_XmOSQualifyFileSpec------------------exit\n"));
}

/*
 * Takes a string and converts /this/dir/. into /this/dir/
 * also converts /this/dir/.. into /this/
 */
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);

//fprintf(stderr,"qualifiedDir:%s< matchpatt:%s<\n",qualifiedDir,matchPattern);
//fprintf(stderr,"qualifiedDir:%s< matchpatt:%s<\n",qualifiedDir,matchPattern);
//fprintf(stderr,"qualifiedDir:%s< matchpatt:%s<\n",qualifiedDir,matchPattern);
//fprintf(stderr,"qualifiedDir:%s< matchpatt:%s<\n",qualifiedDir,matchPattern);
//fprintf(stderr,"qualifiedDir:%s< matchpatt:%s<\n",qualifiedDir,matchPattern);
//fprintf(stderr,"qualifiedDir:%s< matchpatt:%s<\n",qualifiedDir,matchPattern);
//fprintf(stderr,"qualifiedDir:%s< matchpatt:%s<\n",qualifiedDir,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++)
    {
//fprintf(stderr,"GetDirEntries:%s\n",result.gl_pathv[i]);

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

	if (fileType == XmFILE_ANY_TYPE)
	{
DEBUGOUT(fprintf(stderr,"XmFILE_ANY_TYPE\n"));

	    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)&&
                   ((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] != '/')
	{
DEBUGOUT(fprintf(stderr,"XmFILE_REGULAR\n"));
	    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)&&
                   ((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 
//#ifndef AMIGA
		 && result.gl_pathv[i][strlen(result.gl_pathv[i]) - 1] == '/'
//#endif
                )
	{
DEBUGOUT(fprintf(stderr,"XmFILE_DIRECTORY\n"));

	    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)&&
                   ((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));

    _XmOSGetDotDot(dirPath);
    _XmOSGetDotDot(pattern);

//fprintf(stderr,"dirpath:%s< patt:%s<\n",dirPath,pattern);
//fprintf(stderr,"dirpath:%s< patt:%s<\n",dirPath,pattern);
//fprintf(stderr,"dirpath:%s< patt:%s<\n",dirPath,pattern);
//fprintf(stderr,"dirpath:%s< patt:%s<\n",dirPath,pattern);
//fprintf(stderr,"dirpath:%s< patt:%s<\n",dirPath,pattern);
//fprintf(stderr,"dirpath:%s< patt:%s<\n",dirPath,pattern);
//fprintf(stderr,"dirpath:%s< patt:%s<\n",dirPath,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] != '/')&&(buf[i] != ':'))
	    strcat(buf, "/");
	strcat(buf, pattern);
    }

    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++)
    {
//fprintf(stderr,"BuildFileList:%s\n",result.gl_pathv[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] != '/')
	{
DEBUGOUT(fprintf(stderr,"XmFILE_REGULAR"));
	    if ((ret[cnt] = XtNewString(result.gl_pathv[i])) == NULL)
	    {
		_XmError(NULL, "Out of memory in _XmOSGetDirEntries.");
	    }
	    cnt++;
	}

	if (typeMask & XmFILE_DIRECTORY
//#ifndef AMIGA
	    && result.gl_pathv[i][strlen(result.gl_pathv[i]) - 1] == '/'
//#endif
           )
	{
DEBUGOUT(fprintf(stderr,"XmFILE_DIRECTORY\n"));
	    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;
    }

    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?
 */
int
_XmOSFileCompare(XmConst void *sp1, XmConst void *sp2)
{
    return strcmp(sp1, sp2);
}

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