Couple mouse wheel to v-scrolls in file browser
[xboard.git] / filebrowser / path.c
1 /*
2  * Copyright 1989 Software Research Associates, Inc., Tokyo, Japan
3  *
4  * Permission to use, copy, modify, and distribute this software and its
5  * documentation for any purpose and without fee is hereby granted, provided
6  * that the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation, and that the name of Software Research Associates not be used
9  * in advertising or publicity pertaining to distribution of the software
10  * without specific, written prior permission.  Software Research Associates
11  * makes no representations about the suitability of this software for any
12  * purpose.  It is provided "as is" without express or implied warranty.
13  *
14  * SOFTWARE RESEARCH ASSOCIATES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
15  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
16  * IN NO EVENT SHALL SOFTWARE RESEARCH ASSOCIATES BE LIABLE FOR ANY SPECIAL,
17  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
18  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
19  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20  * PERFORMANCE OF THIS SOFTWARE.
21  *
22  * Author: Erik M. van der Poel
23  *         Software Research Associates, Inc., Tokyo, Japan
24  *         erik@sra.co.jp
25  */
26
27 #include <stdio.h>
28 #include <stdlib.h> /* for qsort */
29 #include "../config.h"
30
31 #ifdef SEL_FILE_IGNORE_CASE
32 #include <ctype.h>
33 #endif /* def SEL_FILE_IGNORE_CASE */
34
35 #include <X11/Xos.h>
36 #include <pwd.h>
37 #include "selfile.h"
38 #include "xstat.h"
39 #include <X11/Xaw/Scrollbar.h>
40
41 #ifndef MAXPATHLEN
42 #define MAXPATHLEN 1024
43 #endif /* ndef MAXPATHLEN */
44
45 #ifdef HAS_DIRENT_H
46 extern uid_t getuid();
47 #endif /* def HAS_DIRENT_H */
48
49 /* added missing prototypes */
50 extern void SFtextChanged();
51 extern int SFgetDir(SFDir *);
52 extern void SFdrawLists(int);
53 extern void SFdrawList(int, int);
54 extern void SFclearList(int, int);
55 extern void SFmotionList(Widget, int, XMotionEvent*);
56
57 typedef struct {
58         char    *name;
59         char    *dir;
60 } SFLogin;
61
62 SFDir *SFdirs = NULL;
63
64 int SFdirEnd;
65
66 int SFdirPtr;
67
68 int SFbuttonPressed = 0;
69
70 static int SFdoNotTouchDirPtr = 0;
71
72 static int SFdoNotTouchVorigin = 0;
73
74 static SFDir SFrootDir, SFhomeDir;
75
76 static SFLogin *SFlogins;
77
78 static int SFtwiddle = 0;
79
80 void SFsetText(char *path);
81
82 int
83 SFchdir(path)
84         char    *path;
85 {
86         int     result;
87
88         result = 0;
89
90         if (strcmp(path, SFcurrentDir)) {
91                 result = chdir(path);
92                 if (!result) {
93                   (void) strncpy(SFcurrentDir, path, MAXPATHLEN);
94                 }
95         }
96
97         return result;
98 }
99
100 static void
101 SFfree(i)
102         int     i;
103 {
104         register SFDir  *dir;
105         register int    j;
106
107         dir = &(SFdirs[i]);
108
109         for (j = dir->nEntries - 1; j >= 0; j--) {
110                 if (dir->entries[j].shown != dir->entries[j].real) {
111                         XtFree(dir->entries[j].shown);
112                 }
113                 XtFree(dir->entries[j].real);
114         }
115
116         XtFree((char *) dir->entries);
117
118         XtFree(dir->dir);
119
120         dir->dir = NULL;
121         return;
122 }
123
124 static void
125 SFstrdup(s1, s2)
126         char    **s1;
127         char    *s2;
128 {
129         *s1 = strcpy(XtMalloc((unsigned) (strlen(s2) + 1)), s2);
130         return;
131 }
132
133 static void
134 SFunreadableDir(dir)
135         SFDir   *dir;
136 {
137         char    *cannotOpen = "<cannot open> ";
138
139         dir->entries = (SFEntry *) XtMalloc(sizeof(SFEntry));
140         dir->entries[0].statDone = 1;
141         SFstrdup(&dir->entries[0].real, cannotOpen);
142         dir->entries[0].shown = dir->entries[0].real;
143         dir->nEntries = 1;
144         dir->nChars = strlen(cannotOpen);
145         return;
146 }
147
148 #ifdef SEL_FILE_IGNORE_CASE
149 static
150 SFstrncmp(p, q, n)
151         register char   *p, *q;
152         register int    n;
153 {
154         register char   c1, c2;
155         char            *psave, *qsave;
156         int             nsave;
157
158         psave = p;
159         qsave = q;
160         nsave = n;
161
162         c1 = *p++;
163         if (islower(c1)) {
164                 c1 = toupper(c1);
165         }
166         c2 = *q++;
167         if (islower(c2)) {
168                 c2 = toupper(c2);
169         }
170
171         while ((--n >= 0) && (c1 == c2)) {
172                 if (!c1) {
173                         return strncmp(psave, qsave, nsave);
174                 }
175                 c1 = *p++;
176                 if (islower(c1)) {
177                         c1 = toupper(c1);
178                 }
179                 c2 = *q++;
180                 if (islower(c2)) {
181                         c2 = toupper(c2);
182                 }
183         }
184
185         if (n < 0) {
186                 return strncmp(psave, qsave, nsave);
187         }
188
189         return c1 - c2;
190 }
191 #endif /* def SEL_FILE_IGNORE_CASE */
192
193 void
194 SFsetText(path)
195         char    *path;
196 {
197         XawTextBlock    text;
198
199         text.firstPos = 0;
200         text.length = strlen(path);
201         text.ptr = path;
202         text.format = FMT8BIT;
203
204         XawTextReplace(selFileField, 0, strlen(SFtextBuffer), &text);
205         XawTextSetInsertionPoint(selFileField, strlen(SFtextBuffer));
206
207         return;
208 }
209
210 static void
211 SFreplaceText(dir, str)
212         SFDir   *dir;
213         char    *str;
214 {
215         int     len;
216
217         *(dir->path) = 0;
218         len = strlen(str);
219         if (str[len - 1] == '/') {
220                 (void) strcat(SFcurrentPath, str);
221         } else {
222                 (void) strncat(SFcurrentPath, str, len - 1);
223         }
224         if (strncmp(SFcurrentPath, SFstartDir, strlen(SFstartDir))) {
225                 SFsetText(SFcurrentPath);
226         } else {
227                 SFsetText(&(SFcurrentPath[strlen(SFstartDir)]));
228         }
229
230         SFtextChanged();
231         return;
232 }
233
234 static void
235 SFexpand(str)
236         char    *str;
237 {
238         int     len;
239         int     cmp;
240         char    *name, *growing;
241         SFDir   *dir;
242         SFEntry *entry, *max;
243
244         len = strlen(str);
245
246         dir = &(SFdirs[SFdirEnd - 1]);
247
248         if (dir->beginSelection == -1) {
249                 SFstrdup(&str, str);
250                 SFreplaceText(dir, str);
251                 XtFree(str);
252                 return;
253         } else if (dir->beginSelection == dir->endSelection) {
254                 SFreplaceText(dir, dir->entries[dir->beginSelection].shown);
255                 return;
256         }
257
258         max = &(dir->entries[dir->endSelection + 1]);
259
260         name = dir->entries[dir->beginSelection].shown;
261         SFstrdup(&growing, name);
262
263         cmp = 0;
264         while (!cmp) {
265                 entry = &(dir->entries[dir->beginSelection]);
266                 while (entry < max) {
267                         if (cmp = strncmp(growing, entry->shown, len)) {
268                                 break;
269                         }
270                         entry++;
271                 }
272                 len++;
273         }
274
275         /*
276          * SFreplaceText() expects filename
277          */
278         growing[len - 2] = ' ';
279
280         growing[len - 1] = 0;
281         SFreplaceText(dir, growing);
282         XtFree(growing);
283 }
284
285 static int
286 SFfindFile(dir, str)
287         SFDir           *dir;
288         register char   *str;
289 {
290         register int    i, last, max;
291         register char   *name, save;
292         SFEntry         *entries;
293         int             len;
294         int             begin, end;
295         int             result;
296
297         len = strlen(str);
298
299         if (str[len - 1] == ' ') {
300                 SFexpand(str);
301                 return 1;
302         } else if (str[len - 1] == '/') {
303                 len--;
304         }
305
306         max = dir->nEntries;
307
308         entries = dir->entries;
309
310         i = 0;
311         while (i < max) {
312                 name = entries[i].shown;
313                 last = strlen(name) - 1;
314                 save = name[last];
315                 name[last] = 0;
316
317 #ifdef SEL_FILE_IGNORE_CASE
318                 result = SFstrncmp(str, name, len);
319 #else /* def SEL_FILE_IGNORE_CASE */
320                 result = strncmp(str, name, len);
321 #endif /* def SEL_FILE_IGNORE_CASE */
322
323                 name[last] = save;
324                 if (result <= 0) {
325                         break;
326                 }
327                 i++;
328         }
329         begin = i;
330         while (i < max) {
331                 name = entries[i].shown;
332                 last = strlen(name) - 1;
333                 save = name[last];
334                 name[last] = 0;
335
336 #ifdef SEL_FILE_IGNORE_CASE
337                 result = SFstrncmp(str, name, len);
338 #else /* def SEL_FILE_IGNORE_CASE */
339                 result = strncmp(str, name, len);
340 #endif /* def SEL_FILE_IGNORE_CASE */
341
342                 name[last] = save;
343                 if (result) {
344                         break;
345                 }
346                 i++;
347         }
348         end = i;
349
350         if (begin != end) {
351                 if (
352                         (dir->beginSelection != begin) ||
353                         (dir->endSelection != end - 1)
354                 ) {
355                         dir->changed = 1;
356                         dir->beginSelection = begin;
357                         if (str[strlen(str) - 1] == '/') {
358                                 dir->endSelection = begin;
359                         } else {
360                                 dir->endSelection = end - 1;
361                         }
362                 }
363         } else {
364                 if (dir->beginSelection != -1) {
365                         dir->changed = 1;
366                         dir->beginSelection = -1;
367                         dir->endSelection = -1;
368                 }
369         }
370
371         if (
372                 SFdoNotTouchVorigin ||
373                 ((begin > dir->vOrigin) && (end < dir->vOrigin + SFlistSize))
374         ) {
375                 SFdoNotTouchVorigin = 0;
376                 return 0;
377         }
378
379         i = begin - 1;
380         if (i > max - SFlistSize) {
381                 i = max - SFlistSize;
382         }
383         if (i < 0) {
384                 i = 0;
385         }
386
387         if (dir->vOrigin != i) {
388                 dir->vOrigin = i;
389                 dir->changed = 1;
390         }
391
392         return 0;
393 }
394
395 static void
396 SFunselect()
397 {
398         SFDir   *dir;
399
400         dir = &(SFdirs[SFdirEnd - 1]);
401         if (dir->beginSelection != -1) {
402                 dir->changed = 1;
403         }
404         dir->beginSelection = -1;
405         dir->endSelection = -1;
406         return;
407 }
408
409 static int
410 SFcompareLogins(p, q)
411         SFLogin *p, *q;
412 {
413         return strcmp(p->name, q->name);
414 }
415
416 static void
417 SFgetHomeDirs()
418 {
419         struct passwd   *pw;
420         int             alloc;
421         int             i;
422         SFEntry         *entries = NULL;
423         int             len;
424         int             maxChars;
425
426         {
427                         alloc = 1;
428                         i = 1;
429                         entries = (SFEntry *) XtMalloc(sizeof(SFEntry));
430                         SFlogins = (SFLogin *) XtMalloc(sizeof(SFLogin));
431                         entries[0].real = XtMalloc(3);
432                         (void) strcpy(entries[0].real, "~");
433                         entries[0].shown = entries[0].real;
434                         entries[0].statDone = 1;
435                         SFlogins[0].name = "";
436                         pw = getpwuid((int) getuid());
437                         SFstrdup(&SFlogins[0].dir, pw ? pw->pw_dir : "/");
438                         maxChars = 0;
439         }
440
441         (void) setpwent();
442
443         while ((pw = getpwent()) && (*(pw->pw_name))) {
444                         if (i >= alloc) {
445                                 alloc *= 2;
446                                 entries = (SFEntry *) XtRealloc(
447                                         (char *) entries,
448                                         (unsigned) (alloc * sizeof(SFEntry))
449                                 );
450                                 SFlogins = (SFLogin *) XtRealloc(
451                                         (char *) SFlogins,
452                                         (unsigned) (alloc * sizeof(SFLogin))
453                                 );
454                         }
455                         len = strlen(pw->pw_name);
456                         entries[i].real = XtMalloc((unsigned) (len + 3));
457                         (void) strcat(strcpy(entries[i].real, "~"),
458                                 pw->pw_name);
459                         entries[i].shown = entries[i].real;
460                         entries[i].statDone = 1;
461                         if (len > maxChars) {
462                                 maxChars = len;
463                         }
464                         SFstrdup(&SFlogins[i].name, pw->pw_name);
465                         SFstrdup(&SFlogins[i].dir, pw->pw_dir);
466                         i++;
467         }
468
469         SFhomeDir.dir                   = XtMalloc(1)   ;
470         SFhomeDir.dir[0]                = 0             ;
471         SFhomeDir.path                  = SFcurrentPath ;
472         SFhomeDir.entries               = entries       ;
473         SFhomeDir.nEntries              = i             ;
474         SFhomeDir.vOrigin               = 0             ;       /* :-) */
475         SFhomeDir.nChars                = maxChars + 2  ;
476         SFhomeDir.hOrigin               = 0             ;
477         SFhomeDir.changed               = 1             ;
478         SFhomeDir.beginSelection        = -1            ;
479         SFhomeDir.endSelection          = -1            ;
480
481         qsort((char *) entries, (size_t)i, sizeof(SFEntry), SFcompareEntries);
482         qsort((char *) SFlogins, (size_t)i, sizeof(SFLogin), SFcompareLogins);
483
484         for (i--; i >= 0; i--) {
485                 (void) strcat(entries[i].real, "/");
486         }
487         return;
488 }
489
490 static int
491 SFfindHomeDir(begin, end)
492         char    *begin, *end;
493 {
494         char    save;
495         char    *theRest;
496         int     i;
497
498         save = *end;
499         *end = 0;
500
501         for (i = SFhomeDir.nEntries - 1; i >= 0; i--) {
502                 if (!strcmp(SFhomeDir.entries[i].real, begin)) {
503                         *end = save;
504                         SFstrdup(&theRest, end);
505                         (void) strcat(strcat(strncpy(SFcurrentPath,SFlogins[i].dir,
506                                                      MAXPATHLEN), "/"),
507                                       theRest);
508                         XtFree(theRest);
509                         SFsetText(SFcurrentPath);
510                         SFtextChanged();
511                         return 1;
512                 }
513         }
514
515         *end = save;
516
517         return 0;
518 }
519
520 void
521 SFupdatePath()
522 {
523         static int      alloc;
524         static int      wasTwiddle = 0;
525         char            *begin, *end;
526         int             i, j;
527         int             prevChange;
528         int             SFdirPtrSave, SFdirEndSave;
529         SFDir           *dir;
530
531         if (!SFdirs) {
532                 SFdirs = (SFDir *) XtMalloc((alloc = 10) * sizeof(SFDir));
533                 dir = &(SFdirs[0]);
534                 SFstrdup(&dir->dir, "/");
535                 (void) SFchdir("/");
536                 (void) SFgetDir(dir);
537                 for (j = 1; j < alloc; j++) {
538                         SFdirs[j].dir = NULL;
539                 }
540                 dir->path = SFcurrentPath + 1;
541                 dir->vOrigin = 0;
542                 dir->hOrigin = 0;
543                 dir->changed = 1;
544                 dir->beginSelection = -1;
545                 dir->endSelection = -1;
546                 SFhomeDir.dir = NULL;
547         }
548
549         SFdirEndSave = SFdirEnd;
550         SFdirEnd = 1;
551
552         SFdirPtrSave = SFdirPtr;
553         SFdirPtr = 0;
554
555         begin = NULL;
556
557         if (SFcurrentPath[0] == '~') {
558                 if (!SFtwiddle) {
559                         SFtwiddle = 1;
560                         dir = &(SFdirs[0]);
561                         SFrootDir = *dir;
562                         if (!SFhomeDir.dir) {
563                                 SFgetHomeDirs();
564                         }
565                         *dir = SFhomeDir;
566                         dir->changed = 1;
567                 }
568                 end = SFcurrentPath;
569                 SFdoNotTouchDirPtr = 1;
570                 wasTwiddle = 1;
571         } else {
572                 if (SFtwiddle) {
573                         SFtwiddle = 0;
574                         dir = &(SFdirs[0]);
575                         *dir = SFrootDir;
576                         dir->changed = 1;
577                 }
578                 end = SFcurrentPath + 1;
579         }
580
581         i = 0;
582
583         prevChange = 0;
584
585         while (*end) {
586                 while (*end++ == '/') {
587                         ;
588                 }
589                 end--;
590                 begin = end;
591                 while ((*end) && (*end++ != '/')) {
592                         ;
593                 }
594                 if ((end - SFcurrentPath <= SFtextPos) && (*(end - 1) == '/')) {
595                         SFdirPtr = i - 1;
596                         if (SFdirPtr < 0) {
597                                 SFdirPtr = 0;
598                         }
599                 }
600                 if (*begin) {
601                         if (*(end - 1) == '/') {
602                                 char save = *end;
603
604                                 if (SFtwiddle) {
605                                         if (SFfindHomeDir(begin, end)) {
606                                                 return;
607                                         }
608                                 }
609                                 *end = 0;
610                                 i++;
611                                 SFdirEnd++;
612                                 if (i >= alloc) {
613                                         SFdirs = (SFDir *) XtRealloc(
614                                                 (char *) SFdirs,
615                                                 (unsigned) ((alloc *= 2) *
616                                                         sizeof(SFDir))
617                                         );
618                                         for (j = alloc / 2; j < alloc; j++) {
619                                                 SFdirs[j].dir = NULL;
620                                         }
621                                 }
622                                 dir = &(SFdirs[i]);
623                                 if (
624                                         (!(dir->dir)) ||
625                                         prevChange ||
626                                         strcmp(dir->dir, begin)
627                                 ) {
628                                         if (dir->dir) {
629                                                 SFfree(i);
630                                         }
631                                         prevChange = 1;
632                                         SFstrdup(&dir->dir, begin);
633                                         dir->path = end;
634                                         dir->vOrigin = 0;
635                                         dir->hOrigin = 0;
636                                         dir->changed = 1;
637                                         dir->beginSelection = -1;
638                                         dir->endSelection = -1;
639                                         (void) SFfindFile(dir - 1, begin);
640                                         if (
641                                                 SFchdir(SFcurrentPath) ||
642                                                 SFgetDir(dir)
643                                         ) {
644                                                 SFunreadableDir(dir);
645                                                 break;
646                                         }
647                                 }
648                                 *end = save;
649                                 if (!save) {
650                                         SFunselect();
651                                 }
652                         } else {
653                                 if (SFfindFile(&(SFdirs[SFdirEnd-1]), begin)) {
654                                         return;
655                                 }
656                         }
657                 } else {
658                         SFunselect();
659                 }
660         }
661
662         if ((end == SFcurrentPath + 1) && (!SFtwiddle)) {
663                 SFunselect();
664         }
665
666         for (i = SFdirEnd; i < alloc; i++) {
667                 if (SFdirs[i].dir) {
668                         SFfree(i);
669                 }
670         }
671
672         if (SFdoNotTouchDirPtr) {
673                 if (wasTwiddle) {
674                         wasTwiddle = 0;
675                         SFdirPtr = SFdirEnd - 1;
676                         if (SFdirPtr < 0) {
677                                 SFdirPtr = 0;
678                         }
679                 } else {
680                         SFdirPtr = SFdirPtrSave;
681                 }
682                 SFdoNotTouchDirPtr = 0;
683         }
684
685         if ((SFdirPtr != SFdirPtrSave) || (SFdirEnd != SFdirEndSave)) {
686                 XawScrollbarSetThumb(
687                         selFileHScroll,
688                         (float) (((double) SFdirPtr) / SFdirEnd),
689                         (float) (((double) ((SFdirEnd < NR) ? SFdirEnd : NR)) /
690                                 SFdirEnd)
691                 );
692         }
693
694         if (SFdirPtr != SFdirPtrSave) {
695                 SFdrawLists(SF_DO_SCROLL);
696         } else {
697                 for (i = 0; i < NR; i++) {
698                         if (SFdirPtr + i < SFdirEnd) {
699                                 if (SFdirs[SFdirPtr + i].changed) {
700                                         SFdirs[SFdirPtr + i].changed = 0;
701                                         SFdrawList(i, SF_DO_SCROLL);
702                                 }
703                         } else {
704                                 SFclearList(i, SF_DO_SCROLL);
705                         }
706                 }
707         }
708         return;
709 }
710
711 /* ARGSUSED */
712 void
713 SFbuttonPressList(w, n, event)
714         Widget                  w;
715         int                     n;
716         XButtonPressedEvent     *event;
717 {
718         int dir = 0;
719         if(event->button == Button4) dir = -2; // kludge to indicate relative motion
720         if(event->button == Button5) dir = -1;
721         if(dir) SFvSliderMovedCallback(w, n, dir); else
722         SFbuttonPressed = 1;
723 }
724
725 /* ARGSUSED */
726 void
727 SFbuttonReleaseList(w, n, event)
728         Widget                  w;
729         int                     n;
730         XButtonReleasedEvent    *event;
731 {
732         SFDir   *dir;
733
734         if(event->button == Button4 || event->button == Button5) return; // [HGM] mouse wheel does not select
735         SFbuttonPressed = 0;
736
737         if (SFcurrentInvert[n] != -1) {
738                 if (n < 2) {
739                         SFdoNotTouchDirPtr = 1;
740                 }
741                 SFdoNotTouchVorigin = 1;
742                 dir = &(SFdirs[SFdirPtr + n]);
743                 SFreplaceText(
744                         dir,
745                         dir->entries[dir->vOrigin + SFcurrentInvert[n]].shown
746                 );
747                 SFmotionList(w, n, (XMotionEvent *) event);
748         }
749 }
750
751 static int
752 SFcheckDir(n, dir)
753         int             n;
754         SFDir           *dir;
755 {
756         struct stat     statBuf;
757         int             i;
758
759         if (
760                 (!stat(".", &statBuf)) &&
761                 (statBuf.st_mtime != dir->mtime)
762         ) {
763
764                 /*
765                  * If the pointer is currently in the window that we are about
766                  * to update, we must warp it to prevent the user from
767                  * accidentally selecting the wrong file.
768                  */
769                 if (SFcurrentInvert[n] != -1) {
770                         XWarpPointer(
771                                 SFdisplay,
772                                 None,
773                                 XtWindow(selFileLists[n]),
774                                 0,
775                                 0,
776                                 0,
777                                 0,
778                                 0,
779                                 0
780                         );
781                 }
782
783                 for (i = dir->nEntries - 1; i >= 0; i--) {
784                         if (dir->entries[i].shown != dir->entries[i].real) {
785                                 XtFree(dir->entries[i].shown);
786                         }
787                         XtFree(dir->entries[i].real);
788                 }
789                 XtFree((char *) dir->entries);
790                 if (SFgetDir(dir)) {
791                         SFunreadableDir(dir);
792                 }
793                 if (dir->vOrigin > dir->nEntries - SFlistSize) {
794                         dir->vOrigin = dir->nEntries - SFlistSize;
795                 }
796                 if (dir->vOrigin < 0) {
797                         dir->vOrigin = 0;
798                 }
799                 if (dir->hOrigin > dir->nChars - SFcharsPerEntry) {
800                         dir->hOrigin = dir->nChars - SFcharsPerEntry;
801                 }
802                 if (dir->hOrigin < 0) {
803                         dir->hOrigin = 0;
804                 }
805                 dir->beginSelection = -1;
806                 dir->endSelection = -1;
807                 SFdoNotTouchVorigin = 1;
808                 if ((dir + 1)->dir) {
809                         (void) SFfindFile(dir, (dir + 1)->dir);
810                 } else {
811                         (void) SFfindFile(dir, dir->path);
812                 }
813
814                 if (!SFworkProcAdded) {
815                         (void) XtAppAddWorkProc(SFapp, SFworkProc, NULL);
816                         SFworkProcAdded = 1;
817                 }
818
819                 return 1;
820         }
821
822         return 0;
823 }
824
825 /* Return a single character describing what kind of file STATBUF is.  */
826
827 char
828 SFstatChar (statBuf)
829         struct stat *statBuf;
830 {
831         if (S_ISDIR (statBuf->st_mode)) {
832                 return '/';
833         } else if (S_ISREG (statBuf->st_mode)) {
834           return S_ISXXX (statBuf->st_mode) ? '*' : ' ';
835 #ifdef S_ISSOCK
836         } else if (S_ISSOCK (statBuf->st_mode)) {
837                 return '=';
838 #endif /* S_ISSOCK */
839         } else {
840                 return ' ';
841         }
842 }
843
844 static int
845 SFcheckFiles(dir)
846         SFDir   *dir;
847 {
848         int             from, to;
849         int             result;
850         char            old, new;
851         int             i;
852         char            *str;
853         int             last;
854         struct stat     statBuf;
855
856         result = 0;
857
858         from = dir->vOrigin;
859         to = dir->vOrigin + SFlistSize;
860         if (to > dir->nEntries) {
861                 to = dir->nEntries;
862         }
863
864         for (i = from; i < to; i++) {
865                 str = dir->entries[i].real;
866                 last = strlen(str) - 1;
867                 old = str[last];
868                 str[last] = 0;
869                 if (stat(str, &statBuf)) {
870                         new = ' ';
871                 } else {
872                         new = SFstatChar(&statBuf);
873                 }
874                 str[last] = new;
875                 if (new != old) {
876                         result = 1;
877                 }
878         }
879
880         return result;
881 }
882
883 void
884 SFdirModTimer(cl, id)
885         XtPointer       cl;
886         XtIntervalId    *id;
887 {
888         static int      n = -1;
889         static int      f = 0;
890         char            save;
891         SFDir           *dir;
892
893         if ((!SFtwiddle) && (SFdirPtr < SFdirEnd)) {
894                 n++;
895                 if ((n > NR-1) || (SFdirPtr + n >= SFdirEnd)) {
896                         n = 0;
897                         f++;
898                         if ((f > NR-1) || (SFdirPtr + f >= SFdirEnd)) {
899                                 f = 0;
900                         }
901                 }
902                 dir = &(SFdirs[SFdirPtr + n]);
903                 save = *(dir->path);
904                 *(dir->path) = 0;
905                 if (SFchdir(SFcurrentPath)) {
906                         *(dir->path) = save;
907
908                         /*
909                          * force a re-read
910                          */
911                         *(dir->dir) = 0;
912
913                         SFupdatePath();
914                 } else {
915                         *(dir->path) = save;
916                         if (
917                                 SFcheckDir(n, dir) ||
918                                 ((f == n) && SFcheckFiles(dir))
919                         ) {
920                                 SFdrawList(n, SF_DO_SCROLL);
921                         }
922                 }
923         }
924
925         SFdirModTimerId = XtAppAddTimeOut(SFapp, (unsigned long) 1000,
926                 SFdirModTimer, (XtPointer) NULL);
927 }