/* 
 * wishGather.c --
 *
 *	Gathering and sorting file names.  Putting them in
 *	data structures.
 *
 * Copyright 1987 Regents of the University of California
 * 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.  The University of California
 * makes no representations about the suitability of this
 * software for any purpose.  It is provided "as is" without
 * express or implied warranty.
 */

#ifndef lint
static char rcsid[] = "$Header: /a/newcmds/wish/RCS/wishGather.c,v 1.4 89/01/11 11:31:37 mlgray Exp Locker: mgbaker $ SPRITE (Berkeley)";
#endif not lint


#include <sys/types.h>
#include <sys/stat.h>
#include <sys/dir.h>
#include <errno.h>
#include "sx.h"
#include "string.h"
#include "util.h"
#include "wishInt.h"

/* ClientData for scandir select proc's */
static	ClientData	scanData;

/* used before defined */
extern	Boolean	SelectFiles();
extern	int	AlphaForwards();
extern	int	AlphaReverse();
extern	int	AtimeForwards();
extern	int	AtimeReverse();
extern	int	CtimeForwards();
extern	int	CtimeReverse();
extern	int	MtimeForwards();
extern	int	MtimeReverse();
extern	int	DtimeForwards();
extern	int	DtimeReverse();
extern	int	SizeForwards();
extern	int	SizeReverse();
static	int	AtimeSort();
static	int	MtimeSort();
static	int	DtimeSort();
static	int	SizeSort();


/*
 *----------------------------------------------------------------------
 *
 * WishGatherNames --
 *
 *	Using scandir() and a bunch of rules and call-back thingies,
 *	gather the appropriate names into the appropriate places.
 *
 * Results:
 *	TCL_OK if all went well.  Some TCL error if not.
 *
 * Side effects:
 *	The names are gathered and stuff is allocated.
 *
 *----------------------------------------------------------------------
 */
