[comp.sources.x] v04i015: xdir -- Directory Browser, Part02/02

argv@island.uu.net (Dan Heller) (06/04/89)

Submitted-by: Erik M. van der Poel <erik@sra.co.jp>
Posting-number: Volume 4, Issue 15
Archive-name: xdir/part02

#!/bin/sh
# to extract, remove the header and type "sh filename"
if `test ! -s ./Dir.c`
then
echo "writing ./Dir.c"
cat > ./Dir.c << '\End\Of\File\'
#ifndef lint
static char rcsid[] = "$Header: Dir.c,v 1.3 89/05/29 15:03:50 erik Exp $";
#endif

/*
 * Copyright 1989 Software Research Associates, Inc., Tokyo, Japan
 *
 * 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 Software Research Associates not be used
 * in advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission.  Software Research Associates
 * makes no representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied warranty.
 *
 * SOFTWARE RESEARCH ASSOCIATES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
 * IN NO EVENT SHALL SOFTWARE RESEARCH ASSOCIATES BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 *
 * Author: Erik M. van der Poel
 *         Software Research Associates, Inc., Tokyo, Japan
 */

#include <stdio.h>

#ifdef SEL_FILE_IGNORE_CASE
#include <ctype.h>
#endif /* def SEL_FILE_IGNORE_CASE */

#include "SFinternal.h"

#ifdef SYSV
#include <dirent.h>
#else /* def SYSV */
#include <sys/dir.h>
#endif /* def SYSV */

#include <sys/stat.h>

#ifdef SYSV
extern void qsort();
#endif /* def SYSV */

#ifdef SEL_FILE_IGNORE_CASE
int
SFcompareEntries(p, q)
	SFEntry	*p;
	SFEntry	*q;
{
	register char	*r, *s;
	register char	c1, c2;

	r = p->real;
	s = q->real;

	c1 = *r++;
	if (islower(c1)) {
		c1 = toupper(c1);
	}
	c2 = *s++;
	if (islower(c2)) {
		c2 = toupper(c2);
	}

	while (c1 == c2) {
		if (!c1) {
			return strcmp(p->real, q->real);
		}
		c1 = *r++;
		if (islower(c1)) {
			c1 = toupper(c1);
		}
		c2 = *s++;
		if (islower(c2)) {
			c2 = toupper(c2);
		}
	}

	return c1 - c2;
}
#else /* def SEL_FILE_IGNORE_CASE */
int
SFcompareEntries(p, q)
	SFEntry	*p;
	SFEntry	*q;
{
	return strcmp(p->real, q->real);
}
#endif /* def SEL_FILE_IGNORE_CASE */

int
SFgetDir(dir)
	SFDir	*dir;
{
	SFEntry		*result = NULL;
	int		alloc = 0;
	int		i;
	DIR		*dirp;

#ifdef SYSV
	struct dirent	*dp;
#else /* def SYSV */
	struct direct	*dp;
#endif /* def SYSV */

	char		*str;
	int		len;
	int		maxChars;
	struct stat	statBuf;

	maxChars = strlen(dir->dir) - 1;

	dir->entries = NULL;
	dir->nEntries = 0;
	dir->nChars = 0;

	result = NULL;
	i = 0;

	dirp = opendir(".");
	if (!dirp) {
		return 1;
	}

	(void) fstat(dirp->dd_fd, &statBuf);
	dir->st_mtime = statBuf.st_mtime;

	(void) readdir(dirp);	/* throw away "." */

#ifndef S_IFLNK
	(void) readdir(dirp);	/* throw away ".." */
#endif /* ndef S_IFLNK */

	while (dp = readdir(dirp)) {
		if (i >= alloc) {
			alloc = 2 * (alloc + 1);
			result = (SFEntry *) XtRealloc((char *) result,
				(unsigned) (alloc * sizeof(SFEntry)));
		}
		result[i].statDone = 0;
		str = dp->d_name;
		len = strlen(str);
		result[i].real = XtMalloc((unsigned) (len + 2));
		(void) strcat(strcpy(result[i].real, str), " ");
		if (len > maxChars) {
			maxChars = len;
		}
		result[i].shown = result[i].real;
		i++;
	}

#ifdef SYSV
	qsort((char *) result, (unsigned) i, sizeof(SFEntry), SFcompareEntries);
#else /* def SYSV */
	qsort((char *) result, i, sizeof(SFEntry), SFcompareEntries);
#endif /* def SYSV */

	dir->entries = result;
	dir->nEntries = i;
	dir->nChars = maxChars + 1;

	closedir(dirp);

	return 0;
}
\End\Of\File\
else
  echo "will not over write ./Dir.c"
fi
if `test ! -s ./Draw.c`
then
echo "writing ./Draw.c"
cat > ./Draw.c << '\End\Of\File\'
#ifndef lint
static char rcsid[] = "$Header: Draw.c,v 1.6 89/05/29 15:04:26 erik Exp $";
#endif

/*
 * Copyright 1989 Software Research Associates, Inc., Tokyo, Japan
 *
 * 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 Software Research Associates not be used
 * in advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission.  Software Research Associates
 * makes no representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied warranty.
 *
 * SOFTWARE RESEARCH ASSOCIATES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
 * IN NO EVENT SHALL SOFTWARE RESEARCH ASSOCIATES BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 *
 * Author: Erik M. van der Poel
 *         Software Research Associates, Inc., Tokyo, Japan
 */

#include <stdio.h>
#include "SFinternal.h"
#include <sys/stat.h>
#include <X11/StringDefs.h>

#if defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR)
#include <Xw/Xw.h>
#include <Xw/ScrollBar.h>
#else /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */
#include <X11/Scroll.h>
#endif /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */

#define SF_DEFAULT_FONT "9x15"

#define ABS(x) (((x) < 0) ? (-(x)) : (x))

typedef struct {
	char *fontname;
} TextData, *textPtr;

int SFcharWidth, SFcharAscent, SFcharHeight;

int SFcurrentInvert[3] = { -1, -1, -1 };

static GC SFlineGC, SFscrollGC, SFinvertGC, SFtextGC;

static XtResource textResources[] = {
	{XtNfont, XtCFont, XtRString, sizeof (char *),
		XtOffset(textPtr, fontname), XtRString, SF_DEFAULT_FONT},
};

static XFontStruct *SFfont;

static int SFcurrentListY;

static XtIntervalId SFscrollTimerId;

SFinitFont()
{
	TextData	*data;

	data = XtNew(TextData);

	XtGetApplicationResources(selFileForm, (caddr_t) data, textResources,
		XtNumber(textResources), (Arg *) NULL, 0);

	SFfont = XLoadQueryFont(SFdisplay, data->fontname);
	if (!SFfont) {
		SFfont = XLoadQueryFont(SFdisplay, SF_DEFAULT_FONT);
		if (!SFfont) {
			char	sbuf[256];

			(void) sprintf(sbuf, "XsraSelFile: can't get font %s",
				SF_DEFAULT_FONT);
			XtAppError(SFapp, sbuf);
		}
	}

	SFcharWidth = (SFfont->max_bounds.width + SFfont->min_bounds.width) / 2;
	SFcharAscent = SFfont->max_bounds.ascent;
	SFcharHeight = SFcharAscent + SFfont->max_bounds.descent;
}

