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