int
WishGatherNames(aWindow)
    WishWindow	*aWindow;
{
    int		nitems;
    struct	direct	**namelist;
    int		(*compareProc)();
    int		i;
    WishFile	*tmpPtr, *backTmpPtr;
    WishGroup	*grpPtr;
    int		result;
    Boolean	getAttrsP = FALSE;
    struct	stat	attrs;

    if (aWindow->groupList == NULL) {
	/* get it out of the file */
#ifdef NOTDEF
	Sx_Panic(wishDisplay,
		"WishGatherNames: Should source .wish file, but can't.");
#endif NOTDEF
#ifdef NOTDEF
	WishReadRules(aWindow, name);
#endif NOTDEF
    }
    if (aWindow->groupList == NULL) {
	/*
	 * There wasn't a file, select everything (or should the default
	 * be to select nothing?
	 */
	aWindow->groupList = (WishGroup *) malloc(sizeof (WishGroup));
	aWindow->groupList->myColumn = -1;
	aWindow->groupList->headerWindow = UNINITIALIZED;
	aWindow->groupList->x = aWindow->groupList->y = -1;
	aWindow->groupList->width = aWindow->groupList->height = -1;
	aWindow->groupList->entry_x = aWindow->groupList->entry_y =
		aWindow->groupList->entry_width = -1;
	aWindow->groupList->fileList = NULL;
	aWindow->groupList->defType = COMPARISON;
	aWindow->groupList->rule = Util_Strcpy(NULL, "*");
	aWindow->groupList->groupBindings = NULL;
	aWindow->groupList->length = -1;
	aWindow->groupList->selectedP = FALSE;
	aWindow->groupList->highlightP = FALSE;
	aWindow->groupList->nextPtr = NULL;

	aWindow->numElements = UNINITIALIZED;
	aWindow->numGroups = 1;
	aWindow->numHiddenGroups = 0;
    }
    if (aWindow->numElements >= 0) {
	/*
	 * For now, this means this has already been set up, since there's
	 * no garbage collection yet.
	 */
	return TCL_OK;
    }
    WishGetCompareProc(aWindow, &compareProc, FALSE);
    getAttrsP = WISH_ATTR_NECESSARY_P; 

    scanData = (ClientData) aWindow;
    nitems = scandir(aWindow->dir, &namelist, SelectFiles, compareProc);
    /*
     * I use Stat_GetMsg here.  Am I doing it correctly with errno?
     */
    if (nitems < 0) {
	sprintf(aWindow->interp->result, "%s %s %s %s.  %s %s",
		"Directory scan failed in", aWindow->dir, "with error",
		Stat_GetMsg(errno),
		"Either the directory doesn't exist, or it",
		"is unreadable, or we've run out of memory");
	return TCL_ERROR;
    }
    /*
     * For each file, run through all the groups putting a copy of the file
     * into the group if it matches the rule for that group.  This allows
     * one file to show up in all groups that can select it.  If files should
     * only show up in one place, then skip to the next file after we've
     * found one group for it.  Right now, I use the first policy.  Maybe
     * this should be an option.
     *
     * Since the files are in proper sorted order already and I run through
     * them in order, they will come out in proper sorted order inside the
     * groups.
     *
     * NOTE:  This may be way too slow to use Pattern_Match() in 2 passes
     * like this.
     */
    aWindow->numElements = 0;		/* since it's initialized to -1 */
    aWindow->numHiddenGroups = 0;
    for (i = 0; i < nitems; i++) {
	if (getAttrsP) {
	    /*
	     * If the files were sorted by something involving an attribute,
	     * then there should be a way in the sort routines to cache that
	     * info so we don't do a second GetAttributes()'s here...
	     * This means dealing with the garbage collection aspects too.
	     */
	    if (lstat(namelist[i]->d_name, &attrs)
		    != 0) {
		char	buffer[MAXPATHLEN];
		sprintf(wishErrorMsg, "%s %s.  %s %s.  %s %s.",
			"Couldn't get attributes for file",
			namelist[i]->d_name,
			"Error was",
			Stat_GetMsg(errno),
			"Current dir is",
			getwd(buffer) == NULL ? "NULL" : buffer);
		aWindow->notifierP = TRUE;
		Sx_Notify(wishDisplay, aWindow->surroundingWindow, -1, -1, 0,
			wishErrorMsg,
			NULL, TRUE, "Skip offending file", (char *) NULL);
		aWindow->notifierP = FALSE;
		continue;
	    }
	}
	for (grpPtr = aWindow->groupList; grpPtr != NULL;
		grpPtr = grpPtr->nextPtr) {
	    result = Pattern_Match(grpPtr->rule, namelist[i]->d_name);
	    if (result == 0) {		/* it matched */
		tmpPtr = (WishFile *) malloc(sizeof (WishFile));
		tmpPtr->name = Util_Strcpy(NULL, namelist[i]->d_name);
		if (getAttrsP) {
		    tmpPtr->attrPtr = (struct	stat *)
			    malloc(sizeof (struct	stat));
		    *(tmpPtr->attrPtr) = attrs;
		} else {
		    tmpPtr->attrPtr = NULL;
		}
		tmpPtr->length = -1;
		tmpPtr->x = tmpPtr->y = -1;
		tmpPtr->myColumn = -1;
		tmpPtr->selectedP = FALSE;
		tmpPtr->lineP = FALSE;
		tmpPtr->highlightP = FALSE;
		tmpPtr->myGroupPtr = grpPtr;
		tmpPtr->nextPtr = NULL;
		if (grpPtr->fileList == NULL) {
		    grpPtr->fileList = tmpPtr;
		} else {
		    /* add file to end of list */
		    for (backTmpPtr = grpPtr->fileList;
			    backTmpPtr->nextPtr != NULL;
			    backTmpPtr = backTmpPtr->nextPtr) {
			/* do nothing */
		    }
		    backTmpPtr->nextPtr = tmpPtr;
		}
		aWindow->numElements++;
	    } else if (result < 0) {
		/*
		 * I check the rules as they are created, or the following
		 * would cause a billion notifiers!  This is just for extreme
		 * caution...
		 */
		sprintf(wishErrorMsg, "The rule %s contains an error.",
		    grpPtr->rule);
		aWindow->notifierP = TRUE;
		Sx_Notify(wishDisplay, aWindow->displayWindow, -1, -1, 0,
			wishErrorMsg, NULL, TRUE, "Continue", NULL);
		aWindow->notifierP = FALSE;
	    }
	}
    }
    if (aWindow->hideEmptyGroupsP) {
	for (grpPtr = aWindow->groupList; grpPtr != NULL;
		grpPtr = grpPtr->nextPtr) {
	    if (grpPtr->fileList == NULL) {
		aWindow->numHiddenGroups++;
	    }
	}
    }
    /*
     * total number of things that could be displayed is the number of files
     * plus the number of visible group headers plus the number of spaces
     * between visible groups.
     */
    aWindow->totalDisplayEntries = aWindow->numElements +
	    (2 * (aWindow->numGroups - aWindow->numHiddenGroups)) - 1;
    free_scandir(nitems, &namelist);

    return TCL_OK;
}