SFcreateGC()
{
	XGCValues	gcValues;
	XRectangle	rectangles[1];

	gcValues.foreground = SFfore;

	SFlineGC = XtGetGC(
		selFileLists[0],
		(XtGCMask)
			GCForeground		|
			0,
		&gcValues
	);

	SFscrollGC = XtGetGC(
		selFileLists[0],
		(XtGCMask)
			0,
		&gcValues
	);

	gcValues.function = GXinvert;
	gcValues.plane_mask = (SFfore ^ SFback);

	SFinvertGC = XtGetGC(
		selFileLists[0],
		(XtGCMask)
			GCFunction		|
			GCPlaneMask		|
			0,
		&gcValues
	);

	gcValues.foreground = SFfore;
	gcValues.background = SFback;
	gcValues.font = SFfont->fid;

	SFtextGC = XCreateGC(
		SFdisplay,
		XtWindow(selFileLists[0]),
		(u_long)
			GCForeground		|
			GCBackground		|
			GCFont			|
			0,
		&gcValues
	);

	rectangles[0].x = SFlineToTextH + SFbesideText;
	rectangles[0].y = 0;
	rectangles[0].width = SFcharsPerEntry * SFcharWidth;
	rectangles[0].height = SFupperY + 1;

	XSetClipRectangles(
		SFdisplay,
		SFtextGC,
		0,
		0,
		rectangles,
		1,
		Unsorted
	);
}

SFclearList(n, doScroll)
	int	n;
	int	doScroll;
{
	SFDir	*dir;

#if defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR)
	int	i;
	Arg	arglist[20];
#endif /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */

	SFcurrentInvert[n] = -1;

	XClearWindow(SFdisplay, XtWindow(selFileLists[n]));

	XDrawSegments(SFdisplay, XtWindow(selFileLists[n]), SFlineGC, SFsegs,
		2);

	if (doScroll) {
		dir = &(SFdirs[SFdirPtr + n]);

#if defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR)
		i = 0;
		if (SFdirPtr + n < SFdirEnd) {
			XtSetArg(arglist[i], XtNsliderMax, dir->nEntries);
									i++;
			XtSetArg(arglist[i], XtNsliderExtent,
				dir->nEntries < SFlistSize ? dir->nEntries :
				SFlistSize);				i++;
			XtSetArg(arglist[i], XtNsliderOrigin, dir->vOrigin);
									i++;
		} else {
			XtSetArg(arglist[i], XtNsliderMax, SFlistSize);	i++;
			XtSetArg(arglist[i], XtNsliderExtent, SFlistSize);
									i++;
			XtSetArg(arglist[i], XtNsliderOrigin, 0);	i++;
		}
		XtSetValues(selFileVScrolls[n], arglist, i);

		i = 0;
		if (SFdirPtr + n < SFdirEnd) {
			XtSetArg(arglist[i], XtNsliderMax, dir->nChars);i++;
			XtSetArg(arglist[i], XtNsliderExtent,
				dir->nChars < SFcharsPerEntry ? dir->nChars :
				SFcharsPerEntry);			i++;
			XtSetArg(arglist[i], XtNsliderOrigin, dir->hOrigin);
									i++;
		} else {
			XtSetArg(arglist[i], XtNsliderMax, SFcharsPerEntry);
									i++;
			XtSetArg(arglist[i], XtNsliderExtent, SFcharsPerEntry);
									i++;
			XtSetArg(arglist[i], XtNsliderOrigin, 0);	i++;
		}
		XtSetValues(selFileHScrolls[n], arglist, i);
#else /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */
		if (SFdirPtr + n < SFdirEnd) {
			XtScrollBarSetThumb(
				selFileVScrolls[n],
				(float) (((double) dir->vOrigin) /
					dir->nEntries),
				(float) (((double) ((dir->nEntries < SFlistSize)
					? dir->nEntries : SFlistSize)) /
					dir->nEntries)
			);
			XtScrollBarSetThumb(
				selFileHScrolls[n],
				(float) (((double) dir->hOrigin) / dir->nChars),
				(float) (((double) ((dir->nChars <
					SFcharsPerEntry) ? dir->nChars :
					SFcharsPerEntry)) / dir->nChars)
			);
		} else {
			XtScrollBarSetThumb(selFileVScrolls[n], (float) 0.0,
				(float) 1.0);
			XtScrollBarSetThumb(selFileHScrolls[n], (float) 0.0,
				(float) 1.0);
		}
#endif /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */

	}
}

static
SFdeleteEntry(dir, entry)
	SFDir	*dir;
	SFEntry	*entry;
{
	register SFEntry	*e;
	register SFEntry	*end;
	int			n;

#if defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR)

	int			i;
	Arg			arglist[20];

#endif /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */

	XtFree(entry->real);

	end = &(dir->entries[dir->nEntries - 1]);

	for (e = entry; e < end; e++) {
		*e = *(e + 1);
	}

	dir->nEntries--;

	n = dir - &(SFdirs[SFdirPtr]);
	if ((n < 0) || (n > 2)) {
		return;
	}

#if defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR)

	i = 0;
	XtSetArg(arglist[i], XtNsliderMax, dir->nEntries);		i++;
	XtSetArg(arglist[i], XtNsliderExtent, dir->nEntries < SFlistSize ?
		dir->nEntries : SFlistSize);				i++;
	XtSetValues(selFileVScrolls[n], arglist, i);

#else /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */

	XtScrollBarSetThumb(
		selFileVScrolls[n],
		(float) (((double) dir->vOrigin) / dir->nEntries),
		(float) (((double) ((dir->nEntries < SFlistSize) ?
			dir->nEntries : SFlistSize)) / dir->nEntries)
	);

#endif /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */

}

static
SFwriteStatChar(name, last, statBuf)
	char		*name;
	int		last;
	struct stat	*statBuf;
{
	switch (statBuf->st_mode & S_IFMT) {
	case S_IFDIR:
		name[last] = '/';
		break;
	case S_IFREG:
		if (statBuf->st_mode & 0111) {
			name[last] = '*';
		} else {
			name[last] = ' ';
		}
		break;

#ifdef S_IFSOCK
	case S_IFSOCK:
		name[last] = '=';
		break;
#endif /* def S_IFSOCK */

	default:
		name[last] = ' ';
		break;
	}
}

static int
SFstatAndCheck(dir, entry)
	SFDir	*dir;
	SFEntry	*entry;
{
	struct stat	statBuf;
	char		save;
	int		last;

	/*
	 * must be restored before returning
	 */
	save = *(dir->path);
	*(dir->path) = 0;

	if (!SFchdir(SFcurrentPath)) {
		last = strlen(entry->real) - 1;
		entry->real[last] = 0;
		entry->statDone = 1;
		if (!stat(entry->real, &statBuf)) {
			if (SFfunc) {
				char *shown;

				shown = NULL;
				if (SFfunc(entry->real, &shown, &statBuf)) {
					if (shown) {
						int len;

						len = strlen(shown);
						entry->shown = XtMalloc(
							(unsigned) (len + 2)
						);
						(void) strcpy(entry->shown,
							shown);
						SFwriteStatChar(
							entry->shown,
							len,
							&statBuf
						);
						entry->shown[len + 1] = 0;
					}
				} else {
					SFdeleteEntry(dir, entry);

					*(dir->path) = save;
					return 1;
				}
			}
			SFwriteStatChar(entry->real, last, &statBuf);
		} else {
			entry->real[last] = ' ';
		}
	}

	*(dir->path) = save;
	return 0;
}

