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