1 #if	!defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: browse.c 942 2008-03-04 18:21:33Z hubert@u.washington.edu $";
3 #endif
4 
5 /*
6  * ========================================================================
7  * Copyright 2006-2008 University of Washington
8  * Copyright 2013-2021 Eduardo Chappa
9  *
10  * Licensed under the Apache License, Version 2.0 (the "License");
11  * you may not use this file except in compliance with the License.
12  * You may obtain a copy of the License at
13  *
14  *     http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * ========================================================================
17  *
18  * Program:	Routines to support file browser in pico and Pine composer
19  *
20  *
21  * NOTES:
22  *
23  *   Misc. thoughts (mss, 5 Apr 92)
24  *
25  *      This is supposed to be just a general purpose browser, equally
26  *	callable from either pico or the pine composer.  Someday, it could
27  * 	even be used to "wrap" the unix file business for really novice
28  *      users.  The stubs are here for renaming, copying, creating directories,
29  *      deleting, undeleting (thought is delete just moves files to
30  *      ~/.pico.deleted directory or something and undelete offers the
31  *      files in there for undeletion: kind of like the mac trashcan).
32  *
33  *   Nice side effects
34  *
35  *      Since the full path name is always maintained and referencing ".."
36  *      stats the path stripped of its trailing name, the unpleasantness of
37  *      symbolic links is hidden.
38  *
39  *   Fleshed out the file managements stuff (mss, 24 June 92)
40  *
41  *
42  */
43 #include "headers.h"
44 #include "../c-client/mail.h"
45 #include "../c-client/rfc822.h"
46 #include "../pith/osdep/collate.h"
47 #include "../pith/charconv/filesys.h"
48 #include "../pith/conf.h"
49 
50 #ifndef	_WINDOWS
51 
52 #if	defined(bsd) || defined(lnx)
53 extern int errno;
54 #endif
55 
56 
57 /*
58  * directory cell structure
59  */
60 struct fcell {
61     char *fname;				/* file name 	       */
62     unsigned mode;				/* file's mode	       */
63     char size[16];				/* file's size in s    */
64     struct fcell *next;
65     struct fcell *prev;
66 };
67 
68 
69 /*
70  * master browser structure
71  */
72 static struct bmaster {
73     struct fcell *head;				/* first cell in list  */
74     struct fcell *bottom;			/* last cell in list  */
75     struct fcell *top;				/* cell in top left    */
76     struct fcell *current;			/* currently selected  */
77     int    longest;				/* longest file name (in screen width) */
78     int	   fpl;					/* file names per line */
79     int    cpf;					/* chars / file / line */
80     int    menu;					/* current menu to display */
81     int    flags;
82     char   dname[NLINE];			/* this dir's name (UTF-8) */
83     char   *names;				/* malloc'd name array (UTF-8) */
84     LMLIST *lm;
85 } *gmp;						/* global master ptr   */
86 
87 
88 /*
89  * title for exported browser display
90  */
91 static	char	*browser_title = NULL;
92 
93 
94 struct bmaster *getfcells(char *, int);
95 void   PaintCell(int, int, int, struct fcell *, int);
96 void   PaintBrowser(struct bmaster *, int, int *, int *);
97 struct bmaster *RepaintBrowser(struct bmaster *, int);
98 void   BrowserKeys(void);
99 void   layoutcells(struct bmaster *);
100 void   percdircells(struct bmaster *);
101 int    PlaceCell(struct bmaster *, struct fcell *, int *, int *);
102 void   zotfcells(struct fcell *);
103 void   zotmaster(struct bmaster **);
104 struct fcell *FindCell(struct bmaster *, char *, int);
105 int    sisin(char *, char *);
106 void   p_chdir(struct bmaster *);
107 void   BrowserAnchor(char *);
108 void   ClearBrowserScreen(void);
109 void   BrowserRunChild(char *, char *);
110 int    fcell_is_selected(struct fcell *, struct bmaster *);
111 void   add_cell_to_lmlist(struct fcell *, struct bmaster *);
112 void   del_cell_from_lmlist(struct fcell *, struct bmaster *);
113 
114 
115 #define	HELP_MENU	{"?", N_("Get Help"), KS_SCREENHELP}
116 #define	OTHER_MENU	{"O", N_("OTHER CMDS"), KS_NONE}
117 static	KEYMENU menu_browse[] = {
118     HELP_MENU,		{NULL, NULL, KS_NONE},
119     {NULL, NULL, KS_NONE},		{"-", N_("Prev Pg"), KS_PREVPAGE},
120     {"D", N_("Delete"), KS_NONE},		{"C",N_("Copy"), KS_NONE},
121     OTHER_MENU,		{NULL, NULL, KS_NONE},
122     {"W", N_("Where is"), KS_NONE},		{"Spc", N_("Next Pg"), KS_NEXTPAGE},
123     {"R", N_("Rename"), KS_NONE},		{NULL, NULL, KS_NONE}
124 };
125 static	KEYMENU menu_other[] = {
126     HELP_MENU,		{NULL, NULL, KS_NONE},
127     {NULL, NULL, KS_NONE},		{NULL, NULL, KS_NONE},
128     {NULL, NULL, KS_NONE},		{"1", N_("One Col"), KS_NONE},
129     OTHER_MENU,		{NULL, NULL, KS_NONE},
130     {NULL, NULL, KS_NONE},		{NULL, NULL, KS_NONE},
131     {NULL, NULL, KS_NONE},		{".", N_("Dot files"), KS_NONE}
132 };
133 #define	QUIT_KEY	1
134 #define	EXEC_KEY	2
135 #define	GOTO_KEY	10
136 #define	SELECT_KEY	7
137 #define	PICO_KEY	11
138 
139 
140 #define DIRWORD "dir"
141 #define PARENTDIR "parent"
142 #define SELECTWORD "SELECT"
143 
144 
145 /*
146  * Default pager used by the stand-alone file browser.
147  */
148 #define	BROWSER_PAGER	((gmode & MDFKEY) ? "alpine -k -F" : "alpine -F")
149 
150 
151 /*
152  * function key mappings for callable browser
153  */
154 static UCS  bfmappings[2][12][2] = { { { F1,  '?'},	/* stand alone */
155 				       { F2,  NODATA },	/* browser function */
156 				       { F3,  'q'},	/* key mappings... */
157 				       { F4,  'v'},
158 				       { F5,  'l'},
159 				       { F6,  'w'},
160 				       { F7,  '-'},
161 				       { F8,  ' '},
162 				       { F9,  'd'},
163 				       { F10, 'r'},
164 				       { F11, 'c'},
165 				       { F12, 'e'} },
166 				     { { F1,  '?'},	/* callable browser */
167 				       { F2,  NODATA },	/* function key */
168 				       { F3,  'e'},	/* mappings... */
169 				       { F4,  's'},
170 				       { F5,  NODATA },
171 				       { F6,  'w'},
172 				       { F7,  '-'},
173 				       { F8,  ' '},
174 				       { F9,  'd'},
175 				       { F10, 'r'},
176 				       { F11, 'c'},
177 				       { F12, 'a'} } };
178 
179 
180 /*
181  * Browser help for pico (pine composer help handed to us by pine)
182  */
183 static char *BrowseHelpText[] = {
184 /* TRANSLATORS: The next several lines go together. The ~ characters
185    should be left in front of the characters they cause to be bold. */
186 N_("Help for Browse Command"),
187 "  ",
188 N_("        Pico's file browser is used to select a file from the"),
189 N_("        file system for inclusion in the edited text."),
190 "  ",
191 N_("~        Both directories and files are displayed.  Press ~S"),
192 N_("~        or ~R~e~t~u~r~n to select a file or directory.  When a file"),
193 N_("        is selected during the \"Read File\" command, it is"),
194 N_("        inserted into edited text.  Answering \"yes\" to the"),
195 N_("        verification question after a directory is selected causes"),
196 N_("        the contents of that directory to be displayed for selection."),
197 "  ",
198 N_("        The file named \"..\" is special, and means the \"parent\""),
199 N_("        of the directory being displayed.  Select this directory"),
200 N_("        to move upward in the directory tree."),
201 "  ",
202 N_("End of Browser Help."),
203 "  ",
204 NULL
205 };
206 
207 /*
208  * Help for standalone browser (pilot)
209  */
210 static char *sa_BrowseHelpText[] = {
211 /* TRANSLATORS: Some more help text */
212 N_("Help for Pilot (PIne's Looker-upper Of Things"),
213 " ",
214 N_("        Pilot is a simple, display-oriented file system browser based on the"),
215 N_("~        Alpine message system composer.  As with Alpine, commands are displayed at"),
216 N_("~        the bottom of the screen, and context-sensitive help is provided."),
217 " ",
218 N_("~        Pilot displays the current working directory at the top of the screen."),
219 N_("~        The directory's contents are displayed in columns of file name, file"),
220 N_("~        size pairs.  Names that are directories are indicated by the name"),
221 N_("~        ~(~d~i~r~) in place of the file size.  The parent of the current working"),
222 N_("~        directory is indicated by the file name ~.~. and size of ~(~p~a~r~e~n~t~ ~d~i~r~)."),
223 N_("~        File names that are symbolic links to other files are displayed with a"),
224 N_("~        file size of ~-~-."),
225 " ",
226 N_("        The following function keys are available in Pilot:"),
227 " ",
228 N_("~        ~?        Display this help text."),
229 N_("~        ~Q        Quit Pilot."),
230 " ",
231 N_("~        ~V        View the currently selected file or open the selected directory."),
232 N_("~                 Note: Pilot invokes ~a~l~p~i~n~e ~-~F, or the program defined by the ~P~A~G~E~R"),
233 N_("~                       environment variable, to view the file."),
234 N_("~        ~L        Launch an external application program."),
235 " ",
236 N_("~        ~W        Search for a file by name."),
237 N_("~        ~-        Scroll up one page."),
238 N_("~        ~S~p~a~c~e    Scroll down one page."),
239 N_("~        ~N,~^~F     Move forward (right) one column."),
240 N_("~        ~P,~^~B     Move back (left) one column."),
241 N_("~        ~^~N       Move down one row."),
242 N_("~        ~^~P       Move up one row."),
243 N_("~        ~.        Switch show dot files."),
244 N_("~        ~1        Switch single column display."),
245 " ",
246 N_("~        ~D        Delete the selected file."),
247 N_("~        ~R        Rename the selected file or directory."),
248 N_("~        ~C        Copy the selected file."),
249 N_("~        ~E        Edit the selected file."),
250 N_("~                 Note: Pilot invokes ~p~i~c~o, or the program defined by the ~E~D~I~T~O~R"),
251 N_("~                       environment variable, to edit the file."),
252 " ",
253 N_("End of Pilot Help."),
254 NULL
255 };
256 
257 
258 
259 /*
260  * pico_file_browse - Exported version of FileBrowse below.
261  */
262 int
pico_file_browse(PICO * pdata,char * dir,size_t dirlen,char * fn,size_t fnlen,char * sz,size_t szlen,int flags)263 pico_file_browse(PICO *pdata, char *dir, size_t dirlen, char *fn, size_t fnlen,
264 		 char *sz, size_t szlen, int flags)
265 {
266     int  rv;
267     char title_buf[64];
268 
269     Pmaster = pdata;
270     gmode = pdata->pine_flags | MDEXTFB;
271     km_popped = 0;
272 
273     /* only init screen bufs for display and winch handler */
274     if(!vtinit())
275       return(-1);
276 
277     if(Pmaster){
278 	term.t_mrow = Pmaster->menu_rows;
279 	if(Pmaster->oper_dir)
280 	  strncpy(opertree, Pmaster->oper_dir, NLINE);
281 
282 	if(*opertree)
283 	  fixpath(opertree, sizeof(opertree));
284     }
285 
286     /* install any necessary winch signal handler */
287     ttresize();
288 
289     snprintf(title_buf, sizeof(title_buf), "%s FILE", pdata->pine_anchor);
290     set_browser_title(title_buf);
291     rv = FileBrowse(dir, dirlen, fn, fnlen, sz, szlen, flags, NULL);
292     set_browser_title(NULL);
293     vttidy();			/* clean up tty handling */
294     zotdisplay();		/* and display structures */
295     Pmaster = NULL;		/* and global config structure */
296     return(rv);
297 }
298 
299 
300 
301 /*
302  * FileBrowse - display contents of given directory dir
303  *
304  *	    input:
305  *		     dir points to initial dir to browse.
306  *                   dirlen- buffer size of dir
307  *		     fn  initial file name.
308  *                   fnlen- buffer size of fn
309  *
310  *         returns:
311  *                   dir points to currently selected directory (without
312  *			trailing file system delimiter)
313  *                   fn  points to currently selected file
314  *                   sz  points to size of file if ptr passed was non-NULL
315  *		     flags
316  *
317  *                 Special dispensation for FB_LMODE. If the caller sets
318  *                 FB_LMODEPOS, and the caller passes a non-null lmreturn,
319  *                 then the return values will be in lmreturn instead of
320  *                 in dir and fn. The caller is responsible for freeing
321  *                 the contents of lmreturn.
322  *
323  *                   1 if a file's been selected
324  *                   0 if no files selected
325  *                  -1 if there were problems
326  */
327 int
FileBrowse(char * dir,size_t dirlen,char * fn,size_t fnlen,char * sz,size_t szlen,int fb_flags,LMLIST ** lmreturn)328 FileBrowse(char *dir, size_t dirlen, char *fn, size_t fnlen,
329 	   char *sz, size_t szlen, int fb_flags, LMLIST **lmreturn)
330 {
331     UCS c, new_c;
332     int status, i, j;
333     int row, col, crow, ccol;
334     int flags;
335     int add_file;
336     char *p, *envp, child[2*NLINE+2], tmp[2*NLINE+1];
337     struct bmaster *mp;
338     struct fcell *tp;
339     EML eml;
340 #ifdef MOUSE
341     MOUSEPRESS mousep;
342 #endif
343 
344     child[0] = '\0';
345 
346     if((gmode&MDTREE) && !in_oper_tree(dir)){
347 	eml.s = opertree;
348 	emlwwrite(_("Can't read outside of %s in restricted mode"), &eml);
349 	sleep(2);
350 	return(0);
351     }
352 
353     if(gmode&MDGOTO){
354 	/* fix up function key mapping table */
355 	/* fix up key menu labels */
356     }
357 
358     /* build contents of cell structures */
359     if((gmp = getfcells(dir, fb_flags)) == NULL)
360       return(-1);
361 
362     tp = NULL;
363     if(fn && *fn){
364 	if((tp = FindCell(gmp, fn, 0)) != NULL){
365 	    gmp->current = tp;
366 	    PlaceCell(gmp, gmp->current, &row, &col);
367 	}
368     }
369 
370     /* paint screen */
371     PaintBrowser(gmp, 0, &crow, &ccol);
372 
373     while(1){						/* the big loop */
374 	if(!(gmode&MDSHOCUR)){
375 	    crow = term.t_nrow-term.t_mrow;
376 	    ccol = 0;
377 	}
378 
379 	if(!(gmode&MDSHOCUR))
380 	  movecursor(crow, ccol);
381 	else if(gmp->flags & FB_LMODEPOS){
382 	    if(gmp->flags & FB_LMODE && gmp->current->mode != FIODIR)
383 	      movecursor(crow, ccol+1);
384 	    else
385 	      movecursor(crow, ccol+4);
386 	}
387 	else
388 	  movecursor(crow, ccol);
389 
390 	if(km_popped){
391 	    km_popped--;
392 	    if(km_popped == 0)
393 	      /* cause bottom three lines to repaint */
394 	      PaintBrowser(gmp, 0, &crow, &ccol);
395 	}
396 
397 	if(km_popped){  /* temporarily change to cause menu to paint */
398 	    term.t_mrow = 2;
399 	    movecursor(term.t_nrow-2, 0);  /* clear status line */
400 	    peeol();
401 	    BrowserKeys();
402 	    term.t_mrow = 0;
403 	}
404 
405 	(*term.t_flush)();
406 
407 #ifdef MOUSE
408 	mouse_in_content(KEY_MOUSE, -1, -1, 0, 0);
409 	register_mfunc(mouse_in_content,2,0,term.t_nrow-(term.t_mrow+1),
410 	    term.t_ncol);
411 #endif
412 	c = GetKey();
413 #ifdef	MOUSE
414 	clear_mfunc(mouse_in_content);
415 #endif
416 
417 	if(Pmaster){
418 	    if(Pmaster->newmail && (c == NODATA || time_to_check())){
419 		if((*Pmaster->newmail)(c == NODATA ? 0 : 2, 1) >= 0){
420 		    int rv;
421 
422 		    if(km_popped){		/* restore display */
423 			km_popped = 0;
424 			PaintBrowser(gmp, 0, &crow, &ccol);
425 		    }
426 
427 		    clearcursor();
428 		    mlerase();
429 		    rv = (*Pmaster->showmsg)(c);
430 		    ttresize();
431 		    picosigs();
432 		    if(rv)	/* Did showmsg corrupt the display? */
433 		      PaintBrowser(gmp, 0, &crow, &ccol); /* Yes, repaint */
434 
435 		    mpresf = 1;
436 		}
437 
438 		clearcursor();
439 		if(gmp->flags & FB_LMODEPOS){
440 		    if(gmp->flags & FB_LMODE && gmp->current->mode != FIODIR)
441 		      movecursor(crow, ccol+1);
442 		    else
443 		      movecursor(crow, ccol+4);
444 		}
445 		else
446 		  movecursor(crow, ccol);
447 	    }
448 	}
449 	else{
450 	    if(get_input_timeout() && (c == NODATA || time_to_check()))
451 	      if(pico_new_mail())
452 		emlwrite(_("You may possibly have new mail."), NULL);
453 	}
454 
455 	if(km_popped)
456 	  switch(c){
457 	    case NODATA:
458 	    case (CTRL|'L'):
459 	      km_popped++;
460 	      break;
461 
462 	    default:
463 	      /* clear bottom three lines */
464 	      movecursor(term.t_nrow-2, 0);
465 	      peeol();
466 	      movecursor(term.t_nrow-1, 0);
467 	      peeol();
468 	      movecursor(term.t_nrow, 0);
469 	      peeol();
470 	      break;
471 	  }
472 
473 	if(c == NODATA)				/* GetKey timed out */
474 	  continue;
475 	else if(Pmaster)
476 	  (*Pmaster->keybinput)();
477 
478 
479 	if(mpresf){				/* blast old messages */
480 	    if(mpresf++ > MESSDELAY){		/* every few keystrokes */
481 		mlerase();
482 	    }
483         }
484 
485 						/* process commands */
486 	switch(new_c = normalize_cmd(c,bfmappings[(gmode&MDBRONLY)?0:1],2)){
487 
488 	  case KEY_RIGHT:			/* move right */
489 	  case (CTRL|'@'):
490 	  case (CTRL|'F'):			/* forward  */
491 	  case 'n' :
492 	  case 'N' :
493 	    if(gmp->current->next == NULL){
494 		(*term.t_beep)();
495 		break;
496 	    }
497 
498 	    PlaceCell(gmp, gmp->current, &row, &col);
499 	    PaintCell(row, col, gmp->cpf, gmp->current, 0);
500 	    gmp->current = gmp->current->next;
501 	    if(PlaceCell(gmp, gmp->current, &row, &col)){
502 		PaintBrowser(gmp, 1, &crow, &ccol);
503 	    }
504 	    else{
505 		PaintCell(row, col, gmp->cpf, gmp->current, 1);
506 		crow = row;
507 		ccol = col;
508 	    }
509 	    break;
510 
511 	  case KEY_LEFT:				/* move left */
512 	  case (CTRL|'B'):				/* back */
513 	  case 'p' :
514 	  case 'P' :
515 	    if(gmp->current->prev == NULL){
516 		(*term.t_beep)();
517 		break;
518 	    }
519 
520 	    PlaceCell(gmp, gmp->current, &row, &col);
521 	    PaintCell(row, col, gmp->cpf, gmp->current, 0);
522 	    gmp->current = gmp->current->prev;
523 	    if(PlaceCell(gmp, gmp->current, &row, &col)){
524 		PaintBrowser(gmp, 1, &crow, &ccol);
525 	    }
526 	    else{
527 		PaintCell(row, col, gmp->cpf, gmp->current, 1);
528 		crow = row;
529 		ccol = col;
530 	    }
531 	    break;
532 
533 	  case (CTRL|'A'):				/* beginning of line */
534 	    tp = gmp->current;
535 	    i = col;
536 	    while(i > 0){
537 		i -= gmp->cpf;
538 		if(tp->prev != NULL)
539 		  tp = tp->prev;
540 	    }
541 	    PlaceCell(gmp, gmp->current, &row, &col);
542 	    PaintCell(row, col, gmp->cpf, gmp->current, 0);
543 	    gmp->current = tp;
544 	    if(PlaceCell(gmp, tp, &row, &col)){
545 		PaintBrowser(gmp, 1, &crow, &ccol);
546 	    }
547 	    else{
548 		PaintCell(row, col, gmp->cpf, gmp->current, 1);
549 		crow = row;
550 		ccol = col;
551 	    }
552 	    break;
553 
554 	  case (CTRL|'E'):				/* end of line */
555 	    tp = gmp->current;
556 	    i = col + gmp->cpf;
557 	    while(i+gmp->cpf <= gmp->cpf * gmp->fpl){
558 		i += gmp->cpf;
559 		if(tp->next != NULL)
560 		  tp = tp->next;
561 	    }
562 
563 	    PlaceCell(gmp, gmp->current, &row, &col);
564 	    PaintCell(row, col, gmp->cpf, gmp->current, 0);
565 	    gmp->current = tp;
566 	    if(PlaceCell(gmp, tp, &row, &col)){
567 		PaintBrowser(gmp, 1, &crow, &ccol);
568 	    }
569 	    else{
570 		PaintCell(row, col, gmp->cpf, gmp->current, 1);
571 		crow = row;
572 		ccol = col;
573 	    }
574 	    break;
575 
576 	  case (CTRL|'V'):				/* page forward */
577 	  case ' ':
578 	  case KEY_PGDN :
579 	  case KEY_END :
580 	    tp = gmp->top;
581 	    i = term.t_nrow - term.t_mrow - 2;
582 
583 	    while(i-- && tp->next != NULL){
584 		j = 0;
585 		while(++j <= gmp->fpl  && tp->next != NULL)
586 		  tp = tp->next;
587 	    }
588 
589 	    if(tp == NULL)
590 	      continue;
591 
592 	    PlaceCell(gmp, gmp->current, &row, &col);
593 	    PaintCell(row, col, gmp->cpf, gmp->current, 0);
594 	    gmp->current = tp;
595 	    if(PlaceCell(gmp, tp, &row, &col)){
596 		PaintBrowser(gmp, 1, &crow, &ccol);
597 	    }
598 	    else{
599 		PaintCell(row, col, gmp->cpf, gmp->current, 1);
600 		crow = row;
601 		ccol = col;
602 	    }
603 	    break;
604 
605 	  case '-' :
606 	  case (CTRL|'Y'):				/* page backward */
607 	  case KEY_PGUP :
608 	  case KEY_HOME :
609 	    tp = gmp->top;
610 	    i = term.t_nrow - term.t_mrow - 4;
611 	    while(i-- && tp != NULL){
612 		j = gmp->fpl;
613 		while(j-- && tp != NULL)
614 		  tp = tp->prev;
615 	    }
616 
617 	    if(tp || (gmp->current != gmp->top)){	/* clear old hilite */
618 		PlaceCell(gmp, gmp->current, &row, &col);
619 		PaintCell(row, col, gmp->cpf, gmp->current, 0);
620 	    }
621 
622 	    if(tp)					/* new page ! */
623 		gmp->current = tp;
624 	    else if(gmp->current != gmp->top)		/* goto top of page */
625 		gmp->current = gmp->top;
626 	    else					/* do nothing */
627 	      continue;
628 
629 	    if(PlaceCell(gmp, gmp->current, &row, &col)){
630 		PaintBrowser(gmp, 1, &crow, &ccol);
631 	    }
632 	    else{
633 		PaintCell(row, col, gmp->cpf, gmp->current, 1);
634 		crow = row;
635 		ccol = col;
636 	    }
637 
638 	    break;
639 
640 	  case KEY_DOWN :
641 	  case (CTRL|'N'):				/* next */
642 	    tp = gmp->current;
643 	    i = gmp->fpl;
644 	    while(i--){
645 		if(tp->next == NULL){
646 		    (*term.t_beep)();
647 		    break;
648 		}
649 		else
650 		  tp = tp->next;
651 	    }
652 	    if(i != -1)					/* can't go down */
653 	      break;
654 
655 	    PlaceCell(gmp, gmp->current, &row, &col);
656 	    PaintCell(row, col, gmp->cpf, gmp->current, 0);
657 	    gmp->current = tp;
658 	    if(PlaceCell(gmp, tp, &row, &col)){
659 		PaintBrowser(gmp, 1, &crow, &ccol);
660 	    }
661 	    else{
662 		PaintCell(row, col, gmp->cpf, gmp->current, 1);
663 		crow = row;
664 		ccol = col;
665 	    }
666 	    break;
667 
668 	  case KEY_UP :
669 	  case (CTRL|'P'):				/* previous */
670 	    tp = gmp->current;
671 	    i = gmp->fpl;
672 	    while(i-- && tp)
673 	      tp = tp->prev;
674 
675 	    if(tp == NULL)
676 	      break;
677 
678 	    PlaceCell(gmp, gmp->current, &row, &col);
679 	    PaintCell(row, col, gmp->cpf, gmp->current, 0);
680 	    gmp->current = tp;
681 	    if(PlaceCell(gmp, tp, &row, &col)){
682 		PaintBrowser(gmp, 1, &crow, &ccol);
683 	    }
684 	    else{
685 		PaintCell(row, col, gmp->cpf, gmp->current, 1);
686 		crow = row;
687 		ccol = col;
688 	    }
689 	    break;
690 
691 #ifdef MOUSE
692 	  case KEY_MOUSE:
693 	    mouse_get_last (NULL, &mousep);
694 	    if (mousep.doubleclick) {
695 		goto Selected;
696 	    }
697 	    else {
698 		row = mousep.row -= 2;		/* Adjust for header*/
699 		col = mousep.col;
700 		i = row * gmp->fpl + (col / gmp->cpf);	/* Count from top */
701 		tp = gmp->top;				/* start at top. */
702 		for (; i > 0 && tp != NULL; --i)	/* Count cells. */
703 		  tp = tp->next;
704 		if (tp != NULL) {			/* Valid cell? */
705 		    PlaceCell(gmp, gmp->current, &row, &col);
706 		    PaintCell(row, col, gmp->cpf, gmp->current, 0);
707 		    gmp->current = tp;
708 		    if(PlaceCell(gmp, tp, &row, &col)){
709 			PaintBrowser(gmp, 1, &crow, &ccol);
710 		    }
711 		    else{
712 			PaintCell(row, col, gmp->cpf, gmp->current, 1);
713 			crow = row;
714 			ccol = col;
715 		    }
716 		}
717             }
718 	    break;
719 #endif
720 
721 	  case 'e':					/* exit or edit */
722 	  case 'E':
723 	    if(gmode&MDBRONLY){				/* run "pico" */
724 		char *t;
725 		snprintf(child, sizeof(child), "%.*s%c%.*s", NLINE, gmp->dname, C_FILESEP,
726 			NLINE, gmp->current->fname);
727 		/* make sure selected isn't a directory or executable */
728 		if(!LikelyASCII(child)){
729 		    emlwrite(_("Can't edit non-text file.  Try Launch."), NULL);
730 		    break;
731 		}
732 
733 		if((envp = (char *) getenv("EDITOR")) != NULL){
734 		  t = fs_get(strlen(envp) + strlen(child) + 3 + 1);
735 		  sprintf(t, "%s \'%s\'", envp, child);
736 		}
737 		else{
738 		  t = fs_get(strlen(child) + 16 + 1);
739 		  sprintf(t, "pico%s%s%s \'%s\'",
740 			  (gmode & MDFKEY) ? " -f" : "",
741 			  (gmode & MDSHOCUR) ? " -g" : "",
742 			  (gmode & MDMOUSE) ? " -m" : "",
743 			  child);
744 		}
745 
746 		BrowserRunChild(t, gmp->dname);	/* spawn pico */
747 		PaintBrowser(gmp, 0, &crow, &ccol);	/* redraw browser */
748 		if(t) fs_give((void **) &t);
749 	    }
750 	    else{
751 		zotmaster(&gmp);
752 		return(0);
753 	    }
754 
755 	    break;
756 
757 	  case 'q':			            /* user exits wrong */
758 	  case 'Q':
759 	    if(gmode&MDBRONLY){
760 		zotmaster(&gmp);
761 		return(0);
762 	    }
763 
764 	    unknown_command(c);
765 	    break;
766 
767 	  case 'x':				    /* toggle selection */
768 	  case 'X':
769 	    if(!(gmp->flags & FB_LMODE)){
770 		if(gmp->flags & FB_LMODEPOS)
771 		  emlwwrite(_("Type L command to use ListMode"), NULL);
772 		else
773 		  unknown_command(c);
774 
775 		break;
776 	    }
777 
778 	    if(gmp->current->mode == FIODIR){
779 		emlwwrite(_("Can't Set directories"), NULL);
780 		break;
781 	    }
782 
783 	    if(fcell_is_selected(gmp->current, gmp))
784 	      del_cell_from_lmlist(gmp->current, gmp);
785 	    else
786 	      add_cell_to_lmlist(gmp->current, gmp);
787 
788 	    PlaceCell(gmp, gmp->current, &row, &col);
789 	    PaintCell(row, col, gmp->cpf, gmp->current, 1);
790 	    break;
791 
792 	  case 'l':			            /* run Command */
793 	  case 'L':				    /* or ListMode */
794 	    if(gmp->flags & FB_LMODEPOS){
795 		if(gmp->flags & FB_LMODE){
796 		    /*
797 		     * Unless we make it so you can get out of ListMode
798 		     * once you're in ListMode, this must be an error.
799 		     */
800 		    emlwwrite(_("Already in ListMode"), NULL);
801 		    break;
802 		}
803 		else{
804 		    gmp->flags |= FB_LMODE;
805 		    PaintBrowser(gmp, 0, &crow, &ccol);
806 		}
807 
808 		break;
809 	    }
810 
811 	    if(!(gmode&MDBRONLY)){
812 		unknown_command(c);
813 		break;
814 	    }
815 
816 /* add subcommands to invoke pico and insert selected filename */
817 /* perhaps: add subcmd to scroll command history */
818 
819 	    tmp[0] = '\0';
820 	    i = 0;
821 	    snprintf(child, sizeof(child), "%.*s%c%.*s", NLINE, gmp->dname, C_FILESEP,
822 		    NLINE, gmp->current->fname);
823 	    while(!i){
824 		static EXTRAKEYS opts[] = {
825 		    {"^X", N_("Add Name"), CTRL|'X', KS_NONE},
826 		    {NULL, NULL, 0, KS_NONE},
827 		    {NULL, NULL, 0, KS_NONE},
828 		    {NULL, NULL, 0, KS_NONE},
829 		    {NULL, NULL, 0, KS_NONE},
830 		    {NULL, NULL, 0, KS_NONE},
831 		    {NULL, NULL, 0, KS_NONE},
832 		    {NULL, NULL, 0, KS_NONE},
833 		    {NULL, NULL, 0, KS_NONE},
834 		    {NULL, NULL, 0, KS_NONE}
835 		};
836 
837 		status = mlreply_utf8(_("Command to execute: "),
838 				 tmp, NLINE, QNORML, opts);
839 		switch(status){
840 		  case HELPCH:
841 		    emlwwrite(_("No help yet!"), NULL);
842 /* remove break and sleep after help text is installed */
843 		    sleep(3);
844 		    break;
845 		  case (CTRL|'X'):
846 		    strncat(tmp, child, sizeof(tmp)-strlen(tmp)-1);
847 		    tmp[sizeof(tmp)-1] = '\0';
848 		    break;
849 		  case (CTRL|'L'):
850 		    PaintBrowser(gmp, 0, &crow, &ccol);
851 		    break;
852 		  case ABORT:
853 		    emlwrite(_("Command cancelled"), NULL);
854 		    i++;
855 		    break;
856 		  case FALSE:
857 		  case TRUE:
858 		    i++;
859 
860 		    if(tmp[0] == '\0'){
861 			emlwrite(_("No command specified"), NULL);
862 			break;
863 		    }
864 
865 		    BrowserRunChild(tmp, gmp->dname);
866 		    PaintBrowser(gmp, 0, &crow, &ccol);
867 		    break;
868 		  default:
869 		    break;
870 		}
871 	    }
872 
873 	    BrowserKeys();
874 	    break;
875 
876 	  case 'd':					/* delete */
877 	  case 'D':
878 	    if(gmp->current->mode == FIODIR){
879 /* BUG: if dir is empty it should be deleted */
880 		emlwwrite(_("Can't delete a directory"), NULL);
881 		break;
882 	    }
883 
884 	    if(gmode&MDSCUR){				/* not allowed! */
885 		emlwrite(_("Delete not allowed in restricted mode"),NULL);
886 		break;
887 	    }
888 
889 	    snprintf(child, sizeof(child), "%s%c%s", gmp->dname, C_FILESEP,
890 		    gmp->current->fname);
891 
892 	    i = 0;
893 	    while(i++ < 2){		/* verify twice!! */
894 		if(i == 1){
895 		    if(fexist(child, "w", (off_t *)NULL) != FIOSUC){
896 		      strncpy(tmp, _("File is write protected! OVERRIDE"), sizeof(tmp));
897 		      tmp[sizeof(tmp)-1] = '\0';
898 		    }
899 		    else
900 		      /* TRANSLATORS: This is a question, Delete file <filename> */
901 		      snprintf(tmp, sizeof(tmp), _("Delete file \"%.*s\""), NLINE - 20, child);
902 		}
903 		else{
904 		  strncpy(tmp, _("File CANNOT be UNdeleted!  Really delete"), sizeof(tmp));
905 		  tmp[sizeof(tmp)-1] = '\0';
906 		}
907 
908 		if((status = mlyesno_utf8(tmp, FALSE)) != TRUE){
909 		    emlwrite((status ==  ABORT)
910 			       ? _("Delete Cancelled")
911 			       : _("File Not Deleted"),
912 			     NULL);
913 		    break;
914 		}
915 	    }
916 
917 	    if(status == TRUE){
918 		if(our_unlink(child) < 0){
919 		    eml.s = errstr(errno);
920 		    emlwrite(_("Delete Failed: %s"), &eml);
921 		}
922 		else{			/* fix up pointers and redraw */
923 		    tp = gmp->current;
924 		    if(tp->next){
925 			gmp->current = tp->next;
926 			if((tp->next->prev = tp->prev) != NULL)
927 			  tp->prev->next = tp->next;
928 		    }
929 		    else if(tp->prev) {
930 			gmp->current = tp->prev;
931 			if((tp->prev->next = tp->next) != NULL)
932 			  tp->next->prev = tp->prev;
933 		    }
934 
935 		    if(tp == gmp->head)
936 		      gmp->head = tp->next;
937 
938 		    if(tp == gmp->bottom)
939 		      gmp->bottom = tp->prev;
940 
941 		    tp->fname = NULL;
942 		    tp->next = tp->prev = NULL;
943 		    if(tp != gmp->current)
944 		      free((char *) tp);
945 
946 		    if((tp = FindCell(gmp, gmp->current->fname, 0)) != NULL){
947 			gmp->current = tp;
948 			PlaceCell(gmp, gmp->current, &row, &col);
949 		    }
950 
951 		    PaintBrowser(gmp, 1, &crow, &ccol);
952 		    mlerase();
953 		}
954 	    }
955 
956 	    BrowserKeys();
957 	    break;
958 
959 	  case '?':					/* HELP! */
960 	  case (CTRL|'G'):
961 	    if(term.t_mrow == 0){
962 		if(km_popped == 0){
963 		    km_popped = 2;
964 		    break;
965 		}
966 	    }
967 
968 	    if(Pmaster){
969 		VARS_TO_SAVE *saved_state;
970 
971 		saved_state = save_pico_state();
972 		(*Pmaster->helper)(Pmaster->browse_help,
973 				   _("Help for Browsing"), 1);
974 		if(saved_state){
975 		    restore_pico_state(saved_state);
976 		    free_pico_state(saved_state);
977 		}
978 	    }
979 	    else if(gmode&MDBRONLY)
980 	      pico_help(sa_BrowseHelpText, _("Browser Help"), 1);
981 	    else
982 	      pico_help(BrowseHelpText, _("Help for Browsing"), 1);
983 	    /* fall thru to repaint everything */
984 
985 	  case (CTRL|'L'):
986 	    PaintBrowser(gmp, 0, &crow, &ccol);
987 	    break;
988 
989 	  case 'g':                             /* jump to a directory */
990 	  case 'G':
991 
992 	    if(!(gmode&MDGOTO))
993 	      goto Default;
994 
995 	    i = 0;
996 	    child[0] = '\0';
997 
998 	    while(!i){
999 
1000 		/* TRANSLATORS: A prompt asking for a directory to
1001 		   move into */
1002 		status = mlreply_utf8(_("Directory to go to: "), child, NLINE, QNORML,
1003 				 NULL);
1004 
1005 		switch(status){
1006 		  case HELPCH:
1007 		    emlwwrite(_("No help yet!"), NULL);
1008 		    /* remove break and sleep after help text is installed */
1009 		    sleep(3);
1010 		    break;
1011 		  case (CTRL|'L'):
1012 		    PaintBrowser(gmp, 0, &crow, &ccol);
1013 		  break;
1014 		  case ABORT:
1015 		    emlwrite(_("Goto cancelled"), NULL);
1016 		    i++;
1017 		    break;
1018 		  case FALSE:
1019 		  case TRUE:
1020 		    i++;
1021 
1022 		    if(*child == '\0'){
1023 		      strncpy(child, gethomedir(NULL), sizeof(child));
1024 		      child[sizeof(child)-1] = '\0';
1025 		    }
1026 
1027 		    if(!compresspath(gmp->dname, child, sizeof(child))){
1028 			eml.s = child;
1029 			emlwrite(_("Invalid Directory: %s"), &eml);
1030 			break;
1031 		    }
1032 
1033 		    if((gmode&MDSCUR) && homeless(child)){
1034 			emlwrite(_("Restricted mode browsing limited to home directory"),NULL);
1035 			break;
1036 		    }
1037 
1038 		    if((gmode&MDTREE) && !in_oper_tree(child)){
1039 		      eml.s = opertree;
1040 		      emlwwrite(_("Can't go outside of %s in restricted mode"),
1041 			       &eml);
1042 			break;
1043 		    }
1044 
1045 		    if(isdir(child, (long *) NULL, NULL)){
1046 			if((mp = getfcells(child, fb_flags)) == NULL){
1047 			    /* getfcells should explain what happened */
1048 			    i++;
1049 			    break;
1050 			}
1051 
1052 			zotmaster(&gmp);
1053 			gmp = mp;
1054 			PaintBrowser(gmp, 0, &crow, &ccol);
1055 		    }
1056 		    else{
1057 		      eml.s = child;
1058 		      emlwwrite(_("Not a directory: \"%s\""), &eml);
1059 		    }
1060 
1061 		    break;
1062 		  default:
1063 		    break;
1064 		}
1065 	    }
1066 	    BrowserKeys();
1067 	    break;
1068 
1069 	  case '.':					/* display dot files */
1070 		gmode ^= MDDOTSOK;
1071 		gmp = RepaintBrowser(gmp, fb_flags);
1072 		break;
1073 
1074 	  case '1':					/* One column mode */
1075 		gmode ^= MDONECOL;
1076 		gmp = RepaintBrowser(gmp, fb_flags);
1077 		break;
1078 
1079 	  case 'o':					/* Other menu */
1080 	  case 'O':
1081 		if (gmp){
1082 		  if(gmp->menu == 0){
1083 			gmp->menu = 1;
1084 		  } else {
1085 			gmp->menu = 0;
1086 		  }
1087 		  BrowserKeys();
1088 		}
1089 		break;
1090 
1091 	  case 'a':					/* Add */
1092 	  case 'A':
1093 	    if(gmode&MDSCUR){				/* not allowed! */
1094 		emlwrite(_("Add not allowed in restricted mode"),NULL);
1095 		break;
1096 	    }
1097 
1098 	    add_file = 1;
1099 	    i = 0;
1100 	    child[0] = '\0';
1101 	    /* pass in default filename */
1102 	    if(fn && *fn){
1103 		strncpy(child, fn, sizeof(child) - 1);
1104 		child[sizeof(child) - 1] = '\0';
1105 	    }
1106 
1107 	    while(!i){
1108 		int repaint = 0;
1109 		EXTRAKEYS opts[10];
1110 
1111 		memset((void *) &opts, 0, 10*sizeof(EXTRAKEYS));
1112 		opts[0].name  = "^X";
1113 		opts[0].label = add_file ? N_("Add Dir") : N_("Add File");
1114 		opts[0].key   = (CTRL|'X');
1115 
1116 		switch(status=mlreply_utf8(add_file ? _("Name of file to add: ") : _("Name of directory to add: "), child, NLINE,
1117 				      QFFILE, opts)){
1118 		  case HELPCH:
1119 		    emlwwrite(_("No help yet!"), NULL);
1120 /* remove break and sleep after help text is installed */
1121 		    sleep(3);
1122 		    break;
1123 		  case (CTRL|'L'):
1124 		    PaintBrowser(gmp, 0, &crow, &ccol);
1125 		    break;
1126 		  case (CTRL|'X'):
1127 		    if(add_file > 0) add_file = 0; else add_file = 1;
1128 		    break;
1129 		  case ABORT:
1130 		    emlwrite(add_file > 0 ? _("Add File Cancelled") : _("Add Directory Cancelled"), NULL);
1131 		    i++;
1132 		    break;
1133 		  case FALSE:
1134 		    /*
1135 		     * Support default filename. A return of 'FALSE' means
1136 		     * 'No change in filename' which is fine if we have
1137 		     * provided a default.
1138 		     * John Berthels <john.berthels@nexor.co.uk>
1139 		     */
1140 		    /* FALLTHROUGH */
1141 		  case TRUE:
1142 		    i++;
1143 
1144 		    if(child[0] == '\0'){
1145 			emlwrite(add_file > 0 ? _("No file named.  Add Cancelled.") : _("No directory named. Add Cancelled"), NULL);
1146 			break;
1147 		    }
1148 
1149 		    if(!compresspath(gmp->dname, child, sizeof(child))){
1150 			emlwrite(_("Too many ..'s in name"), NULL);
1151 			break;
1152 		    }
1153 
1154 		    if((gmode&MDTREE) && !in_oper_tree(child)){
1155 		       eml.s = opertree;
1156 		       emlwwrite(_("Restricted mode allows Add in %s only"),
1157 				&eml);
1158 			break;
1159 		    }
1160 
1161 		    if((status = fexist(child, "w", (off_t *)NULL)) == FIOSUC){
1162 			snprintf(tmp, sizeof(tmp), _("%s \"%.*s\" already exists!"),
1163 				add_file > 0 ? "File" : "Directory", NLINE - 20, child);
1164 			emlwrite(tmp, NULL);
1165 			break;
1166 		    }
1167 		    else if(status != FIOFNF){
1168 			fioperr(status, child);
1169 			break;
1170 		    }
1171 
1172 		    if(add_file == 0){
1173 			if(our_mkdir(child, (0700)) < 0){
1174 			   eml.s = child;
1175 			   emlwrite(_("Error adding Directory \"%s\""), &eml);
1176 			}
1177 			else	/* success! Directory added! */
1178 			  repaint = 1;
1179 		    }
1180 		    else if(ffwopen(child, FALSE) != FIOSUC){
1181 			/* ffwopen should've complained */
1182 			break;
1183 		    }
1184 		    else{			/* highlight new file */
1185 			ffclose();
1186 		        eml.s = child;
1187 			emlwrite(_("Added File \"%s\""), &eml);
1188 			repaint = 1;
1189 		    }
1190 
1191 		    if(repaint > 0){
1192 			if((p = strrchr(child, C_FILESEP)) == NULL){
1193 			    emlwrite(_("Problems refiguring browser"), NULL);
1194 			    break;
1195 			}
1196 
1197 			*p = '\0';
1198 			if(p != child){
1199 			    strncpy(tmp, child, sizeof(tmp));
1200 			    tmp[sizeof(tmp)-1] = '\0';
1201 			    j = 0;
1202 			    while((child[j++] = *++p) != '\0')
1203 			      ;
1204 			}
1205 			else{
1206 			  strncpy(tmp, S_FILESEP, sizeof(tmp));
1207 			  tmp[sizeof(tmp)-1] = '\0';
1208 			}
1209 
1210 			/*
1211 			 * new file in same dir? if so, refigure files
1212 			 * and redraw...
1213 			 */
1214 			if(!strcmp(tmp, gmp->dname)){
1215 			    if((mp = getfcells(gmp->dname, fb_flags)) == NULL)
1216 			      /* getfcells should explain what happened */
1217 			      break;
1218 
1219 			    zotmaster(&gmp);
1220 			    gmp = mp;
1221 			    if((tp = FindCell(gmp, child, 0)) != NULL){
1222 				gmp->current = tp;
1223 				PlaceCell(gmp, gmp->current, &row, &col);
1224 			    }
1225 
1226 			    PaintBrowser(gmp, 1, &crow, &ccol);
1227 			}
1228 		    }
1229 		    break;
1230 		  default:
1231 		    break;
1232 		}
1233 	    }
1234 
1235 	    BrowserKeys();
1236 	    break;
1237 
1238 	  case 'c':					/* copy */
1239 	  case 'C':
1240 	    if(gmp->current->mode == FIODIR){
1241 		emlwwrite(_("Can't copy a directory"), NULL);
1242 		break;
1243 	    }
1244 
1245 	    if(gmode&MDSCUR){				/* not allowed! */
1246 		emlwrite(_("Copy not allowed in restricted mode"),NULL);
1247 		break;
1248 	    }
1249 
1250 	    i = 0;
1251 	    child[0] = '\0';
1252 
1253 	    while(!i){
1254 
1255 		switch(status=mlreply_utf8(_("Name of new copy: "), child, NLINE,
1256 				      QFFILE, NULL)){
1257 		  case HELPCH:
1258 		    emlwwrite(_("No help yet!"), NULL);
1259 /* remove break and sleep after help text is installed */
1260 		    sleep(3);
1261 		    break;
1262 		  case (CTRL|'L'):
1263 		    PaintBrowser(gmp, 0, &crow, &ccol);
1264 		    break;
1265 		  case ABORT:
1266 		    emlwrite(_("Make Copy Cancelled"), NULL);
1267 		    i++;
1268 		    break;
1269 		  case FALSE:
1270 		    i++;
1271 		    mlerase();
1272 		    break;
1273 		  case TRUE:
1274 		    i++;
1275 
1276 		    if(child[0] == '\0'){
1277 			emlwrite(_("No destination, file not copied"), NULL);
1278 			break;
1279 		    }
1280 
1281 		    if(!strcmp(gmp->current->fname, child)){
1282 			emlwwrite(_("Can't copy file on to itself!"), NULL);
1283 			break;
1284 		    }
1285 
1286 		    if(!compresspath(gmp->dname, child, sizeof(child))){
1287 			emlwrite(_("Too many ..'s in name"), NULL);
1288 			break;
1289 		    }
1290 
1291 		    if((gmode&MDTREE) && !in_oper_tree(child)){
1292 		       eml.s = opertree;
1293 		       emlwwrite(_("Restricted mode allows Copy in %s only"),
1294 				&eml);
1295 			break;
1296 		    }
1297 
1298 		    if((status = fexist(child, "w", (off_t *)NULL)) == FIOSUC){
1299 			/* TRANSLATORS: A question: File <filename> exists! Replace? */
1300 			snprintf(tmp, sizeof(tmp), _("File \"%.*s\" exists! OVERWRITE"),
1301 				NLINE - 20, child);
1302 			if((status = mlyesno_utf8(tmp, 0)) != TRUE){
1303 			    emlwrite((status == ABORT)
1304 				      ? _("Make copy cancelled")
1305 				      : _("File Not Renamed"),
1306 				     NULL);
1307 			    break;
1308 			}
1309 		    }
1310 		    else if(status != FIOFNF){
1311 			fioperr(status, child);
1312 			break;
1313 		    }
1314 
1315 		    snprintf(tmp, sizeof(tmp), "%.*s%c%.*s", NLINE, gmp->dname, C_FILESEP,
1316 			    NLINE, gmp->current->fname);
1317 
1318 		    if(copy(tmp, child) < 0){
1319 			/* copy()  will report any error messages */
1320 			break;
1321 		    }
1322 		    else{			/* highlight new file */
1323 		        eml.s = child;
1324 			emlwrite(_("File copied to %s"), &eml);
1325 
1326 			if((p = strrchr(child, C_FILESEP)) == NULL){
1327 			    emlwrite(_("Problems refiguring browser"), NULL);
1328 			    break;
1329 			}
1330 
1331 			*p = '\0';
1332 			strncpy(tmp, (p == child) ? S_FILESEP: child, sizeof(tmp));
1333 			tmp[sizeof(tmp)-1] = '\0';
1334 
1335 			/*
1336 			 * new file in same dir? if so, refigure files
1337 			 * and redraw...
1338 			 */
1339 			if(!strcmp(tmp, gmp->dname)){
1340 			    strncpy(child, gmp->current->fname, sizeof(child));
1341 			    child[sizeof(child)-1] = '\0';
1342 			    if((mp = getfcells(gmp->dname, fb_flags)) == NULL)
1343 			      /* getfcells should explain what happened */
1344 			      break;
1345 
1346 			    zotmaster(&gmp);
1347 			    gmp = mp;
1348 			    if((tp = FindCell(gmp, child, 0)) != NULL){
1349 				gmp->current = tp;
1350 				PlaceCell(gmp, gmp->current, &row, &col);
1351 			    }
1352 
1353 			    PaintBrowser(gmp, 1, &crow, &ccol);
1354 			}
1355 		    }
1356 		    break;
1357 		  default:
1358 		    break;
1359 		}
1360 	    }
1361 	    BrowserKeys();
1362 	    break;
1363 
1364 	  case 'r':					/* rename */
1365 	  case 'R':
1366 	    i = 0;
1367 
1368 	    if(!strcmp(gmp->current->fname, "..")){
1369 		emlwwrite(_("Can't rename \"..\""), NULL);
1370 		break;
1371 	    }
1372 
1373 	    if(gmode&MDSCUR){				/* not allowed! */
1374 		emlwrite(_("Rename not allowed in restricted mode"),NULL);
1375 		break;
1376 	    }
1377 
1378 	    strncpy(child, gmp->current->fname, sizeof(child));
1379 	    child[sizeof(child)-1] = '\0';
1380 
1381 	    while(!i){
1382 
1383 		switch(status=mlreply_utf8(_("Rename file to: "), child, NLINE, QFFILE,
1384 				      NULL)){
1385 		  case HELPCH:
1386 		    emlwwrite(_("No help yet!"), NULL);
1387 /* remove break and sleep after help text is installed */
1388 		    sleep(3);
1389 		    break;
1390 		  case (CTRL|'L'):
1391 		    PaintBrowser(gmp, 0, &crow, &ccol);
1392 		    break;
1393 		  case ABORT:
1394 		    emlwrite(_("Rename cancelled"), NULL);
1395 		    i++;
1396 		    break;
1397 		  case FALSE:
1398 		  case TRUE:
1399 		    i++;
1400 
1401 		    if(child[0] == '\0' || status == FALSE){
1402 			mlerase();
1403 			break;
1404 		    }
1405 
1406 		    if(!compresspath(gmp->dname, child, sizeof(child))){
1407 			emlwrite(_("Too many ..'s in name"), NULL);
1408 			break;
1409 		    }
1410 
1411 		    if((gmode&MDTREE) && !in_oper_tree(child)){
1412 		       eml.s = opertree;
1413 		       emlwwrite(_("Restricted mode allows Rename in %s only"),
1414 				&eml);
1415 			break;
1416 		    }
1417 
1418 		    status = fexist(child, "w", (off_t *)NULL);
1419 		    if(status == FIOSUC || status == FIOFNF){
1420 			if(status == FIOSUC){
1421 			    snprintf(tmp, sizeof(tmp), _("File \"%.*s\" exists! OVERWRITE"),
1422 				    NLINE - 20, child);
1423 
1424 			    if((status = mlyesno_utf8(tmp, FALSE)) != TRUE){
1425 				emlwrite((status ==  ABORT)
1426 					  ? _("Rename cancelled")
1427 					  : _("Not Renamed"),
1428 					 NULL);
1429 				break;
1430 			    }
1431 			}
1432 
1433 			snprintf(tmp, sizeof(tmp), "%.*s%c%.*s", NLINE, gmp->dname, C_FILESEP,
1434 				NLINE, gmp->current->fname);
1435 
1436 			if(our_rename(tmp, child) < 0){
1437 			    eml.s = errstr(errno);
1438 			    emlwrite(_("Rename Failed: %s"), &eml);
1439 			}
1440 			else{
1441 			    if((p = strrchr(child, C_FILESEP)) == NULL){
1442 				emlwrite(_("Problems refiguring browser"), NULL);
1443 				break;
1444 			    }
1445 
1446 			    *p = '\0';
1447 			    strncpy(tmp, (p == child) ? S_FILESEP: child, sizeof(tmp));
1448 			    tmp[sizeof(tmp)-1] = '\0';
1449 
1450 			    if((mp = getfcells(tmp, fb_flags)) == NULL)
1451 			      /* getfcells should explain what happened */
1452 			      break;
1453 
1454 			    zotmaster(&gmp);
1455 			    gmp = mp;
1456 
1457 			    if((tp = FindCell(gmp, ++p, 0)) != NULL){
1458 				gmp->current = tp;
1459 				PlaceCell(gmp, gmp->current, &row, &col);
1460 			    }
1461 
1462 			    PaintBrowser(gmp, 1, &crow, &ccol);
1463 			    mlerase();
1464 			}
1465 		    }
1466 		    else{
1467 			fioperr(status, child);
1468 		    }
1469 		    break;
1470 		  default:
1471 		    break;
1472 		}
1473 	    }
1474 	    BrowserKeys();
1475 	    break;
1476 
1477 	  case 'v':					/* stand-alone */
1478 	  case 'V':					/* browser "view" */
1479 	  case 's':					/* user "select" */
1480 	  case 'S':
1481 	  case (CTRL|'M'):
1482 	  Selected:
1483 
1484 	    if(((new_c == 'S' || new_c == 's') && (gmode&MDBRONLY))
1485 	       || ((new_c == 'V' || new_c == 'v') && !(gmode&MDBRONLY)))
1486 	      goto Default;
1487 
1488 	    if(gmp->current->mode == FIODIR){
1489 		*child = '\0';
1490 		strncpy(tmp, gmp->dname, sizeof(tmp));
1491 		tmp[sizeof(tmp)-1] = '\0';
1492 		p = gmp->current->fname;
1493 		if(p[0] == '.' && p[1] == '.' && p[2] == '\0'){
1494 		    if((p=strrchr(tmp, C_FILESEP)) != NULL){
1495 			*p = '\0';
1496 
1497 			if((gmode&MDTREE) && !in_oper_tree(tmp)){
1498 			    eml.s = PARENTDIR;
1499 			    emlwwrite(
1500 				   _("Can't visit %s in restricted mode"),
1501 				   &eml);
1502 			    break;
1503 			}
1504 
1505 			strncpy(child, &p[1], sizeof(child));
1506 			child[sizeof(child)-1] = '\0';
1507 
1508 			if
1509 #if defined(DOS) || defined(OS2)
1510 			  (p == tmp || (tmp[1] == ':' && tmp[2] == '\0'))
1511 #else
1512 			  (p == tmp)
1513 #endif
1514 			  {	/* is it root? */
1515 #if defined(DOS) || defined(OS2)
1516 			      if(*child){
1517 				strncat(tmp, S_FILESEP, sizeof(tmp)-strlen(tmp)-1);
1518 				tmp[sizeof(tmp)-1] = '\0';
1519 			      }
1520 #else
1521 			      if(*child){
1522 			        strncpy(tmp, S_FILESEP, sizeof(tmp));
1523 				tmp[sizeof(tmp)-1] = '\0';
1524 			      }
1525 #endif
1526 			      else{
1527 				  emlwwrite(_("Can't move up a directory"),
1528 					   NULL);
1529 				  break;
1530 			      }
1531 			  }
1532 		    }
1533 		}
1534 		else if((fb_flags&FB_SAVE) && p[0] == '.' && p[1] == '\0'){
1535 		    if ((strlen(gmp->dname) < dirlen) &&
1536 			(strlen(gmp->current->fname) < fnlen)){
1537 			strncpy(dir, gmp->dname, dirlen);
1538 			dir[dirlen-1] = '\0';
1539 		    }
1540 
1541 		    zotmaster(&gmp);
1542 		    return(0);  /* just change the directory, still return no selection */
1543 		}
1544 		else{
1545 		    if(tmp[1] != '\0'){		/* were in root? */
1546 		      strncat(tmp, S_FILESEP, sizeof(tmp)-strlen(tmp)-1);
1547 		      tmp[sizeof(tmp)-1] = '\0';
1548 		    }
1549 
1550 		    strncat(tmp, gmp->current->fname, sizeof(tmp)-strlen(tmp)-1);
1551 		    tmp[sizeof(tmp)-1] = '\0';
1552 		}
1553 
1554 		if((mp = getfcells(tmp, fb_flags)) == NULL)
1555 		  /* getfcells should explain what happened */
1556 		  break;
1557 
1558 		if(gmp->flags & FB_LMODE){
1559 		    mp->flags |= FB_LMODE;
1560 		    mp->lm = gmp->lm;
1561 		    gmp->lm = NULL;
1562 		}
1563 
1564 		zotmaster(&gmp);
1565 		gmp = mp;
1566 		tp  = NULL;
1567 
1568 		if(*child){
1569 		    if((tp = FindCell(gmp, child, 0)) != NULL){
1570 			gmp->current = tp;
1571 			PlaceCell(gmp, gmp->current, &row, &col);
1572 		    }
1573 		    else{
1574 			eml.s = child;
1575 			emlwwrite(_("Problem finding dir \"%s\""), &eml);
1576 		    }
1577 		}
1578 
1579 		PaintBrowser(gmp, 0, &crow, &ccol);
1580 		if(!*child){
1581 		  char b[100];
1582 
1583 		  snprintf(b, sizeof(b), "Select/View \" .. %s %s\" to return to previous directory.", PARENTDIR, DIRWORD);
1584 		  emlwrite(b, NULL);
1585 		}
1586 
1587 		break;
1588 	    }
1589 	    else if(gmode&MDBRONLY){
1590 		snprintf(child, sizeof(child), "%.*s%c%.*s", NLINE, gmp->dname, C_FILESEP,
1591 			NLINE, gmp->current->fname);
1592 
1593 		if(LikelyASCII(child)){
1594 		    char *t;
1595 		    envp = (char *) getenv("PAGER");
1596 		    t = fs_get((envp ? strlen(envp) : strlen(BROWSER_PAGER))
1597 				+ strlen(child) + 3 + 1);
1598 		    sprintf(t, "%s \'%s\'", envp ? envp : BROWSER_PAGER, child);
1599 		    BrowserRunChild(t, gmp->dname);
1600 		    PaintBrowser(gmp, 0, &crow, &ccol);
1601 		    if(t) fs_give((void **) &t);
1602 		}
1603 
1604 		break;
1605 	    }
1606 	    else{				/* just return */
1607 	      if(gmp->flags & FB_LMODEPOS){
1608 
1609 		  if(!lmreturn){
1610 		      emlwrite("Programming error, called FileBrowse with LMODEPOS but no lmreturn", NULL);
1611 		      zotmaster(&gmp);
1612 		      return(-1);
1613 		  }
1614 
1615 		  /* user actually used ListMode, the list is finished */
1616 		  if(gmp->flags & FB_LMODE){
1617 		      if(!gmp->lm){
1618 			  (*term.t_beep)();
1619 			  emlwrite(_("No files are selected, use \"X\" to mark files for selection"), NULL);
1620 			  break;
1621 		      }
1622 
1623 		      *lmreturn = gmp->lm;
1624 		      gmp->lm = NULL;
1625 		  }
1626 		  else{		/* construct an lmreturn for user */
1627 		      LMLIST *new;
1628 		      size_t flen, dlen;
1629 
1630 		      if((new=(LMLIST *)malloc(sizeof(*new))) == NULL
1631 			 || (new->fname=malloc(gmp->current->fname ? (flen=strlen(gmp->current->fname))+1 : 1)) == NULL
1632 			 || (new->dir=malloc((dlen=strlen(gmp->dname))+1)) == NULL){
1633 			emlwwrite(_("Can't malloc space for filename"), NULL);
1634 			return(-1);
1635 		      }
1636 
1637 		      strncpy(new->fname,
1638 			      gmp->current->fname ? gmp->current->fname : "", flen);
1639 		      new->fname[flen] = '\0';
1640 		      strncpy(new->dir, gmp->dname, dlen);
1641 		      new->dir[dlen] = '\0';
1642 		      strncpy(new->size, gmp->current->size, sizeof(new->size));
1643 		      new->size[sizeof(new->size)-1] = '\0';
1644 		      new->next = NULL;
1645 		      *lmreturn = new;
1646 		  }
1647 
1648 		  zotmaster(&gmp);
1649 		  return(1);
1650 	      }
1651 
1652 	      if ((strlen(gmp->dname) < dirlen) &&
1653 		  (strlen(gmp->current->fname) < fnlen)){
1654 		  strncpy(dir, gmp->dname, dirlen);
1655 		  dir[dirlen-1] = '\0';
1656 		  strncpy(fn, gmp->current->fname, fnlen);
1657 		  fn[fnlen-1] = '\0';
1658 	      }
1659 	      else {
1660 		zotmaster(&gmp);
1661 		return(-1);
1662 	      }
1663 	      if(sz != NULL){			/* size uninteresting */
1664 		strncpy(sz, gmp->current->size, szlen);
1665 		sz[szlen-1] = '\0';
1666 	      }
1667 
1668 	      zotmaster (&gmp);
1669 	      return (1);
1670 	    }
1671 	    break;
1672 
1673 	  case 'w':				/* Where is */
1674 	  case 'W':
1675 	  case (CTRL|'W'):
1676 	    i = 0;
1677 	    flags = SR_ORIGMEN | SR_FORWARD | SR_NOEXACT;
1678 
1679 	    while(!i){
1680 		switch(readpattern(_("File name to find"), FALSE, flags)){
1681 		  case HELPCH:
1682 		    emlwwrite(_("No help yet!"), NULL);
1683 /* remove break and sleep after help text is installed */
1684 		    sleep(3);
1685 		    break;
1686 		  case (CTRL|'L'):
1687 		    PaintBrowser(gmp, 0, &crow, &ccol);
1688 		    break;
1689 		  case (CTRL|'P'):
1690 		    if(flags & SR_FORWARD){
1691 			flags &= ~SR_FORWARD;
1692 			flags |=  SR_BACKWRD;
1693 		    } else {
1694 			flags &= ~SR_BACKWRD;
1695 			flags |=  SR_FORWARD;
1696 		    }
1697 		    break;
1698 		  case (CTRL|'Y'):		/* first first cell */
1699 		    for(tp = gmp->top; tp->prev; tp = tp->prev)
1700 		      ;
1701 
1702 		    i++;
1703 		    /* fall thru to repaint */
1704 		  case (CTRL|'V'):
1705 		    if(!i){
1706 			do{
1707 			    tp = gmp->top;
1708 			    if((i = term.t_nrow - term.t_mrow - 2) <= 0)
1709 			      break;
1710 
1711 			    while(i-- && tp->next){
1712 				j = 0;
1713 				while(++j <= gmp->fpl  && tp->next)
1714 				  tp = tp->next;
1715 			    }
1716 
1717 			    if(i < 0)
1718 			      gmp->top = tp;
1719 			}
1720 			while(tp->next);
1721 
1722 			emlwrite(_("Searched to end of directory"), NULL);
1723 		    }
1724 		    else
1725 		      emlwrite(_("Searched to start of directory"), NULL);
1726 
1727 		    if(tp){
1728 			PlaceCell(gmp, gmp->current, &row, &col);
1729 			PaintCell(row, col, gmp->cpf, gmp->current, 0);
1730 			gmp->current = tp;
1731 			if(PlaceCell(gmp, gmp->current, &row, &col)){
1732 			    PaintBrowser(gmp, 1, &crow, &ccol);
1733 			}
1734 			else{
1735 			    PaintCell(row, col, gmp->cpf, gmp->current, 1);
1736 			    crow = row;
1737 			    ccol = col;
1738 			}
1739 		    }
1740 
1741 		    i++;			/* make sure we jump out */
1742 		    break;
1743 		  case ABORT:
1744 		    emlwrite(_("Whereis cancelled"), NULL);
1745 		    i++;
1746 		    break;
1747 		  case FALSE:
1748 		    mlerase();
1749 		    i++;
1750 		    break;
1751 		  case TRUE:
1752 		    {
1753 		    char *utf8 = NULL;
1754 
1755 		    if(pat && pat[0])
1756 		      utf8 = ucs4_to_utf8_cpystr(pat);
1757 
1758 		    if(utf8 && (tp = FindCell(gmp, utf8, flags & SR_BACKWRD)) != NULL){
1759 			PlaceCell(gmp, gmp->current, &row, &col);
1760 			PaintCell(row, col, gmp->cpf, gmp->current, 0);
1761 			gmp->current = tp;
1762 
1763 			if(PlaceCell(gmp, tp, &row, &col)){ /* top changed */
1764 			    PaintBrowser(gmp, 1, &crow, &ccol);
1765 			}
1766 			else{
1767 			    PaintCell(row, col, gmp->cpf, gmp->current, 1);
1768 			    crow = row;
1769 			    ccol = col;
1770 			}
1771 			mlerase();
1772 		    }
1773 		    else{
1774 			eml.s = utf8;
1775 			emlwrite(_("\"%s\" not found"), &eml);
1776 		    }
1777 
1778 		    if(utf8)
1779 		      fs_give((void **) &utf8);
1780 		    }
1781 
1782 		    i++;
1783 		    break;
1784 		  default:
1785 		    break;
1786 		}
1787 	    }
1788 
1789 	    BrowserKeys();
1790 	    break;
1791 
1792 	  case (CTRL|'\\') :
1793 #if defined MOUSE && !defined(_WINDOWS)
1794 	    toggle_xterm_mouse(0,1);
1795 #else
1796 	    unknown_command(c);
1797 #endif
1798 	    break;
1799 
1800 	  case (CTRL|'Z'):
1801 	    if(gmode&MDSSPD){
1802 		bktoshell(0, 1);
1803 		PaintBrowser(gmp, 0, &crow, &ccol);
1804 		break;
1805 	    }					/* fall thru with error! */
1806 
1807 	  default:				/* what? */
1808 	  Default:
1809 	    unknown_command(c);
1810 
1811 	  case NODATA:				/* no op */
1812 	    break;
1813 	}
1814     }
1815 }
1816 
1817 
1818 /*
1819  * getfcells - make a master browser struct and fill it in
1820  *             return NULL if there's a problem.
1821  */
1822 struct bmaster *
getfcells(char * dname,int fb_flags)1823 getfcells(char *dname, int fb_flags)
1824 {
1825     int  i, 					/* various return codes */
1826          flength,
1827          nentries = 0;				/* number of dir ents */
1828     off_t attsz;
1829     char *np,					/* names of files in dir */
1830          *dcp,                                  /* to add file to path */
1831          *tmpstr,
1832          **filtnames,				/* array filtered names */
1833 	 errbuf[NLINE];
1834     struct fcell *ncp,				/* new cell pointer */
1835                  *tcp = NULL;			/* trailing cell ptr */
1836     struct bmaster *mp;
1837     EML  eml;
1838 
1839     errbuf[0] = '\0';
1840     if((mp=(struct bmaster *)malloc(sizeof(struct bmaster))) == NULL){
1841 	emlwwrite(_("Can't malloc space for master filename cell"), NULL);
1842 	return(NULL);
1843     }
1844 
1845     memset(mp, 0, sizeof(*mp));
1846 
1847     if(dname[0] == '.' && dname[1] == '\0'){		/* remember this dir */
1848 	if(!getcwd(mp->dname, 256))
1849 	  mp->dname[0] = '\0';
1850     }
1851     else if(dname[0] == '.' && dname[1] == '.' && dname[2] == '\0'){
1852 	if(!getcwd(mp->dname, 256))
1853 	  mp->dname[0] = '\0';
1854 	else{
1855 	    if((np = (char *)strrchr(mp->dname, C_FILESEP)) != NULL)
1856 	      if(np != mp->dname)
1857 		*np = '\0';
1858 	}
1859     }
1860     else{
1861 	strncpy(mp->dname, dname, sizeof(mp->dname));
1862 	mp->dname[sizeof(mp->dname)-1] = '\0';
1863     }
1864 
1865     mp->bottom = mp->head = mp->top = NULL;
1866     mp->menu = mp->cpf = mp->fpl = 0;
1867     mp->longest = 5;				/* .. must be labeled! */
1868     mp->flags = fb_flags;
1869 
1870     eml.s = mp->dname;
1871     emlwrite("Building file list of %s...", &eml);
1872 
1873     if((mp->names = getfnames(mp->dname, NULL, &nentries, errbuf, sizeof(errbuf))) == NULL){
1874 	free((char *) mp);
1875 	if(*errbuf)
1876 	  emlwwrite(errbuf, NULL);
1877 
1878 	return(NULL);
1879     }
1880 
1881     /*
1882      * this is the fun part.  build an array of pointers to the fnames we're
1883      * interested in (i.e., do any filtering), then pass that off to be
1884      * sorted before building list of cells...
1885      *
1886      * right now default filtering on ".*" except "..", but this could
1887      * easily be made a user option later on...
1888      */
1889     if((filtnames=(char **)malloc((nentries+1) * sizeof(char *))) == NULL){
1890 	emlwwrite(_("Can't malloc space for name array"), NULL);
1891 	zotmaster(&mp);
1892 	return(NULL);
1893     }
1894 
1895     i = 0;					/* index of filt'd array */
1896     np = mp->names;
1897     while(nentries--){
1898 	int ii;
1899 	int width;
1900 
1901 	/*
1902 	 * Filter dot files?  Always filter ".", never filter "..",
1903 	 * and sometimes filter ".*"...
1904 	 */
1905 	if(*np == '.' && (!(*(np+1) == '.' && *(np+2) == '\0')
1906 			  && !(*(np+1) == '\0' && (fb_flags&FB_SAVE)))
1907 	   && (*(np+1) == '\0' || !(gmode & MDDOTSOK))){
1908 	    np += strlen(np) + 1;
1909 	    continue;
1910 	}
1911 
1912 	filtnames[i++] = np;
1913 
1914 	ii = (int) strlen(np);
1915 	if((width = (int) utf8_width(np)) > mp->longest)
1916 	  mp->longest = width;			/* remember longest */
1917 
1918 	np += ii + 1;				/* advance name pointer */
1919     }
1920 
1921     nentries = i;				/* new # of entries */
1922 
1923     /*
1924      * sort files case independently
1925      */
1926     qsort((qsort_t *)filtnames, (size_t)nentries, sizeof(char *), sstrcasecmp);
1927 
1928     /*
1929      * this is so we use absolute path names for stats.
1930      * remember: be careful using dname as directory name, and fix mp->dname
1931      * when we're done
1932      */
1933     dcp = (char *) strchr(mp->dname, '\0');
1934     if(dcp == mp->dname || dcp[-1] != C_FILESEP){
1935 	dcp[0] = C_FILESEP;
1936 	dcp[1] = '\0';
1937     }
1938     else
1939       dcp--;
1940 
1941     i = 0;
1942     while(nentries--){				/* stat filtered files */
1943 	/* get a new cell */
1944 	if((ncp=(struct fcell *)malloc(sizeof(struct fcell))) == NULL){
1945 	    emlwwrite(_("Can't malloc cells for browser!"), NULL);
1946 	    zotfcells(mp->head);		/* clean up cells */
1947 	    free((char *) filtnames);
1948 	    free((char *) mp);
1949 	    return(NULL);			/* bummer. */
1950 	}
1951 
1952 	ncp->next = ncp->prev = NULL;
1953 
1954 	if(mp->head == NULL){			/* tie it onto the list */
1955 	    mp->head = mp->top = mp->current = ncp;
1956 	}
1957 	else{
1958 	    mp->bottom = ncp;
1959 	    tcp->next = ncp;
1960 	    ncp->prev = tcp;
1961 	}
1962 
1963 	tcp = ncp;
1964 
1965 	/* fill in the new cell */
1966 	ncp->fname = filtnames[i++];
1967 
1968 	/* fill in file's mode */
1969 	if ((flength = strlen(ncp->fname) + 1 + strlen(dname)) < sizeof(mp->dname)){
1970 	  strncpy(&dcp[1], ncp->fname, sizeof(mp->dname)-(dcp+1-mp->dname)); /* use absolute path! */
1971 	  mp->dname[sizeof(mp->dname)-1] = '\0';
1972 	  tmpstr = mp->dname;
1973 	}
1974 	else{
1975 	  if((tmpstr = (char *)malloc((flength+1)*sizeof(char))) == NULL){
1976 	    emlwwrite(_("Can't malloc cells for temp buffer!"), NULL);
1977             zotfcells(mp->head);                /* clean up cells */
1978             free((char *) filtnames);
1979             free((char *) mp);
1980             return(NULL);                       /* bummer. */
1981 	  }
1982 
1983 	  strncpy(tmpstr, dname, flength);
1984 	  tmpstr[flength] = '\0';
1985 	  tmpstr = strncat(tmpstr, S_FILESEP, flength+1-1-strlen(tmpstr));
1986 	  tmpstr[flength] = '\0';
1987 	  tmpstr = strncat(tmpstr, ncp->fname, flength+1-1-strlen(tmpstr));
1988 	  tmpstr[flength] = '\0';
1989 	}
1990 
1991 	switch(fexist(tmpstr, "t", &attsz)){
1992 	  case FIODIR :
1993 	    ncp->mode = FIODIR;
1994 	    snprintf(ncp->size, sizeof(ncp->size), "(%s%s%s)",
1995 		    (ncp->fname[0] == '.' && ncp->fname[1] == '.'
1996 		     && ncp->fname[2] == '\0') ? PARENTDIR :
1997 		    ((fb_flags&FB_SAVE) && ncp->fname[0] == '.' && ncp->fname[1] == '\0'
1998 		     ? SELECTWORD : ""),
1999 		    (ncp->fname[0] == '.' && ncp->fname[1] == '.'
2000 		     && ncp->fname[2] == '\0') ? " " :
2001 		    ((fb_flags&FB_SAVE) && ncp->fname[0] == '.' && ncp->fname[1] == '\0'
2002 		     ? " " : ""),
2003 		     DIRWORD);
2004 	    break;
2005 
2006 	  case FIOSYM :
2007 	    ncp->mode = FIOSYM;
2008 	    strncpy(ncp->size, "--", sizeof(ncp->size));
2009 	    ncp->size[sizeof(ncp->size)-1] = '\0';
2010 	    break;
2011 
2012 	  default :
2013 	    ncp->mode = FIOSUC;			/* regular file */
2014 	    strncpy(ncp->size, prettysz(attsz), sizeof(ncp->size));
2015 	    ncp->size[sizeof(ncp->size)-1] = '\0';
2016 	    break;
2017 	}
2018 
2019 	if (flength >= NLINE)
2020 	  free((char *) tmpstr);
2021     }
2022 
2023     dcp[(dcp == mp->dname) ? 1 : 0] = '\0';	/* remember to cap dname */
2024     free((char *) filtnames);			/* 'n blast filt'd array*/
2025 
2026     percdircells(mp);
2027     layoutcells(mp);
2028     if(strlen(mp->dname) < sizeof(browse_dir)){
2029       strncpy(browse_dir, mp->dname, sizeof(browse_dir));
2030       browse_dir[sizeof(browse_dir)-1] = '\0';
2031     }
2032     else
2033       browse_dir[0] = '\0';
2034 
2035     return(mp);
2036 }
2037 
2038 
2039 int
fcell_is_selected(struct fcell * cell,struct bmaster * mp)2040 fcell_is_selected(struct fcell *cell, struct bmaster *mp)
2041 {
2042     LMLIST *lm;
2043 
2044     if(cell && cell->fname){
2045 	for(lm = mp ? mp->lm : NULL; lm; lm = lm->next){
2046 	    /* directory has to match */
2047 	    if(!((mp->dname[0] == '\0' && (!lm->dir || lm->dir[0] =='\0'))
2048 	         || (mp->dname[0] != '\0' && lm->dir && lm->dir[0] !='\0'
2049 	             && !strcmp(mp->dname, lm->dir))))
2050 	      continue;
2051 
2052 	    if(lm->fname && !strcmp(cell->fname, lm->fname))
2053 	      return(1);
2054 	}
2055     }
2056 
2057     return(0);
2058 }
2059 
2060 
2061 /*
2062  * Adds a new name to the head of the lmlist
2063  */
2064 void
add_cell_to_lmlist(struct fcell * cell,struct bmaster * mp)2065 add_cell_to_lmlist(struct fcell *cell, struct bmaster *mp)
2066 {
2067     LMLIST *new;
2068     size_t flen, dlen;
2069 
2070     if(mp && cell && cell->fname && cell->fname[0]){
2071 	if((new=(LMLIST *)malloc(sizeof(*new))) == NULL ||
2072 	   (new->fname=malloc(sizeof(char)*((flen=strlen(cell->fname))+1))) == NULL ||
2073 	   (new->dir=malloc(sizeof(char)*((dlen=strlen(mp->dname))+1))) == NULL){
2074 	    emlwwrite(_("Can't malloc space for filename"), NULL);
2075 	    return;
2076 	}
2077 
2078 	strncpy(new->fname, cell->fname, flen);
2079 	new->fname[flen] = '\0';
2080 	strncpy(new->dir, mp->dname, dlen);
2081 	new->dir[dlen] = '\0';
2082 	new->size[0] = '\0';
2083 	if(cell->size[0]){
2084 	    strncpy(new->size, cell->size, sizeof(new->size));
2085 	    new->size[sizeof(new->size)-1] = '\0';
2086 	}
2087 
2088 	new->next = mp->lm;
2089 	mp->lm = new;
2090     }
2091 }
2092 
2093 
2094 /*
2095  * Deletes a name from the lmlist
2096  */
2097 void
del_cell_from_lmlist(struct fcell * cell,struct bmaster * mp)2098 del_cell_from_lmlist(struct fcell *cell, struct bmaster *mp)
2099 {
2100     LMLIST *lm, *lmprev = NULL;
2101 
2102     if(mp && cell && cell->fname && cell->fname[0])
2103       for(lm = mp ? mp->lm : NULL; lm; lm = lm->next){
2104 	  if(lm->fname && strcmp(cell->fname, lm->fname) == 0){
2105 	      free((char *) lm->fname);
2106 	      if(lm->dir)
2107 	        free((char *) lm->dir);
2108 
2109 	      if(lmprev)
2110 		lmprev->next = lm->next;
2111 	      else
2112 		mp->lm = lm->next;
2113 
2114 	      free((char *) lm);
2115 
2116 	      break;
2117 	  }
2118 
2119 	  lmprev = lm;
2120       }
2121 }
2122 
2123 
2124 /*
2125  * PaintCell - print the given cell at the given location on the display
2126  *             the format of a printed cell is:
2127  *
2128  *                       "<fname>       <size>  "
2129  */
2130 void
PaintCell(int row,int col,int sc,struct fcell * cell,int inverted)2131 PaintCell(int row, int col,
2132 	  int sc,	/* screen columns available for this cell */
2133 	  struct fcell *cell, int inverted)
2134 {
2135     char  buf1[NLINE], buf2[NLINE];
2136     char  lbuf[5];
2137     int   need, l_wid, f_wid, f_to_s_wid, s_wid;
2138     UCS  *ucs;
2139 
2140     if(cell == NULL)
2141 	return;
2142 
2143     l_wid = (gmp && ((gmp->flags & FB_LMODEPOS) > 4)) ? 4 : 0;
2144     f_wid = utf8_width(cell->fname ? cell->fname : "");
2145     f_to_s_wid = 1;
2146     s_wid = utf8_width(cell->size ? cell->size : "");
2147 
2148     /* the two is the space between cell columns */
2149     sc = MIN(sc-2, term.t_ncol-col);
2150 
2151     if(l_wid){
2152 	if(gmp && gmp->flags & FB_LMODE && cell->mode != FIODIR){
2153 	    /*
2154 	     * We have to figure out here if it is selected or not
2155 	     * and use that to write the X or space.
2156 	     */
2157 	    lbuf[0] = '[';
2158 	    if(fcell_is_selected(cell, gmp))
2159 	      lbuf[1] = 'X';
2160 	    else
2161 	      lbuf[1] = ' ';
2162 
2163 	    lbuf[2] = ']';
2164 	    lbuf[3] = ' ';
2165 	}
2166 	else{
2167 	    lbuf[0] = lbuf[1] = lbuf[2] = lbuf[3] = ' ';
2168 	}
2169 
2170 	lbuf[4] = '\0';
2171     }
2172 
2173     sc -= l_wid;
2174 
2175     need = f_wid+f_to_s_wid+s_wid;
2176 
2177     /* space between name and size */
2178     if(need < sc)
2179       f_to_s_wid += (sc-need);
2180 
2181     /*
2182      * If the width isn't enough to handle everything we're just putting a single
2183      * space between fname and size and truncating on the right. That means that
2184      * the sizes in the right hand column won't line up correctly when there is
2185      * a lack of space. Instead, we opt for displaying as much info as possible
2186      * in a slightly discordant way.
2187      */
2188 
2189     movecursor(row, col);
2190     if(l_wid){
2191 	ucs = utf8_to_ucs4_cpystr(lbuf);
2192 	if(ucs){
2193 	    pputs(ucs, 0);
2194 	    fs_give((void **) &ucs);
2195 	}
2196     }
2197 
2198     utf8_snprintf(buf1, sizeof(buf1), "%*.*w%*.*w%*.*w",
2199 		  f_wid, f_wid, cell->fname ? cell->fname : "",
2200 		  f_to_s_wid, f_to_s_wid, "",
2201 		  s_wid, s_wid, cell->size ? cell->size : "");
2202 
2203     utf8_snprintf(buf2, sizeof(buf2), "%*.*w", sc, sc, buf1);
2204 
2205     if(inverted)
2206       (*term.t_rev)(1);
2207     else if (*term.t_eri)
2208       (*term.t_eri)();
2209 
2210     ucs = utf8_to_ucs4_cpystr(buf2);
2211     if(ucs){
2212 	pputs(ucs, 0);
2213 	fs_give((void **) &ucs);
2214     }
2215 
2216     if(inverted)
2217       (*term.t_rev)(0);
2218 }
2219 
2220 
2221 /*
2222  * PaintBrowse - with the current data, display the browser.  if level == 0
2223  *               paint the whole thing, if level == 1 just paint the cells
2224  *               themselves
2225  */
2226 void
PaintBrowser(struct bmaster * mp,int level,int * row,int * col)2227 PaintBrowser(struct bmaster *mp, int level, int *row, int *col)
2228 {
2229     int i, cl;
2230     struct fcell *tp;
2231 
2232     if(!level){
2233 	ClearBrowserScreen();
2234 	BrowserAnchor(mp->dname);
2235     }
2236 
2237     i = 0;
2238     tp = mp->top;
2239     cl = COMPOSER_TOP_LINE;			/* current display line */
2240     while(tp){
2241 
2242 	PaintCell(cl, mp->cpf * i, mp->cpf, tp, tp == mp->current);
2243 
2244 	if(tp == mp->current){
2245 	    if(row)
2246 	      *row = cl;
2247 
2248 	    if(col)
2249 	      *col = mp->cpf * i;
2250 	}
2251 
2252 	if(++i >= mp->fpl){
2253 	    i = 0;
2254 	    if(++cl > term.t_nrow-(term.t_mrow+1))
2255 	      break;
2256 	}
2257 
2258 	tp = tp->next;
2259     }
2260 
2261     if(level){
2262 	while(cl <= term.t_nrow - (term.t_mrow+1)){
2263 	    if(!i)
2264 	      movecursor(cl, 0);
2265 	    peeol();
2266 	    movecursor(++cl, 0);
2267 	}
2268     }
2269     else{
2270 	BrowserKeys();
2271     }
2272 }
2273 
2274 /*
2275  * RepaintBrowser - factorized method to just repaint everything
2276  */
2277 struct bmaster *
RepaintBrowser(struct bmaster * gmp,int fb_flags)2278 RepaintBrowser(struct bmaster *gmp, int fb_flags)
2279 {
2280 	struct bmaster *mp;
2281 	struct fcell *tp;
2282 	int row, col;
2283 	char *fn;
2284 	fn = gmp->current->fname;
2285 
2286 	if((mp = getfcells(gmp->dname, fb_flags)) == NULL){
2287 	  /* getfcells should explain what happened */
2288 	  return(gmp);
2289 	}
2290 	zotmaster(&gmp);
2291 	gmp = mp;
2292 
2293 	tp = NULL;
2294 	if(*fn){
2295 	if((tp = FindCell(gmp, fn, 0)) != NULL){
2296 	  gmp->current = tp;
2297 	  PlaceCell(gmp, gmp->current, &row, &col);
2298 	}
2299 	}
2300 
2301 	PaintBrowser(gmp, 0, NULL, NULL);
2302 	return(gmp);
2303 }
2304 
2305 
2306 /*
2307  * BrowserKeys - just paints the keyhelp at the bottom of the display
2308  */
2309 void
BrowserKeys(void)2310 BrowserKeys(void)
2311 {
2312     if (gmp && gmp->menu == 1){
2313 	menu_other[GOTO_KEY].name  = (gmode&MDGOTO) ? "G" : NULL;
2314 	menu_other[GOTO_KEY].label = (gmode&MDGOTO) ? N_("Goto") : NULL;
2315 	wkeyhelp(menu_other);
2316 	return;
2317     }
2318     menu_browse[QUIT_KEY].name  = (gmode&MDBRONLY) ? "Q" : "E";
2319     /* TRANSLATORS: Brwsr is an abbreviation for Browser, which is
2320        a command used to look through something */
2321     menu_browse[QUIT_KEY].label = (gmode&MDBRONLY) ? N_("Quit") : N_("Exit Brwsr");
2322     if(gmode & MDBRONLY){
2323 	menu_browse[EXEC_KEY].name  = "L";
2324 	menu_browse[EXEC_KEY].label = N_("Launch");
2325 	menu_browse[SELECT_KEY].name  = "V";
2326 	menu_browse[SELECT_KEY].label = "[" N_("View") "]";
2327 	menu_browse[PICO_KEY].name  = "E";
2328 	menu_browse[PICO_KEY].label = N_("Edit");
2329     }
2330     else{
2331 	menu_browse[SELECT_KEY].name  = "S";
2332 	menu_browse[SELECT_KEY].label = "[" N_("Select") "]";
2333 	menu_browse[PICO_KEY].name  = "A";
2334 	menu_browse[PICO_KEY].label = N_("Add");
2335 
2336 	if(gmp && gmp->flags & FB_LMODEPOS){
2337 	    if(gmp && gmp->flags & FB_LMODE){	/* ListMode is already on */
2338 		menu_browse[EXEC_KEY].name  = "X";
2339 		menu_browse[EXEC_KEY].label = N_("Set/Unset");
2340 	    }
2341 	    else{				/* ListMode is possible   */
2342 		menu_browse[EXEC_KEY].name  = "L";
2343 		menu_browse[EXEC_KEY].label = N_("ListMode");
2344 	    }
2345 	}
2346 	else{					/* No ListMode possible   */
2347 	    menu_browse[EXEC_KEY].name  = NULL;
2348 	    menu_browse[EXEC_KEY].label = NULL;
2349 	}
2350     }
2351 
2352     wkeyhelp(menu_browse);
2353 }
2354 
2355 
2356 /*
2357  * layoutcells - figure out max length of cell and how many cells can
2358  *               go on a line of the display
2359  */
2360 void
layoutcells(struct bmaster * mp)2361 layoutcells(struct bmaster *mp)
2362 {
2363     static int wid = -1;
2364     /*
2365      * Max chars/file. Actually this is max screen cells/file
2366      * Longest name + "(parent dir)"
2367      */
2368     if(wid < 0)
2369       wid = MAX(utf8_width(PARENTDIR),utf8_width(SELECTWORD)) + utf8_width(DIRWORD);
2370 
2371     mp->cpf = mp->longest + wid + 3;
2372 
2373     if(mp->flags & FB_LMODEPOS)			/* "[X] " */
2374       mp->cpf += 4;
2375 
2376     if(gmode & MDONECOL){
2377 	mp->fpl = 1;
2378     }
2379     else{
2380 	int i = 1;
2381 
2382 	while(i*mp->cpf - 2 <= term.t_ncol)		/* no force... */
2383 	  i++;					/* like brute force! */
2384 
2385 	mp->fpl = i - 1;			/* files per line */
2386     }
2387 
2388     if(mp->fpl == 0)
2389       mp->fpl = 1;
2390 }
2391 
2392 
2393 /*
2394  * percdircells - bubble all the directory cells to the top of the
2395  *                list.
2396  */
2397 void
percdircells(struct bmaster * mp)2398 percdircells(struct bmaster *mp)
2399 {
2400     struct fcell *dirlp,			/* dir cell list pointer */
2401                  *lp, *nlp;			/* cell list ptr and next */
2402 
2403     dirlp = NULL;
2404     for(lp = mp->head; lp; lp = nlp){
2405 	nlp = lp->next;
2406 	if(lp->mode == FIODIR){
2407 	    if(lp->prev)			/* clip from list */
2408 	      lp->prev->next = lp->next;
2409 
2410 	    if(lp->next)
2411 	      lp->next->prev = lp->prev;
2412 
2413 	    if((lp->prev = dirlp) != NULL){	/* tie it into dir portion */
2414 		if((lp->next = dirlp->next) != NULL)
2415 		  lp->next->prev = lp;
2416 
2417 		dirlp->next = lp;
2418 		dirlp = lp;
2419 	    }
2420 	    else{
2421 		if((dirlp = lp) != mp->head)
2422 		  dirlp->next = mp->head;
2423 
2424 		if(dirlp->next)
2425 		  dirlp->next->prev = dirlp;
2426 
2427 		mp->head = mp->top = mp->current = dirlp;
2428 	    }
2429 	}
2430     }
2431 }
2432 
2433 
2434 /*
2435  * PlaceCell - given a browser master and a cell, return row and col of the display that
2436  *             it should go on.
2437  *
2438  *             return 1 if mp->top has changed, x,y relative to new page
2439  *             return 0 if otherwise (same page)
2440  *             return -1 on error
2441  */
2442 int
PlaceCell(struct bmaster * mp,struct fcell * cp,int * x,int * y)2443 PlaceCell(struct bmaster *mp, struct fcell *cp, int *x, int *y)
2444 {
2445     int cl = COMPOSER_TOP_LINE;			/* current line */
2446     int ci = 0;					/* current index on line */
2447     int rv = 0;
2448     int secondtry = 0;
2449     struct fcell *tp;
2450 
2451     /* will cp fit on screen? */
2452     tp = mp->top;
2453     while(1){
2454 	if(tp == cp){				/* bingo! */
2455 	    *x = cl;
2456 	    *y = ci * mp->cpf;
2457 	    break;
2458 	}
2459 
2460 	if((tp = tp->next) == NULL){		/* above top? */
2461 	    if(secondtry++){
2462 		emlwwrite(_("Internal error: can't find fname cell"), NULL);
2463 		return(-1);
2464 	    }
2465 	    else{
2466 		tp = mp->top = mp->head;	/* try from the top! */
2467 		cl = COMPOSER_TOP_LINE;
2468 		ci = 0;
2469 		rv = 1;
2470 		continue;			/* start over! */
2471 	    }
2472 	}
2473 
2474 	if(++ci >= mp->fpl){			/* next line? */
2475 	    ci = 0;
2476 	    if(++cl > term.t_nrow-(term.t_mrow+1)){ /* next page? */
2477 		ci = mp->fpl;			/* tp is at bottom right */
2478 		while(ci--)			/* find new top */
2479 		  tp = tp->prev;
2480 		mp->top = tp;
2481 		ci = 0;
2482 		cl = COMPOSER_TOP_LINE;		/* keep checking */
2483 		rv = 1;
2484 	    }
2485 	}
2486 
2487     }
2488 
2489     /* not on display! */
2490     return(rv);
2491 }
2492 
2493 
2494 /*
2495  * zotfcells - clean up malloc'd cells of file names
2496  */
2497 void
zotfcells(struct fcell * hp)2498 zotfcells(struct fcell *hp)
2499 {
2500     struct fcell *tp;
2501 
2502     while(hp){
2503 	tp = hp;
2504 	hp = hp->next;
2505 	tp->next = NULL;
2506 	free((char *) tp);
2507     }
2508 }
2509 
2510 
2511 /*
2512  * zotmaster - blast the browser master struct
2513  */
2514 void
zotmaster(struct bmaster ** mp)2515 zotmaster(struct bmaster **mp)
2516 {
2517     if(mp && *mp){
2518 	zotfcells((*mp)->head);				/* free cells       */
2519 	zotlmlist((*mp)->lm);				/* free lmlist      */
2520 	if((*mp)->names)
2521 	  free((char *)(*mp)->names);			/* free file names  */
2522 
2523 	free((char *)*mp);				/* free master      */
2524 	*mp = NULL;					/* make double sure */
2525     }
2526 }
2527 
2528 
2529 /*
2530  * FindCell - starting from the current cell find the first occurance of
2531  *            the given string wrapping around if necessary
2532  */
2533 struct fcell *
FindCell(struct bmaster * mp,char * utf8string,int bsearch)2534 FindCell(struct bmaster *mp, char *utf8string, int bsearch)
2535 {
2536     struct fcell *tp, *fp;
2537 
2538     if(*utf8string == '\0')
2539       return(NULL);
2540 
2541     fp = NULL;
2542     tp = bsearch ? mp->current->prev : mp->current->next;
2543 
2544     while(tp && !fp){
2545 	if(sisin(tp->fname, utf8string))
2546 	  fp = tp;
2547 	else
2548 	  tp = bsearch ? tp->prev : tp->next;
2549     }
2550 
2551     tp = bsearch ? mp->bottom : mp->head;
2552     while(tp != mp->current && !fp){
2553 	if(sisin(tp->fname, utf8string))
2554 	  fp = tp;
2555 	else
2556 	  tp = bsearch ? tp->prev : tp->next;
2557     }
2558 
2559     return(fp);
2560 }
2561 
2562 
2563 /*
2564  * sisin - case insensitive substring matching function
2565  *
2566  *  We can't really do case-insensitive for non-ascii, so restrict
2567  *  that to ascii characters. The strings will be utf8.
2568  */
2569 int
sisin(char * bigstr,char * utf8substr)2570 sisin(char *bigstr, char *utf8substr)
2571 {
2572     register int j;
2573 
2574     while(*bigstr){
2575 	j = 0;
2576 	while(bigstr[j] == utf8substr[j]
2577 	      || ((unsigned char)bigstr[j] < 0x80 && (unsigned char)utf8substr[j] < 0x80
2578 		  && toupper((unsigned char)bigstr[j]) == toupper((unsigned char)utf8substr[j])))
2579 	  if(utf8substr[++j] == '\0')			/* bingo! */
2580 	    return(1);
2581 
2582 	bigstr++;
2583     }
2584 
2585     return(0);
2586 }
2587 
2588 
2589 /*
2590  * set_browser_title -
2591  */
2592 void
set_browser_title(char * s)2593 set_browser_title(char *s)
2594 {
2595     browser_title = s;
2596 }
2597 
2598 
2599 /*
2600  * BrowserAnchor - draw the browser's anchor line.
2601  */
2602 void
BrowserAnchor(char * utf8dir)2603 BrowserAnchor(char *utf8dir)
2604 {
2605     char *pdir;
2606     char  titlebuf[NLINE];
2607     char  buf[NLINE];
2608     char  dirbuf[NLINE];
2609     char *dots = "...";
2610     char *br = "BROWSER";
2611     char *dir = "Dir: ";
2612     UCS  *ucs;
2613     int   need, extra, avail;
2614     int   title_wid, b_wid, t_to_b_wid, b_to_d_wid, d_wid, dot_wid, dir_wid, after_dir_wid;
2615     COLOR_PAIR *lastc = NULL;
2616 
2617     if(!utf8dir)
2618       utf8dir = "";
2619 
2620     pdir = utf8dir;
2621 
2622     if(browser_title)
2623       snprintf(titlebuf, sizeof(buf), "  %s", browser_title);
2624     else if(Pmaster)
2625       snprintf(titlebuf, sizeof(buf), "  ALPINE %s", Pmaster->pine_version);
2626     else
2627       snprintf(titlebuf, sizeof(buf), "  UW PICO %s", (gmode&MDBRONLY) ? "BROWSER" : version);
2628 
2629     title_wid = utf8_width(titlebuf);
2630     t_to_b_wid = 15;
2631     b_wid = utf8_width(br);
2632     b_to_d_wid = 4;
2633     d_wid = utf8_width(dir);
2634     dot_wid = 0;
2635     dir_wid = utf8_width(pdir);
2636 
2637     need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2638 
2639     if(need > term.t_ncol){
2640 	extra = need - term.t_ncol;
2641 	t_to_b_wid -= MIN(extra, 10);
2642 	need -= MIN(extra, 10);
2643 
2644 	if(need > term.t_ncol){
2645 	    extra = need - term.t_ncol;
2646 	    b_to_d_wid -= MIN(extra, 2);
2647 	    need -= MIN(extra, 2);
2648 	}
2649 
2650 	if(need > term.t_ncol){
2651 	    titlebuf[0] = titlebuf[1] = ' ';
2652 	    titlebuf[2] = '\0';
2653 	    title_wid = utf8_width(titlebuf);
2654 	    t_to_b_wid = 0;
2655 	    b_to_d_wid = 4;
2656 	    need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2657 	    if(need > term.t_ncol){
2658 		extra = need - term.t_ncol;
2659 		b_to_d_wid -= MIN(extra, 2);
2660 		need -= MIN(extra, 2);
2661 	    }
2662 
2663 	    need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2664 	    if(need > term.t_ncol){
2665 		t_to_b_wid = 0;
2666 		b_wid = 0;
2667 		b_to_d_wid = 0;
2668 	    }
2669 
2670 	    need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2671 	    if(need > term.t_ncol)
2672 	      d_wid = 0;
2673 
2674 	    need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2675 
2676 	    if(need > term.t_ncol && dir_wid > 0){
2677 		dot_wid = utf8_width(dots);
2678 		need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2679 		while(need > term.t_ncol && (pdir = strchr(pdir+1, C_FILESEP))){
2680 		    dir_wid = utf8_width(pdir);
2681 		    need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2682 		}
2683 
2684 		if(!pdir){		/* adjust other widths to fill up space */
2685 		    avail = term.t_ncol - (title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid);
2686 		    if(avail > 2)
2687 		      utf8_to_width_rhs(dirbuf, utf8dir, sizeof(dirbuf), avail);
2688 		    else
2689 		      dirbuf[0] = '\0';
2690 
2691 		    pdir = dirbuf;
2692 		}
2693 
2694 		dir_wid = utf8_width(pdir);
2695 	    }
2696 	}
2697     }
2698 
2699     extra = term.t_ncol - (title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid);
2700 
2701     if(extra >= 0)
2702       after_dir_wid = extra;
2703     else
2704       after_dir_wid = 0;
2705 
2706     utf8_snprintf(buf, sizeof(buf), "%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w",
2707 		  title_wid, title_wid, titlebuf,
2708 		  t_to_b_wid, t_to_b_wid, "",
2709 		  b_wid, b_wid, br,
2710 		  b_to_d_wid, b_to_d_wid, "",
2711 		  d_wid, d_wid, dir,
2712 		  dot_wid, dot_wid, dots,
2713 		  dir_wid, dir_wid, pdir,
2714 		  after_dir_wid, after_dir_wid, "");
2715 
2716     /* just making sure */
2717     utf8_snprintf(titlebuf, sizeof(titlebuf), "%-*.*w", term.t_ncol, term.t_ncol, buf);
2718 
2719     movecursor(0, 0);
2720     if(Pmaster && Pmaster->colors && Pmaster->colors->tbcp
2721        && pico_is_good_colorpair(Pmaster->colors->tbcp)){
2722 	lastc = pico_get_cur_color();
2723 	(void) pico_set_colorp(Pmaster->colors->tbcp, PSC_NONE);
2724     }
2725     else
2726       (*term.t_rev)(1);
2727 
2728     ucs = utf8_to_ucs4_cpystr(titlebuf);
2729     if(ucs){
2730 	pputs(ucs, 0);
2731 	fs_give((void **) &ucs);
2732     }
2733 
2734     if(lastc){
2735 	(void) pico_set_colorp(lastc, PSC_NONE);
2736 	free_color_pair(&lastc);
2737     }
2738     else
2739       (*term.t_rev)(0);
2740 }
2741 
2742 
2743 /*
2744  * ResizeBrowser - handle a resize event
2745  */
2746 int
ResizeBrowser(void)2747 ResizeBrowser(void)
2748 {
2749     if(gmp){
2750 	layoutcells(gmp);
2751 	PaintBrowser(gmp, 0, NULL, NULL);
2752 	return(1);
2753     }
2754     else
2755       return(0);
2756 }
2757 
2758 
2759 void
ClearBrowserScreen(void)2760 ClearBrowserScreen(void)
2761 {
2762     int i;
2763 
2764     for(i = 0; i <= term.t_nrow; i++){		/* clear screen */
2765 	movecursor(i, 0);
2766 	peeol();
2767     }
2768 }
2769 
2770 
2771 void
BrowserRunChild(char * child,char * dir)2772 BrowserRunChild(char *child, char *dir)
2773 {
2774     int    status;
2775     char   tmp[NLINE];
2776     time_t t_in, t_out;
2777 
2778     ClearBrowserScreen();
2779     movecursor(0, 0);
2780     (*term.t_close)();
2781     if(!isdir(dir, NULL, &t_in))
2782       t_in = 0;
2783 
2784     fflush(stdout);
2785     status = system(child);
2786     (*term.t_open)();
2787     if(t_in && isdir(dir, NULL, &t_out) && t_in < t_out){
2788 	struct bmaster *mp;
2789 
2790 	if((mp = getfcells(dir, 0)) != NULL){
2791 	    zotmaster(&gmp);
2792 	    gmp = mp;
2793 	}
2794 	/* else getfcells should explain what happened */
2795     }
2796 
2797     /* complain about non-zero exit status */
2798     if((status >> 8) & 0xff){
2799 
2800 	movecursor(term.t_nrow - 1, 0);
2801 	snprintf(tmp, sizeof(tmp), "[ \"%.30s\" exit with error value: %d ]",
2802 		 child, (status >> 8) & 0xff);
2803 	pputs_utf8(tmp, 1);
2804 	movecursor(term.t_nrow, 0);
2805 	pputs_utf8("[ Hit RETURN to continue ]", 1);
2806 	fflush(stdout);
2807 
2808 	while(GetKey() != (CTRL|'M')){
2809 	    (*term.t_beep)();
2810 	    fflush(stdout);
2811 	}
2812     }
2813 }
2814 
2815 
2816 /*
2817  * imitate pc-pine memory for where we last called the file browser.
2818  */
2819 void
p_chdir(struct bmaster * mp)2820 p_chdir(struct bmaster *mp)
2821 {
2822     if(mp && mp->dname)
2823       chdir(mp->dname);
2824 }
2825 #else /* _WINDOWS */
2826 
2827 
2828 /*
2829  * pico_file_browse - Exported version of FileBrowse below.
2830  */
2831 int
pico_file_browse(PICO * pdata,char * dir,size_t dirlen,char * fn,size_t fnlen,char * sz,size_t szlen,int flags)2832 pico_file_browse(PICO *pdata, char *dir, size_t dirlen, char *fn, size_t fnlen,
2833 		 char *sz, size_t szlen, int flags)
2834 {
2835     return(FileBrowse(dir, dirlen, fn, fnlen, sz, szlen, flags, NULL));
2836 }
2837 
2838 int
ResizeBrowser(void)2839 ResizeBrowser (void)
2840 {
2841     return (0);
2842 }
2843 
2844 /*
2845  * FileBrowse - Windows version of above function
2846  */
2847 int
FileBrowse(char * dir,size_t dirlen,char * fn,size_t fnlen,char * sz,size_t szlen,int fb_flags,LMLIST ** lmreturn)2848 FileBrowse(char *dir, size_t dirlen, char *fn, size_t fnlen,
2849 	   char *sz, size_t szlen, int fb_flags, LMLIST **lmreturn)
2850 {
2851     struct stat sbuf;
2852     int			rc;
2853     char		lfn[NLINE];
2854 
2855     if (fb_flags & FB_SAVE){
2856 	rc = mswin_savefile(dir, dirlen, fn, MIN(dirlen,fnlen));
2857     }
2858     else{
2859 	*fn = '\0';				/* No initial file names for
2860 						 * open. */
2861 	if(fb_flags & FB_LMODEPOS){
2862 	    /*
2863 	     * We're going to allow multiple filenames to be returned so
2864 	     * we don't want to use the passed in fn for that. Instead, make
2865 	     * a bigger space and use that.
2866 	     */
2867 	    char f[20000];
2868 	    size_t flen, dlen;
2869 
2870 	    memset(f, 0, sizeof(f));
2871 
2872 	    rc = mswin_multopenfile(dir, dirlen, f, sizeof(f),
2873 		   (fb_flags & FB_ATTACH) ? NULL : "Text Files (*.txt)#*.txt");
2874 
2875 	    if(rc == 1){
2876 		LMLIST *lmhead = NULL, *new;
2877 		char   *p;
2878 
2879 		/*
2880 		 * Build an LMLIST to return to the caller.
2881 		 */
2882 		for(p = f; *p; p += strlen(p)+1){
2883 		    flen = strlen(p);
2884 		    dlen = strlen(dir ? dir : "");
2885 		    new = (LMLIST *) fs_get(sizeof(*new));
2886 		    new->fname = (char *) fs_get((flen+1) * sizeof(char));
2887 		    new->dir = (char *) fs_get((dlen+1) * sizeof(char));
2888 
2889 		    strncpy(new->fname, p, flen);
2890 		    new->fname[flen] = '\0';
2891 		    strncpy(new->dir, dir ? dir : "", dlen);
2892 		    new->dir[dlen] = '\0';
2893 
2894 		    /* build full path to stat file. */
2895 		    if((strlen(new->dir) + strlen(S_FILESEP) +
2896 			strlen(new->fname) + 1) < sizeof(lfn)){
2897 			strncpy(lfn, new->dir, sizeof(lfn));
2898 			lfn[sizeof(lfn)-1] = '\0';
2899 			strncat(lfn, S_FILESEP, sizeof(lfn)-strlen(lfn)-1);
2900 			strncat(lfn, new->fname, sizeof(lfn)-strlen(lfn)-1);
2901 			lfn[sizeof(lfn)-1] = '\0';
2902 			if(our_stat(lfn, &sbuf) < 0)
2903 			  strncpy(new->size, "0", 32);
2904 			else
2905 			  strncpy(new->size, prettysz((off_t)sbuf.st_size), 32);
2906 
2907 			new->size[32-1] = '\0';
2908 		    }
2909 
2910 		    new->next = lmhead;
2911 		    lmhead = new;
2912 		}
2913 
2914 		*lmreturn = lmhead;
2915 		return(1);
2916 	    }
2917 	}
2918 	else
2919 	  rc = mswin_openfile (dir, dirlen, fn, fnlen,
2920 		   (fb_flags & FB_ATTACH) ? NULL : "Text Files (*.txt)#*.txt");
2921     }
2922 
2923     if(rc == 1){
2924 	if(sz != NULL){
2925 	    /* build full path to stat file. */
2926 	    if((strlen(dir) + strlen(S_FILESEP) + strlen(fn) + 1) > NLINE)
2927 	      return -1;
2928 
2929 	    strncpy(lfn, dir, sizeof(lfn));
2930 	    lfn[sizeof(lfn)-1] = '\0';
2931 	    strncat(lfn, S_FILESEP, sizeof(lfn)-strlen(lfn)-1);
2932 	    lfn[sizeof(lfn)-1] = '\0';
2933 	    strncat(lfn, fn, sizeof(lfn)-strlen(lfn)-1);
2934 	    lfn[sizeof(lfn)-1] = '\0';
2935 	    if(our_stat(lfn, &sbuf) < 0){
2936 		strncpy(sz, "0", szlen);
2937 		sz[szlen-1] = '\0';
2938 	    }
2939 	    else{
2940 		strncpy(sz, prettysz ((off_t)sbuf.st_size), szlen);
2941 		sz[szlen-1] = '\0';
2942 	    }
2943 	}
2944 
2945 	return(1);
2946     }
2947 
2948     return(rc ? -1 : 0);
2949 }
2950 
2951 #endif	/* _WINDOWS */
2952 
2953 
2954 /*
2955  * LikelyASCII - make a rough guess as to the displayability of the
2956  *		 given file.
2957  */
2958 int
LikelyASCII(char * file)2959 LikelyASCII(char *file)
2960 {
2961 #define	LA_TEST_BUF	1024
2962 #define	LA_LINE_LIMIT	300
2963     int		   n, i, line, rv = FALSE;
2964     unsigned char  buf[LA_TEST_BUF];
2965     FILE	  *fp;
2966     EML            eml;
2967 
2968     if((fp = our_fopen(file, "rb")) != NULL){
2969 	clearerr(fp);
2970 	if((n = fread(buf, sizeof(char), LA_TEST_BUF * sizeof(char), fp)) > 0
2971 	   || !ferror(fp)){
2972 	    /*
2973 	     * If we don't hit any newlines in a reasonable number,
2974 	     * LA_LINE_LIMIT, of characters or the file contains NULLs,
2975 	     * bag out...
2976 	     */
2977 	    rv = TRUE;
2978 	    for(i = line = 0; i < n; i++)
2979 	      if((line = (buf[i] == '\n') ? 0 : line + 1) >= LA_LINE_LIMIT
2980 		 || !buf[i]){
2981 		  rv = FALSE;
2982 		  emlwrite(_("Can't display non-text file.  Try \"Launch\"."),
2983 			   NULL);
2984 		  break;
2985 	      }
2986 	}
2987 	else{
2988 	    eml.s = file;
2989 	    emlwrite(_("Can't read file: %s"), &eml);
2990 	}
2991 
2992 	fclose(fp);
2993     }
2994     else{
2995 	eml.s = file;
2996 	emlwrite(_("Can't open file: %s"), &eml);
2997     }
2998 
2999     return(rv);
3000 }
3001 
3002 
3003 void
zotlmlist(LMLIST * lm)3004 zotlmlist(LMLIST *lm)
3005 {
3006     LMLIST *tp;
3007 
3008     while(lm){
3009 	if(lm->fname)
3010 	  free(lm->fname);
3011 
3012 	if(lm->dir)
3013 	  free(lm->dir);
3014 
3015 	tp = lm;
3016 	lm = lm->next;
3017 	tp->next = NULL;
3018 	free((char *) tp);
3019     }
3020 }
3021 
3022 
3023 /*
3024  * time_to_check - checks the current time against the last time called
3025  *                 and returns true if the elapsed time is > below.
3026  *                 Newmail won't necessarily check, but we want to give it
3027  *                 a chance to check or do a keepalive.
3028  */
3029 int
time_to_check(void)3030 time_to_check(void)
3031 {
3032     static time_t lasttime = 0L;
3033 
3034     if(!get_input_timeout())
3035       return(FALSE);
3036 
3037     if(time((time_t *) 0) - lasttime > (Pmaster ? (time_t)(FUDGE-10) : get_input_timeout())){
3038 	lasttime = time((time_t *) 0);
3039 	return(TRUE);
3040     }
3041     else
3042       return(FALSE);
3043 }
3044