typedef	struct	{
    WishWindow	*aWindow;
    WishGroup		*grpPtr;
} GatherInfo;


/*
 *----------------------------------------------------------------------
 *
 * WishGatherSingleGroup --
 *
 *	Using scandir() and a bunch of rules and call-back thingies,
 *	gather the appropriate names into the appropriate places.
 *
 * Results:
 *	TCL_OK if all went well.  Some TCL error if not.
 *
 * Side effects:
 *	The names are gathered and stuff is allocated.
 *
 *----------------------------------------------------------------------
 */
int
WishGatherSingleGroup(aWindow, grpPtr)
    WishWindow	*aWindow;
    WishGroup		*grpPtr;
{
    Boolean	getAttrsP = FALSE;
    int		nitems;
    struct	direct	**namelist;
    int		(*compareProc)();
    struct	stat	attrs;
    WishFile	*tmpPtr, *backTmpPtr;
    extern	Boolean	SelectGroupFiles();	/* forward reference */
    int		i;
    GatherInfo	gatherInfo;

    WishGetCompareProc(aWindow, &compareProc, FALSE);
    getAttrsP = WISH_ATTR_NECESSARY_P; 

    gatherInfo.aWindow = aWindow;
    gatherInfo.grpPtr = grpPtr;
    scanData = (ClientData) &gatherInfo;
    nitems = scandir(aWindow->dir, &namelist, SelectGroupFiles, compareProc);
    if (nitems < 0) {
	sprintf(aWindow->interp->result, "%s %s %s %s.  %s %s",
		"Directory scan failed in", aWindow->dir, "with error",
		Stat_GetMsg(errno),
		"Either the direcotry doesn't exist, or it",
		"is unreadable, or we've run out of memory");
	return TCL_ERROR;
    }
    /* since they're initialized to -1 */
    if (aWindow->numElements == UNINITIALIZED) {
	aWindow->numElements = 0;
    }
    if (aWindow->numHiddenGroups == UNINITIALIZED) {
	aWindow->numHiddenGroups = 0;
    }
    for (i = 0; i < nitems; i++) {
	if (getAttrsP) {
	    /*
	     * If the files were sorted by something involving an attribute,
	     * then there should be a way in the sort routines to cache that
	     * info so we don't do a second GetAttributes()'s here...
	     * This means dealing with the garbage collection aspects too.
	     */
	    if (lstat(namelist[i]->d_name, &attrs)
		    != 0) {
		char	buffer[MAXPATHLEN];
		sprintf(wishErrorMsg, "%s %s.  %s %s.  %s %s.",
			"Couldn't get attributes for file",
			namelist[i]->d_name,
			"Error was",
			Stat_GetMsg(errno),
			"Current dir is",
			getwd(buffer) == NULL ? "NULL" : buffer);
		aWindow->notifierP = TRUE;
		Sx_Notify(wishDisplay, aWindow->surroundingWindow, -1, -1, 0,
			wishErrorMsg,
			NULL, TRUE, "Leave attributes blank", (char *) NULL);
		aWindow->notifierP = FALSE;
		bzero(&attrs, sizeof (struct	stat));;
	    }
	}
	tmpPtr = (WishFile *) malloc(sizeof (WishFile));
	tmpPtr->name = Util_Strcpy(NULL, namelist[i]->d_name);
	if (getAttrsP) {
	    tmpPtr->attrPtr = (struct	stat *)
		    malloc(sizeof (struct	stat));
	    *(tmpPtr->attrPtr) = attrs;
	} else {
		tmpPtr->attrPtr = NULL;
	}
	tmpPtr->length = -1;
	tmpPtr->x = tmpPtr->y = -1;
	tmpPtr->myColumn = -1;
	tmpPtr->selectedP = FALSE;
	tmpPtr->lineP = FALSE;
	tmpPtr->highlightP = FALSE;
	tmpPtr->myGroupPtr = grpPtr;
	tmpPtr->nextPtr = NULL;
	if (grpPtr->fileList == NULL) {
	    grpPtr->fileList = tmpPtr;
	} else {
	    /* add file to end of list */
	    for (backTmpPtr = grpPtr->fileList;
		    backTmpPtr->nextPtr != NULL;
		    backTmpPtr = backTmpPtr->nextPtr) {
		/* do nothing */
	    }
	    backTmpPtr->nextPtr = tmpPtr;
	}
	aWindow->numElements++;
    }
    if (aWindow->numGroups == UNINITIALIZED) {
	aWindow->numGroups = 1;
	aWindow->numHiddenGroups = 0;
    } else {
	aWindow->numGroups++;
    }
    if (aWindow->hideEmptyGroupsP) {
	if (grpPtr->fileList == NULL) {
	    aWindow->numHiddenGroups++;
	}
    }
    /*
     * Total number of things that could be displayed is the number of files
     * plus the number of visible group headers plus the number of spaces
     * between visible groups.
     */
    aWindow->totalDisplayEntries = aWindow->numElements +
	    (2 * (aWindow->numGroups - aWindow->numHiddenGroups)) - 1;
    free_scandir(nitems, &namelist);

    return TCL_OK;
}


