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