static
SFdrawStrings(w, dir, from, to)
	register Window	w;
	register SFDir	*dir;
	register int	from;
	register int	to;
{
	register int		i;
	register SFEntry	*entry;
	int			x;

	x = SFtextX - dir->hOrigin * SFcharWidth;

	if (dir->vOrigin + to >= dir->nEntries) {
		to = dir->nEntries - dir->vOrigin - 1;
	}
	for (i = from; i <= to; i++) {
		entry = &(dir->entries[dir->vOrigin + i]);
		if (!(entry->statDone)) {
			if (SFstatAndCheck(dir, entry)) {
				if (dir->vOrigin + to >= dir->nEntries) {
					to = dir->nEntries - dir->vOrigin - 1;
				}
				i--;
				continue;
			}
		}
		XDrawImageString(
			SFdisplay,
			w,
			SFtextGC,
			x,
			SFtextYoffset + i * SFentryHeight,
			entry->shown,
			strlen(entry->shown)
		);
		if (dir->vOrigin + i == dir->beginSelection) {
			XDrawLine(
				SFdisplay,
				w,
				SFlineGC,
				SFlineToTextH + 1,
				SFlowerY + i * SFentryHeight,
				SFlineToTextH + SFentryWidth - 2,
				SFlowerY + i * SFentryHeight
			);
		}
		if (
			(dir->vOrigin + i >= dir->beginSelection) &&
			(dir->vOrigin + i <= dir->endSelection)
		) {
			SFcompletionSegs[0].y1 = SFcompletionSegs[1].y1 =
				SFlowerY + i * SFentryHeight;
			SFcompletionSegs[0].y2 = SFcompletionSegs[1].y2 =
				SFlowerY + (i + 1) * SFentryHeight - 1;
			XDrawSegments(
				SFdisplay,
				w,
				SFlineGC,
				SFcompletionSegs,
				2
			);
		}
		if (dir->vOrigin + i == dir->endSelection) {
			XDrawLine(
				SFdisplay,
				w,
				SFlineGC,
				SFlineToTextH + 1,
				SFlowerY + (i + 1) * SFentryHeight - 1,
				SFlineToTextH + SFentryWidth - 2,
				SFlowerY + (i + 1) * SFentryHeight - 1
			);
		}
	}
}

SFdrawList(n, doScroll)
	int	n;
	int	doScroll;
{
	SFDir	*dir;
	Window	w;

	SFclearList(n, doScroll);

	if (SFdirPtr + n < SFdirEnd) {
		dir = &(SFdirs[SFdirPtr + n]);
		w = XtWindow(selFileLists[n]);
		XDrawImageString(
			SFdisplay,
			w,
			SFtextGC,
			SFtextX - dir->hOrigin * SFcharWidth,
			SFlineToTextV + SFaboveAndBelowText + SFcharAscent,
			dir->dir,
			strlen(dir->dir)
		);
		SFdrawStrings(w, dir, 0, SFlistSize - 1);
	}
}

SFdrawLists(doScroll)
	int	doScroll;
{
	int	i;

	for (i = 0; i < 3; i++) {
		SFdrawList(i, doScroll);
	}
}

static
SFinvertEntry(n)
	register int	n;
{
	XFillRectangle(
		SFdisplay,
		XtWindow(selFileLists[n]),
		SFinvertGC,
		SFlineToTextH,
		SFcurrentInvert[n] * SFentryHeight + SFlowerY,
		SFentryWidth,
		SFentryHeight
	);
}

static unsigned long
SFscrollTimerInterval()
{
	static int	maxVal = 200;
	static int	varyDist = 50;
	static int	minDist = 50;
	int		t;
	int		dist;

	if (SFcurrentListY < SFlowerY) {
		dist = SFlowerY - SFcurrentListY;
	} else if (SFcurrentListY > SFupperY) {
		dist = SFcurrentListY - SFupperY;
	} else {
		return (unsigned long) 1;
	}

	t = maxVal - ((maxVal / varyDist) * (dist - minDist));

	if (t < 1) {
		t = 1;
	}

	if (t > maxVal) {
		t = maxVal;
	}

	return (unsigned long) t;
}

static
SFscrollTimer(n)
	int	n;
{
	SFDir	*dir;
	int	save;

#if defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR)
	int	i;
	Arg	arglist[20];
#endif /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */

	dir = &(SFdirs[SFdirPtr + n]);
	save = dir->vOrigin;

	if (SFcurrentListY < SFlowerY) {
		if (dir->vOrigin > 0) {
			SFvSliderMovedCallback(selFileVScrolls[n], n,
				dir->vOrigin - 1);
		}
	} else if (SFcurrentListY > SFupperY) {
		if (dir->vOrigin < dir->nEntries - SFlistSize) {
			SFvSliderMovedCallback(selFileVScrolls[n], n,
				dir->vOrigin + 1);
		}
	}

	if (dir->vOrigin != save) {

#if defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR)
		i = 0;
		XtSetArg(arglist[i], XtNsliderOrigin, dir->vOrigin);	i++;
		XtSetValues(selFileVScrolls[n], arglist, i);
#else /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */
		XtScrollBarSetThumb(
			selFileVScrolls[n],
			(float) (((double) dir->vOrigin) / dir->nEntries),
			(float) (((double) ((dir->nEntries < SFlistSize) ?
				dir->nEntries : SFlistSize)) / dir->nEntries)
		);
#endif /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */

	}

	if (SFbuttonPressed) {
		SFscrollTimerId = XtAppAddTimeOut(SFapp,
			SFscrollTimerInterval(), SFscrollTimer, (caddr_t) n);
	}
}

static int
SFnewInvertEntry(n, event)
	register int		n;
	register XMotionEvent	*event;
{
	register int	x, y;
	register int	new;
	static int	SFscrollTimerAdded = 0;

	x = event->x;
	y = event->y;

	if (SFdirPtr + n >= SFdirEnd) {
		return -1;
	} else if (
		(x >= 0)	&& (x <= SFupperX) &&
		(y >= SFlowerY)	&& (y <= SFupperY)
	) {
		register SFDir *dir = &(SFdirs[SFdirPtr + n]);

		if (SFscrollTimerAdded) {
			SFscrollTimerAdded = 0;
			XtRemoveTimeOut(SFscrollTimerId);
		}

		new = (y - SFlowerY) / SFentryHeight;
		if (dir->vOrigin + new >= dir->nEntries) {
			return -1;
		}
		return new;
	} else {
		if (SFbuttonPressed) {
			SFcurrentListY = y;
			if (!SFscrollTimerAdded) {
				SFscrollTimerAdded = 1;
				SFscrollTimerId = XtAppAddTimeOut(SFapp,
					SFscrollTimerInterval(), SFscrollTimer,
					(caddr_t) n);
			}
		}

		return -1;
	}
}

/* ARGSUSED */
void
SFenterList(w, n, event)
	Widget				w;
	register int			n;
	register XEnterWindowEvent	*event;
{
	register int	new;

	/* sanity */
	if (SFcurrentInvert[n] != -1) {
		SFinvertEntry(n);
		SFcurrentInvert[n] = -1;
	}

	new = SFnewInvertEntry(n, (XMotionEvent *) event);
	if (new != -1) {
		SFcurrentInvert[n] = new;
		SFinvertEntry(n);
	}
}

/* ARGSUSED */
void
SFleaveList(w, n, event)
	Widget		w;
	register int	n;
	XEvent		*event;
{
	if (SFcurrentInvert[n] != -1) {
		SFinvertEntry(n);
		SFcurrentInvert[n] = -1;
	}
}