/*
 *----------------------------------------------------------------------
 *
 * SelectFiles --
 *
 *	Routine passed to scandir(). 
 *
 * Results:
 *	Return TRUE or FALSE depending
 *	upon whether or not there's a group with a rule that would
 *	select this the given file.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */
Boolean
SelectFiles(entryPtr)
    struct	direct	*entryPtr;
{
    WishWindow	*aWindow;
    WishGroup		*grpPtr;
    int			result;

    aWindow = (WishWindow *) scanData;
    for (grpPtr = aWindow->groupList; grpPtr != NULL;
	    grpPtr = grpPtr->nextPtr) {
	result = Pattern_Match(grpPtr->rule, entryPtr->d_name);
	if (result == 0) {
	    break;
	} else if (result < 0) {
	    /*
	     * I must check the rules as they are created, or the following
	     * would cause a billion notifiers!
	     */
	    sprintf(wishErrorMsg, "The rule %s contains an error.",
		grpPtr->rule);
	    aWindow->notifierP = TRUE;
	    Sx_Notify(wishDisplay, aWindow->displayWindow, -1, -1, 0,
		    wishErrorMsg, NULL, TRUE, "Continue", NULL);
	    aWindow->notifierP = FALSE;
	}
    }
    if (grpPtr == NULL) {
	return FALSE;
    }
    return TRUE;
}


/*
 *----------------------------------------------------------------------
 *
 * SelectGroupFiles --
 *
 *	Routine passed to scandir() for selecting files for a single group. 
 *
 * Results:
 *	Return TRUE or FALSE depending
 *	upon whether or not the given group rule will select the given file.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */
Boolean
SelectGroupFiles(entryPtr)
    struct	direct	*entryPtr;
{
    WishGroup		*grpPtr;
    int			result;
    WishWindow	*aWindow;

    aWindow = ((GatherInfo *) scanData)->aWindow;
    grpPtr = ((GatherInfo *) scanData)->grpPtr;
    if (grpPtr->defType == COMPARISON) {
	result = Pattern_Match(grpPtr->rule, entryPtr->d_name);
	if (result == 0) {
	    return TRUE;
	} else if (result < 0) {
	    sprintf(wishErrorMsg, "The rule %s contains an error.",
		grpPtr->rule);
	    aWindow->notifierP = TRUE;
	    Sx_Notify(wishDisplay, aWindow->displayWindow, -1, -1, 0,
		    wishErrorMsg, NULL, TRUE, "Continue", NULL);
	    aWindow->notifierP = FALSE;
	}
	return FALSE;
    }
    if (grpPtr->defType != PROC) {
	aWindow->notifierP = TRUE;
	Sx_Notify(wishDisplay, aWindow->displayWindow, -1, -1, 0,
		"unknown type of rule definition", NULL,
		TRUE, "Continue", NULL);
	aWindow->notifierP = FALSE;
	return FALSE;
    }
    if (WishDoTclSelect(aWindow->interp, grpPtr->rule, entryPtr->d_name,
	    &result) != TCL_OK) {
	aWindow->notifierP = TRUE;
	Sx_Notify(wishDisplay, aWindow->displayWindow, -1, -1, 0,
		aWindow->interp->result, NULL, TRUE, "Continue", NULL);
	aWindow->notifierP = FALSE;
	return FALSE;
    }
    return result;
}


