Interface XBoard to GhostView file-browser dialog
[xboard.git] / filebrowser / path.c
diff --git a/filebrowser/path.c b/filebrowser/path.c
new file mode 100644 (file)
index 0000000..6379e79
--- /dev/null
@@ -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 ' ';
+       }
+}