/* ARGSUSED */
void
SFmotionList(w, n, event)
	Widget			w;
	register int		n;
	register XMotionEvent	*event;
{
	register int	new;

	new = SFnewInvertEntry(n, event);

	if (new != SFcurrentInvert[n]) {
		if (SFcurrentInvert[n] != -1) {
			SFinvertEntry(n);
		}
		SFcurrentInvert[n] = new;
		if (new != -1) {
			SFinvertEntry(n);
		}
	}
}

/* ARGSUSED */

#if defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR)
#else /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */
SFvFloatSliderMovedCallback(w, n, fnew)
	Widget	w;
	int	n;
	float	*fnew;
{
	int	new;

	new = (*fnew) * SFdirs[SFdirPtr + n].nEntries;

	SFvSliderMovedCallback(w, n, new);
}
#endif /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */

/* ARGSUSED */
SFvSliderMovedCallback(w, n, new)
	Widget	w;
	int	n;
	int	new;
{
	int		old;
	register Window	win;
	SFDir		*dir;

	dir = &(SFdirs[SFdirPtr + n]);

	old = dir->vOrigin;
	dir->vOrigin = new;

#if defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR)
#else /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */
	if (old == new) {
		return;
	}
#endif /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */

	win = XtWindow(selFileLists[n]);

	if (ABS(new - old) < SFlistSize) {
		if (new > old) {
			XCopyArea(
				SFdisplay,
				win,
				win,
				SFscrollGC,
				SFlineToTextH,
				SFlowerY + (new - old) * SFentryHeight,
				SFentryWidth + SFlineToTextH,
				(SFlistSize - (new - old)) * SFentryHeight,
				SFlineToTextH,
				SFlowerY
			);
			XClearArea(
				SFdisplay,
				win,
				SFlineToTextH,
				SFlowerY + (SFlistSize - (new - old)) *
					SFentryHeight,
				SFentryWidth + SFlineToTextH,
				(new - old) * SFentryHeight,
				False
			);
			SFdrawStrings(win, dir, SFlistSize - (new - old),
				SFlistSize - 1);
		} else {
			XCopyArea(
				SFdisplay,
				win,
				win,
				SFscrollGC,
				SFlineToTextH,
				SFlowerY,
				SFentryWidth + SFlineToTextH,
				(SFlistSize - (old - new)) * SFentryHeight,
				SFlineToTextH,
				SFlowerY + (old - new) * SFentryHeight
			);
			XClearArea(
				SFdisplay,
				win,
				SFlineToTextH,
				SFlowerY,
				SFentryWidth + SFlineToTextH,
				(old - new) * SFentryHeight,
				False
			);
			SFdrawStrings(win, dir, 0, old - new);
		}
	} else {
		XClearArea(
			SFdisplay,
			win,
			SFlineToTextH,
			SFlowerY,
			SFentryWidth + SFlineToTextH,
			SFlistSize * SFentryHeight,
			False
		);
		SFdrawStrings(win, dir, 0, SFlistSize - 1);
	}
}

/* ARGSUSED */

#if defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR)
SFvAreaSelectedCallback(w, n, new)
	Widget	w;
	int	n;
	int	new;
#else /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */
SFvAreaSelectedCallback(w, n, pnew)
	Widget	w;
	int	n;
	int	pnew;
#endif /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */

{
	SFDir	*dir;

#if defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR)
	int	i;
	Arg	arglist[20];
#else /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */
	int	new;
#endif /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */

	dir = &(SFdirs[SFdirPtr + n]);

#if defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR)
	new -= (SFlistSize / 2);
#else /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */
	new = dir->vOrigin +
		(((double) pnew) / SFvScrollHeight) * dir->nEntries;
#endif /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */

	if (new > dir->nEntries - SFlistSize) {
		new = dir->nEntries - SFlistSize;
	}

	if (new < 0) {
		new = 0;
	}

#if defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR)
	i = 0;
	XtSetArg(arglist[i], XtNsliderOrigin, new);			i++;
	XtSetValues(w, arglist, i);
#else /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */
	{
		float	f;

		f = ((double) new) / dir->nEntries;
		XtScrollBarSetThumb(
			w,
			f,
			(float) (((double) ((dir->nEntries < SFlistSize) ?
				dir->nEntries : SFlistSize)) / dir->nEntries)
		);
	}
#endif /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */

	SFvSliderMovedCallback(w, n, new);
}

/* ARGSUSED */
SFhSliderMovedCallback(w, n, new)
	Widget	w;
	int	n;

#if defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR)
	int	new;
#else /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */
	float	*new;
#endif /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */

{

#if defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR)
	SFdirs[SFdirPtr + n].hOrigin = new;
#else /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */
	{
		SFDir	*dir;
		int	save;

		dir = &(SFdirs[SFdirPtr + n]);
		save = dir->hOrigin;
		dir->hOrigin = (*new) * dir->nChars;
		if (dir->hOrigin == save) {
			return;
		}
	}
#endif /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */

	SFdrawList(n, SF_DO_NOT_SCROLL);
}

/* ARGSUSED */

#if defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR)
SFhAreaSelectedCallback(w, n, new)
	Widget	w;
	int	n;
	int	new;
#else /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */
SFhAreaSelectedCallback(w, n, pnew)
	Widget	w;
	int	n;
	int	pnew;
#endif /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */

{
	SFDir	*dir;

#if defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR)
	int	i;
	Arg	arglist[20];
#else /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */
	int	new;
#endif /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */

	dir = &(SFdirs[SFdirPtr + n]);

#if defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR)
	new -= (SFcharsPerEntry / 2);
#else /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */
	new = dir->hOrigin +
		(((double) pnew) / SFhScrollWidth) * dir->nChars;
#endif /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */

	if (new > dir->nChars - SFcharsPerEntry) {
		new = dir->nChars - SFcharsPerEntry;
	}

	if (new < 0) {
		new = 0;
	}

#if defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR)
	i = 0;
	XtSetArg(arglist[i], XtNsliderOrigin, new);			i++;
	XtSetValues(w, arglist, i);

	SFhSliderMovedCallback(w, n, new);
#else /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */
	{
		float	f;

		f = ((double) new) / dir->nChars;
		XtScrollBarSetThumb(
			w,
			f,
			(float) (((double) ((dir->nChars < SFcharsPerEntry) ?
				dir->nChars : SFcharsPerEntry)) / dir->nChars)
		);

		SFhSliderMovedCallback(w, n, &f);
	}
#endif /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */

}

/* ARGSUSED */
SFpathSliderMovedCallback(w, client_data, new)
	Widget	w;
	caddr_t	client_data;

#if defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR)
	int	new;
#else /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */
	float	*new;
#endif /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */

{
	SFDir		*dir;
	int		n;

#if defined(SEL_FILE_XW) || defined(SEL_FILE_XWTEXTEDIT)
	XwTextPosition	pos;
#else /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWTEXTEDIT) */
	XtTextPosition	pos;
#endif /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWTEXTEDIT) */

#if defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR)
	SFdirPtr = new;
#else /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */
	{
		int	SFdirPtrSave;

		SFdirPtrSave = SFdirPtr;
		SFdirPtr = (*new) * SFdirEnd;
		if (SFdirPtr == SFdirPtrSave) {
			return;
		}
	}
#endif /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */

	SFdrawLists(SF_DO_SCROLL);

	n = 2;
	while (SFdirPtr + n >= SFdirEnd) {
		n--;
	}

	dir = &(SFdirs[SFdirPtr + n]);

	pos = dir->path - SFcurrentPath;

	if (!strncmp(SFcurrentPath, SFstartDir, strlen(SFstartDir))) {
		pos -= strlen(SFstartDir);
		if (pos < 0) {
			pos = 0;
		}
	}

#if defined(SEL_FILE_XW) || defined(SEL_FILE_XWTEXTEDIT)
	XwTextSetInsertPos(selFileField, pos);
#else /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWTEXTEDIT) */
	XtTextSetInsertionPoint(selFileField, pos);
#endif /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWTEXTEDIT) */

}