/*
 *----------------------------------------------------------------------
 *
 * WishDoTclSelect --
 *
 *	Execute the tcl rule procName on the fileName arg and determine
 *	whether the file matches the rule.
 *
 * Results:
 *	TCL_OK if things work ok, and TCL_ERROR if not.  If the routine
 *	returns TCL_OK, then boolean value
 *	determining whether the file matches the rule is returned in the
 *	selectValPtr parameter.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */
int
WishDoTclSelect(interp, procName, arg, selectValPtr)
    Tcl_Interp		*interp;
    char		*procName;
    char		*arg;
    int			*selectValPtr;
{
    char	*buffer;
    char	*cPtr;

    if (procName == NULL || arg == NULL) {
	strcpy(interp->result,
		"Null procedure name or argument for file selection");
	return TCL_ERROR;
    }
    buffer = (char *) malloc(strlen(procName) + strlen(arg) + 2);
    sprintf(buffer, "%s %s", procName, arg);
    if (Tcl_Eval(interp, buffer, '\0', NULL) != TCL_OK) {
	sprintf(interp->result,
		"Something is wrong with the rule definition %s.", procName);
	free(buffer);
	return TCL_ERROR;
    }
    free(buffer);
    if ((*selectValPtr = strtol(interp->result, &cPtr, 10)) == 0
	    && cPtr == interp->result) {
	sprintf(interp->result, "Bad proc definition %s", procName);
	return TCL_ERROR;
    }
    if (*selectValPtr < 0) {
	sprintf(interp->result, "proc %s returned an error value", procName);
	return TCL_ERROR;
    }
    /* reverse the meaning, since pattern match returns 0 for success... */
    if (*selectValPtr == 0) {
	*selectValPtr = 1;
    } else {
	*selectValPtr = 0;
    }
    return TCL_OK;
}


/*
 *----------------------------------------------------------------------
 *
 * AlphaForwards --
 *
 *	Compare two files for alphabetical order.
 *
 * Results:
 *	Negative if the first is before the second.  Zero if they are the
 *	same.  Positive if the first is after the second.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */
int
AlphaForwards(first, second)
    struct	direct	**first, **second;
{
    return alphasort(first, second);
}


/*
 *----------------------------------------------------------------------
 *
 * AlphaReverse --
 *
 *	Compare two files for reverse alphabetical order.
 *
 * Results:
 *	Negative if the first is before the second.  Zero if they are the
 *	same.  Positive if the first is after the second.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */
int
AlphaReverse(first, second)
    struct	direct	**first, **second;
{
    return (-alphasort(first, second));
}


int
AtimeForwards(first, second)
    struct	direct	**first, **second;
{
    return AtimeSort(first, second);
}

int
AtimeReverse(first, second)
    struct	direct	**first, **second;
{
    return (-AtimeSort(first, second));
}

#ifdef NOTDEF
int
CtimeForwards(first, second)
    struct	direct	**first, **second;
{
    return CtimeSort(first, second);
}

int
CtimeReverse(first, second)
    struct	direct	**first, **second;
{
    return (-CtimeSort(first, second));
}
#endif /* NOTDEF */

int
MtimeForwards(first, second)
    struct	direct	**first, **second;
{
    return MtimeSort(first, second);
}

int
MtimeReverse(first, second)
    struct	direct	**first, **second;
{
    return (-MtimeSort(first, second));
}

int
DtimeForwards(first, second)
    struct	direct	**first, **second;
{
    return DtimeSort(first, second);
}

int
DtimeReverse(first, second)
    struct	direct	**first, **second;
{
    return (-DtimeSort(first, second));
}

int
SizeForwards(first, second)
    struct	direct	**first, **second;
{
    return SizeSort(first, second);
}

int
SizeReverse(first, second)
    struct	direct	**first, **second;
{
    return (-SizeSort(first, second));
}

static int
FileAlphaSort(file1, file2)
    WishFile	**file1;
    WishFile	**file2;
{
    return strcmp((*file1)->name, (*file2)->name);
}

