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