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