1 /*-------------------------------------------------------------------------*/
2 /* filelist.c --- Xcircuit routines for the filelist widget		   */
3 /* Copyright (c) 2002  Tim Edwards, Johns Hopkins University        	   */
4 /*-------------------------------------------------------------------------*/
5 
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <signal.h>
10 #include <sys/types.h>
11 #include <ctype.h>	/* For isspace() */
12 
13 #ifdef HAVE_DIRENT_H
14 #include <dirent.h>
15 #include <unistd.h>
16 #define direct dirent
17 #elif !defined(_MSC_VER)
18 #include <sys/dir.h>
19 #endif
20 
21 #include <sys/stat.h>
22 #include <errno.h>
23 #include <limits.h>
24 
25 #ifndef XC_WIN32
26 #include <X11/Intrinsic.h>
27 #include <X11/StringDefs.h>
28 #include <X11/Shell.h>
29 #include <X11/Xutil.h>
30 #include <X11/cursorfont.h>
31 #endif
32 #ifdef TCL_WRAPPER
33 #include <tk.h>
34 #else
35 #ifndef XC_WIN32
36 #include "Xw/Xw.h"
37 #include "Xw/WorkSpace.h"
38 #include "Xw/TextEdit.h"
39 #include "Xw/Toggle.h"
40 #endif
41 #endif
42 
43 #if defined(XC_WIN32) && defined(TCL_WRAPPER)
44 #include <X11/Xlib.h>
45 #include <X11/Xutil.h>
46 #include <X11/cursorfont.h>
47 #endif
48 
49 
50 /*-------------------------------------------------------------------------*/
51 /* Local includes							   */
52 /*-------------------------------------------------------------------------*/
53 
54 #include "colordefs.h"
55 #include "xcircuit.h"
56 
57 /*----------------------------------------------------------------------*/
58 /* Function prototype declarations                                      */
59 /*----------------------------------------------------------------------*/
60 #include "prototypes.h"
61 
62 /*-------------------------------------------------------------------------*/
63 /* Local definitions							   */
64 /*-------------------------------------------------------------------------*/
65 
66 #define INITDIRS 10
67 
68 /*-------------------------------------------------------------------------*/
69 /* Global Variable definitions						   */
70 /*-------------------------------------------------------------------------*/
71 
72 #ifdef TCL_WRAPPER
73 extern Tcl_Interp *xcinterp;
74 #endif
75 
76 extern Display    *dpy;
77 extern XCWindowData *areawin;
78 extern ApplicationData appdata;
79 extern colorindex *colorlist;
80 extern short	  popups;     /* total number of popup windows */
81 extern char	  _STR2[250];
82 extern char	  _STR[150];
83 extern Globaldata xobjs;
84 
85 Pixmap   flistpix = (Pixmap)NULL;    /* For file-selection widget */
86 short    flstart, flfiles, flcurrent;
87 int	 flcurwidth;
88 
89 GC	 hgc = NULL, sgc = NULL;
90 char	 *cwdname = NULL;
91 fileliststruct *files;
92 
93 #if !defined(XC_WIN32) || defined(TCL_WRAPPER)
94 
95 /*-------------------------------------------------------------------------*/
96 /* Compare two filenames (for use by qsort())				   */
97 /*-------------------------------------------------------------------------*/
98 
fcompare(const void * a,const void * b)99 int fcompare(const void *a, const void *b)
100 {
101    return (strcmp((char *)(((fileliststruct *)a)->filename),
102 	(char *)(((fileliststruct *)b)->filename)));
103 }
104 
105 /*-------------------------------------------------------------------------*/
106 /* Routines for drawing a box around the currently selected file	   */
107 /*-------------------------------------------------------------------------*/
108 
dragfilebox(xcWidget w,caddr_t clientdata,XMotionEvent * event)109 void dragfilebox(xcWidget w, caddr_t clientdata, XMotionEvent *event)
110 {
111    short filenum;
112    int twidth;
113    Window lwin = xcWindow(w);
114 
115    filenum = (event->y - 10 + FILECHARHEIGHT) / FILECHARHEIGHT + flstart - 1;
116    if (filenum < 0) filenum = 0;
117    else if (filenum >= flfiles) filenum = flfiles - 1;
118 
119    if (filenum == flcurrent) return;
120 
121    if (flcurrent >= 0) 		/* erase previous box */
122       XDrawRectangle(dpy, lwin, areawin->gc, 5,
123 	   10 + FILECHARHEIGHT * (flcurrent
124 	   - flstart), flcurwidth + 10, FILECHARHEIGHT);
125 
126    if (files == NULL) return;
127 
128    twidth = XTextWidth(appdata.filefont, files[filenum].filename,
129 	    strlen(files[filenum].filename));
130    XDrawRectangle(dpy, lwin, areawin->gc, 5,
131 	   10 + FILECHARHEIGHT * (filenum
132 	   - flstart), twidth + 10, FILECHARHEIGHT);
133 
134    flcurrent = filenum;
135    flcurwidth = twidth;
136 }
137 
138 /*-------------------------------------------------------------------------*/
139 /* Begin tracking the cursor position relative to the files in the list    */
140 /*-------------------------------------------------------------------------*/
141 
startfiletrack(xcWidget w,caddr_t clientdata,XCrossingEvent * event)142 void startfiletrack(xcWidget w, caddr_t clientdata, XCrossingEvent *event)
143 {
144 #ifdef TCL_WRAPPER
145    Tk_CreateEventHandler(w, PointerMotionMask,
146 		(Tk_EventProc *)xctk_dragfilebox, (ClientData)w);
147 #else
148    xcAddEventHandler(w, PointerMotionMask, False, (xcEventHandler)dragfilebox, NULL);
149 #endif
150 
151    XSetFunction(dpy, areawin->gc, GXcopy);
152    XSetForeground(dpy, areawin->gc, colorlist[AUXCOLOR].color.pixel);
153    XSetLineAttributes(dpy, areawin->gc, 1, LineSolid, CapRound, JoinMiter);
154 
155    /* draw initial box */
156 
157    flcurrent = -1;
158    dragfilebox(w, NULL, (XMotionEvent *)event);
159 
160    XSetFunction(dpy, areawin->gc, GXxor);
161    XSetForeground(dpy, areawin->gc, colorlist[AUXCOLOR].color.pixel ^
162 		colorlist[BACKGROUND].color.pixel);
163 }
164 
165 /*-------------------------------------------------------------------------*/
166 /* Stop tracking the cursor and return to default state			   */
167 /*-------------------------------------------------------------------------*/
168 
endfiletrack(xcWidget w,caddr_t clientdata,XCrossingEvent * event)169 void endfiletrack(xcWidget w, caddr_t clientdata, XCrossingEvent *event)
170 {
171    Window lwin = xcWindow(w);
172 
173    XDrawRectangle(dpy, lwin, areawin->gc, 5,
174 	   10 + FILECHARHEIGHT * (flcurrent
175 	   - flstart), flcurwidth + 10, FILECHARHEIGHT);
176 
177 #ifdef TCL_WRAPPER
178    Tk_DeleteEventHandler(w, PointerMotionMask,
179 		(Tk_EventProc *)xctk_dragfilebox, (ClientData)w);
180 #else
181    xcRemoveEventHandler(w, PointerMotionMask, False,
182 		(xcEventHandler)dragfilebox, NULL);
183 #endif
184 
185    /* Restore graphics state values */
186    XSetForeground(dpy, areawin->gc, colorlist[areawin->gccolor].color.pixel);
187    XSetFunction(dpy, areawin->gc, GXcopy);
188 }
189 
190 #endif
191 
192 /*----------------------------------------------------------------------*/
193 /* Read a crash file to find the name of the original file.		*/
194 /*----------------------------------------------------------------------*/
195 
getcrashfilename()196 char *getcrashfilename()
197 {
198    FILE *fi;
199    char temp[256];
200    char *retstr = NULL, *tpos, *spos;
201    int slen;
202 
203    if ((fi = fopen(_STR2, "r")) != NULL) {
204       while (fgets(temp, 255, fi) != NULL) {
205 	 if ((tpos = strstr(temp, "Title:")) != NULL) {
206 	    ridnewline(temp);
207 	    tpos += 7;
208 	    if ((spos = strrchr(temp, '/')) != NULL)
209 	       tpos = spos + 1;
210 	    retstr = (char *)malloc(1 + strlen(tpos));
211 	    strcpy(retstr, tpos);
212 	 }
213 	 else if ((tpos = strstr(temp, "CreationDate:")) != NULL) {
214 	    ridnewline(temp);
215 	    tpos += 14;
216 	    slen = strlen(retstr);
217 	    retstr = (char *)realloc(retstr, 4 + slen + strlen(tpos));
218 	    sprintf(retstr + slen, " (%s)", tpos);
219 	    break;
220 	 }
221       }
222       fclose(fi);
223    }
224    return retstr;
225 }
226 
227 /*----------------------------------------------------------------------*/
228 /* Crash recovery:  load the file, and link the tempfile name to it.    */
229 /*----------------------------------------------------------------------*/
230 
crashrecover()231 void crashrecover()
232 {
233    if (xobjs.tempfile != NULL) {
234       unlink(xobjs.tempfile);
235       free(xobjs.tempfile);
236       xobjs.tempfile = NULL;
237    }
238    if (strlen(_STR2) == 0) {
239       Wprintf("Error: No temp file name for crash recovery!");
240    }
241    else {
242       xobjs.tempfile = strdup(_STR2);
243       startloadfile(-1);
244    }
245 }
246 
247 /*----------------------------------------------------------------------*/
248 /* Look for any files left over from a crash.                           */
249 /*----------------------------------------------------------------------*/
250 
findcrashfiles()251 void findcrashfiles()
252 {
253    DIR *cwd;
254    struct direct *dp;
255    struct stat sbuf;
256 #ifndef _MSC_VER
257    uid_t userid = getuid();
258 #endif
259    time_t recent = 0;
260    char *snptr;
261    int pid;
262 
263    cwd = opendir(xobjs.tempdir);
264    if (cwd == NULL) return;	/* No tmp directory, no tmp files! */
265 
266    while ((dp = readdir(cwd)) != NULL) {
267       sprintf(_STR, "%s/%s", xobjs.tempdir, dp->d_name);
268       snptr = _STR + strlen(xobjs.tempdir) + 1;
269       if (!strncmp(snptr, "XC", 2)) {
270 	 char *dotptr = strchr(snptr, '.');
271 	 pid = -1;
272 	 if (dotptr && dotptr > snptr + 2) {
273 	    *dotptr = '\0';
274 	    if (sscanf(snptr + 2, "%d", &pid) != 1)
275 	       pid = -1;
276 	    *dotptr = '.';
277          }
278          if ((!stat(_STR, &sbuf))
279 #ifndef _MSC_VER
280 	     && (sbuf.st_uid == userid)
281 #endif
282 	     ) {
283 	    if ((recent == 0) || (sbuf.st_ctime > recent)) {
284 
285 	       /* Check if the PID belongs to an active process	*/
286 	       /* by sending a CONT signal and checking if 	*/
287 	       /* there was no error result.			*/
288 
289 #ifndef _MSC_VER
290 	       if (pid != -1)
291 		  if (kill((pid_t)pid, SIGCONT) == 0)
292 		     continue;
293 #endif
294 
295 	       recent = sbuf.st_ctime;
296 	       strcpy(_STR2, _STR);
297 	    }
298 	 }
299       }
300    }
301    closedir(cwd);
302 
303    if (recent > 0) {	/* There exists at least one temporary file */
304 			/* belonging to this user.  Ask to recover  */
305 			/* the most recent one.			    */
306 
307       /* Warn user of existing tempfile, and ask user if file	*/
308       /* should be recovered immediately.			*/
309 
310 #ifdef TCL_WRAPPER
311       char *cfile = getcrashfilename();
312 
313       sprintf(_STR, ".query.title.field configure -text "
314 		"\"Recover file \'%s\'?\"",
315 		(cfile == NULL) ? "(unknown)" : cfile);
316       Tcl_Eval(xcinterp, _STR);
317       Tcl_Eval(xcinterp, ".query.bbar.okay configure -command "
318 		"{filerecover; wm withdraw .query}; wm deiconify .query");
319       if (cfile != NULL) free(cfile);
320 #else
321       getfile(NULL, (pointertype)RECOVER, NULL);   /* Crash recovery mode */
322 #endif
323    }
324 }
325 
326 /*----------------------------------------------------------------------*/
327 /* Match a filename extension against the file filter list.		*/
328 /*----------------------------------------------------------------------*/
329 
330 #if !defined(XC_WIN32) || defined(TCL_WRAPPER)
331 
match_filter(char * fname,char * filter)332 Boolean match_filter(char *fname, char *filter)
333 {
334    char *dotptr = strrchr(fname, '.');
335    char *filtptr, *endptr;
336    int filtlen, extlen;
337 
338    if (filter == NULL) return False;
339    if (dotptr == NULL) return False;
340 
341    /* New 11/08:  Empty string for filter is a wildcard match, the same */
342    /* as turning off the filter.					*/
343 
344    if (filter[0] == '\0') return True;
345 
346    dotptr++;
347    extlen = strlen(dotptr);
348    filtptr = filter;
349 
350    while (*filtptr != '\0') {
351       endptr = filtptr;
352       while (*endptr != '\0' && !isspace(*endptr)) endptr++;
353       filtlen = (int)(endptr - filtptr);
354       if (filtlen == extlen)
355          if (!strncmp(dotptr, filtptr, extlen))
356 	    return True;
357 
358       filtptr = endptr;
359       while (*filtptr != '\0' && isspace(*filtptr)) filtptr++;
360    }
361    return False;
362 }
363 
364 /*----------------------------------------------------------------------*/
365 /* Make a list of the files in the list widget window			*/
366 /*----------------------------------------------------------------------*/
367 
listfiles(xcWidget w,popupstruct * okaystruct,caddr_t calldata)368 void listfiles(xcWidget w, popupstruct *okaystruct, caddr_t calldata)
369 {
370    XGCValues	values;
371 #ifndef TCL_WRAPPER
372    Arg wargs[2];
373 #endif
374    DIR *cwd;
375    char *filter;
376    Window lwin = xcWindow(w);
377    short allocd = INITDIRS;
378    short n = 0;
379    struct direct *dp;
380    struct stat statbuf;
381    int pixheight;
382    Dimension textwidth, textheight;
383 
384    filter = okaystruct->filter;
385 
386    if (sgc == NULL) {
387       values.foreground = colorlist[FOREGROUND].color.pixel;
388       values.font = appdata.filefont->fid;
389       values.function = GXcopy;
390       values.graphics_exposures = False;
391       sgc = XCreateGC(dpy, lwin, GCForeground | GCFont | GCFunction
392 		| GCGraphicsExposures, &values);
393    }
394 
395 #ifdef TCL_WRAPPER
396    textwidth = Tk_Width(w);
397    textheight = Tk_Height(w);
398 #else
399    XtnSetArg(XtNwidth, &textwidth);
400    XtnSetArg(XtNheight, &textheight);
401    XtGetValues(w, wargs, n);
402 #endif
403 
404    /* Generate a new flistpix pixmap if currently nonexistent */
405 
406    if (!flistpix) {
407 
408       /* get list of files in the current directory (cwd) */
409 
410       flfiles = 0;
411       if (cwdname == NULL) {
412 	 cwdname = (char *) malloc (sizeof(char));
413 	 cwdname[0] = '\0';
414       }
415       if (cwdname[0] == '\0')
416          cwd = opendir(".");
417       else
418          cwd = opendir(cwdname);
419 
420       /* If current directory cannot be accessed for some reason, */
421       /* print "Invalid Directory" to the file list window.	  */
422 
423       if (cwd == NULL) {
424          XSetForeground(dpy, sgc, colorlist[BACKGROUND].color.pixel);
425          XFillRectangle(dpy, lwin, sgc, 0, 0, textwidth, textheight);
426          XSetForeground(dpy, sgc, colorlist[AUXCOLOR].color.pixel);
427          XDrawString(dpy, lwin, sgc, 10, textheight / 2,
428 	    "(Invalid Directory)", 19);
429 	 return;
430       }
431       else {
432 	 if (files == NULL)
433 	    files = (fileliststruct *) malloc (INITDIRS * sizeof(fileliststruct));
434 
435 	 /* write the contents of the current directory into the   */
436 	 /* array "filenames[]" (except for current directory ".") */
437 
438          while ((dp = readdir(cwd)) != NULL) {
439 	    /* don't put current directory in list */
440 	    if (!strcmp(dp->d_name, ".")) continue;
441 
442 	    /* record the type of file */
443 
444 	    sprintf(_STR2, "%s%s", cwdname, dp->d_name);
445 	    if (stat(_STR2, &statbuf)) continue;
446 	    if ((statbuf.st_mode & S_IFDIR) != 0) /* is a directory */
447 	       files[flfiles].filetype = DIRECTORY;
448 	    else if (match_filter(dp->d_name, filter))
449 	       files[flfiles].filetype = MATCH;
450 	    else {
451 	       if (xobjs.filefilter)
452 		  continue;
453 	       else
454 	          files[flfiles].filetype = NONMATCH;
455 	    }
456 
457 	    /* save the filename */
458 
459 	    files[flfiles].filename = (char *) malloc ((strlen(dp->d_name) +
460 		 ((files[flfiles].filetype == DIRECTORY) ? 2 : 1)) * sizeof(char));
461 	    strcpy(files[flfiles].filename, dp->d_name);
462 	    if (files[flfiles].filetype == DIRECTORY)
463 	       strcat(files[flfiles].filename, "/");
464 	    if (++flfiles == allocd) {
465 	       allocd += INITDIRS;
466 	       files = (fileliststruct *) realloc(files,
467 			allocd * sizeof(fileliststruct));
468 	    }
469          }
470       }
471       closedir(cwd);
472 
473       /* sort the files[] array into alphabetical order (like "ls") */
474 
475       qsort((void *)files, (size_t)flfiles, sizeof(fileliststruct), fcompare);
476 
477       pixheight = flfiles * FILECHARHEIGHT + 25;
478       if (pixheight < textheight) pixheight = textheight;
479 
480       flistpix = XCreatePixmap(dpy, areawin->window, textwidth, pixheight,
481 	   DefaultDepthOfScreen(xcScreen(w)));
482 
483       /* Write the filenames onto the pixmap */
484 
485       XSetForeground(dpy, sgc, colorlist[BACKGROUND].color.pixel);
486       XFillRectangle(dpy, flistpix, sgc, 0, 0, textwidth, pixheight);
487       XSetForeground(dpy, sgc, colorlist[FOREGROUND].color.pixel);
488       for (n = 0; n < flfiles; n++) {
489 	 switch (files[n].filetype) {
490 	    case DIRECTORY:
491 	       XSetForeground(dpy, sgc, colorlist[SELECTCOLOR].color.pixel);
492 	       break;
493 	    case MATCH:
494 	       XSetForeground(dpy, sgc, colorlist[FILTERCOLOR].color.pixel);
495 	       break;
496 	    case NONMATCH:
497 	       XSetForeground(dpy, sgc, colorlist[FOREGROUND].color.pixel);
498 	       break;
499 	 }
500          XDrawString(dpy, flistpix, sgc, 10, 10 + FILECHARASCENT + n * FILECHARHEIGHT,
501 	    files[n].filename, strlen(files[n].filename));
502       }
503    }
504 
505    /* Copy the pixmap of filenames into the file list window */
506 
507    XSetForeground(dpy, sgc, colorlist[BACKGROUND].color.pixel);
508    XFillRectangle(dpy, lwin, sgc, 0, 0, textwidth, textheight);
509    XCopyArea(dpy, flistpix, lwin, sgc, 0, flstart * FILECHARHEIGHT,
510 	textwidth, textheight, 0, 0);
511 }
512 
513 /*-------------------------------------------------------------------------*/
514 /* Generate a new pixmap for writing the filelist and set the scrollbar    */
515 /* size accordingly.							   */
516 /*-------------------------------------------------------------------------*/
517 
newfilelist(xcWidget w,popupstruct * okaystruct)518 void newfilelist(xcWidget w, popupstruct *okaystruct)
519 {
520    short n;
521 
522 #ifdef TCL_WRAPPER
523    int bval;
524    int result;
525    char *cstr = (char *)Tcl_GetVar2(xcinterp, "XCOps", "filter", 0);
526    if (cstr == NULL) {
527       Wprintf("Error: No variable $XCOps(filter) in Tcl!");
528       return;
529    }
530    result = Tcl_GetBoolean(xcinterp, cstr, &bval);
531    if (result != TCL_OK) {
532       Wprintf("Error: Bad variable $XCOps(filter) in Tcl!");
533       return;
534    }
535    xobjs.filefilter = bval;
536 #else
537    xcWidget textw = okaystruct->textw;
538 #endif
539 
540    for (n = 0; n < flfiles; n++)
541       free(files[n].filename);
542    free(files);
543    if (flistpix != (Pixmap)NULL) XFreePixmap(dpy, flistpix);
544    files = NULL;
545    flistpix = (Pixmap)NULL;
546    flstart = 0;
547    listfiles(w, okaystruct, NULL);
548 #ifdef TCL_WRAPPER
549    showlscroll(Tk_NameToWindow(xcinterp, ".filelist.listwin.sb", w), NULL, NULL);
550    Tcl_Eval(xcinterp, ".filelist.textent.txt delete 0 end");
551    sprintf(_STR2, ".filelist.textent.txt insert 0 %s", cwdname);
552    Tcl_Eval(xcinterp, _STR2);
553 #else
554    showlscroll(XtNameToWidget(xcParent(w), "LScroll"), NULL, NULL);
555    XwTextClearBuffer(textw);
556    XwTextInsert(textw, cwdname);
557    XwTextResize(textw);
558 #endif
559 }
560 
561 /*-------------------------------------------------------------------------*/
562 /* Button press handler for file list window				   */
563 /*-------------------------------------------------------------------------*/
564 
fileselect(xcWidget w,popupstruct * okaystruct,XButtonEvent * event)565 void fileselect(xcWidget w, popupstruct *okaystruct, XButtonEvent *event)
566 {
567    Window lwin = xcWindow(w);
568    Dimension textwidth, textheight;
569    short filenum;
570    char *tbuf, *ebuf;
571 
572 #ifdef TCL_WRAPPER
573    textwidth = Tk_Width(w);
574    textheight = Tk_Height(w);
575 #else
576    Arg wargs[2];
577    short n = 0;
578    xcWidget textw = okaystruct->textw;
579 
580    XtnSetArg(XtNwidth, &textwidth);
581    XtnSetArg(XtNheight, &textheight);
582    XtGetValues(w, wargs, n);
583 #endif
584 
585    flcurrent = -1;
586 
587    if (files == NULL) return;	/* shouldn't happen */
588 
589    /* third mouse button cancels selection and reverts buffer to cwd name */
590 
591    if (event->button == Button3) {
592       newfilelist(w, okaystruct);
593       return;
594    }
595 
596    filenum = (event->y - 10 + FILECHARHEIGHT) / FILECHARHEIGHT + flstart - 1;
597    if (filenum < 0) filenum = 0;
598    else if (filenum >= flfiles) filenum = flfiles - 1;
599 
600    /* Attempt to enter invalid directory. . . treat like button 3 */
601 
602    if (filenum < 0) {
603       newfilelist(w, okaystruct);
604       return;
605    }
606 
607    /* check if this file is a directory or not */
608 
609    if (strchr(files[filenum].filename, '/') == NULL)	{
610 
611       /* highlight the entry. . . */
612 
613       XSetForeground(dpy, sgc, colorlist[AUXCOLOR].color.pixel);
614       XDrawString(dpy, flistpix, sgc, 10, 10 + FILECHARASCENT + filenum * FILECHARHEIGHT,
615    	   files[filenum].filename, strlen(files[filenum].filename));
616       XCopyArea(dpy, flistpix, lwin, sgc, 0, flstart * FILECHARHEIGHT,
617 	   textwidth, textheight, 0, 0);
618 
619       /* . . .and append it to the text field */
620 
621 #ifdef TCL_WRAPPER
622       Tcl_Eval(xcinterp, ".filelist.textent.txt get");
623       ebuf = (char *)Tcl_GetStringResult(xcinterp);
624       tbuf = (char *)malloc((strlen(ebuf) +
625 		strlen(files[filenum].filename) + 6) * sizeof(char));
626 #else
627       ebuf = (char *)XwTextCopyBuffer(textw);
628       tbuf = (char *)malloc((XwTextGetLastPos(textw)
629 	     + strlen(files[filenum].filename) + 5) * sizeof(char));
630 #endif
631       strcpy(tbuf, ebuf);
632 
633       /* add a comma if there is already text in the destination buffer */
634 
635       if (tbuf[0] != '\0') {
636          if (tbuf[strlen(tbuf) - 1] != '/') strcat(tbuf, ",");
637       }
638       else {
639 	 if (cwdname != NULL) {
640 	    if (cwdname[0] != '\0') {
641 	       tbuf = (char *)realloc(tbuf, (strlen(cwdname) +
642 			strlen(files[filenum].filename) + 5) * sizeof(char));
643 	       strcpy(tbuf, cwdname);
644 	    }
645  	 }
646       }
647       strcat(tbuf, files[filenum].filename);
648 #ifdef TCL_WRAPPER
649       Tcl_Eval(xcinterp, ".filelist.textent.txt delete 0 end");
650       sprintf(_STR2, ".filelist.textent.txt insert 0 %s", tbuf);
651       Tcl_Eval(xcinterp, _STR2);
652 #else
653       XwTextClearBuffer(textw);
654       XwTextInsert(textw, tbuf);
655       XwTextResize(textw);
656 #endif
657       free(tbuf);
658    }
659    else {  /* move to new directory */
660 
661       if (!strcmp(files[filenum].filename, "../")) {
662          char *cptr, *sptr = cwdname;
663 	 if (!strcmp(cwdname, "/")) return;	/* no way up from root dir. */
664 	 while(strstr(sptr, "../") != NULL) sptr += 3;
665 	 if ((cptr = strrchr(sptr, '/')) != NULL) {
666 	    *cptr = '\0';
667 	    if ((cptr = strrchr(sptr, '/')) != NULL) *(cptr + 1) = '\0';
668 	    else *sptr = '\0';
669          }
670 	 else {
671       	    cwdname = (char *)realloc(cwdname, (strlen(cwdname) +
672 	        4) * sizeof(char));
673             strcat(cwdname, "../");
674 	 }
675       }
676       else {
677 	 cwdname = (char *)realloc(cwdname, (strlen(cwdname) +
678 	        strlen(files[filenum].filename) + 1) * sizeof(char));
679          strcat(cwdname, files[filenum].filename);
680       }
681       newfilelist(w, okaystruct);
682    }
683 }
684 
685 /*-------------------------------------------------------------------------*/
686 /* Scrollbar handler for file list widget				   */
687 /*-------------------------------------------------------------------------*/
688 
showlscroll(xcWidget w,caddr_t clientdata,caddr_t calldata)689 void showlscroll(xcWidget w, caddr_t clientdata, caddr_t calldata)
690 {
691    Window swin = xcWindow(w);
692    Dimension swidth, sheight;
693    int pstart, pheight, finscr;
694 
695 #ifdef TCL_WRAPPER
696    swidth = Tk_Width(w);
697    sheight = Tk_Height(w);
698 #else
699 
700    Arg wargs[2];
701    short n = 0;
702 
703    XtnSetArg(XtNwidth, &swidth);
704    XtnSetArg(XtNheight, &sheight);
705    XtGetValues(w, wargs, n);
706 #endif
707 
708    XClearWindow(dpy, swin);
709 
710    if (flfiles > 0) {	/* no files, no bar */
711 
712       finscr = sheight / FILECHARHEIGHT;
713       if (finscr > flfiles) finscr = flfiles;
714 
715       pstart = (flstart * sheight) / flfiles;
716       pheight = (finscr * sheight) / flfiles;
717 
718       XSetForeground(dpy, sgc, colorlist[BARCOLOR].color.pixel);
719       XFillRectangle(dpy, swin, sgc, 0, pstart, swidth, pheight);
720    }
721    flcurrent = -1;
722 }
723 
724 /*-------------------------------------------------------------------------*/
725 /* Button Motion handler for moving the scrollbar up and down		   */
726 /*-------------------------------------------------------------------------*/
727 
draglscroll(xcWidget w,popupstruct * okaystruct,XButtonEvent * event)728 void draglscroll(xcWidget w, popupstruct *okaystruct, XButtonEvent *event)
729 {
730    Dimension sheight;
731    int phheight, finscr, flsave = flstart;
732    xcWidget filew = okaystruct->filew;
733 
734 #ifdef TCL_WRAPPER
735    sheight = Tk_Height(w);
736 #else
737    Arg wargs[1];
738    short n = 0;
739 
740    XtnSetArg(XtNheight, &sheight);
741    XtGetValues(w, wargs, n);
742 #endif
743 
744    finscr = sheight / FILECHARHEIGHT;
745    if (finscr > flfiles) finscr = flfiles;
746 
747    /* center scrollbar on pointer vertical position */
748 
749    phheight = (finscr * sheight) / (flfiles * 2);
750    flstart = (event->y > phheight) ? ((event->y - phheight) * flfiles) / sheight : 0;
751    if (flstart > (flfiles - finscr + 2)) flstart = (flfiles - finscr + 2);
752 
753    if (flstart != flsave) {
754       showlscroll(w, NULL, NULL);
755       listfiles(filew, okaystruct, NULL);
756    }
757 }
758 
759 /*----------------------------------------------------------------------*/
760 /* Set/unset the file filtering function				*/
761 /*----------------------------------------------------------------------*/
762 
763 #ifndef TCL_WRAPPER
764 
setfilefilter(xcWidget w,popupstruct * okaystruct,caddr_t calldata)765 void setfilefilter(xcWidget w, popupstruct *okaystruct, caddr_t calldata)
766 {
767    xobjs.filefilter = (xobjs.filefilter) ? False : True;
768 
769    /* Force regeneration of the file list */
770 
771    newfilelist(okaystruct->filew, okaystruct);
772 }
773 
774 #endif
775 
776 /*----------------------------------------------------------------------*/
777 /* Generate the file list window					*/
778 /*----------------------------------------------------------------------*/
779 
780 #ifdef TCL_WRAPPER
781 
genfilelist(xcWidget parent,popupstruct * okaystruct,Dimension width)782 void genfilelist(xcWidget parent, popupstruct *okaystruct, Dimension width)
783 {
784    xcWidget 	listarea, lscroll, entertext;
785 
786    entertext = okaystruct->textw;
787    listarea = Tk_NameToWindow(xcinterp, ".filelist.listwin.win", parent);
788 
789    xcAddEventHandler(listarea, ButtonPressMask, False,
790 	  (xcEventHandler)fileselect, okaystruct);
791    xcAddEventHandler(listarea, EnterWindowMask, False,
792 	  (xcEventHandler)startfiletrack, NULL);
793    xcAddEventHandler(listarea, LeaveWindowMask, False,
794 	  (xcEventHandler)endfiletrack, NULL);
795    flstart = 0;
796    okaystruct->filew = listarea;
797 
798    lscroll = Tk_NameToWindow(xcinterp, ".filelist.listwin.sb", parent);
799 
800    Tk_CreateEventHandler(lscroll, Button1MotionMask | Button2MotionMask,
801 		(Tk_EventProc *)xctk_draglscroll, (ClientData)okaystruct);
802 
803    /* force new file list, in case the highlight filter changed */
804 
805    if (flistpix != (Pixmap)NULL) XFreePixmap(dpy, flistpix);
806    flistpix = (Pixmap)NULL;
807 }
808 
809 #else
810 
genfilelist(xcWidget parent,popupstruct * okaystruct,Dimension width)811 void genfilelist(xcWidget parent, popupstruct *okaystruct, Dimension width)
812 {
813    Arg		wargs[8];
814    xcWidget 	listarea, lscroll, entertext, dofilter;
815    short 	n = 0;
816    int		wwidth;
817 
818    XtnSetArg(XtNx, 20);
819    XtnSetArg(XtNy, FILECHARHEIGHT - 10);
820    XtnSetArg(XtNwidth, width - SBARSIZE - 40);
821    XtnSetArg(XtNheight, LISTHEIGHT - FILECHARHEIGHT);
822    XtnSetArg(XtNfont, appdata.filefont);
823 
824    entertext = okaystruct->textw;
825 
826    listarea = XtCreateManagedWidget("Filelist", XwworkSpaceWidgetClass,
827 	  parent, wargs, n); n = 0;
828    XtAddCallback(listarea, XtNexpose, (XtCallbackProc)listfiles, okaystruct);
829 
830    xcAddEventHandler(listarea, ButtonPressMask, False,
831 	  (xcEventHandler)fileselect, okaystruct);
832    xcAddEventHandler(listarea, EnterWindowMask, False,
833 	  (xcEventHandler)startfiletrack, NULL);
834    xcAddEventHandler(listarea, LeaveWindowMask, False,
835 	  (xcEventHandler)endfiletrack, NULL);
836    flstart = 0;
837    okaystruct->filew = listarea;
838 
839    XtnSetArg(XtNx, width - SBARSIZE - 20);
840    XtnSetArg(XtNy, FILECHARHEIGHT - 10);
841    XtnSetArg(XtNwidth, SBARSIZE);
842    XtnSetArg(XtNheight, LISTHEIGHT - FILECHARHEIGHT);
843    XtnSetArg(XtNfont, appdata.xcfont);
844 
845    lscroll = XtCreateManagedWidget("LScroll", XwworkSpaceWidgetClass,
846 	     parent, wargs, n); n = 0;
847 
848    XtAddCallback(lscroll, XtNexpose, (XtCallbackProc)showlscroll, NULL);
849    xcAddEventHandler(lscroll, Button1MotionMask | Button2MotionMask,
850 	     False, (xcEventHandler)draglscroll, okaystruct);
851 
852    /* Add a toggle widget to turn file filtering on/off */
853 
854    wwidth = XTextWidth(appdata.xcfont, "filter", strlen("filter"));
855    XtnSetArg(XtNx, width - wwidth - 50);
856    XtnSetArg(XtNy, LISTHEIGHT + 10);
857    XtnSetArg(XtNset, xobjs.filefilter);
858    XtnSetArg(XtNsquare, True);
859    XtnSetArg(XtNborderWidth, 0);
860    XtnSetArg(XtNfont, appdata.xcfont);
861    XtnSetArg(XtNlabelLocation, XwLEFT);
862    dofilter = XtCreateManagedWidget("Filter", XwtoggleWidgetClass,
863 		parent, wargs, n); n = 0;
864    XtAddCallback(dofilter, XtNselect, (XtCallbackProc)setfilefilter, okaystruct);
865    XtAddCallback(dofilter, XtNrelease, (XtCallbackProc)setfilefilter, okaystruct);
866 
867    /* force new file list, in case the highlight filter changed */
868 
869    if (flistpix != (Pixmap)NULL) XFreePixmap(dpy, flistpix);
870    flistpix = (Pixmap)NULL;
871 }
872 
873 #endif 	/* TCL_WRAPPER */
874 
875 #endif
876 
877 /*-------------------------------------------------------------------------*/
878 /* Look for a directory name in a string and update cwdname accordingly	   */
879 /*-------------------------------------------------------------------------*/
880 
lookdirectory(char * lstring,int nchars)881 int lookdirectory(char *lstring, int nchars)
882 {
883    int slen;
884    DIR *cwd = NULL;
885 
886    xc_tilde_expand(lstring, nchars);
887    slen = strlen(lstring);
888 
889    if (lstring[slen - 1] == '/' || ((cwd=opendir(lstring)) != NULL)) {
890       if (cwd) closedir(cwd);
891       if (lstring[slen - 1] != '/') strcat(lstring, "/");
892       cwdname = (char *)realloc(cwdname, (slen + 2) * sizeof(char));
893       strcpy(cwdname, lstring);
894       return(1);
895    }
896    return(0);
897 }
898 
899 /*-------------------------------------------------------------------------*/
900