/* ARGSUSED */

#if defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR)
SFpathAreaSelectedCallback(w, client_data, new)
	Widget	w;
	caddr_t	client_data;
	int	new;
#else /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */
SFpathAreaSelectedCallback(w, client_data, pnew)
	Widget	w;
	caddr_t	client_data;
	int	pnew;
#endif /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */

{

#if defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR)
	int	i;
	Arg	arglist[20];
#else /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */
	int	new;
#endif /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */

#if defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR)
	new -= (3 / 2);
#else /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */
	new = SFdirPtr + (((double) pnew) / SFpathScrollWidth) * SFdirEnd;
#endif /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */

	if (new > SFdirEnd - 3) {
		new = SFdirEnd - 3;
	}

	if (new < 0) {
		new = 0;
	}

#if defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR)
	i = 0;
	XtSetArg(arglist[i], XtNsliderOrigin, new);			i++;
	XtSetValues(w, arglist, i);

	SFpathSliderMovedCallback(w, client_data, new);
#else /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */
	{
		float	f;

		f = ((double) new) / SFdirEnd;
		XtScrollBarSetThumb(
			w,
			f,
			(float) (((double) ((SFdirEnd < 3) ? SFdirEnd : 3)) /
				SFdirEnd)
		);

		SFpathSliderMovedCallback(w, (caddr_t) NULL, &f);
	}
#endif /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */

}

Boolean
SFworkProc()
{
	register SFDir		*dir;
	register SFEntry	*entry;

	for (dir = &(SFdirs[SFdirEnd - 1]); dir >= SFdirs; dir--) {
		for (
			entry = &(dir->entries[dir->nEntries - 1]);
			entry >= dir->entries;
			entry--
		) {
			if (!(entry->statDone)) {
				(void) SFstatAndCheck(dir, entry);
				return False;
			}
		}
	}

	SFworkProcAdded = 0;

	return True;
}
\End\Of\File\
else
  echo "will not over write ./Draw.c"
fi
if `test ! -s ./FILES`
then
echo "writing ./FILES"
cat > ./FILES << '\End\Of\File\'
Dir.c
Draw.c
FILES
Imakefile
Path.c
README
SFinternal.h
SelFile.c
SelFile.man
TODO
callback.c
xdir.c
\End\Of\File\
else
  echo "will not over write ./FILES"
fi
if `test ! -s ./Imakefile`
then
echo "writing ./Imakefile"
cat > ./Imakefile << '\End\Of\File\'
#
# This file describes how to build xdir, a simple application that uses the
# XsraSelFile file selection dialog package.
#
# The program is linked with Athena widgets (Xaw) by default. It can be linked
# with Hewlett-Packard widgets (Xw) by defining one or more of the defines below
# and adding Xw to the list of libraries. Defining `SEL_FILE_XW' is equivalent
# to defining all of them.
#
# -DSEL_FILE_XWFORM
# -DSEL_FILE_XWPUSHBUTTON
# -DSEL_FILE_XWSCROLLBAR
# -DSEL_FILE_XWSTATICTEXT
# -DSEL_FILE_XWTEXTEDIT
#
# -DSEL_FILE_XW
#
#        DEFINES = -DSEL_FILE_XW

           SRCS = xdir.c SelFile.c Dir.c Path.c Draw.c
           OBJS = xdir.o SelFile.o Dir.o Path.o Draw.o

          XWLIB = $(CONTRIBSRC)/widgets/Xhp/lib/libXw.a
LOCAL_LIBRARIES = $(XAWLIB) $(XMULIB) $(XTOOLLIB) $(XLIB)
#LOCAL_LIBRARIES = $(XWLIB) $(XAWLIB) $(XMULIB) $(XTOOLLIB) $(XLIB)

ComplexProgramTarget(xdir)

NormalLintTarget($(SRCS))
\End\Of\File\
else
  echo "will not over write ./Imakefile"
fi
if `test ! -s ./Path.c`
then
echo "writing ./Path.c"
cat > ./Path.c << '\End\Of\File\'
#ifndef lint
static char rcsid[] = "$Header: Path.c,v 1.5 89/05/29 15:05:30 erik Exp $";
#endif

/*
 * Copyright 1989 Software Research Associates, Inc., Tokyo, Japan
 *
 * 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 Software Research Associates not be used
 * in advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission.  Software Research Associates
 * makes no representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied warranty.
 *
 * SOFTWARE RESEARCH ASSOCIATES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
 * IN NO EVENT SHALL SOFTWARE RESEARCH ASSOCIATES BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 *
 * Author: Erik M. van der Poel
 *         Software Research Associates, Inc., Tokyo, Japan
 */

#include <stdio.h>

#ifdef SEL_FILE_IGNORE_CASE
#include <ctype.h>
#endif /* def SEL_FILE_IGNORE_CASE */

#include <pwd.h>
#include "SFinternal.h"
#include <sys/stat.h>

#if defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR)
#include <Xw/Xw.h>
#include <Xw/ScrollBar.h>
#else /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */
#include <X11/Scroll.h>
#endif /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */

#ifdef SYSV
extern unsigned short getuid();
extern void qsort();
#endif /* def SYSV */

typedef struct {
	char	*name;
	char	*dir;
} SFLogin;

SFDir *SFdirs = NULL;

int SFdirEnd;

int SFdirPtr;

int SFbuttonPressed = 0;

static int SFdoNotTouchDirPtr = 0;

static int SFdoNotTouchVorigin = 0;

static SFDir SFrootDir, SFhomeDir;

static SFLogin *SFlogins;

static int SFtwiddle = 0;

int
SFchdir(path)
	char	*path;
{
	int	result;

	result = 0;

	if (strcmp(path, SFcurrentDir)) {
		result = chdir(path);
		if (!result) {
			(void) strcpy(SFcurrentDir, path);
		}
	}

	return result;
}

static
SFfree(i)
	int	i;
{
	register SFDir	*dir;
	register int	j;

	dir = &(SFdirs[i]);

	for (j = dir->nEntries - 1; j >= 0; j--) {
		if (dir->entries[j].shown != dir->entries[j].real) {
			XtFree(dir->entries[j].shown);
		}
		XtFree(dir->entries[j].real);
	}

	XtFree((char *) dir->entries);

	XtFree(dir->dir);

	dir->dir = NULL;
}

static
SFunreadableDir(dir)
	SFDir	*dir;
{
	char	*cannotOpen = "<cannot open> ";

	dir->entries = (SFEntry *) XtMalloc(sizeof(SFEntry));
	dir->entries[0].statDone = 1;
	dir->entries[0].real = XtMalloc((unsigned) (strlen(cannotOpen) + 1));
	(void) strcpy(dir->entries[0].real, cannotOpen);
	dir->entries[0].shown = dir->entries[0].real;
	dir->nEntries = 1;
	dir->nChars = strlen(cannotOpen);
}

