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