static int
FileAlphaReverseSort(file1, file2)
    WishFile	**file1;
    WishFile	**file2;
{
    return (-strcmp((*file1)->name, (*file2)->name));
}

static int
AtimeCompare(attrs1, attrs2)
    struct	stat	*attrs1;
    struct	stat	*attrs2;
{
    if (attrs1->st_atime > attrs2->st_atime) {
	return -1;
    }
    if (attrs1->st_atime< attrs2->st_atime) {
	return 1;
    }
#ifdef NOTDEF
    /*
     * What to do about this?  Sprite gives microseconds as well as
     * seconds...
     */
    if (attrs1->st_atime.microseconds > attrs2->st_atime.microseconds) {
	return -1;
    }
    if (attrs1->st_atime.microseconds < attrs2->st_atime.microseconds) {
	return 1;
    }
#endif /* NOTDEF */
    return 0;
}

static int
FileAtimeSort(file1, file2)
    WishFile	**file1;
    WishFile	**file2;
{
    return AtimeCompare((*file1)->attrPtr, (*file2)->attrPtr);
}

static int
FileAtimeReverseSort(file1, file2)
    WishFile	**file1;
    WishFile	**file2;
{
    return (-AtimeCompare((*file1)->attrPtr, (*file2)->attrPtr));
}

static	int
AtimeSort(d1, d2)
    struct	direct	**d1, **d2;
{
    struct	stat	attrs1;
    struct	stat	attrs2;

    if (lstat((*d1)->d_name, &attrs1) != 0) {
	sprintf(wishErrorMsg, "Couldn't get attributes for %s.",
		(*d1)->d_name);
	/* Is panic the right thing? */
	Sx_Panic(wishDisplay, wishErrorMsg);
    }
    if (lstat((*d2)->d_name, &attrs2) != 0) {
	sprintf(wishErrorMsg, "Couldn't get attributes for %s.",
		(*d2)->d_name);
	/* Is panic the right thing? */
	Sx_Panic(wishDisplay, wishErrorMsg);
    }
    return AtimeCompare(&attrs1, &attrs2);
 
}

#ifdef NOTDEF
static int
CtimeCompare(attrs1, attrs2)
    struct	stat	*attrs1;
    struct	stat	*attrs2;
{
    if (attrs1->createTime.seconds > attrs2->createTime.seconds) {
	return -1;
    }
    if (attrs1->createTime.seconds < attrs2->createTime.seconds) {
	return 1;
    }
    if (attrs1->createTime.microseconds > attrs2->createTime.microseconds) {
	return -1;
    }
    if (attrs1->createTime.microseconds < attrs2->createTime.microseconds) {
	return 1;
    }
    return 0;
}

static int
FileCtimeSort(file1, file2)
    WishFile	**file1;
    WishFile	**file2;
{
    return CtimeCompare((*file1)->attrPtr, (*file2)->attrPtr);
}

static int
FileCtimeReverseSort(file1, file2)
    WishFile	**file1;
    WishFile	**file2;
{
    return (-CtimeCompare((*file1)->attrPtr, (*file2)->attrPtr));
}

static	int
CtimeSort(d1, d2)
    struct	direct	**d1, **d2;
{
    struct	stat	attrs1;
    struct	stat	attrs2;

    if (lstat((*d1)->d_name, &attrs1) != 0) {
	sprintf(wishErrorMsg, "Couldn't get attributes for %s.",
		(*d1)->d_name);
	/* Is panic the right thing? */
	Sx_Panic(wishDisplay, wishErrorMsg);
    }
    if (lstat((*d2)->d_name, &attrs2) != 0) {
	sprintf(wishErrorMsg, "Couldn't get attributes for %s.",
		(*d2)->d_name);
	/* Is panic the right thing? */
	Sx_Panic(wishDisplay, wishErrorMsg);
    }
    return CtimeCompare(&attrs1, &attrs2);
}
#endif /* NOTDEF */

static int
MtimeCompare(attrs1, attrs2)
    struct	stat	*attrs1;
    struct	stat	*attrs2;
{
    if (attrs1->st_mtime > attrs2->st_mtime) {
	return -1;
    }
    if (attrs1->st_mtime < attrs2->st_mtime) {
	return 1;
    }
#ifdef NOTDEF
    if (attrs1->dataModifyTime.microseconds >
	    attrs2->dataModifyTime.microseconds) {
	return -1;
    }
    if (attrs1->dataModifyTime.microseconds <
	    attrs2->dataModifyTime.microseconds) {
	return 1;
    }
#endif /* NOTDEF */
    return 0;
}