#ifdef SEL_FILE_IGNORE_CASE
static
SFstrncmp(p, q, n)
	register char	*p, *q;
	register int	n;
{
	register char	c1, c2;
	char		*psave, *qsave;
	int		nsave;

	psave = p;
	qsave = q;
	nsave = n;

	c1 = *p++;
	if (islower(c1)) {
		c1 = toupper(c1);
	}
	c2 = *q++;
	if (islower(c2)) {
		c2 = toupper(c2);
	}

	while ((--n >= 0) && (c1 == c2)) {
		if (!c1) {
			return strncmp(psave, qsave, nsave);
		}
		c1 = *p++;
		if (islower(c1)) {
			c1 = toupper(c1);
		}
		c2 = *q++;
		if (islower(c2)) {
			c2 = toupper(c2);
		}
	}

	if (n < 0) {
		return strncmp(psave, qsave, nsave);
	}

	return c1 - c2;
}
#endif /* def SEL_FILE_IGNORE_CASE */

static
SFreplaceText(dir, str)
	SFDir	*dir;
	char	*str;
{
	int	len;

	*(dir->path) = 0;
	len = strlen(str);
	if (str[len - 1] == '/') {
		(void) strcat(SFcurrentPath, str);
	} else {
		(void) strncat(SFcurrentPath, str, len - 1);
	}
	if (strncmp(SFcurrentPath, SFstartDir, strlen(SFstartDir))) {
		SFsetText(SFcurrentPath);
	} else {
		SFsetText(&(SFcurrentPath[strlen(SFstartDir)]));
	}

	SFtextChanged();
}

static int
SFexpand(str)
	char	*str;
{
	int	len;
	int	cmp;
	char	*name, *growing;
	SFDir	*dir;
	SFEntry	*entry, *max;

	len = strlen(str);

	dir = &(SFdirs[SFdirEnd - 1]);

	if (dir->beginSelection == -1) {
		str = strcpy(XtMalloc((unsigned) (strlen(str) + 1)), str);
		SFreplaceText(dir, str);
		XtFree(str);
		return;
	} else if (dir->beginSelection == dir->endSelection) {
		SFreplaceText(dir, dir->entries[dir->beginSelection].shown);
		return;
	}

	max = &(dir->entries[dir->endSelection + 1]);

	name = dir->entries[dir->beginSelection].shown;
	(void) strcpy((growing = XtMalloc((unsigned) (strlen(name) + 1))),
		name);

	cmp = 0;
	while (!cmp) {
		entry = &(dir->entries[dir->beginSelection]);
		while (entry < max) {
			if (cmp = strncmp(growing, entry->shown, len)) {
				break;
			}
			entry++;
		}
		len++;
	}

	/*
	 * SFreplaceText() expects filename
	 */
	growing[len - 2] = ' ';

	growing[len - 1] = 0;
	SFreplaceText(dir, growing);
	XtFree(growing);
}

static int
SFfindFile(dir, str)
	SFDir		*dir;
	register char	*str;
{
	register int	i, last, max;
	register char	*name, save;
	SFEntry		*entries;
	int		len;
	int		begin, end;
	int		result;

	len = strlen(str);

	if (str[len - 1] == ' ') {
		SFexpand(str);
		return 1;
	} else if (str[len - 1] == '/') {
		len--;
	}

	max = dir->nEntries;

	entries = dir->entries;

	i = 0;
	while (i < max) {
		name = entries[i].shown;
		last = strlen(name) - 1;
		save = name[last];
		name[last] = 0;

#ifdef SEL_FILE_IGNORE_CASE
		result = SFstrncmp(str, name, len);
#else /* def SEL_FILE_IGNORE_CASE */
		result = strncmp(str, name, len);
#endif /* def SEL_FILE_IGNORE_CASE */

		name[last] = save;
		if (result <= 0) {
			break;
		}
		i++;
	}
	begin = i;
	while (i < max) {
		name = entries[i].shown;
		last = strlen(name) - 1;
		save = name[last];
		name[last] = 0;

#ifdef SEL_FILE_IGNORE_CASE
		result = SFstrncmp(str, name, len);
#else /* def SEL_FILE_IGNORE_CASE */
		result = strncmp(str, name, len);
#endif /* def SEL_FILE_IGNORE_CASE */

		name[last] = save;
		if (result) {
			break;
		}
		i++;
	}
	end = i;

	if (begin != end) {
		if (
			(dir->beginSelection != begin) ||
			(dir->endSelection != end - 1)
		) {
			dir->changed = 1;
			dir->beginSelection = begin;
			if (str[strlen(str) - 1] == '/') {
				dir->endSelection = begin;
			} else {
				dir->endSelection = end - 1;
			}
		}
	} else {
		if (dir->beginSelection != -1) {
			dir->changed = 1;
			dir->beginSelection = -1;
			dir->endSelection = -1;
		}
	}

	if (
		SFdoNotTouchVorigin ||
		((begin > dir->vOrigin) && (end < dir->vOrigin + SFlistSize))
	) {
		SFdoNotTouchVorigin = 0;
		return 0;
	}

	i = begin - 1;
	if (i > max - SFlistSize) {
		i = max - SFlistSize;
	}
	if (i < 0) {
		i = 0;
	}

	if (dir->vOrigin != i) {
		dir->vOrigin = i;
		dir->changed = 1;
	}

	return 0;
}

static
SFunselect()
{
	SFDir	*dir;

	dir = &(SFdirs[SFdirEnd - 1]);
	if (dir->beginSelection != -1) {
		dir->changed = 1;
	}
	dir->beginSelection = -1;
	dir->endSelection = -1;
}

static int
SFcompareLogins(p, q)
	SFLogin	*p, *q;
{
	return strcmp(p->name, q->name);
}

