Interface XBoard to GhostView file-browser dialog
authorH.G. Muller <h.g.muller@hccnet.nl>
Sat, 30 Jan 2010 10:05:52 +0000 (11:05 +0100)
committerH.G. Muller <h.g.muller@hccnet.nl>
Sun, 7 Feb 2010 09:42:09 +0000 (10:42 +0100)
Note: The Ghostview dialog was using some deprecated variables for error
printing, and conditionally used getwd() on some systems that presumably
do not have getcwd(), but it did not assess the latter properly.

Makefile.am
dir.c [new file with mode: 0644]
draw.c [new file with mode: 0644]
path.c [new file with mode: 0644]
selfile.c [new file with mode: 0644]
selfile.h [new file with mode: 0644]
xboard.c
xstat.h [new file with mode: 0644]

index 3abd176..c921c3a 100644 (file)
@@ -26,6 +26,8 @@ xboard_SOURCES = backend.c backend.h backendz.h \
                 xgamelist.c xgamelist.h\
                 xhistory.c  xhistory.h \
                 xoptions.c \
+                selfile.c selfile.h \
+                draw.c path.c dir.c xstat.h \
                 $(ZPY)
 xboard_LDADD = -lm @XAW_LIBS@ @X_LIBS@ 
 
diff --git a/dir.c b/dir.c
new file mode 100644 (file)
index 0000000..102187e
--- /dev/null
+++ b/dir.c
@@ -0,0 +1,163 @@
+/*
+ * 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
+ *         erik@sra.co.jp
+ */
+
+#include <stdio.h>
+
+#ifdef SEL_FILE_IGNORE_CASE
+#include <ctype.h>
+#endif /* def SEL_FILE_IGNORE_CASE */
+
+#include "selfile.h"
+
+#if defined(SVR4) || defined(SYSV) || defined(USG) || defined(__osf__)
+#include <dirent.h>
+#else /* defined(SVR4) || defined(SYSV) || defined(USG) */
+#include <sys/dir.h>
+#define dirent direct
+#endif /* defined(SVR4) || defined(SYSV) || defined(USG) */
+
+#include <sys/stat.h>
+
+#if defined(SVR4) || defined(SYSV) || defined(USG)
+extern void qsort();
+#endif /* defined(SVR4) || defined(SYSV) || defined(USG) */
+
+#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;
+       struct dirent   *dp;
+       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) stat(".", &statBuf);
+       dir->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++;
+       }
+
+#if defined(SVR4) || defined(SYSV) || defined(USG)
+       qsort((char *) result, (unsigned) i, sizeof(SFEntry), SFcompareEntries);
+#else /* defined(SVR4) || defined(SYSV) || defined(USG) */
+       qsort((char *) result, i, sizeof(SFEntry), SFcompareEntries);
+#endif /* defined(SVR4) || defined(SYSV) || defined(USG) */
+
+       dir->entries = result;
+       dir->nEntries = i;
+       dir->nChars = maxChars + 1;
+
+       closedir(dirp);
+
+       return 0;
+}
diff --git a/draw.c b/draw.c
new file mode 100644 (file)
index 0000000..47efa17
--- /dev/null
+++ b/draw.c
@@ -0,0 +1,920 @@
+/*
+ * 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
+ *         erik@sra.co.jp
+ */
+
+#include <stdio.h>
+#include "selfile.h"
+#include "xstat.h"
+#include <X11/StringDefs.h>
+#include <X11/Xaw/Scrollbar.h>
+#include <X11/Xaw/Cardinals.h>
+
+#define SF_DEFAULT_FONT "9x15"
+
+#ifdef ABS
+#undef ABS
+#endif
+#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, (XtPointer) data, textResources,
+               XtNumber(textResources), (Arg *) NULL, ZERO);
+
+       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]),
+               (unsigned 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;
+
+       SFcurrentInvert[n] = -1;
+
+       XClearWindow(SFdisplay, XtWindow(selFileLists[n]));
+
+       XDrawSegments(SFdisplay, XtWindow(selFileLists[n]), SFlineGC, SFsegs,
+               2);
+
+       if (doScroll) {
+               dir = &(SFdirs[SFdirPtr + n]);
+
+               if ((SFdirPtr + n < SFdirEnd) && dir->nEntries && dir->nChars) {
+                       XawScrollbarSetThumb(
+                               selFileVScrolls[n],
+                               (float) (((double) dir->vOrigin) /
+                                       dir->nEntries),
+                               (float) (((double) ((dir->nEntries < SFlistSize)
+                                       ? dir->nEntries : SFlistSize)) /
+                                       dir->nEntries)
+                       );
+
+                       XawScrollbarSetThumb(
+                               selFileHScrolls[n],
+                               (float) (((double) dir->hOrigin) / dir->nChars),
+                               (float) (((double) ((dir->nChars <
+                                       SFcharsPerEntry) ? dir->nChars :
+                                       SFcharsPerEntry)) / dir->nChars)
+                       );
+               } else {
+                       XawScrollbarSetThumb(selFileVScrolls[n], (float) 0.0,
+                               (float) 1.0);
+                       XawScrollbarSetThumb(selFileHScrolls[n], (float) 0.0,
+                               (float) 1.0);
+               }
+       }
+}
+
+static
+SFdeleteEntry(dir, entry)
+       SFDir   *dir;
+       SFEntry *entry;
+{
+       register SFEntry        *e;
+       register SFEntry        *end;
+       int                     n;
+       int                     idx;
+
+       idx = entry - dir->entries;
+
+       if (idx < dir->beginSelection) {
+               dir->beginSelection--;
+       }
+       if (idx <= dir->endSelection) {
+               dir->endSelection--;
+       }
+       if (dir->beginSelection > dir->endSelection) {
+               dir->beginSelection = dir->endSelection = -1;
+       }
+
+       if (idx < dir->vOrigin) {
+               dir->vOrigin--;
+       }
+
+       XtFree(entry->real);
+
+       end = &(dir->entries[dir->nEntries - 1]);
+
+       for (e = entry; e < end; e++) {
+               *e = *(e + 1);
+       }
+
+       if (!(--dir->nEntries)) {
+               return;
+       }
+
+       n = dir - &(SFdirs[SFdirPtr]);
+       if ((n < 0) || (n > 2)) {
+               return;
+       }
+
+       XawScrollbarSetThumb(
+               selFileVScrolls[n],
+               (float) (((double) dir->vOrigin) / dir->nEntries),
+               (float) (((double) ((dir->nEntries < SFlistSize) ?
+                       dir->nEntries : SFlistSize)) / dir->nEntries)
+       );
+}
+
+static
+SFwriteStatChar(name, last, statBuf)
+       char            *name;
+       int             last;
+       struct stat     *statBuf;
+{
+       name[last] = SFstatChar(statBuf);
+}
+
+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))
+
+#ifdef S_IFLNK
+
+                    || (!lstat(entry->real, &statBuf))
+
+#endif /* ndef S_IFLNK */
+
+               ) {
+                       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 + (3-NR) + n < SFdirEnd) {
+               dir = &(SFdirs[SFdirPtr + n + (3-NR)]);
+               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 < NR; 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 void
+SFscrollTimer(p, id)
+       XtPointer       p;
+        XtIntervalId    *id;
+{
+       SFDir   *dir;
+       int     save;
+       int     n;
+
+        n = (int) p;
+
+       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 (dir->nEntries) {
+                   XawScrollbarSetThumb(
+                       selFileVScrolls[n],
+                       (float) (((double) dir->vOrigin) / dir->nEntries),
+                       (float) (((double) ((dir->nEntries < SFlistSize) ?
+                               dir->nEntries : SFlistSize)) / dir->nEntries)
+                   );
+               }
+       }
+
+       if (SFbuttonPressed) {
+               SFscrollTimerId = XtAppAddTimeOut(SFapp,
+                       SFscrollTimerInterval(), SFscrollTimer, (XtPointer) 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,
+                                       (XtPointer) 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 */
+void
+SFvFloatSliderMovedCallback(w, n, fnew)
+       Widget  w;
+       int     n;
+       float   *fnew;
+{
+       int     new;
+
+       new = (*fnew) * SFdirs[SFdirPtr + n].nEntries;
+
+       SFvSliderMovedCallback(w, n, new);
+}
+
+/* ARGSUSED */
+void
+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 (old == new) {
+               return;
+       }
+
+       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 */
+void
+SFvAreaSelectedCallback(w, n, pnew)
+       Widget  w;
+       int     n;
+       int     pnew;
+{
+       SFDir   *dir;
+       int     new;
+
+       dir = &(SFdirs[SFdirPtr + n]);
+
+       new = dir->vOrigin +
+               (((double) pnew) / SFvScrollHeight) * dir->nEntries;
+
+       if (new > dir->nEntries - SFlistSize) {
+               new = dir->nEntries - SFlistSize;
+       }
+
+       if (new < 0) {
+               new = 0;
+       }
+
+       if (dir->nEntries) {
+               float   f;
+
+               f = ((double) new) / dir->nEntries;
+
+               XawScrollbarSetThumb(
+                       w,
+                       f,
+                       (float) (((double) ((dir->nEntries < SFlistSize) ?
+                               dir->nEntries : SFlistSize)) / dir->nEntries)
+               );
+       }
+
+       SFvSliderMovedCallback(w, n, new);
+}
+
+/* ARGSUSED */
+void
+SFhSliderMovedCallback(w, n, new)
+       Widget  w;
+       int     n;
+       float   *new;
+{
+       SFDir   *dir;
+       int     save;
+
+       dir = &(SFdirs[SFdirPtr + n]);
+       save = dir->hOrigin;
+       dir->hOrigin = (*new) * dir->nChars;
+       if (dir->hOrigin == save) {
+               return;
+       }
+
+       SFdrawList(n, SF_DO_NOT_SCROLL);
+}
+
+/* ARGSUSED */
+void
+SFhAreaSelectedCallback(w, n, pnew)
+       Widget  w;
+       int     n;
+       int     pnew;
+{
+       SFDir   *dir;
+       int     new;
+
+       dir = &(SFdirs[SFdirPtr + n]);
+
+       new = dir->hOrigin +
+               (((double) pnew) / SFhScrollWidth) * dir->nChars;
+
+       if (new > dir->nChars - SFcharsPerEntry) {
+               new = dir->nChars - SFcharsPerEntry;
+       }
+
+       if (new < 0) {
+               new = 0;
+       }
+
+       if (dir->nChars) {
+               float   f;
+
+               f = ((double) new) / dir->nChars;
+
+               XawScrollbarSetThumb(
+                       w,
+                       f,
+                       (float) (((double) ((dir->nChars < SFcharsPerEntry) ?
+                               dir->nChars : SFcharsPerEntry)) / dir->nChars)
+               );
+
+               SFhSliderMovedCallback(w, n, &f);
+       }
+}
+
+/* ARGSUSED */
+void
+SFpathSliderMovedCallback(w, client_data, new)
+       Widget          w;
+       XtPointer       client_data;
+       float   *new;
+{
+       SFDir           *dir;
+       int             n;
+       XawTextPosition pos;
+       int     SFdirPtrSave;
+
+       SFdirPtrSave = SFdirPtr;
+       SFdirPtr = (*new) * SFdirEnd;
+       if (SFdirPtr == SFdirPtrSave) {
+               return;
+       }
+
+       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;
+               }
+       }
+
+       XawTextSetInsertionPoint(selFileField, pos);
+}
+
+/* ARGSUSED */
+
+void
+SFpathAreaSelectedCallback(w, client_data, pnew)
+       Widget          w;
+       XtPointer       client_data;
+       int             pnew;
+{
+       int     new;
+       float   f;
+
+       new = SFdirPtr + (((double) pnew) / SFpathScrollWidth) * SFdirEnd;
+
+       if (new > SFdirEnd - 3) {
+               new = SFdirEnd - 3;
+       }
+
+       if (new < 0) {
+               new = 0;
+       }
+
+       f = ((double) new) / SFdirEnd;
+
+       XawScrollbarSetThumb(
+               w,
+               f,
+               (float) (((double) ((SFdirEnd < 3) ? SFdirEnd : 3)) /
+                       SFdirEnd)
+       );
+
+       SFpathSliderMovedCallback(w, (XtPointer) NULL, &f);
+}
+
+Boolean
+SFworkProc()
+{
+       register SFDir          *dir;
+       register SFEntry        *entry;
+
+       for (dir = &(SFdirs[SFdirEnd - 1]); dir >= SFdirs; dir--) {
+               if (!(dir->nEntries)) {
+                       continue;
+               }
+               for (
+                       entry = &(dir->entries[dir->nEntries - 1]);
+                       entry >= dir->entries;
+                       entry--
+               ) {
+                       if (!(entry->statDone)) {
+                               (void) SFstatAndCheck(dir, entry);
+                               return False;
+                       }
+               }
+       }
+
+       SFworkProcAdded = 0;
+
+       return True;
+}
diff --git a/path.c b/path.c
new file mode 100644 (file)
index 0000000..6379e79
--- /dev/null
+++ b/path.c
@@ -0,0 +1,900 @@
+/*
+ * 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
+ *         erik@sra.co.jp
+ */
+
+#include <stdio.h>
+
+#ifdef SEL_FILE_IGNORE_CASE
+#include <ctype.h>
+#endif /* def SEL_FILE_IGNORE_CASE */
+
+#include <X11/Xos.h>
+#include <pwd.h>
+#include "selfile.h"
+#include "xstat.h"
+#include <X11/Xaw/Scrollbar.h>
+
+#if defined(SVR4) || defined(SYSV) || defined(USG)
+extern uid_t getuid();
+extern void qsort();
+#endif /* defined(SVR4) || defined(SYSV) || defined(USG) */
+
+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
+SFstrdup(s1, s2)
+       char    **s1;
+       char    *s2;
+{
+       *s1 = strcpy(XtMalloc((unsigned) (strlen(s2) + 1)), s2);
+}
+
+static
+SFunreadableDir(dir)
+       SFDir   *dir;
+{
+       char    *cannotOpen = "<cannot open> ";
+
+       dir->entries = (SFEntry *) XtMalloc(sizeof(SFEntry));
+       dir->entries[0].statDone = 1;
+       SFstrdup(&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 void
+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) {
+               SFstrdup(&str, 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;
+       SFstrdup(&growing, 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;
+       int             alloc;
+       int             i;
+       SFEntry         *entries = NULL;
+       int             len;
+       int             maxChars;
+
+       {
+                       alloc = 1;
+                       i = 1;
+                       entries = (SFEntry *) XtMalloc(sizeof(SFEntry));
+                       SFlogins = (SFLogin *) XtMalloc(sizeof(SFLogin));
+                       entries[0].real = XtMalloc(3);
+                       (void) strcpy(entries[0].real, "~");
+                       entries[0].shown = entries[0].real;
+                       entries[0].statDone = 1;
+                       SFlogins[0].name = "";
+                       pw = getpwuid((int) getuid());
+                       SFstrdup(&SFlogins[0].dir, pw ? pw->pw_dir : "/");
+                       maxChars = 0;
+       }
+
+       (void) setpwent();
+
+       while ((pw = getpwent()) && (*(pw->pw_name))) {
+                       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;
+                       }
+                       SFstrdup(&SFlogins[i].name, pw->pw_name);
+                       SFstrdup(&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            ;
+
+#if defined(SVR4) || defined(SYSV) || defined(USG)
+       qsort((char *) entries, (unsigned)i, sizeof(SFEntry), SFcompareEntries);
+       qsort((char *) SFlogins, (unsigned)i, sizeof(SFLogin), SFcompareLogins);
+#else /* defined(SVR4) || defined(SYSV) || defined(USG) */
+       qsort((char *) entries, i, sizeof(SFEntry), SFcompareEntries);
+       qsort((char *) SFlogins, i, sizeof(SFLogin), SFcompareLogins);
+#endif /* defined(SVR4) || defined(SYSV) || defined(USG) */
+
+       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;
+                       SFstrdup(&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             prevChange;
+       int             SFdirPtrSave, SFdirEndSave;
+       SFDir           *dir;
+
+       if (!SFdirs) {
+               SFdirs = (SFDir *) XtMalloc((alloc = 10) * sizeof(SFDir));
+               dir = &(SFdirs[0]);
+               SFstrdup(&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;
+               SFhomeDir.dir = NULL;
+       }
+
+       SFdirEndSave = SFdirEnd;
+       SFdirEnd = 1;
+
+       SFdirPtrSave = SFdirPtr;
+       SFdirPtr = 0;
+
+       begin = NULL;
+
+       if (SFcurrentPath[0] == '~') {
+               if (!SFtwiddle) {
+                       SFtwiddle = 1;
+                       dir = &(SFdirs[0]);
+                       SFrootDir = *dir;
+                       if (!SFhomeDir.dir) {
+                               SFgetHomeDirs();
+                       }
+                       *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;
+                                       SFstrdup(&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 - 1;
+                       if (SFdirPtr < 0) {
+                               SFdirPtr = 0;
+                       }
+               } else {
+                       SFdirPtr = SFdirPtrSave;
+               }
+               SFdoNotTouchDirPtr = 0;
+       }
+
+       if ((SFdirPtr != SFdirPtrSave) || (SFdirEnd != SFdirEndSave)) {
+               XawScrollbarSetThumb(
+                       selFileHScroll,
+                       (float) (((double) SFdirPtr) / SFdirEnd),
+                       (float) (((double) ((SFdirEnd < NR) ? SFdirEnd : NR)) /
+                               SFdirEnd)
+               );
+       }
+
+       if (SFdirPtr != SFdirPtrSave) {
+               SFdrawLists(SF_DO_SCROLL);
+       } else {
+               for (i = 0; i < NR; 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;
+{
+       XawTextBlock    text;
+
+       text.firstPos = 0;
+       text.length = strlen(path);
+       text.ptr = path;
+       text.format = FMT8BIT;
+
+       XawTextReplace(selFileField, 0, strlen(SFtextBuffer), &text);
+       XawTextSetInsertionPoint(selFileField, strlen(SFtextBuffer));
+}
+
+/* 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->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);
+               }
+
+               if (!SFworkProcAdded) {
+                       (void) XtAppAddWorkProc(SFapp, SFworkProc, NULL);
+                       SFworkProcAdded = 1;
+               }
+
+               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 {
+                       new = SFstatChar(&statBuf);
+               }
+               str[last] = new;
+               if (new != old) {
+                       result = 1;
+               }
+       }
+
+       return result;
+}
+
+void
+SFdirModTimer(cl, id)
+        XtPointer       cl;
+        XtIntervalId    *id;
+{
+       static int      n = -1;
+       static int      f = 0;
+       char            save;
+       SFDir           *dir;
+
+       if ((!SFtwiddle) && (SFdirPtr < SFdirEnd)) {
+               n++;
+               if ((n > NR-1) || (SFdirPtr + n >= SFdirEnd)) {
+                       n = 0;
+                       f++;
+                       if ((f > NR-1) || (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, (XtPointer) NULL);
+}
+
+/* Return a single character describing what kind of file STATBUF is.  */
+
+char
+SFstatChar (statBuf)
+       struct stat *statBuf;
+{
+       if (S_ISDIR (statBuf->st_mode)) {
+               return '/';
+       } else if (S_ISREG (statBuf->st_mode)) {
+         return S_ISXXX (statBuf->st_mode) ? '*' : ' ';
+#ifdef S_ISSOCK
+       } else if (S_ISSOCK (statBuf->st_mode)) {
+               return '=';
+#endif /* S_ISSOCK */
+       } else {
+               return ' ';
+       }
+}
diff --git a/selfile.c b/selfile.c
new file mode 100644 (file)
index 0000000..4298e30
--- /dev/null
+++ b/selfile.c
@@ -0,0 +1,753 @@
+/*
+ * 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
+ *         erik@sra.co.jp
+ */
+
+/*
+ * Author's address:
+ *
+ *     erik@sra.co.jp
+ *                                            OR
+ *     erik%sra.co.jp@uunet.uu.net
+ *                                            OR
+ *     erik%sra.co.jp@mcvax.uucp
+ *                                            OR
+ *     try junet instead of co.jp
+ *                                            OR
+ *     Erik M. van der Poel
+ *     Software Research Associates, Inc.
+ *     1-1-1 Hirakawa-cho, Chiyoda-ku
+ *     Tokyo 102 Japan. TEL +81-3-234-2692
+ */
+
+#include <stdio.h>
+#include <errno.h>
+/* BSD 4.3 errno.h does not declare errno */
+extern int errno;
+//extern int sys_nerr;
+//extern char *sys_errlist[]; // [HGM] this produced a compile error in Ubuntu 8.04
+
+#include <sys/param.h>
+#include <X11/cursorfont.h>
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Composite.h>
+#include <X11/Shell.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/Command.h>
+#include <X11/Xaw/Scrollbar.h>
+#include <X11/Xaw/Label.h>
+#include <X11/Xaw/Cardinals.h>
+
+#include "selfile.h"
+
+#ifndef MAXPATHLEN
+#define MAXPATHLEN 1024
+#endif /* ndef MAXPATHLEN */
+
+#if !defined(SVR4) && !defined(SYSV) && !defined(USG)
+extern char *getwd();
+#endif /* !defined(SVR4) && !defined(SYSV) && !defined(USG) */
+
+int SFstatus = SEL_FILE_NULL;
+
+char
+       SFstartDir[MAXPATHLEN],
+       SFcurrentPath[MAXPATHLEN],
+       SFcurrentDir[MAXPATHLEN];
+
+Widget
+       selFile,
+       selFileCancel,
+       selFileField,
+       selFileForm,
+       selFileHScroll,
+       selFileHScrolls[3],
+       selFileLists[3],
+       selFileOK,
+       selFilePrompt,
+       selFileVScrolls[3];
+
+Display *SFdisplay;
+
+Pixel SFfore, SFback;
+
+Atom   SFwmDeleteWindow;
+
+XSegment SFsegs[2], SFcompletionSegs[2];
+
+XawTextPosition SFtextPos;
+
+int SFupperX, SFlowerY, SFupperY;
+
+int SFtextX, SFtextYoffset;
+
+int SFentryWidth, SFentryHeight;
+
+int SFlineToTextH = 3;
+
+int SFlineToTextV = 3;
+
+int SFbesideText = 3;
+
+int SFaboveAndBelowText = 2;
+
+int SFcharsPerEntry = 15;
+
+int SFlistSize = 10;
+
+int SFworkProcAdded = 0;
+
+XtAppContext SFapp;
+
+int SFpathScrollWidth, SFvScrollHeight, SFhScrollWidth;
+
+char SFtextBuffer[MAXPATHLEN];
+
+XtIntervalId SFdirModTimerId;
+
+int (*SFfunc)();
+
+static char *oneLineTextEditTranslations = "\
+       <Key>Return:    redraw-display()\n\
+       Ctrl<Key>M:     redraw-display()\n\
+";
+
+/* ARGSUSED */
+static void
+SFexposeList(w, n, event, cont)
+       Widget          w;
+       XtPointer       n;
+        XEvent         *event;
+        Boolean         *cont;
+{
+       if ((event->type == NoExpose) || event->xexpose.count) {
+               return;
+       }
+
+       SFdrawList(n, SF_DO_NOT_SCROLL);
+}
+
+/* ARGSUSED */
+static void
+SFmodVerifyCallback(w, client_data, event, cont)
+       Widget                  w;
+       XtPointer               client_data;
+        XEvent                 *event;
+        Boolean                 *cont;
+{
+       char    buf[2];
+
+       if (
+               (XLookupString(&(event->xkey), buf, 2, NULL, NULL) == 1) &&
+               ((*buf) == '\r')
+       ) {
+               SFstatus = SEL_FILE_OK;
+       } else {
+               SFstatus = SEL_FILE_TEXT;
+       }
+}
+
+/* ARGSUSED */
+static void
+SFokCallback(w, cl, cd)
+       Widget  w;
+        XtPointer cl, cd;
+{
+       SFstatus = SEL_FILE_OK;
+}
+
+static XtCallbackRec SFokSelect[] = {
+       { SFokCallback, (XtPointer) NULL },
+       { NULL, (XtPointer) NULL },
+};
+
+/* ARGSUSED */
+static void
+SFcancelCallback(w, cl, cd)
+       Widget  w;
+        XtPointer cl, cd;
+{
+       SFstatus = SEL_FILE_CANCEL;
+}
+
+static XtCallbackRec SFcancelSelect[] = {
+       { SFcancelCallback, (XtPointer) NULL },
+       { NULL, (XtPointer) NULL },
+};
+
+/* ARGSUSED */
+static void
+SFdismissAction(w, event, params, num_params)
+       Widget  w;
+       XEvent *event;
+       String *params;
+       Cardinal *num_params;
+{
+       if (event->type == ClientMessage &&
+           event->xclient.data.l[0] != SFwmDeleteWindow) return;
+
+       SFstatus = SEL_FILE_CANCEL;
+}
+
+static char *wmDeleteWindowTranslation = "\
+       <Message>WM_PROTOCOLS:  SelFileDismiss()\n\
+";
+
+static XtActionsRec actions[] = {
+       {"SelFileDismiss",      SFdismissAction},
+};
+
+static void
+SFcreateWidgets(toplevel, prompt, ok, cancel)
+       Widget  toplevel;
+       char    *prompt;
+       char    *ok;
+       char    *cancel;
+{
+       Cardinal        i, n;
+       int             listWidth, listHeight;
+       int             listSpacing = 10;
+       int             scrollThickness = 15;
+       int             hScrollX, hScrollY;
+       int             vScrollX, vScrollY;
+       Cursor
+                       xtermCursor,
+                       sbRightArrowCursor,
+                       dotCursor;
+       Arg             arglist[20];
+
+       i = 0;
+       XtSetArg(arglist[i], XtNtransientFor, toplevel);                i++;
+
+       selFile = XtAppCreateShell("Browse", "SelFile",
+               transientShellWidgetClass, SFdisplay, arglist, i);
+
+       /* Add WM_DELETE_WINDOW protocol */
+       XtAppAddActions(XtWidgetToApplicationContext(selFile),
+               actions, XtNumber(actions));
+       XtOverrideTranslations(selFile,
+               XtParseTranslationTable(wmDeleteWindowTranslation));
+
+       i = 0;
+       XtSetArg(arglist[i], XtNdefaultDistance, 30);                   i++;
+       selFileForm = XtCreateManagedWidget("selFileForm",
+               formWidgetClass, selFile, arglist, i);
+
+       i = 0;
+       XtSetArg(arglist[i], XtNlabel, prompt);                         i++;
+       XtSetArg(arglist[i], XtNresizable, True);                       i++;
+       XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
+       XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
+       XtSetArg(arglist[i], XtNleft, XtChainLeft);                     i++;
+       XtSetArg(arglist[i], XtNright, XtChainLeft);                    i++;
+       XtSetArg(arglist[i], XtNborderWidth, 0);                        i++;
+       selFilePrompt = XtCreateManagedWidget("selFilePrompt",
+               labelWidgetClass, selFileForm, arglist, i);
+
+       i = 0;
+       XtSetArg(arglist[i], XtNforeground, &SFfore);                   i++;
+       XtSetArg(arglist[i], XtNbackground, &SFback);                   i++;
+       XtGetValues(selFilePrompt, arglist, i);
+
+       SFinitFont();
+
+       SFentryWidth = SFbesideText + SFcharsPerEntry * SFcharWidth +
+                       SFbesideText;
+       SFentryHeight = SFaboveAndBelowText + SFcharHeight +
+                       SFaboveAndBelowText;
+
+       listWidth = SFlineToTextH + SFentryWidth + SFlineToTextH + 1 +
+                       scrollThickness;
+       listHeight = SFlineToTextV + SFentryHeight + SFlineToTextV + 1 +
+                       SFlineToTextV + SFlistSize * SFentryHeight +
+                       SFlineToTextV + 1 + scrollThickness;
+
+       SFpathScrollWidth = NR * listWidth + (NR-1) * listSpacing + 4;
+
+       hScrollX = -1;
+       hScrollY = SFlineToTextV + SFentryHeight + SFlineToTextV + 1 +
+                       SFlineToTextV + SFlistSize * SFentryHeight +
+                       SFlineToTextV;
+       SFhScrollWidth = SFlineToTextH + SFentryWidth + SFlineToTextH;
+
+       vScrollX = SFlineToTextH + SFentryWidth + SFlineToTextH;
+       vScrollY = SFlineToTextV + SFentryHeight + SFlineToTextV;
+       SFvScrollHeight = SFlineToTextV + SFlistSize * SFentryHeight +
+                       SFlineToTextV;
+
+       SFupperX = SFlineToTextH + SFentryWidth + SFlineToTextH - 1;
+       SFlowerY = SFlineToTextV + SFentryHeight + SFlineToTextV + 1 +
+                       SFlineToTextV;
+       SFupperY = SFlineToTextV + SFentryHeight + SFlineToTextV + 1 +
+                       SFlineToTextV + SFlistSize * SFentryHeight - 1;
+
+       SFtextX = SFlineToTextH + SFbesideText;
+       SFtextYoffset = SFlowerY + SFaboveAndBelowText + SFcharAscent;
+
+       SFsegs[0].x1 = 0;
+       SFsegs[0].y1 = vScrollY;
+       SFsegs[0].x2 = vScrollX - 1;
+       SFsegs[0].y2 = vScrollY;
+       SFsegs[1].x1 = vScrollX;
+       SFsegs[1].y1 = 0;
+       SFsegs[1].x2 = vScrollX;
+       SFsegs[1].y2 = vScrollY - 1;
+
+       SFcompletionSegs[0].x1 = SFcompletionSegs[0].x2 = SFlineToTextH;
+       SFcompletionSegs[1].x1 = SFcompletionSegs[1].x2 =
+               SFlineToTextH + SFentryWidth - 1;
+
+       i = 0;
+       XtSetArg(arglist[i], XtNwidth, NR * listWidth + (NR - 1) * listSpacing + 4);
+                                                                       i++;
+       XtSetArg(arglist[i], XtNborderColor, SFfore);                   i++;
+
+       XtSetArg(arglist[i], XtNfromVert, selFilePrompt);               i++;
+       XtSetArg(arglist[i], XtNvertDistance, 10);                      i++;
+       XtSetArg(arglist[i], XtNresizable, True);                       i++;
+       XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
+       XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
+       XtSetArg(arglist[i], XtNleft, XtChainLeft);                     i++;
+       XtSetArg(arglist[i], XtNright, XtChainLeft);                    i++;
+       XtSetArg(arglist[i], XtNstring, SFtextBuffer);                  i++;
+       XtSetArg(arglist[i], XtNlength, MAXPATHLEN);                    i++;
+       XtSetArg(arglist[i], XtNeditType, XawtextEdit);                 i++;
+       XtSetArg(arglist[i], XtNwrap, XawtextWrapWord);                 i++;
+       XtSetArg(arglist[i], XtNresize, XawtextResizeHeight);           i++;
+       XtSetArg(arglist[i], XtNuseStringInPlace, True);                i++;
+       selFileField = XtCreateManagedWidget("selFileField",
+               asciiTextWidgetClass, selFileForm, arglist, i);
+
+       XtOverrideTranslations(selFileField,
+               XtParseTranslationTable(oneLineTextEditTranslations));
+       XtSetKeyboardFocus(selFileForm, selFileField);
+
+       i = 0;
+       XtSetArg(arglist[i], XtNorientation, XtorientHorizontal);       i++;
+       XtSetArg(arglist[i], XtNwidth, SFpathScrollWidth);              i++;
+       XtSetArg(arglist[i], XtNheight, scrollThickness);               i++;
+       XtSetArg(arglist[i], XtNborderColor, SFfore);                   i++;
+       XtSetArg(arglist[i], XtNfromVert, selFileField);                i++;
+       XtSetArg(arglist[i], XtNvertDistance, 30);                      i++;
+       XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
+       XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
+       XtSetArg(arglist[i], XtNleft, XtChainLeft);                     i++;
+       XtSetArg(arglist[i], XtNright, XtChainLeft);                    i++;
+       selFileHScroll = XtCreateManagedWidget("selFileHScroll",
+               scrollbarWidgetClass, selFileForm, arglist, i);
+
+       XtAddCallback(selFileHScroll, XtNjumpProc,
+               SFpathSliderMovedCallback, (XtPointer) NULL);
+       XtAddCallback(selFileHScroll, XtNscrollProc,
+               SFpathAreaSelectedCallback, (XtPointer) NULL);
+
+       i = 0;
+       XtSetArg(arglist[i], XtNwidth, listWidth);                      i++;
+       XtSetArg(arglist[i], XtNheight, listHeight);                    i++;
+       XtSetArg(arglist[i], XtNborderColor, SFfore);                   i++;
+       XtSetArg(arglist[i], XtNfromVert, selFileHScroll);              i++;
+       XtSetArg(arglist[i], XtNvertDistance, 10);                      i++;
+       XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
+       XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
+       XtSetArg(arglist[i], XtNleft, XtChainLeft);                     i++;
+       XtSetArg(arglist[i], XtNright, XtChainLeft);                    i++;
+       selFileLists[0] = XtCreateManagedWidget("selFileList1",
+               compositeWidgetClass, selFileForm, arglist, i);
+#if (NR == 3)
+       i = 0;
+       XtSetArg(arglist[i], XtNwidth, listWidth);                      i++;
+       XtSetArg(arglist[i], XtNheight, listHeight);                    i++;
+       XtSetArg(arglist[i], XtNborderColor, SFfore);                   i++;
+       XtSetArg(arglist[i], XtNfromHoriz, selFileLists[0]);            i++;
+       XtSetArg(arglist[i], XtNfromVert, selFileHScroll);              i++;
+       XtSetArg(arglist[i], XtNhorizDistance, listSpacing);            i++;
+       XtSetArg(arglist[i], XtNvertDistance, 10);                      i++;
+       XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
+       XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
+       XtSetArg(arglist[i], XtNleft, XtChainLeft);                     i++;
+       XtSetArg(arglist[i], XtNright, XtChainLeft);                    i++;
+       selFileLists[1] = XtCreateManagedWidget("selFileList2",
+               compositeWidgetClass, selFileForm, arglist, i);
+
+       i = 0;
+       XtSetArg(arglist[i], XtNwidth, listWidth);                      i++;
+       XtSetArg(arglist[i], XtNheight, listHeight);                    i++;
+       XtSetArg(arglist[i], XtNborderColor, SFfore);                   i++;
+       XtSetArg(arglist[i], XtNfromHoriz, selFileLists[1]);            i++;
+       XtSetArg(arglist[i], XtNfromVert, selFileHScroll);              i++;
+       XtSetArg(arglist[i], XtNhorizDistance, listSpacing);            i++;
+       XtSetArg(arglist[i], XtNvertDistance, 10);                      i++;
+       XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
+       XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
+       XtSetArg(arglist[i], XtNleft, XtChainLeft);                     i++;
+       XtSetArg(arglist[i], XtNright, XtChainLeft);                    i++;
+       selFileLists[2] = XtCreateManagedWidget("selFileList3",
+               compositeWidgetClass, selFileForm, arglist, i);
+#endif
+       for (n = 0; n < NR; n++) {
+
+               i = 0;
+               XtSetArg(arglist[i], XtNx, vScrollX);                   i++;
+               XtSetArg(arglist[i], XtNy, vScrollY);                   i++;
+               XtSetArg(arglist[i], XtNwidth, scrollThickness);        i++;
+               XtSetArg(arglist[i], XtNheight, SFvScrollHeight);       i++;
+               XtSetArg(arglist[i], XtNborderColor, SFfore);           i++;
+               selFileVScrolls[n] = XtCreateManagedWidget("selFileVScroll",
+                       scrollbarWidgetClass, selFileLists[n], arglist, i);
+
+               XtAddCallback(selFileVScrolls[n], XtNjumpProc,
+                       SFvFloatSliderMovedCallback, (XtPointer) n);
+               XtAddCallback(selFileVScrolls[n], XtNscrollProc,
+                       SFvAreaSelectedCallback, (XtPointer) n);
+
+               i = 0;
+
+               XtSetArg(arglist[i], XtNorientation, XtorientHorizontal);
+                                                                       i++;
+               XtSetArg(arglist[i], XtNx, hScrollX);                   i++;
+               XtSetArg(arglist[i], XtNy, hScrollY);                   i++;
+               XtSetArg(arglist[i], XtNwidth, SFhScrollWidth);         i++;
+               XtSetArg(arglist[i], XtNheight, scrollThickness);       i++;
+               XtSetArg(arglist[i], XtNborderColor, SFfore);           i++;
+               selFileHScrolls[n] = XtCreateManagedWidget("selFileHScroll",
+                       scrollbarWidgetClass, selFileLists[n], arglist, i);
+
+               XtAddCallback(selFileHScrolls[n], XtNjumpProc,
+                       SFhSliderMovedCallback, (XtPointer) n);
+               XtAddCallback(selFileHScrolls[n], XtNscrollProc,
+                       SFhAreaSelectedCallback, (XtPointer) n);
+       }
+
+       i = 0;
+       XtSetArg(arglist[i], XtNlabel, ok);                             i++;
+       XtSetArg(arglist[i], XtNresizable, True);                       i++;
+       XtSetArg(arglist[i], XtNcallback, SFokSelect);                  i++;
+       XtSetArg(arglist[i], XtNborderColor, SFfore);                   i++;
+       XtSetArg(arglist[i], XtNfromVert, selFileLists[0]);             i++;
+       XtSetArg(arglist[i], XtNvertDistance, 30);                      i++;
+       XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
+       XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
+       XtSetArg(arglist[i], XtNleft, XtChainLeft);                     i++;
+       XtSetArg(arglist[i], XtNright, XtChainLeft);                    i++;
+       selFileOK = XtCreateManagedWidget("selFileOK", commandWidgetClass,
+               selFileForm, arglist, i);
+
+       i = 0;
+       XtSetArg(arglist[i], XtNlabel, cancel);                         i++;
+       XtSetArg(arglist[i], XtNresizable, True);                       i++;
+       XtSetArg(arglist[i], XtNcallback, SFcancelSelect);              i++;
+       XtSetArg(arglist[i], XtNborderColor, SFfore);                   i++;
+       XtSetArg(arglist[i], XtNfromHoriz, selFileOK);                  i++;
+       XtSetArg(arglist[i], XtNfromVert, selFileLists[0]);             i++;
+       XtSetArg(arglist[i], XtNhorizDistance, 30);                     i++;
+       XtSetArg(arglist[i], XtNvertDistance, 30);                      i++;
+       XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
+       XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
+       XtSetArg(arglist[i], XtNleft, XtChainLeft);                     i++;
+       XtSetArg(arglist[i], XtNright, XtChainLeft);                    i++;
+       selFileCancel = XtCreateManagedWidget("selFileCancel",
+               commandWidgetClass, selFileForm, arglist, i);
+
+       XtSetMappedWhenManaged(selFile, False);
+       XtRealizeWidget(selFile);
+
+       /* Add WM_DELETE_WINDOW protocol */
+       SFwmDeleteWindow = XInternAtom(SFdisplay, "WM_DELETE_WINDOW", False);
+       XSetWMProtocols(SFdisplay, XtWindow(selFile), &SFwmDeleteWindow, 1);
+
+       SFcreateGC();
+
+       xtermCursor = XCreateFontCursor(SFdisplay, XC_xterm);
+
+       sbRightArrowCursor = XCreateFontCursor(SFdisplay, XC_sb_right_arrow);
+       dotCursor = XCreateFontCursor(SFdisplay, XC_dot);
+
+       XDefineCursor(SFdisplay, XtWindow(selFileForm), xtermCursor);
+       XDefineCursor(SFdisplay, XtWindow(selFileField), xtermCursor);
+
+       for (n = 0; n < NR; n++) {
+               XDefineCursor(SFdisplay, XtWindow(selFileLists[n]),
+                       sbRightArrowCursor);
+       }
+       XDefineCursor(SFdisplay, XtWindow(selFileOK), dotCursor);
+       XDefineCursor(SFdisplay, XtWindow(selFileCancel), dotCursor);
+
+       for (n = 0; n < NR; n++) {
+               XtAddEventHandler(selFileLists[n], ExposureMask, True,
+                       SFexposeList, (XtPointer) n);
+               XtAddEventHandler(selFileLists[n], EnterWindowMask, False,
+                       SFenterList, (XtPointer) n);
+               XtAddEventHandler(selFileLists[n], LeaveWindowMask, False,
+                       SFleaveList, (XtPointer) n);
+               XtAddEventHandler(selFileLists[n], PointerMotionMask, False,
+                       SFmotionList, (XtPointer) n);
+               XtAddEventHandler(selFileLists[n], ButtonPressMask, False,
+                       SFbuttonPressList, (XtPointer) n);
+               XtAddEventHandler(selFileLists[n], ButtonReleaseMask, False,
+                       SFbuttonReleaseList, (XtPointer) n);
+       }
+
+       XtAddEventHandler(selFileField, KeyPressMask, False,
+               SFmodVerifyCallback, (XtPointer) NULL);
+
+       SFapp = XtWidgetToApplicationContext(selFile);
+
+}
+
+/* position widget under the cursor */
+void
+SFpositionWidget(w)
+    Widget w;
+{
+    Arg args[3];
+    Cardinal num_args;
+    Dimension width, height, b_width;
+    int x, y, max_x, max_y;
+    Window root, child;
+    int dummyx, dummyy;
+    unsigned int dummymask;
+    
+    XQueryPointer(XtDisplay(w), XtWindow(w), &root, &child, &x, &y,
+                 &dummyx, &dummyy, &dummymask);
+    num_args = 0;
+    XtSetArg(args[num_args], XtNwidth, &width); num_args++;
+    XtSetArg(args[num_args], XtNheight, &height); num_args++;
+    XtSetArg(args[num_args], XtNborderWidth, &b_width); num_args++;
+    XtGetValues(w, args, num_args);
+
+    width += 2 * b_width;
+    height += 2 * b_width;
+
+    x -= ( (Position) width/2 );
+    if (x < 0) x = 0;
+    if ( x > (max_x = (Position) (XtScreen(w)->width - width)) ) x = max_x;
+
+    y -= ( (Position) height/2 );
+    if (y < 0) y = 0;
+    if ( y > (max_y = (Position) (XtScreen(w)->height - height)) ) y = max_y;
+    
+    num_args = 0;
+    XtSetArg(args[num_args], XtNx, x); num_args++;
+    XtSetArg(args[num_args], XtNy, y); num_args++;
+    XtSetValues(w, args, num_args);
+}
+
+FILE *
+SFopenFile(name, mode, prompt, failed)
+    char *name;
+    char *mode;
+    char *prompt;
+    char *failed;
+{
+    Arg args[1];
+    FILE *fp;
+
+    SFchdir(SFstartDir);
+    if ((fp = fopen(name, mode)) == NULL) {
+       char *buf;
+       if (1) { // [HGM] always use strerror
+           buf = XtMalloc(strlen(failed) + strlen(strerror(errno)) + 
+                          strlen(prompt) + 2);
+           strcpy(buf, failed);
+           strcat(buf, strerror(errno));
+           strcat(buf, "\n");
+           strcat(buf, prompt);
+       } else {
+           buf = XtMalloc(strlen(failed) + strlen(prompt) + 2);
+           strcpy(buf, failed);
+           strcat(buf, "\n");
+           strcat(buf, prompt);
+       }
+       XtSetArg(args[0], XtNlabel, buf);
+       XtSetValues(selFilePrompt, args, ONE);
+       XtFree(buf);
+       return NULL;
+    }
+    return fp;
+}
+
+SFtextChanged()
+{
+
+       if ((SFtextBuffer[0] == '/') || (SFtextBuffer[0] == '~')) {
+               (void) strcpy(SFcurrentPath, SFtextBuffer);
+
+               SFtextPos = XawTextGetInsertionPoint(selFileField);
+       } else {
+               (void) strcat(strcpy(SFcurrentPath, SFstartDir), SFtextBuffer);
+
+               SFtextPos = XawTextGetInsertionPoint(selFileField) +
+                       strlen(SFstartDir);
+       }
+
+       if (!SFworkProcAdded) {
+               (void) XtAppAddWorkProc(SFapp, SFworkProc, NULL);
+               SFworkProcAdded = 1;
+       }
+
+       SFupdatePath();
+}
+
+static char *
+SFgetText()
+{
+       return strcpy(XtMalloc((unsigned) (strlen(SFtextBuffer) + 1)),
+               SFtextBuffer);
+}
+
+static
+SFprepareToReturn()
+{
+       SFstatus = SEL_FILE_NULL;
+       XtRemoveGrab(selFile);
+       XtUnmapWidget(selFile);
+       XtRemoveTimeOut(SFdirModTimerId);
+       if (SFchdir(SFstartDir)) {
+               XtAppError(
+                       SFapp,
+                       "XsraSelFile: can't return to current directory"
+               );
+       }
+}
+
+FILE *
+XsraSelFile(toplevel, prompt, ok, cancel, failed,
+           init_path, mode, show_entry, name_return)
+       Widget          toplevel;
+       char            *prompt;
+       char            *ok;
+       char            *cancel;
+       char            *failed;
+       char            *init_path;
+       char            *mode;
+       int             (*show_entry)();
+       char            **name_return;
+{
+       static int      firstTime = 1;
+       Cardinal        i;
+       Arg             arglist[20];
+       XEvent          event;
+       FILE            *fp;
+
+       if (!prompt) {
+               prompt = "Pathname:";
+       }
+
+       if (!ok) {
+               ok = "OK";
+       }
+
+       if (!cancel) {
+               cancel = "Cancel";
+       }
+
+       if (firstTime) {
+               firstTime = 0;
+               SFdisplay = XtDisplay(toplevel);
+               SFcreateWidgets(toplevel, prompt, ok, cancel);
+       } else {
+               i = 0;
+
+               XtSetArg(arglist[i], XtNlabel, prompt);                 i++;
+               XtSetValues(selFilePrompt, arglist, i);
+
+               i = 0;
+               XtSetArg(arglist[i], XtNlabel, ok);                     i++;
+               XtSetValues(selFileOK, arglist, i);
+
+               i = 0;
+               XtSetArg(arglist[i], XtNlabel, cancel);                 i++;
+               XtSetValues(selFileCancel, arglist, i);
+       }
+
+       SFpositionWidget(selFile);
+       XtMapWidget(selFile);
+
+#if defined(SVR4) || defined(SYSV) || defined(USG) || 1
+       if (!getcwd(SFstartDir, MAXPATHLEN)) { // [HGM] always do this, as I do not know when exactly to do it
+#else /* defined(SVR4) || defined(SYSV) || defined(USG) */
+       if (!getwd(SFstartDir)) {
+#endif /* defined(SVR4) || defined(SYSV) || defined(USG) */
+
+               XtAppError(SFapp, "XsraSelFile: can't get current directory");
+       }
+       (void) strcat(SFstartDir, "/");
+       (void) strcpy(SFcurrentDir, SFstartDir);
+
+       if (init_path) {
+               if (init_path[0] == '/') {
+                       (void) strcpy(SFcurrentPath, init_path);
+                       if (strncmp(
+                               SFcurrentPath,
+                               SFstartDir,
+                               strlen(SFstartDir)
+                       )) {
+                               SFsetText(SFcurrentPath);
+                       } else {
+                               SFsetText(&(SFcurrentPath[strlen(SFstartDir)]));
+                       }
+               } else {
+                       (void) strcat(strcpy(SFcurrentPath, SFstartDir),
+                               init_path);
+                       SFsetText(&(SFcurrentPath[strlen(SFstartDir)]));
+               }
+       } else {
+               (void) strcpy(SFcurrentPath, SFstartDir);
+       }
+
+       SFfunc = show_entry;
+
+       SFtextChanged();
+
+       XtAddGrab(selFile, True, True);
+
+       SFdirModTimerId = XtAppAddTimeOut(SFapp, (unsigned long) 1000,
+               SFdirModTimer, (XtPointer) NULL);
+
+       while (1) {
+               XtAppNextEvent(SFapp, &event);
+               XtDispatchEvent(&event);
+               switch (SFstatus) {
+               case SEL_FILE_TEXT:
+                       SFstatus = SEL_FILE_NULL;
+                       SFtextChanged();
+                       break;
+               case SEL_FILE_OK:
+                       *name_return = SFgetText();
+                       if (fp = SFopenFile(*name_return, mode,
+                                           prompt, failed)) {
+                               SFprepareToReturn();
+                               return fp;
+                       }
+                       SFstatus = SEL_FILE_NULL;
+                       break;
+               case SEL_FILE_CANCEL:
+                       SFprepareToReturn();
+                       return NULL;
+               case SEL_FILE_NULL:
+                       break;
+               }
+       }
+}
diff --git a/selfile.h b/selfile.h
new file mode 100644 (file)
index 0000000..558175e
--- /dev/null
+++ b/selfile.h
@@ -0,0 +1,149 @@
+/*
+ * File-Selector dialog of GhostView 1.5, incorporated in XBoard by H.G.Muller
+ *
+ * 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
+ *         erik@sra.co.jp
+ */
+
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Xos.h>
+#include <X11/Xaw/Text.h>
+#include <X11/Xaw/AsciiText.h>
+
+#define SEL_FILE_CANCEL                -1
+#define SEL_FILE_OK            0
+#define SEL_FILE_NULL          1
+#define SEL_FILE_TEXT          2
+
+#define SF_DO_SCROLL           1
+#define SF_DO_NOT_SCROLL       0
+
+#define NR 3 /* [HGM] (so far failed) attempt to suppress some of the director listings */
+
+typedef struct {
+       int     statDone;
+       char    *real;
+       char    *shown;
+} SFEntry;
+
+typedef struct {
+       char    *dir;
+       char    *path;
+       SFEntry *entries;
+       int     nEntries;
+       int     vOrigin;
+       int     nChars;
+       int     hOrigin;
+       int     changed;
+       int     beginSelection;
+       int     endSelection;
+       time_t  mtime;
+} SFDir;
+
+extern int SFstatus;
+
+extern char SFcurrentPath[], SFstartDir[], SFcurrentDir[];
+
+extern Widget
+               selFile,
+               selFileCancel,
+               selFileField,
+               selFileForm,
+               selFileHScroll,
+               selFileHScrolls[],
+               selFileLists[],
+               selFileOK,
+               selFilePrompt,
+               selFileVScrolls[];
+
+extern Display *SFdisplay;
+
+extern int SFcharWidth, SFcharHeight, SFcharAscent;
+
+extern SFDir *SFdirs;
+
+extern int SFdirEnd, SFdirPtr;
+
+extern Pixel SFfore, SFback;
+
+extern Atom SFwmDeleteWindow;
+
+extern XSegment SFsegs[], SFcompletionSegs[];
+
+extern XawTextPosition SFtextPos;
+
+extern void
+       SFenterList(),
+       SFleaveList(),
+       SFmotionList(),
+       SFbuttonPressList(),
+       SFbuttonReleaseList();
+
+extern void
+       SFvSliderMovedCallback(),
+       SFvFloatSliderMovedCallback(),
+       SFhSliderMovedCallback(),
+       SFpathSliderMovedCallback(),
+       SFvAreaSelectedCallback(),
+       SFhAreaSelectedCallback(),
+       SFpathAreaSelectedCallback();
+
+extern int SFupperX, SFlowerY, SFupperY;
+
+extern int SFtextX, SFtextYoffset;
+
+extern int SFentryWidth, SFentryHeight;
+
+extern int SFlineToTextH, SFlineToTextV;
+
+extern int SFbesideText, SFaboveAndBelowText;
+
+extern int SFcharsPerEntry;
+
+extern int SFlistSize;
+
+extern int SFcurrentInvert[];
+
+extern int SFworkProcAdded;
+
+extern Boolean SFworkProc();
+
+extern XtAppContext SFapp;
+
+extern int SFpathScrollWidth, SFvScrollHeight, SFhScrollWidth;
+
+extern char SFtextBuffer[];
+
+extern int SFbuttonPressed;
+
+extern int SFcompareEntries();
+
+extern void SFdirModTimer();
+
+extern char SFstatChar();
+
+extern XtIntervalId SFdirModTimerId;
+
+extern int (*SFfunc)();
index edf20c6..3a9e5ba 100644 (file)
--- a/xboard.c
+++ b/xboard.c
@@ -233,6 +233,8 @@ typedef struct {
 } Menu;
 
 int main P((int argc, char **argv));
+FILE * XsraSelFile P((Widget w, char *prompt, char *ok, char *cancel, char *failed,
+               char *init_path, char *mode, int (*show_entry)(), char **name_return));
 RETSIGTYPE CmailSigHandler P((int sig));
 RETSIGTYPE IntSigHandler P((int sig));
 RETSIGTYPE TermSizeSigHandler P((int sig));
@@ -4993,45 +4995,14 @@ void FileNamePopUp(label, def, proc, openMode)
 
     fileProc = proc;           /* I can't see a way not */
     fileOpenMode = openMode;   /*   to use globals here */
-
-    i = 0;
-    XtSetArg(args[i], XtNresizable, True); i++;
-    XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
-    XtSetArg(args[i], XtNtitle, XtNewString(_("File name prompt"))); i++;
-    fileNameShell = popup =
-      XtCreatePopupShell("File name prompt", transientShellWidgetClass,
-                        shellWidget, args, i);
-
-    layout =
-      XtCreateManagedWidget(layoutName, formWidgetClass, popup,
-                           layoutArgs, XtNumber(layoutArgs));
-
-    i = 0;
-    XtSetArg(args[i], XtNlabel, label); i++;
-    XtSetArg(args[i], XtNvalue, def); i++;
-    XtSetArg(args[i], XtNborderWidth, 0); i++;
-    dialog = XtCreateManagedWidget("fileName", dialogWidgetClass,
-                                  layout, args, i);
-
-    XawDialogAddButton(dialog, _("ok"), FileNameCallback, (XtPointer) dialog);
-    XawDialogAddButton(dialog, _("cancel"), FileNameCallback,
-                      (XtPointer) dialog);
-
-    XtRealizeWidget(popup);
-    CatchDeleteWindow(popup, "FileNamePopDown");
-
-    XQueryPointer(xDisplay, xBoardWindow, &root, &child,
-                 &x, &y, &win_x, &win_y, &mask);
-
-    XtSetArg(args[0], XtNx, x - 10);
-    XtSetArg(args[1], XtNy, y - 30);
-    XtSetValues(popup, args, 2);
-
-    XtPopup(popup, XtGrabExclusive);
-    filenameUp = True;
-
-    edit = XtNameToWidget(dialog, "*value");
-    XtSetKeyboardFocus(popup, edit);
+    {   // [HGM] use file-selector dialog stolen from Ghostview
+       char *name;
+       int index; // this is not supported yet
+       FILE *f;
+       if(f = XsraSelFile(shellWidget, label, NULL, NULL, "could not open: ",
+           NULL, openMode, NULL, &name))
+               (void) (*fileProc)(f, index=0, name);
+    }
 }
 
 void FileNamePopDown()
diff --git a/xstat.h b/xstat.h
new file mode 100644 (file)
index 0000000..2b4826e
--- /dev/null
+++ b/xstat.h
@@ -0,0 +1,23 @@
+#include <sys/stat.h>
+#if !defined(S_ISDIR) && defined(S_IFDIR)
+#define        S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif
+#if !defined(S_ISREG) && defined(S_IFREG)
+#define        S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#endif
+#if !defined(S_ISSOCK) && defined(S_IFSOCK)
+#define        S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
+#endif
+
+#ifndef S_IXUSR
+#define S_IXUSR 0100
+#endif
+#ifndef S_IXGRP
+#define S_IXGRP 0010
+#endif
+#ifndef S_IXOTH
+#define S_IXOTH 0001
+#endif
+
+#define S_ISXXX(m) ((m) & (S_IXUSR | S_IXGRP | S_IXOTH))
+