static int
FileMtimeSort(file1, file2)
    WishFile	**file1;
    WishFile	**file2;
{
    return MtimeCompare((*file1)->attrPtr, (*file2)->attrPtr);
}

static int
FileMtimeReverseSort(file1, file2)
    WishFile	**file1;
    WishFile	**file2;
{
    return (-MtimeCompare((*file1)->attrPtr, (*file2)->attrPtr));
}

static	int
MtimeSort(d1, d2)
    struct	direct	**d1, **d2;
{
    struct	stat	attrs1;
    struct	stat	attrs2;

    if (lstat((*d1)->d_name, &attrs1) != 0) {
	sprintf(wishErrorMsg, "Couldn't get attributes for %s.",
		(*d1)->d_name);
	/* Is panic the right thing? */
	Sx_Panic(wishDisplay, wishErrorMsg);
    }
    if (lstat((*d2)->d_name, &attrs2) != 0) {
	sprintf(wishErrorMsg, "Couldn't get attributes for %s.",
		(*d2)->d_name);
	/* Is panic the right thing? */
	Sx_Panic(wishDisplay, wishErrorMsg);
    }
    return MtimeCompare(&attrs1, &attrs2);
}

static int
DtimeCompare(attrs1, attrs2)
    struct	stat	*attrs1;
    struct	stat	*attrs2;
{
    if (attrs1->st_ctime > attrs2->st_ctime) {
	return -1;
    }
    if (attrs1->st_ctime < attrs2->st_ctime) {
	return 1;
    }
#ifdef NOTDEF
    if (attrs1->st_ctime.microseconds > attrs2->st_ctime.microseconds) {
	return -1;
    }
    if (attrs1->st_ctime.microseconds < attrs2->st_ctime.microseconds) {
	return 1;
    }
#endif /* NOTDEF */
    return 0;
}

static int
FileDtimeSort(file1, file2)
    WishFile	**file1;
    WishFile	**file2;
{
    return DtimeCompare((*file1)->attrPtr, (*file2)->attrPtr);
}

static int
FileDtimeReverseSort(file1, file2)
    WishFile	**file1;
    WishFile	**file2;
{
    return (-DtimeCompare((*file1)->attrPtr, (*file2)->attrPtr));
}

static	int
DtimeSort(d1, d2)
    struct	direct	**d1, **d2;
{
    struct	stat	attrs1;
    struct	stat	attrs2;

    if (lstat((*d1)->d_name, &attrs1) != 0) {
	sprintf(wishErrorMsg, "Couldn't get attributes for %s.",
		(*d1)->d_name);
	/* Is panic the right thing? */
	Sx_Panic(wishDisplay, wishErrorMsg);
    }
    if (lstat((*d2)->d_name, &attrs2) != 0) {
	sprintf(wishErrorMsg, "Couldn't get attributes for %s.",
		(*d2)->d_name);
	/* Is panic the right thing? */
	Sx_Panic(wishDisplay, wishErrorMsg);
    }
    return DtimeCompare(&attrs1, &attrs2);
}

static int
SizeCompare(attrs1, attrs2)
    struct	stat	*attrs1;
    struct	stat	*attrs2;
{
    if (attrs1->st_size > attrs2->st_size) {
	return 1;
    }
    if (attrs1->st_size < attrs2->st_size) {
	return -1;
    }
    return 0;
}

static int
FileSizeSort(file1, file2)
    WishFile	**file1;
    WishFile	**file2;
{
    return SizeCompare((*file1)->attrPtr, (*file2)->attrPtr);
}

static int
FileSizeReverseSort(file1, file2)
    WishFile	**file1;
    WishFile	**file2;
{
    return (-SizeCompare((*file1)->attrPtr, (*file2)->attrPtr));
}

static	int
SizeSort(d1, d2)
    struct	direct	**d1, **d2;
{
    struct	stat	attrs1;
    struct	stat	attrs2;