static
SFgetHomeDirs()
{
	struct passwd	*pw;
	struct stat	statBuf;
	int		alloc;
	int		i;
	SFEntry		*entries;
	int		len;
	int		maxChars;

	alloc = 0;
	i = 0;

	maxChars = -1;

	if (pw = getpwuid((int) getuid())) {
		if (
			(!stat(pw->pw_dir, &statBuf)) &&
			((statBuf.st_mode & S_IFMT) == S_IFDIR)
		) {
			alloc = 1;
			i = 1;
			entries = (SFEntry *) XtMalloc(sizeof(SFEntry));
			SFlogins = (SFLogin *) XtMalloc(sizeof(SFLogin));
			entries[0].real = XtMalloc(2);
			(void) strcpy(entries[0].real, "~");
			entries[0].shown = entries[0].real;
			entries[0].statDone = 1;
			SFlogins[0].name = "";
			SFlogins[0].dir = XtMalloc((unsigned)
				(strlen(pw->pw_dir) + 1));
			(void) strcpy(SFlogins[0].dir, pw->pw_dir);
		}
	}

	(void) setpwent();

	while ((pw = getpwent()) && (*(pw->pw_name))) {
		if (
			(!stat(pw->pw_dir, &statBuf)) &&
			((statBuf.st_mode & S_IFMT) == S_IFDIR)
		) {
			if (i >= alloc) {
				alloc *= 2;
				entries = (SFEntry *) XtRealloc(
					(char *) entries,
					(unsigned) (alloc * sizeof(SFEntry))
				);
				SFlogins = (SFLogin *) XtRealloc(
					(char *) SFlogins,
					(unsigned) (alloc * sizeof(SFLogin))
				);
			}
			len = strlen(pw->pw_name);
			entries[i].real = XtMalloc((unsigned) (len + 3));
			(void) strcat(strcpy(entries[i].real, "~"),
				pw->pw_name);
			entries[i].shown = entries[i].real;
			entries[i].statDone = 1;
			if (len > maxChars) {
				maxChars = len;
			}
			SFlogins[i].name = XtMalloc((unsigned)
				(strlen(pw->pw_name) + 1));
			(void) strcpy(SFlogins[i].name, pw->pw_name);
			SFlogins[i].dir = XtMalloc((unsigned)
				(strlen(pw->pw_dir) + 1));
			(void) strcpy(SFlogins[i].dir, pw->pw_dir);
			i++;
		}
	}

	SFhomeDir.dir			= XtMalloc(1)	;
	SFhomeDir.dir[0]		= 0		;
	SFhomeDir.path			= SFcurrentPath	;
	SFhomeDir.entries		= entries	;
	SFhomeDir.nEntries		= i		;
	SFhomeDir.vOrigin		= 0		;	/* :-) */
	SFhomeDir.nChars		= maxChars + 2	;
	SFhomeDir.hOrigin		= 0		;
	SFhomeDir.changed		= 1		;
	SFhomeDir.beginSelection	= -1		;
	SFhomeDir.endSelection		= -1		;

#ifdef SYSV
	qsort((char *) entries, (unsigned)i, sizeof(SFEntry), SFcompareEntries);
	qsort((char *) SFlogins, (unsigned)i, sizeof(SFLogin), SFcompareLogins);
#else /* def SYSV */
	qsort((char *) entries, i, sizeof(SFEntry), SFcompareEntries);
	qsort((char *) SFlogins, i, sizeof(SFLogin), SFcompareLogins);
#endif /* def SYSV */

	for (i--; i >= 0; i--) {
		(void) strcat(entries[i].real, "/");
	}
}

static int
SFfindHomeDir(begin, end)
	char	*begin, *end;
{
	char	save;
	char	*theRest;
	int	i;

	save = *end;
	*end = 0;

	for (i = SFhomeDir.nEntries - 1; i >= 0; i--) {
		if (!strcmp(SFhomeDir.entries[i].real, begin)) {
			*end = save;
			theRest = XtMalloc((unsigned) (strlen(end) + 1));
			(void) strcpy(theRest, end);
			(void) strcat(strcat(strcpy(SFcurrentPath,
				SFlogins[i].dir), "/"), theRest);
			XtFree(theRest);
			SFsetText(SFcurrentPath);
			SFtextChanged();
			return 1;
		}
	}

	*end = save;

	return 0;
}

SFupdatePath()
{
	static int	alloc;
	static int	wasTwiddle = 0;
	char		*begin, *end;
	int		i, j;
	int		len;
	int		prevChange;
	int		SFdirPtrSave, SFdirEndSave;
	SFDir		*dir;

#if defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR)
	Arg		arglist[20];
#endif /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */

	if (!SFdirs) {
		SFdirs = (SFDir *) XtMalloc((alloc = 10) * sizeof(SFDir));
		dir = &(SFdirs[0]);
		dir->dir = XtMalloc(2);
		(void) strcpy(dir->dir, "/");
		(void) SFchdir("/");
		(void) SFgetDir(dir);
		for (j = 1; j < alloc; j++) {
			SFdirs[j].dir = NULL;
		}
		dir->path = SFcurrentPath + 1;
		dir->vOrigin = 0;
		dir->hOrigin = 0;
		dir->changed = 1;
		dir->beginSelection = -1;
		dir->endSelection = -1;
		SFrootDir = *dir;
		SFgetHomeDirs();
	}

	SFdirEndSave = SFdirEnd;
	SFdirEnd = 1;

	SFdirPtrSave = SFdirPtr;
	SFdirPtr = 0;

	begin = NULL;

	if (SFcurrentPath[0] == '~') {
		if (!SFtwiddle) {
			SFtwiddle = 1;
			dir = &(SFdirs[0]);
			*dir = SFhomeDir;
			dir->changed = 1;
		}
		end = SFcurrentPath;
		SFdoNotTouchDirPtr = 1;
		wasTwiddle = 1;
	} else {
		if (SFtwiddle) {
			SFtwiddle = 0;
			dir = &(SFdirs[0]);
			*dir = SFrootDir;
			dir->changed = 1;
		}
		end = SFcurrentPath + 1;
	}

	i = 0;

	prevChange = 0;

	while (*end) {
		while (*end++ == '/') {
			;
		}
		end--;
		begin = end;
		while ((*end) && (*end++ != '/')) {
			;
		}
		if ((end - SFcurrentPath <= SFtextPos) && (*(end - 1) == '/')) {
			SFdirPtr = i - 1;
			if (SFdirPtr < 0) {
				SFdirPtr = 0;
			}
		}
		if (*begin) {
			if (*(end - 1) == '/') {
				char save = *end;

				if (SFtwiddle) {
					if (SFfindHomeDir(begin, end)) {
						return;
					}
				}
				*end = 0;
				i++;
				SFdirEnd++;
				if (i >= alloc) {
					SFdirs = (SFDir *) XtRealloc(
						(char *) SFdirs,
						(unsigned) ((alloc *= 2) *
							sizeof(SFDir))
					);
					for (j = alloc / 2; j < alloc; j++) {
						SFdirs[j].dir = NULL;
					}
				}
				dir = &(SFdirs[i]);
				if (
					(!(dir->dir)) ||
					prevChange ||
					strcmp(dir->dir, begin)
				) {
					if (dir->dir) {
						SFfree(i);
					}
					prevChange = 1;
					len = strlen(begin) + 1;
					dir->dir = XtMalloc((unsigned) len);
					(void) strcpy(dir->dir, begin);
					dir->path = end;
					dir->vOrigin = 0;
					dir->hOrigin = 0;
					dir->changed = 1;
					dir->beginSelection = -1;
					dir->endSelection = -1;
					(void) SFfindFile(dir - 1, begin);
					if (
						SFchdir(SFcurrentPath) ||
						SFgetDir(dir)
					) {
						SFunreadableDir(dir);
						break;
					}
				}
				*end = save;
				if (!save) {
					SFunselect();
				}
			} else {
				if (SFfindFile(&(SFdirs[SFdirEnd-1]), begin)) {
					return;
				}
			}
		} else {
			SFunselect();
		}
	}

	if ((end == SFcurrentPath + 1) && (!SFtwiddle)) {
		SFunselect();
	}

	for (i = SFdirEnd; i < alloc; i++) {
		if (SFdirs[i].dir) {
			SFfree(i);
		}
	}

	if (SFdoNotTouchDirPtr) {
		if (wasTwiddle) {
			wasTwiddle = 0;
			SFdirPtr = SFdirEnd - 2;
			if (SFdirPtr < 0) {
				SFdirPtr = 0;
			}
		} else {
			SFdirPtr = SFdirPtrSave;
		}
		SFdoNotTouchDirPtr = 0;
	}

	if ((SFdirPtr != SFdirPtrSave) || (SFdirEnd != SFdirEndSave)) {

#if defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR)
		i = 0;
		XtSetArg(arglist[i], XtNsliderMax, SFdirEnd + 3 - 1);	i++;
		XtSetArg(arglist[i], XtNsliderOrigin, SFdirPtr);	i++;
		XtSetValues(selFileHScroll, arglist, i);
#else /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */
		XtScrollBarSetThumb(
			selFileHScroll,
			(float) (((double) SFdirPtr) / SFdirEnd),
			(float) (((double) ((SFdirEnd < 3) ? SFdirEnd : 3)) /
				SFdirEnd)
		);
#endif /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWSCROLLBAR) */

	}

	if (SFdirPtr != SFdirPtrSave) {
		SFdrawLists(SF_DO_SCROLL);
	} else {
		for (i = 0; i < 3; i++) {
			if (SFdirPtr + i < SFdirEnd) {
				if (SFdirs[SFdirPtr + i].changed) {
					SFdirs[SFdirPtr + i].changed = 0;
					SFdrawList(i, SF_DO_SCROLL);
				}
			} else {
				SFclearList(i, SF_DO_SCROLL);
			}
		}
	}
}