    if (lstat((*d1)->d_name, &attrs1) != 0) {
	sprintf(wishErrorMsg, "Couldn't get attributes for %s.",
		(*d1)->d_name);
	/* Is panic the right thing? */
	Sx_Panic(wishDisplay, wishErrorMsg);
    }
    if (lstat((*d2)->d_name, &attrs2) != 0) {
	sprintf(wishErrorMsg, "Couldn't get attributes for %s.",
		(*d2)->d_name);
	/* Is panic the right thing? */
	Sx_Panic(wishDisplay, wishErrorMsg);
    }
    return SizeCompare(&attrs1, &attrs2);
}


/*
 *----------------------------------------------------------------------
 *
 * WishGetCompareProc --
 *
 *	Figure out what the comparison routine is and return it in
 *	the compareProc parameter.
 *	If fileProcP is true, return a function that takes pointers
 *	to WishFiles.  Otherwise it takes struct	direct's.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */
void
WishGetCompareProc(aWindow, compareProcPtr, fileProcP)
    WishWindow	*aWindow;
    int	(**compareProcPtr)();
    Boolean		fileProcP;	/* which comp func type */
{
    if (aWindow->sortingInstructions == 0) {
	aWindow->sortingInstructions = WISH_ALPHA_SORT;	/* default */
    }
    if (aWindow->sortingInstructions & WISH_REVERSE_SORT) {
	if (aWindow->sortingInstructions & WISH_ALPHA_SORT) {
	    if (fileProcP) {
		*compareProcPtr = FileAlphaReverseSort;
	    } else {
		*compareProcPtr = AlphaReverse;
	    }
	} else if (aWindow->sortingInstructions & WISH_ATIME_SORT) {
	    if (fileProcP) {
		*compareProcPtr = FileAtimeReverseSort;
	    } else {
		*compareProcPtr = AtimeReverse;
	    }
#ifdef NOTDEF
	} else if (aWindow->sortingInstructions & WISH_CTIME_SORT) {
	    if (fileProcP) {
		*compareProcPtr = FileCtimeReverseSort;
	    } else {
		*compareProcPtr = CtimeReverse;
	    }
#endif NOTDEF
	} else if (aWindow->sortingInstructions & WISH_MTIME_SORT) {
	    if (fileProcP) {
		*compareProcPtr = FileMtimeReverseSort;
	    } else {
		*compareProcPtr = MtimeReverse;
	    }
	} else if (aWindow->sortingInstructions & WISH_DTIME_SORT) {
	    if (fileProcP) {
		*compareProcPtr = FileDtimeReverseSort;
	    } else {
		*compareProcPtr = DtimeReverse;
	    }
	} else if (aWindow->sortingInstructions & WISH_SIZE_SORT) {
	    if (fileProcP) {
		*compareProcPtr = FileSizeReverseSort;
	    } else {
		*compareProcPtr = SizeReverse;
	    }
	}
    } else {
	if (aWindow->sortingInstructions & WISH_ALPHA_SORT) {
	    if (fileProcP) {
		*compareProcPtr = FileAlphaSort;
	    } else {
		*compareProcPtr = AlphaForwards;
	    }
	} else if (aWindow->sortingInstructions & WISH_ATIME_SORT) {
	    if (fileProcP) {
		*compareProcPtr = FileAtimeSort;
	    } else {
		*compareProcPtr = AtimeForwards;
	    }
#ifdef NOTDEF
	} else if (aWindow->sortingInstructions & WISH_CTIME_SORT) {
	    if (fileProcP) {
		*compareProcPtr = FileCtimeSort;
	    } else {
		*compareProcPtr = CtimeForwards;
	    }
#endif /* NOTDEF */
	} else if (aWindow->sortingInstructions & WISH_MTIME_SORT) {
	    if (fileProcP) {
		*compareProcPtr = FileMtimeSort;
	    } else {
		*compareProcPtr = MtimeForwards;
	    }
	} else if (aWindow->sortingInstructions & WISH_DTIME_SORT) {
	    if (fileProcP) {
		*compareProcPtr = FileDtimeSort;
	    } else {
		*compareProcPtr = DtimeForwards;
	    }
	} else if (aWindow->sortingInstructions & WISH_SIZE_SORT) {
	    if (fileProcP) {
		*compareProcPtr = FileSizeSort;
	    } else {
		*compareProcPtr = SizeForwards;
	    }
	}
    }

    return;
}