SFsetText(path)
	char	*path;
{

#if defined(SEL_FILE_XW) || defined(SEL_FILE_XWTEXTEDIT)

#ifdef SEL_FILE_JAPANESE
	static wchar_t	*wstr = NULL;
	static int	alloc = 0;
	int		len;

	len = convEUCtoWS((unsigned char *) path, (wchar_t *) NULL);
	while (len + 1 > alloc) {
		alloc = 2 * (alloc + 1);
		wstr = (wchar_t *) XtRealloc((char *) wstr, (unsigned) (alloc *
			sizeof(wchar_t)));
	}
	(void) convEUCtoWS((unsigned char *) path, wstr);

	XwTextClearBuffer(selFileField);
	XwTextInsert(selFileField, wstr);
#else /* def SEL_FILE_JAPANESE */
	XwTextClearBuffer(selFileField);
	XwTextInsert(selFileField, path);
#endif /* def SEL_FILE_JAPANESE */

#else /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWTEXTEDIT) */
	{
		XtTextBlock	text;

		text.firstPos = 0;
		text.length = strlen(path);
		text.ptr = path;
		text.format = FMT8BIT;
		XtTextReplace(selFileField, 0, strlen(SFtextBuffer), &text);
	}
#endif /* defined(SEL_FILE_XW) || defined(SEL_FILE_XWTEXTEDIT) */

}

/* ARGSUSED */
void
SFbuttonPressList(w, n, event)
	Widget			w;
	int			n;
	XButtonPressedEvent	*event;
{
	SFbuttonPressed = 1;
}

/* ARGSUSED */
void
SFbuttonReleaseList(w, n, event)
	Widget			w;
	int			n;
	XButtonReleasedEvent	*event;
{
	SFDir	*dir;

	SFbuttonPressed = 0;

	if (SFcurrentInvert[n] != -1) {
		if (n < 2) {
			SFdoNotTouchDirPtr = 1;
		}
		SFdoNotTouchVorigin = 1;
		dir = &(SFdirs[SFdirPtr + n]);
		SFreplaceText(
			dir,
			dir->entries[dir->vOrigin + SFcurrentInvert[n]].shown
		);
		SFmotionList(w, n, event);
	}
}

static int
SFcheckDir(n, dir)
	int		n;
	SFDir		*dir;
{
	struct stat	statBuf;
	int		i;

	if (
		(!stat(".", &statBuf)) &&
		(statBuf.st_mtime != dir->st_mtime)
	) {

		/*
		 * If the pointer is currently in the window that we are about
		 * to update, we must warp it to prevent the user from
		 * accidentally selecting the wrong file.
		 */
		if (SFcurrentInvert[n] != -1) {
			XWarpPointer(
				SFdisplay,
				None,
				XtWindow(selFileLists[n]),
				0,
				0,
				0,
				0,
				0,
				0
			);
		}

		for (i = dir->nEntries - 1; i >= 0; i--) {
			if (dir->entries[i].shown != dir->entries[i].real) {
				XtFree(dir->entries[i].shown);
			}
			XtFree(dir->entries[i].real);
		}
		XtFree((char *) dir->entries);
		if (SFgetDir(dir)) {
			SFunreadableDir(dir);
		}
		if (dir->vOrigin > dir->nEntries - SFlistSize) {
			dir->vOrigin = dir->nEntries - SFlistSize;
		}
		if (dir->vOrigin < 0) {
			dir->vOrigin = 0;
		}
		if (dir->hOrigin > dir->nChars - SFcharsPerEntry) {
			dir->hOrigin = dir->nChars - SFcharsPerEntry;
		}
		if (dir->hOrigin < 0) {
			dir->hOrigin = 0;
		}
		dir->beginSelection = -1;
		dir->endSelection = -1;
		SFdoNotTouchVorigin = 1;
		if ((dir + 1)->dir) {
			(void) SFfindFile(dir, (dir + 1)->dir);
		} else {
			(void) SFfindFile(dir, dir->path);
		}
		return 1;
	}

	return 0;
}

static int
SFcheckFiles(dir)
	SFDir	*dir;
{
	int		from, to;
	int		result;
	char		old, new;
	int		i;
	char		*str;
	int		last;
	struct stat	statBuf;

	result = 0;

	from = dir->vOrigin;
	to = dir->vOrigin + SFlistSize;
	if (to > dir->nEntries) {
		to = dir->nEntries;
	}

	for (i = from; i < to; i++) {
		str = dir->entries[i].real;
		last = strlen(str) - 1;
		old = str[last];
		str[last] = 0;
		if (stat(str, &statBuf)) {
			new = ' ';
		} else {
			switch (statBuf.st_mode & S_IFMT) {
			case S_IFDIR:
				new = '/';
				break;
			case S_IFREG:
				if (statBuf.st_mode & 0111) {
					new = '*';
				} else {
					new = ' ';
				}
				break;

#ifdef S_IFSOCK
			case S_IFSOCK:
				new = '=';
				break;
#endif /* def S_IFSOCK */

			default:
				new = ' ';
				break;
			}
		}
		str[last] = new;
		if (new != old) {
			result = 1;
		}
	}

	return result;
}

SFdirModTimer()
{
	static int	n = -1;
	static int	f = 0;
	char		save;
	SFDir		*dir;

	if ((!SFtwiddle) && (SFdirPtr < SFdirEnd)) {
		n++;
		if ((n > 2) || (SFdirPtr + n >= SFdirEnd)) {
			n = 0;
			f++;
			if ((f > 2) || (SFdirPtr + f >= SFdirEnd)) {
				f = 0;
			}
		}
		dir = &(SFdirs[SFdirPtr + n]);
		save = *(dir->path);
		*(dir->path) = 0;
		if (SFchdir(SFcurrentPath)) {
			*(dir->path) = save;

			/*
			 * force a re-read
			 */
			*(dir->dir) = 0;

			SFupdatePath();
		} else {
			*(dir->path) = save;
			if (
				SFcheckDir(n, dir) ||
				((f == n) && SFcheckFiles(dir))
			) {
				SFdrawList(n, SF_DO_SCROLL);
			}
		}
	}

	SFdirModTimerId = XtAppAddTimeOut(SFapp, (unsigned long) 1000,
		SFdirModTimer, (caddr_t) NULL);
}
\End\Of\File\
else
  echo "will not over write ./Path.c"
fi
echo "Finished archive 2 of 2"
exit
--
Erik M. van der Poel                  erik@sra.co.jp             (Japan)
SRA, 1-1-1 Hirakawa-cho, Chiyoda-ku   erik%sra.co.jp@uunet.uu.net  (USA)
Tokyo 102 Japan. TEL +81-3-234-2692   erik%sra.co.jp@mcvax.uucp (Europe)