* Fix of the list of files in the browser for terminals that need to erase
[alpine.git] / pico / browse.c
blob8113e4dc8cae920c556396e483631c028d9d50f3
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
5 /*
6 * ========================================================================
7 * Copyright 2006-2008 University of Washington
8 * Copyright 2013-2021 Eduardo Chappa
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
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
18 * Program: Routines to support file browser in pico and Pine composer
21 * NOTES:
23 * Misc. thoughts (mss, 5 Apr 92)
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).
33 * Nice side effects
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.
39 * Fleshed out the file managements stuff (mss, 24 June 92)
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"
50 #ifndef _WINDOWS
52 #if defined(bsd) || defined(lnx)
53 extern int errno;
54 #endif
58 * directory cell structure
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;
70 * master browser structure
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 */
89 * title for exported browser display
91 static char *browser_title = NULL;
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 *);
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}
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}
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
140 #define DIRWORD "dir"
141 #define PARENTDIR "parent"
142 #define SELECTWORD "SELECT"
146 * Default pager used by the stand-alone file browser.
148 #define BROWSER_PAGER ((gmode & MDFKEY) ? "alpine -k -F" : "alpine -F")
152 * function key mappings for callable browser
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'} } };
181 * Browser help for pico (pine composer help handed to us by pine)
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
208 * Help for standalone browser (pilot)
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
260 * pico_file_browse - Exported version of FileBrowse below.
263 pico_file_browse(PICO *pdata, char *dir, size_t dirlen, char *fn, size_t fnlen,
264 char *sz, size_t szlen, int flags)
266 int rv;
267 char title_buf[64];
269 Pmaster = pdata;
270 gmode = pdata->pine_flags | MDEXTFB;
271 km_popped = 0;
273 /* only init screen bufs for display and winch handler */
274 if(!vtinit())
275 return(-1);
277 if(Pmaster){
278 term.t_mrow = Pmaster->menu_rows;
279 if(Pmaster->oper_dir)
280 strncpy(opertree, Pmaster->oper_dir, NLINE);
282 if(*opertree)
283 fixpath(opertree, sizeof(opertree));
286 /* install any necessary winch signal handler */
287 ttresize();
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);
302 * FileBrowse - display contents of given directory dir
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
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
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.
323 * 1 if a file's been selected
324 * 0 if no files selected
325 * -1 if there were problems
328 FileBrowse(char *dir, size_t dirlen, char *fn, size_t fnlen,
329 char *sz, size_t szlen, int fb_flags, LMLIST **lmreturn)
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[NLINE], tmp[NLINE];
337 struct bmaster *mp;
338 struct fcell *tp;
339 EML eml;
340 #ifdef MOUSE
341 MOUSEPRESS mousep;
342 #endif
344 child[0] = '\0';
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);
353 if(gmode&MDGOTO){
354 /* fix up function key mapping table */
355 /* fix up key menu labels */
358 /* build contents of cell structures */
359 if((gmp = getfcells(dir, fb_flags)) == NULL)
360 return(-1);
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);
370 /* paint screen */
371 PaintBrowser(gmp, 0, &crow, &ccol);
373 while(1){ /* the big loop */
374 if(!(gmode&MDSHOCUR)){
375 crow = term.t_nrow-term.t_mrow;
376 ccol = 0;
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);
387 else
388 movecursor(crow, ccol);
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);
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;
405 (*term.t_flush)();
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
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;
422 if(km_popped){ /* restore display */
423 km_popped = 0;
424 PaintBrowser(gmp, 0, &crow, &ccol);
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 */
435 mpresf = 1;
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);
445 else
446 movecursor(crow, ccol);
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);
455 if(km_popped)
456 switch(c){
457 case NODATA:
458 case (CTRL|'L'):
459 km_popped++;
460 break;
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;
473 if(c == NODATA) /* GetKey timed out */
474 continue;
475 else if(Pmaster)
476 (*Pmaster->keybinput)();
479 if(mpresf){ /* blast old messages */
480 if(mpresf++ > MESSDELAY){ /* every few keystrokes */
481 mlerase();
485 /* process commands */
486 switch(new_c = normalize_cmd(c,bfmappings[(gmode&MDBRONLY)?0:1],2)){
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;
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);
504 else{
505 PaintCell(row, col, gmp->cpf, gmp->current, 1);
506 crow = row;
507 ccol = col;
509 break;
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;
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);
526 else{
527 PaintCell(row, col, gmp->cpf, gmp->current, 1);
528 crow = row;
529 ccol = col;
531 break;
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;
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);
547 else{
548 PaintCell(row, col, gmp->cpf, gmp->current, 1);
549 crow = row;
550 ccol = col;
552 break;
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;
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);
569 else{
570 PaintCell(row, col, gmp->cpf, gmp->current, 1);
571 crow = row;
572 ccol = col;
574 break;
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;
583 while(i-- && tp->next != NULL){
584 j = 0;
585 while(++j <= gmp->fpl && tp->next != NULL)
586 tp = tp->next;
589 if(tp == NULL)
590 continue;
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);
598 else{
599 PaintCell(row, col, gmp->cpf, gmp->current, 1);
600 crow = row;
601 ccol = col;
603 break;
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;
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);
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;
629 if(PlaceCell(gmp, gmp->current, &row, &col)){
630 PaintBrowser(gmp, 1, &crow, &ccol);
632 else{
633 PaintCell(row, col, gmp->cpf, gmp->current, 1);
634 crow = row;
635 ccol = col;
638 break;
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;
649 else
650 tp = tp->next;
652 if(i != -1) /* can't go down */
653 break;
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);
661 else{
662 PaintCell(row, col, gmp->cpf, gmp->current, 1);
663 crow = row;
664 ccol = col;
666 break;
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;
675 if(tp == NULL)
676 break;
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);
684 else{
685 PaintCell(row, col, gmp->cpf, gmp->current, 1);
686 crow = row;
687 ccol = col;
689 break;
691 #ifdef MOUSE
692 case KEY_MOUSE:
693 mouse_get_last (NULL, &mousep);
694 if (mousep.doubleclick) {
695 goto Selected;
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);
711 else{
712 PaintCell(row, col, gmp->cpf, gmp->current, 1);
713 crow = row;
714 ccol = col;
718 break;
719 #endif
721 case 'e': /* exit or edit */
722 case 'E':
723 if(gmode&MDBRONLY){ /* run "pico" */
724 snprintf(child, sizeof(child), "%s%c%s", gmp->dname, C_FILESEP,
725 gmp->current->fname);
726 /* make sure selected isn't a directory or executable */
727 if(!LikelyASCII(child)){
728 emlwrite(_("Can't edit non-text file. Try Launch."), NULL);
729 break;
732 if((envp = (char *) getenv("EDITOR")) != NULL)
733 snprintf(tmp, sizeof(tmp), "%s \'%s\'", envp, child);
734 else
735 snprintf(tmp, sizeof(tmp), "pico%s%s%s \'%s\'",
736 (gmode & MDFKEY) ? " -f" : "",
737 (gmode & MDSHOCUR) ? " -g" : "",
738 (gmode & MDMOUSE) ? " -m" : "",
739 child);
741 BrowserRunChild(tmp, gmp->dname); /* spawn pico */
742 PaintBrowser(gmp, 0, &crow, &ccol); /* redraw browser */
744 else{
745 zotmaster(&gmp);
746 return(0);
749 break;
751 case 'q': /* user exits wrong */
752 case 'Q':
753 if(gmode&MDBRONLY){
754 zotmaster(&gmp);
755 return(0);
758 unknown_command(c);
759 break;
761 case 'x': /* toggle selection */
762 case 'X':
763 if(!(gmp->flags & FB_LMODE)){
764 if(gmp->flags & FB_LMODEPOS)
765 emlwwrite(_("Type L command to use ListMode"), NULL);
766 else
767 unknown_command(c);
769 break;
772 if(gmp->current->mode == FIODIR){
773 emlwwrite(_("Can't Set directories"), NULL);
774 break;
777 if(fcell_is_selected(gmp->current, gmp))
778 del_cell_from_lmlist(gmp->current, gmp);
779 else
780 add_cell_to_lmlist(gmp->current, gmp);
782 PlaceCell(gmp, gmp->current, &row, &col);
783 PaintCell(row, col, gmp->cpf, gmp->current, 1);
784 break;
786 case 'l': /* run Command */
787 case 'L': /* or ListMode */
788 if(gmp->flags & FB_LMODEPOS){
789 if(gmp->flags & FB_LMODE){
791 * Unless we make it so you can get out of ListMode
792 * once you're in ListMode, this must be an error.
794 emlwwrite(_("Already in ListMode"), NULL);
795 break;
797 else{
798 gmp->flags |= FB_LMODE;
799 PaintBrowser(gmp, 0, &crow, &ccol);
802 break;
805 if(!(gmode&MDBRONLY)){
806 unknown_command(c);
807 break;
810 /* add subcommands to invoke pico and insert selected filename */
811 /* perhaps: add subcmd to scroll command history */
813 tmp[0] = '\0';
814 i = 0;
815 snprintf(child, sizeof(child), "%s%c%s", gmp->dname, C_FILESEP,
816 gmp->current->fname);
817 while(!i){
818 static EXTRAKEYS opts[] = {
819 {"^X", N_("Add Name"), CTRL|'X', KS_NONE},
820 {NULL, NULL, 0, KS_NONE},
821 {NULL, NULL, 0, KS_NONE},
822 {NULL, NULL, 0, KS_NONE},
823 {NULL, NULL, 0, KS_NONE},
824 {NULL, NULL, 0, KS_NONE},
825 {NULL, NULL, 0, KS_NONE},
826 {NULL, NULL, 0, KS_NONE},
827 {NULL, NULL, 0, KS_NONE},
828 {NULL, NULL, 0, KS_NONE}
831 status = mlreply_utf8(_("Command to execute: "),
832 tmp, NLINE, QNORML, opts);
833 switch(status){
834 case HELPCH:
835 emlwwrite(_("No help yet!"), NULL);
836 /* remove break and sleep after help text is installed */
837 sleep(3);
838 break;
839 case (CTRL|'X'):
840 strncat(tmp, child, sizeof(tmp)-strlen(tmp)-1);
841 tmp[sizeof(tmp)-1] = '\0';
842 break;
843 case (CTRL|'L'):
844 PaintBrowser(gmp, 0, &crow, &ccol);
845 break;
846 case ABORT:
847 emlwrite(_("Command cancelled"), NULL);
848 i++;
849 break;
850 case FALSE:
851 case TRUE:
852 i++;
854 if(tmp[0] == '\0'){
855 emlwrite(_("No command specified"), NULL);
856 break;
859 BrowserRunChild(tmp, gmp->dname);
860 PaintBrowser(gmp, 0, &crow, &ccol);
861 break;
862 default:
863 break;
867 BrowserKeys();
868 break;
870 case 'd': /* delete */
871 case 'D':
872 if(gmp->current->mode == FIODIR){
873 /* BUG: if dir is empty it should be deleted */
874 emlwwrite(_("Can't delete a directory"), NULL);
875 break;
878 if(gmode&MDSCUR){ /* not allowed! */
879 emlwrite(_("Delete not allowed in restricted mode"),NULL);
880 break;
883 snprintf(child, sizeof(child), "%s%c%s", gmp->dname, C_FILESEP,
884 gmp->current->fname);
886 i = 0;
887 while(i++ < 2){ /* verify twice!! */
888 if(i == 1){
889 if(fexist(child, "w", (off_t *)NULL) != FIOSUC){
890 strncpy(tmp, _("File is write protected! OVERRIDE"), sizeof(tmp));
891 tmp[sizeof(tmp)-1] = '\0';
893 else
894 /* TRANSLATORS: This is a question, Delete file <filename> */
895 snprintf(tmp, sizeof(tmp), _("Delete file \"%.*s\""), NLINE - 20, child);
897 else{
898 strncpy(tmp, _("File CANNOT be UNdeleted! Really delete"), sizeof(tmp));
899 tmp[sizeof(tmp)-1] = '\0';
902 if((status = mlyesno_utf8(tmp, FALSE)) != TRUE){
903 emlwrite((status == ABORT)
904 ? _("Delete Cancelled")
905 : _("File Not Deleted"),
906 NULL);
907 break;
911 if(status == TRUE){
912 if(our_unlink(child) < 0){
913 eml.s = errstr(errno);
914 emlwrite(_("Delete Failed: %s"), &eml);
916 else{ /* fix up pointers and redraw */
917 tp = gmp->current;
918 if(tp->next){
919 gmp->current = tp->next;
920 if((tp->next->prev = tp->prev) != NULL)
921 tp->prev->next = tp->next;
923 else if(tp->prev) {
924 gmp->current = tp->prev;
925 if((tp->prev->next = tp->next) != NULL)
926 tp->next->prev = tp->prev;
929 if(tp == gmp->head)
930 gmp->head = tp->next;
932 if(tp == gmp->bottom)
933 gmp->bottom = tp->prev;
935 tp->fname = NULL;
936 tp->next = tp->prev = NULL;
937 if(tp != gmp->current)
938 free((char *) tp);
940 if((tp = FindCell(gmp, gmp->current->fname, 0)) != NULL){
941 gmp->current = tp;
942 PlaceCell(gmp, gmp->current, &row, &col);
945 PaintBrowser(gmp, 1, &crow, &ccol);
946 mlerase();
950 BrowserKeys();
951 break;
953 case '?': /* HELP! */
954 case (CTRL|'G'):
955 if(term.t_mrow == 0){
956 if(km_popped == 0){
957 km_popped = 2;
958 break;
962 if(Pmaster){
963 VARS_TO_SAVE *saved_state;
965 saved_state = save_pico_state();
966 (*Pmaster->helper)(Pmaster->browse_help,
967 _("Help for Browsing"), 1);
968 if(saved_state){
969 restore_pico_state(saved_state);
970 free_pico_state(saved_state);
973 else if(gmode&MDBRONLY)
974 pico_help(sa_BrowseHelpText, _("Browser Help"), 1);
975 else
976 pico_help(BrowseHelpText, _("Help for Browsing"), 1);
977 /* fall thru to repaint everything */
979 case (CTRL|'L'):
980 PaintBrowser(gmp, 0, &crow, &ccol);
981 break;
983 case 'g': /* jump to a directory */
984 case 'G':
986 if(!(gmode&MDGOTO))
987 goto Default;
989 i = 0;
990 child[0] = '\0';
992 while(!i){
994 /* TRANSLATORS: A prompt asking for a directory to
995 move into */
996 status = mlreply_utf8(_("Directory to go to: "), child, NLINE, QNORML,
997 NULL);
999 switch(status){
1000 case HELPCH:
1001 emlwwrite(_("No help yet!"), NULL);
1002 /* remove break and sleep after help text is installed */
1003 sleep(3);
1004 break;
1005 case (CTRL|'L'):
1006 PaintBrowser(gmp, 0, &crow, &ccol);
1007 break;
1008 case ABORT:
1009 emlwrite(_("Goto cancelled"), NULL);
1010 i++;
1011 break;
1012 case FALSE:
1013 case TRUE:
1014 i++;
1016 if(*child == '\0'){
1017 strncpy(child, gethomedir(NULL), sizeof(child));
1018 child[sizeof(child)-1] = '\0';
1021 if(!compresspath(gmp->dname, child, sizeof(child))){
1022 eml.s = child;
1023 emlwrite(_("Invalid Directory: %s"), &eml);
1024 break;
1027 if((gmode&MDSCUR) && homeless(child)){
1028 emlwrite(_("Restricted mode browsing limited to home directory"),NULL);
1029 break;
1032 if((gmode&MDTREE) && !in_oper_tree(child)){
1033 eml.s = opertree;
1034 emlwwrite(_("Can't go outside of %s in restricted mode"),
1035 &eml);
1036 break;
1039 if(isdir(child, (long *) NULL, NULL)){
1040 if((mp = getfcells(child, fb_flags)) == NULL){
1041 /* getfcells should explain what happened */
1042 i++;
1043 break;
1046 zotmaster(&gmp);
1047 gmp = mp;
1048 PaintBrowser(gmp, 0, &crow, &ccol);
1050 else{
1051 eml.s = child;
1052 emlwwrite(_("Not a directory: \"%s\""), &eml);
1055 break;
1056 default:
1057 break;
1060 BrowserKeys();
1061 break;
1063 case '.': /* display dot files */
1064 gmode ^= MDDOTSOK;
1065 gmp = RepaintBrowser(gmp, fb_flags);
1066 break;
1068 case '1': /* One column mode */
1069 gmode ^= MDONECOL;
1070 gmp = RepaintBrowser(gmp, fb_flags);
1071 break;
1073 case 'o': /* Other menu */
1074 case 'O':
1075 if (gmp){
1076 if(gmp->menu == 0){
1077 gmp->menu = 1;
1078 } else {
1079 gmp->menu = 0;
1081 BrowserKeys();
1083 break;
1085 case 'a': /* Add */
1086 case 'A':
1087 if(gmode&MDSCUR){ /* not allowed! */
1088 emlwrite(_("Add not allowed in restricted mode"),NULL);
1089 break;
1092 add_file = 1;
1093 i = 0;
1094 child[0] = '\0';
1095 /* pass in default filename */
1096 if(fn && *fn){
1097 strncpy(child, fn, sizeof(child) - 1);
1098 child[sizeof(child) - 1] = '\0';
1101 while(!i){
1102 int repaint = 0;
1103 EXTRAKEYS opts[10];
1105 memset((void *) &opts, 0, 10*sizeof(EXTRAKEYS));
1106 opts[0].name = "^X";
1107 opts[0].label = add_file ? N_("Add Dir") : N_("Add File");
1108 opts[0].key = (CTRL|'X');
1110 switch(status=mlreply_utf8(add_file ? _("Name of file to add: ") : _("Name of directory to add: "), child, NLINE,
1111 QFFILE, opts)){
1112 case HELPCH:
1113 emlwwrite(_("No help yet!"), NULL);
1114 /* remove break and sleep after help text is installed */
1115 sleep(3);
1116 break;
1117 case (CTRL|'L'):
1118 PaintBrowser(gmp, 0, &crow, &ccol);
1119 break;
1120 case (CTRL|'X'):
1121 if(add_file > 0) add_file = 0; else add_file = 1;
1122 break;
1123 case ABORT:
1124 emlwrite(add_file > 0 ? _("Add File Cancelled") : _("Add Directory Cancelled"), NULL);
1125 i++;
1126 break;
1127 case FALSE:
1129 * Support default filename. A return of 'FALSE' means
1130 * 'No change in filename' which is fine if we have
1131 * provided a default.
1132 * John Berthels <john.berthels@nexor.co.uk>
1134 /* FALLTHROUGH */
1135 case TRUE:
1136 i++;
1138 if(child[0] == '\0'){
1139 emlwrite(add_file > 0 ? _("No file named. Add Cancelled.") : _("No directory named. Add Cancelled"), NULL);
1140 break;
1143 if(!compresspath(gmp->dname, child, sizeof(child))){
1144 emlwrite(_("Too many ..'s in name"), NULL);
1145 break;
1148 if((gmode&MDTREE) && !in_oper_tree(child)){
1149 eml.s = opertree;
1150 emlwwrite(_("Restricted mode allows Add in %s only"),
1151 &eml);
1152 break;
1155 if((status = fexist(child, "w", (off_t *)NULL)) == FIOSUC){
1156 snprintf(tmp, sizeof(tmp), _("%s \"%.*s\" already exists!"),
1157 add_file > 0 ? "File" : "Directory", NLINE - 20, child);
1158 emlwrite(tmp, NULL);
1159 break;
1161 else if(status != FIOFNF){
1162 fioperr(status, child);
1163 break;
1166 if(add_file == 0){
1167 if(our_mkdir(child, (0700)) < 0){
1168 eml.s = child;
1169 emlwrite(_("Error adding Directory \"%s\""), &eml);
1171 else /* success! Directory added! */
1172 repaint = 1;
1174 else if(ffwopen(child, FALSE) != FIOSUC){
1175 /* ffwopen should've complained */
1176 break;
1178 else{ /* highlight new file */
1179 ffclose();
1180 eml.s = child;
1181 emlwrite(_("Added File \"%s\""), &eml);
1182 repaint = 1;
1185 if(repaint > 0){
1186 if((p = strrchr(child, C_FILESEP)) == NULL){
1187 emlwrite(_("Problems refiguring browser"), NULL);
1188 break;
1191 *p = '\0';
1192 if(p != child){
1193 strncpy(tmp, child, sizeof(tmp));
1194 tmp[sizeof(tmp)-1] = '\0';
1195 j = 0;
1196 while((child[j++] = *++p) != '\0')
1199 else{
1200 strncpy(tmp, S_FILESEP, sizeof(tmp));
1201 tmp[sizeof(tmp)-1] = '\0';
1205 * new file in same dir? if so, refigure files
1206 * and redraw...
1208 if(!strcmp(tmp, gmp->dname)){
1209 if((mp = getfcells(gmp->dname, fb_flags)) == NULL)
1210 /* getfcells should explain what happened */
1211 break;
1213 zotmaster(&gmp);
1214 gmp = mp;
1215 if((tp = FindCell(gmp, child, 0)) != NULL){
1216 gmp->current = tp;
1217 PlaceCell(gmp, gmp->current, &row, &col);
1220 PaintBrowser(gmp, 1, &crow, &ccol);
1223 break;
1224 default:
1225 break;
1229 BrowserKeys();
1230 break;
1232 case 'c': /* copy */
1233 case 'C':
1234 if(gmp->current->mode == FIODIR){
1235 emlwwrite(_("Can't copy a directory"), NULL);
1236 break;
1239 if(gmode&MDSCUR){ /* not allowed! */
1240 emlwrite(_("Copy not allowed in restricted mode"),NULL);
1241 break;
1244 i = 0;
1245 child[0] = '\0';
1247 while(!i){
1249 switch(status=mlreply_utf8(_("Name of new copy: "), child, NLINE,
1250 QFFILE, NULL)){
1251 case HELPCH:
1252 emlwwrite(_("No help yet!"), NULL);
1253 /* remove break and sleep after help text is installed */
1254 sleep(3);
1255 break;
1256 case (CTRL|'L'):
1257 PaintBrowser(gmp, 0, &crow, &ccol);
1258 break;
1259 case ABORT:
1260 emlwrite(_("Make Copy Cancelled"), NULL);
1261 i++;
1262 break;
1263 case FALSE:
1264 i++;
1265 mlerase();
1266 break;
1267 case TRUE:
1268 i++;
1270 if(child[0] == '\0'){
1271 emlwrite(_("No destination, file not copied"), NULL);
1272 break;
1275 if(!strcmp(gmp->current->fname, child)){
1276 emlwwrite(_("Can't copy file on to itself!"), NULL);
1277 break;
1280 if(!compresspath(gmp->dname, child, sizeof(child))){
1281 emlwrite(_("Too many ..'s in name"), NULL);
1282 break;
1285 if((gmode&MDTREE) && !in_oper_tree(child)){
1286 eml.s = opertree;
1287 emlwwrite(_("Restricted mode allows Copy in %s only"),
1288 &eml);
1289 break;
1292 if((status = fexist(child, "w", (off_t *)NULL)) == FIOSUC){
1293 /* TRANSLATORS: A question: File <filename> exists! Replace? */
1294 snprintf(tmp, sizeof(tmp), _("File \"%.*s\" exists! OVERWRITE"),
1295 NLINE - 20, child);
1296 if((status = mlyesno_utf8(tmp, 0)) != TRUE){
1297 emlwrite((status == ABORT)
1298 ? _("Make copy cancelled")
1299 : _("File Not Renamed"),
1300 NULL);
1301 break;
1304 else if(status != FIOFNF){
1305 fioperr(status, child);
1306 break;
1309 snprintf(tmp, sizeof(tmp), "%s%c%s", gmp->dname, C_FILESEP,
1310 gmp->current->fname);
1312 if(copy(tmp, child) < 0){
1313 /* copy() will report any error messages */
1314 break;
1316 else{ /* highlight new file */
1317 eml.s = child;
1318 emlwrite(_("File copied to %s"), &eml);
1320 if((p = strrchr(child, C_FILESEP)) == NULL){
1321 emlwrite(_("Problems refiguring browser"), NULL);
1322 break;
1325 *p = '\0';
1326 strncpy(tmp, (p == child) ? S_FILESEP: child, sizeof(tmp));
1327 tmp[sizeof(tmp)-1] = '\0';
1330 * new file in same dir? if so, refigure files
1331 * and redraw...
1333 if(!strcmp(tmp, gmp->dname)){
1334 strncpy(child, gmp->current->fname, sizeof(child));
1335 child[sizeof(child)-1] = '\0';
1336 if((mp = getfcells(gmp->dname, fb_flags)) == NULL)
1337 /* getfcells should explain what happened */
1338 break;
1340 zotmaster(&gmp);
1341 gmp = mp;
1342 if((tp = FindCell(gmp, child, 0)) != NULL){
1343 gmp->current = tp;
1344 PlaceCell(gmp, gmp->current, &row, &col);
1347 PaintBrowser(gmp, 1, &crow, &ccol);
1350 break;
1351 default:
1352 break;
1355 BrowserKeys();
1356 break;
1358 case 'r': /* rename */
1359 case 'R':
1360 i = 0;
1362 if(!strcmp(gmp->current->fname, "..")){
1363 emlwwrite(_("Can't rename \"..\""), NULL);
1364 break;
1367 if(gmode&MDSCUR){ /* not allowed! */
1368 emlwrite(_("Rename not allowed in restricted mode"),NULL);
1369 break;
1372 strncpy(child, gmp->current->fname, sizeof(child));
1373 child[sizeof(child)-1] = '\0';
1375 while(!i){
1377 switch(status=mlreply_utf8(_("Rename file to: "), child, NLINE, QFFILE,
1378 NULL)){
1379 case HELPCH:
1380 emlwwrite(_("No help yet!"), NULL);
1381 /* remove break and sleep after help text is installed */
1382 sleep(3);
1383 break;
1384 case (CTRL|'L'):
1385 PaintBrowser(gmp, 0, &crow, &ccol);
1386 break;
1387 case ABORT:
1388 emlwrite(_("Rename cancelled"), NULL);
1389 i++;
1390 break;
1391 case FALSE:
1392 case TRUE:
1393 i++;
1395 if(child[0] == '\0' || status == FALSE){
1396 mlerase();
1397 break;
1400 if(!compresspath(gmp->dname, child, sizeof(child))){
1401 emlwrite(_("Too many ..'s in name"), NULL);
1402 break;
1405 if((gmode&MDTREE) && !in_oper_tree(child)){
1406 eml.s = opertree;
1407 emlwwrite(_("Restricted mode allows Rename in %s only"),
1408 &eml);
1409 break;
1412 status = fexist(child, "w", (off_t *)NULL);
1413 if(status == FIOSUC || status == FIOFNF){
1414 if(status == FIOSUC){
1415 snprintf(tmp, sizeof(tmp), _("File \"%.*s\" exists! OVERWRITE"),
1416 NLINE - 20, child);
1418 if((status = mlyesno_utf8(tmp, FALSE)) != TRUE){
1419 emlwrite((status == ABORT)
1420 ? _("Rename cancelled")
1421 : _("Not Renamed"),
1422 NULL);
1423 break;
1427 snprintf(tmp, sizeof(tmp), "%s%c%s", gmp->dname, C_FILESEP,
1428 gmp->current->fname);
1430 if(our_rename(tmp, child) < 0){
1431 eml.s = errstr(errno);
1432 emlwrite(_("Rename Failed: %s"), &eml);
1434 else{
1435 if((p = strrchr(child, C_FILESEP)) == NULL){
1436 emlwrite(_("Problems refiguring browser"), NULL);
1437 break;
1440 *p = '\0';
1441 strncpy(tmp, (p == child) ? S_FILESEP: child, sizeof(tmp));
1442 tmp[sizeof(tmp)-1] = '\0';
1444 if((mp = getfcells(tmp, fb_flags)) == NULL)
1445 /* getfcells should explain what happened */
1446 break;
1448 zotmaster(&gmp);
1449 gmp = mp;
1451 if((tp = FindCell(gmp, ++p, 0)) != NULL){
1452 gmp->current = tp;
1453 PlaceCell(gmp, gmp->current, &row, &col);
1456 PaintBrowser(gmp, 1, &crow, &ccol);
1457 mlerase();
1460 else{
1461 fioperr(status, child);
1463 break;
1464 default:
1465 break;
1468 BrowserKeys();
1469 break;
1471 case 'v': /* stand-alone */
1472 case 'V': /* browser "view" */
1473 case 's': /* user "select" */
1474 case 'S':
1475 case (CTRL|'M'):
1476 Selected:
1478 if(((new_c == 'S' || new_c == 's') && (gmode&MDBRONLY))
1479 || ((new_c == 'V' || new_c == 'v') && !(gmode&MDBRONLY)))
1480 goto Default;
1482 if(gmp->current->mode == FIODIR){
1483 *child = '\0';
1484 strncpy(tmp, gmp->dname, sizeof(tmp));
1485 tmp[sizeof(tmp)-1] = '\0';
1486 p = gmp->current->fname;
1487 if(p[0] == '.' && p[1] == '.' && p[2] == '\0'){
1488 if((p=strrchr(tmp, C_FILESEP)) != NULL){
1489 *p = '\0';
1491 if((gmode&MDTREE) && !in_oper_tree(tmp)){
1492 eml.s = PARENTDIR;
1493 emlwwrite(
1494 _("Can't visit %s in restricted mode"),
1495 &eml);
1496 break;
1499 strncpy(child, &p[1], sizeof(child));
1500 child[sizeof(child)-1] = '\0';
1503 #if defined(DOS) || defined(OS2)
1504 (p == tmp || (tmp[1] == ':' && tmp[2] == '\0'))
1505 #else
1506 (p == tmp)
1507 #endif
1508 { /* is it root? */
1509 #if defined(DOS) || defined(OS2)
1510 if(*child){
1511 strncat(tmp, S_FILESEP, sizeof(tmp)-strlen(tmp)-1);
1512 tmp[sizeof(tmp)-1] = '\0';
1514 #else
1515 if(*child){
1516 strncpy(tmp, S_FILESEP, sizeof(tmp));
1517 tmp[sizeof(tmp)-1] = '\0';
1519 #endif
1520 else{
1521 emlwwrite(_("Can't move up a directory"),
1522 NULL);
1523 break;
1528 else if((fb_flags&FB_SAVE) && p[0] == '.' && p[1] == '\0'){
1529 if ((strlen(gmp->dname) < dirlen) &&
1530 (strlen(gmp->current->fname) < fnlen)){
1531 strncpy(dir, gmp->dname, dirlen);
1532 dir[dirlen-1] = '\0';
1535 zotmaster(&gmp);
1536 return(0); /* just change the directory, still return no selection */
1538 else{
1539 if(tmp[1] != '\0'){ /* were in root? */
1540 strncat(tmp, S_FILESEP, sizeof(tmp)-strlen(tmp)-1);
1541 tmp[sizeof(tmp)-1] = '\0';
1544 strncat(tmp, gmp->current->fname, sizeof(tmp)-strlen(tmp)-1);
1545 tmp[sizeof(tmp)-1] = '\0';
1548 if((mp = getfcells(tmp, fb_flags)) == NULL)
1549 /* getfcells should explain what happened */
1550 break;
1552 if(gmp->flags & FB_LMODE){
1553 mp->flags |= FB_LMODE;
1554 mp->lm = gmp->lm;
1555 gmp->lm = NULL;
1558 zotmaster(&gmp);
1559 gmp = mp;
1560 tp = NULL;
1562 if(*child){
1563 if((tp = FindCell(gmp, child, 0)) != NULL){
1564 gmp->current = tp;
1565 PlaceCell(gmp, gmp->current, &row, &col);
1567 else{
1568 eml.s = child;
1569 emlwwrite(_("Problem finding dir \"%s\""), &eml);
1573 PaintBrowser(gmp, 0, &crow, &ccol);
1574 if(!*child){
1575 char b[100];
1577 snprintf(b, sizeof(b), "Select/View \" .. %s %s\" to return to previous directory.", PARENTDIR, DIRWORD);
1578 emlwrite(b, NULL);
1581 break;
1583 else if(gmode&MDBRONLY){
1584 snprintf(child, sizeof(child), "%s%c%s", gmp->dname, C_FILESEP,
1585 gmp->current->fname);
1587 if(LikelyASCII(child)){
1588 snprintf(tmp, sizeof(tmp), "%s \'%s\'",
1589 (envp = (char *) getenv("PAGER"))
1590 ? envp : BROWSER_PAGER, child);
1591 BrowserRunChild(tmp, gmp->dname);
1592 PaintBrowser(gmp, 0, &crow, &ccol);
1595 break;
1597 else{ /* just return */
1598 if(gmp->flags & FB_LMODEPOS){
1600 if(!lmreturn){
1601 emlwrite("Programming error, called FileBrowse with LMODEPOS but no lmreturn", NULL);
1602 zotmaster(&gmp);
1603 return(-1);
1606 /* user actually used ListMode, the list is finished */
1607 if(gmp->flags & FB_LMODE){
1608 if(!gmp->lm){
1609 (*term.t_beep)();
1610 emlwrite(_("No files are selected, use \"X\" to mark files for selection"), NULL);
1611 break;
1614 *lmreturn = gmp->lm;
1615 gmp->lm = NULL;
1617 else{ /* construct an lmreturn for user */
1618 LMLIST *new;
1619 size_t flen, dlen;
1621 if((new=(LMLIST *)malloc(sizeof(*new))) == NULL
1622 || (new->fname=malloc(gmp->current->fname ? (flen=strlen(gmp->current->fname))+1 : 1)) == NULL
1623 || (new->dir=malloc((dlen=strlen(gmp->dname))+1)) == NULL){
1624 emlwwrite(_("Can't malloc space for filename"), NULL);
1625 return(-1);
1628 strncpy(new->fname,
1629 gmp->current->fname ? gmp->current->fname : "", flen);
1630 new->fname[flen] = '\0';
1631 strncpy(new->dir, gmp->dname, dlen);
1632 new->dir[dlen] = '\0';
1633 strncpy(new->size, gmp->current->size, sizeof(new->size));
1634 new->size[sizeof(new->size)-1] = '\0';
1635 new->next = NULL;
1636 *lmreturn = new;
1639 zotmaster(&gmp);
1640 return(1);
1643 if ((strlen(gmp->dname) < dirlen) &&
1644 (strlen(gmp->current->fname) < fnlen)){
1645 strncpy(dir, gmp->dname, dirlen);
1646 dir[dirlen-1] = '\0';
1647 strncpy(fn, gmp->current->fname, fnlen);
1648 fn[fnlen-1] = '\0';
1650 else {
1651 zotmaster(&gmp);
1652 return(-1);
1654 if(sz != NULL){ /* size uninteresting */
1655 strncpy(sz, gmp->current->size, szlen);
1656 sz[szlen-1] = '\0';
1659 zotmaster (&gmp);
1660 return (1);
1662 break;
1664 case 'w': /* Where is */
1665 case 'W':
1666 case (CTRL|'W'):
1667 i = 0;
1668 flags = SR_ORIGMEN | SR_FORWARD | SR_NOEXACT;
1670 while(!i){
1671 switch(readpattern(_("File name to find"), FALSE, flags)){
1672 case HELPCH:
1673 emlwwrite(_("No help yet!"), NULL);
1674 /* remove break and sleep after help text is installed */
1675 sleep(3);
1676 break;
1677 case (CTRL|'L'):
1678 PaintBrowser(gmp, 0, &crow, &ccol);
1679 break;
1680 case (CTRL|'P'):
1681 if(flags & SR_FORWARD){
1682 flags &= ~SR_FORWARD;
1683 flags |= SR_BACKWRD;
1684 } else {
1685 flags &= ~SR_BACKWRD;
1686 flags |= SR_FORWARD;
1688 break;
1689 case (CTRL|'Y'): /* first first cell */
1690 for(tp = gmp->top; tp->prev; tp = tp->prev)
1693 i++;
1694 /* fall thru to repaint */
1695 case (CTRL|'V'):
1696 if(!i){
1698 tp = gmp->top;
1699 if((i = term.t_nrow - term.t_mrow - 2) <= 0)
1700 break;
1702 while(i-- && tp->next){
1703 j = 0;
1704 while(++j <= gmp->fpl && tp->next)
1705 tp = tp->next;
1708 if(i < 0)
1709 gmp->top = tp;
1711 while(tp->next);
1713 emlwrite(_("Searched to end of directory"), NULL);
1715 else
1716 emlwrite(_("Searched to start of directory"), NULL);
1718 if(tp){
1719 PlaceCell(gmp, gmp->current, &row, &col);
1720 PaintCell(row, col, gmp->cpf, gmp->current, 0);
1721 gmp->current = tp;
1722 if(PlaceCell(gmp, gmp->current, &row, &col)){
1723 PaintBrowser(gmp, 1, &crow, &ccol);
1725 else{
1726 PaintCell(row, col, gmp->cpf, gmp->current, 1);
1727 crow = row;
1728 ccol = col;
1732 i++; /* make sure we jump out */
1733 break;
1734 case ABORT:
1735 emlwrite(_("Whereis cancelled"), NULL);
1736 i++;
1737 break;
1738 case FALSE:
1739 mlerase();
1740 i++;
1741 break;
1742 case TRUE:
1744 char *utf8 = NULL;
1746 if(pat && pat[0])
1747 utf8 = ucs4_to_utf8_cpystr(pat);
1749 if(utf8 && (tp = FindCell(gmp, utf8, flags & SR_BACKWRD)) != NULL){
1750 PlaceCell(gmp, gmp->current, &row, &col);
1751 PaintCell(row, col, gmp->cpf, gmp->current, 0);
1752 gmp->current = tp;
1754 if(PlaceCell(gmp, tp, &row, &col)){ /* top changed */
1755 PaintBrowser(gmp, 1, &crow, &ccol);
1757 else{
1758 PaintCell(row, col, gmp->cpf, gmp->current, 1);
1759 crow = row;
1760 ccol = col;
1762 mlerase();
1764 else{
1765 eml.s = utf8;
1766 emlwrite(_("\"%s\" not found"), &eml);
1769 if(utf8)
1770 fs_give((void **) &utf8);
1773 i++;
1774 break;
1775 default:
1776 break;
1780 BrowserKeys();
1781 break;
1783 case (CTRL|'\\') :
1784 #if defined MOUSE && !defined(_WINDOWS)
1785 toggle_xterm_mouse(0,1);
1786 #else
1787 unknown_command(c);
1788 #endif
1789 break;
1791 case (CTRL|'Z'):
1792 if(gmode&MDSSPD){
1793 bktoshell(0, 1);
1794 PaintBrowser(gmp, 0, &crow, &ccol);
1795 break;
1796 } /* fall thru with error! */
1798 default: /* what? */
1799 Default:
1800 unknown_command(c);
1802 case NODATA: /* no op */
1803 break;
1810 * getfcells - make a master browser struct and fill it in
1811 * return NULL if there's a problem.
1813 struct bmaster *
1814 getfcells(char *dname, int fb_flags)
1816 int i, /* various return codes */
1817 flength,
1818 nentries = 0; /* number of dir ents */
1819 off_t attsz;
1820 char *np, /* names of files in dir */
1821 *dcp, /* to add file to path */
1822 *tmpstr,
1823 **filtnames, /* array filtered names */
1824 errbuf[NLINE];
1825 struct fcell *ncp, /* new cell pointer */
1826 *tcp; /* trailing cell ptr */
1827 struct bmaster *mp;
1828 EML eml;
1830 errbuf[0] = '\0';
1831 if((mp=(struct bmaster *)malloc(sizeof(struct bmaster))) == NULL){
1832 emlwwrite(_("Can't malloc space for master filename cell"), NULL);
1833 return(NULL);
1836 memset(mp, 0, sizeof(*mp));
1838 if(dname[0] == '.' && dname[1] == '\0'){ /* remember this dir */
1839 if(!getcwd(mp->dname, 256))
1840 mp->dname[0] = '\0';
1842 else if(dname[0] == '.' && dname[1] == '.' && dname[2] == '\0'){
1843 if(!getcwd(mp->dname, 256))
1844 mp->dname[0] = '\0';
1845 else{
1846 if((np = (char *)strrchr(mp->dname, C_FILESEP)) != NULL)
1847 if(np != mp->dname)
1848 *np = '\0';
1851 else{
1852 strncpy(mp->dname, dname, sizeof(mp->dname));
1853 mp->dname[sizeof(mp->dname)-1] = '\0';
1856 mp->bottom = mp->head = mp->top = NULL;
1857 mp->menu = mp->cpf = mp->fpl = 0;
1858 mp->longest = 5; /* .. must be labeled! */
1859 mp->flags = fb_flags;
1861 eml.s = mp->dname;
1862 emlwrite("Building file list of %s...", &eml);
1864 if((mp->names = getfnames(mp->dname, NULL, &nentries, errbuf, sizeof(errbuf))) == NULL){
1865 free((char *) mp);
1866 if(*errbuf)
1867 emlwwrite(errbuf, NULL);
1869 return(NULL);
1873 * this is the fun part. build an array of pointers to the fnames we're
1874 * interested in (i.e., do any filtering), then pass that off to be
1875 * sorted before building list of cells...
1877 * right now default filtering on ".*" except "..", but this could
1878 * easily be made a user option later on...
1880 if((filtnames=(char **)malloc((nentries+1) * sizeof(char *))) == NULL){
1881 emlwwrite(_("Can't malloc space for name array"), NULL);
1882 zotmaster(&mp);
1883 return(NULL);
1886 i = 0; /* index of filt'd array */
1887 np = mp->names;
1888 while(nentries--){
1889 int ii;
1890 int width;
1893 * Filter dot files? Always filter ".", never filter "..",
1894 * and sometimes filter ".*"...
1896 if(*np == '.' && (!(*(np+1) == '.' && *(np+2) == '\0')
1897 && !(*(np+1) == '\0' && (fb_flags&FB_SAVE)))
1898 && (*(np+1) == '\0' || !(gmode & MDDOTSOK))){
1899 np += strlen(np) + 1;
1900 continue;
1903 filtnames[i++] = np;
1905 ii = (int) strlen(np);
1906 if((width = (int) utf8_width(np)) > mp->longest)
1907 mp->longest = width; /* remember longest */
1909 np += ii + 1; /* advance name pointer */
1912 nentries = i; /* new # of entries */
1915 * sort files case independently
1917 qsort((qsort_t *)filtnames, (size_t)nentries, sizeof(char *), sstrcasecmp);
1920 * this is so we use absolute path names for stats.
1921 * remember: be careful using dname as directory name, and fix mp->dname
1922 * when we're done
1924 dcp = (char *) strchr(mp->dname, '\0');
1925 if(dcp == mp->dname || dcp[-1] != C_FILESEP){
1926 dcp[0] = C_FILESEP;
1927 dcp[1] = '\0';
1929 else
1930 dcp--;
1932 i = 0;
1933 while(nentries--){ /* stat filtered files */
1934 /* get a new cell */
1935 if((ncp=(struct fcell *)malloc(sizeof(struct fcell))) == NULL){
1936 emlwwrite(_("Can't malloc cells for browser!"), NULL);
1937 zotfcells(mp->head); /* clean up cells */
1938 free((char *) filtnames);
1939 free((char *) mp);
1940 return(NULL); /* bummer. */
1943 ncp->next = ncp->prev = NULL;
1945 if(mp->head == NULL){ /* tie it onto the list */
1946 mp->head = mp->top = mp->current = ncp;
1948 else{
1949 mp->bottom = ncp;
1950 tcp->next = ncp;
1951 ncp->prev = tcp;
1954 tcp = ncp;
1956 /* fill in the new cell */
1957 ncp->fname = filtnames[i++];
1959 /* fill in file's mode */
1960 if ((flength = strlen(ncp->fname) + 1 + strlen(dname)) < sizeof(mp->dname)){
1961 strncpy(&dcp[1], ncp->fname, sizeof(mp->dname)-(dcp+1-mp->dname)); /* use absolute path! */
1962 mp->dname[sizeof(mp->dname)-1] = '\0';
1963 tmpstr = mp->dname;
1965 else{
1966 if((tmpstr = (char *)malloc((flength+1)*sizeof(char))) == NULL){
1967 emlwwrite(_("Can't malloc cells for temp buffer!"), NULL);
1968 zotfcells(mp->head); /* clean up cells */
1969 free((char *) filtnames);
1970 free((char *) mp);
1971 return(NULL); /* bummer. */
1974 strncpy(tmpstr, dname, flength);
1975 tmpstr[flength] = '\0';
1976 tmpstr = strncat(tmpstr, S_FILESEP, flength+1-1-strlen(tmpstr));
1977 tmpstr[flength] = '\0';
1978 tmpstr = strncat(tmpstr, ncp->fname, flength+1-1-strlen(tmpstr));
1979 tmpstr[flength] = '\0';
1982 switch(fexist(tmpstr, "t", &attsz)){
1983 case FIODIR :
1984 ncp->mode = FIODIR;
1985 snprintf(ncp->size, sizeof(ncp->size), "(%s%s%s)",
1986 (ncp->fname[0] == '.' && ncp->fname[1] == '.'
1987 && ncp->fname[2] == '\0') ? PARENTDIR :
1988 ((fb_flags&FB_SAVE) && ncp->fname[0] == '.' && ncp->fname[1] == '\0'
1989 ? SELECTWORD : ""),
1990 (ncp->fname[0] == '.' && ncp->fname[1] == '.'
1991 && ncp->fname[2] == '\0') ? " " :
1992 ((fb_flags&FB_SAVE) && ncp->fname[0] == '.' && ncp->fname[1] == '\0'
1993 ? " " : ""),
1994 DIRWORD);
1995 break;
1997 case FIOSYM :
1998 ncp->mode = FIOSYM;
1999 strncpy(ncp->size, "--", sizeof(ncp->size));
2000 ncp->size[sizeof(ncp->size)-1] = '\0';
2001 break;
2003 default :
2004 ncp->mode = FIOSUC; /* regular file */
2005 strncpy(ncp->size, prettysz(attsz), sizeof(ncp->size));
2006 ncp->size[sizeof(ncp->size)-1] = '\0';
2007 break;
2010 if (flength >= NLINE)
2011 free((char *) tmpstr);
2014 dcp[(dcp == mp->dname) ? 1 : 0] = '\0'; /* remember to cap dname */
2015 free((char *) filtnames); /* 'n blast filt'd array*/
2017 percdircells(mp);
2018 layoutcells(mp);
2019 if(strlen(mp->dname) < sizeof(browse_dir)){
2020 strncpy(browse_dir, mp->dname, sizeof(browse_dir));
2021 browse_dir[sizeof(browse_dir)-1] = '\0';
2023 else
2024 browse_dir[0] = '\0';
2026 return(mp);
2031 fcell_is_selected(struct fcell *cell, struct bmaster *mp)
2033 LMLIST *lm;
2035 if(cell && cell->fname){
2036 for(lm = mp ? mp->lm : NULL; lm; lm = lm->next){
2037 /* directory has to match */
2038 if(!((mp->dname[0] == '\0' && (!lm->dir || lm->dir[0] =='\0'))
2039 || (mp->dname[0] != '\0' && lm->dir && lm->dir[0] !='\0'
2040 && !strcmp(mp->dname, lm->dir))))
2041 continue;
2043 if(lm->fname && !strcmp(cell->fname, lm->fname))
2044 return(1);
2048 return(0);
2053 * Adds a new name to the head of the lmlist
2055 void
2056 add_cell_to_lmlist(struct fcell *cell, struct bmaster *mp)
2058 LMLIST *new;
2059 size_t flen, dlen;
2061 if(mp && cell && cell->fname && cell->fname[0]){
2062 if((new=(LMLIST *)malloc(sizeof(*new))) == NULL ||
2063 (new->fname=malloc(sizeof(char)*((flen=strlen(cell->fname))+1))) == NULL ||
2064 (new->dir=malloc(sizeof(char)*((dlen=strlen(mp->dname))+1))) == NULL){
2065 emlwwrite(_("Can't malloc space for filename"), NULL);
2066 return;
2069 strncpy(new->fname, cell->fname, flen);
2070 new->fname[flen] = '\0';
2071 strncpy(new->dir, mp->dname, dlen);
2072 new->dir[dlen] = '\0';
2073 new->size[0] = '\0';
2074 if(cell->size[0]){
2075 strncpy(new->size, cell->size, sizeof(new->size));
2076 new->size[sizeof(new->size)-1] = '\0';
2079 new->next = mp->lm;
2080 mp->lm = new;
2086 * Deletes a name from the lmlist
2088 void
2089 del_cell_from_lmlist(struct fcell *cell, struct bmaster *mp)
2091 LMLIST *lm, *lmprev = NULL;
2093 if(mp && cell && cell->fname && cell->fname[0])
2094 for(lm = mp ? mp->lm : NULL; lm; lm = lm->next){
2095 if(lm->fname && strcmp(cell->fname, lm->fname) == 0){
2096 free((char *) lm->fname);
2097 if(lm->dir)
2098 free((char *) lm->dir);
2100 if(lmprev)
2101 lmprev->next = lm->next;
2102 else
2103 mp->lm = lm->next;
2105 free((char *) lm);
2107 break;
2110 lmprev = lm;
2116 * PaintCell - print the given cell at the given location on the display
2117 * the format of a printed cell is:
2119 * "<fname> <size> "
2121 void
2122 PaintCell(int row, int col,
2123 int sc, /* screen columns available for this cell */
2124 struct fcell *cell, int inverted)
2126 char buf1[NLINE], buf2[NLINE];
2127 char lbuf[5];
2128 int need, l_wid, f_wid, f_to_s_wid, s_wid;
2129 UCS *ucs;
2131 if(cell == NULL)
2132 return;
2134 l_wid = (gmp && ((gmp->flags & FB_LMODEPOS) > 4)) ? 4 : 0;
2135 f_wid = utf8_width(cell->fname ? cell->fname : "");
2136 f_to_s_wid = 1;
2137 s_wid = utf8_width(cell->size ? cell->size : "");
2139 /* the two is the space between cell columns */
2140 sc = MIN(sc-2, term.t_ncol-col);
2142 if(l_wid){
2143 if(gmp && gmp->flags & FB_LMODE && cell->mode != FIODIR){
2145 * We have to figure out here if it is selected or not
2146 * and use that to write the X or space.
2148 lbuf[0] = '[';
2149 if(fcell_is_selected(cell, gmp))
2150 lbuf[1] = 'X';
2151 else
2152 lbuf[1] = ' ';
2154 lbuf[2] = ']';
2155 lbuf[3] = ' ';
2157 else{
2158 lbuf[0] = lbuf[1] = lbuf[2] = lbuf[3] = ' ';
2161 lbuf[4] = '\0';
2164 sc -= l_wid;
2166 need = f_wid+f_to_s_wid+s_wid;
2168 /* space between name and size */
2169 if(need < sc)
2170 f_to_s_wid += (sc-need);
2173 * If the width isn't enough to handle everything we're just putting a single
2174 * space between fname and size and truncating on the right. That means that
2175 * the sizes in the right hand column won't line up correctly when there is
2176 * a lack of space. Instead, we opt for displaying as much info as possible
2177 * in a slightly discordant way.
2180 movecursor(row, col);
2181 if(l_wid){
2182 ucs = utf8_to_ucs4_cpystr(lbuf);
2183 if(ucs){
2184 pputs(ucs, 0);
2185 fs_give((void **) &ucs);
2189 utf8_snprintf(buf1, sizeof(buf1), "%*.*w%*.*w%*.*w",
2190 f_wid, f_wid, cell->fname ? cell->fname : "",
2191 f_to_s_wid, f_to_s_wid, "",
2192 s_wid, s_wid, cell->size ? cell->size : "");
2194 utf8_snprintf(buf2, sizeof(buf2), "%*.*w", sc, sc, buf1);
2196 if(inverted)
2197 (*term.t_rev)(1);
2198 else if (*term.t_eri)
2199 (*term.t_eri)();
2201 ucs = utf8_to_ucs4_cpystr(buf2);
2202 if(ucs){
2203 pputs(ucs, 0);
2204 fs_give((void **) &ucs);
2207 if(inverted)
2208 (*term.t_rev)(0);
2213 * PaintBrowse - with the current data, display the browser. if level == 0
2214 * paint the whole thing, if level == 1 just paint the cells
2215 * themselves
2217 void
2218 PaintBrowser(struct bmaster *mp, int level, int *row, int *col)
2220 int i, cl;
2221 struct fcell *tp;
2223 if(!level){
2224 ClearBrowserScreen();
2225 BrowserAnchor(mp->dname);
2228 i = 0;
2229 tp = mp->top;
2230 cl = COMPOSER_TOP_LINE; /* current display line */
2231 while(tp){
2233 PaintCell(cl, mp->cpf * i, mp->cpf, tp, tp == mp->current);
2235 if(tp == mp->current){
2236 if(row)
2237 *row = cl;
2239 if(col)
2240 *col = mp->cpf * i;
2243 if(++i >= mp->fpl){
2244 i = 0;
2245 if(++cl > term.t_nrow-(term.t_mrow+1))
2246 break;
2249 tp = tp->next;
2252 if(level){
2253 while(cl <= term.t_nrow - (term.t_mrow+1)){
2254 if(!i)
2255 movecursor(cl, 0);
2256 peeol();
2257 movecursor(++cl, 0);
2260 else{
2261 BrowserKeys();
2266 * RepaintBrowser - factorized method to just repaint everything
2268 struct bmaster *
2269 RepaintBrowser(struct bmaster *gmp, int fb_flags)
2271 struct bmaster *mp;
2272 struct fcell *tp;
2273 int row, col;
2274 char *fn;
2275 fn = gmp->current->fname;
2277 if((mp = getfcells(gmp->dname, fb_flags)) == NULL){
2278 /* getfcells should explain what happened */
2279 return(gmp);
2281 zotmaster(&gmp);
2282 gmp = mp;
2284 tp = NULL;
2285 if(*fn){
2286 if((tp = FindCell(gmp, fn, 0)) != NULL){
2287 gmp->current = tp;
2288 PlaceCell(gmp, gmp->current, &row, &col);
2292 PaintBrowser(gmp, 0, NULL, NULL);
2293 return(gmp);
2298 * BrowserKeys - just paints the keyhelp at the bottom of the display
2300 void
2301 BrowserKeys(void)
2303 if (gmp && gmp->menu == 1){
2304 menu_other[GOTO_KEY].name = (gmode&MDGOTO) ? "G" : NULL;
2305 menu_other[GOTO_KEY].label = (gmode&MDGOTO) ? N_("Goto") : NULL;
2306 wkeyhelp(menu_other);
2307 return;
2309 menu_browse[QUIT_KEY].name = (gmode&MDBRONLY) ? "Q" : "E";
2310 /* TRANSLATORS: Brwsr is an abbreviation for Browser, which is
2311 a command used to look through something */
2312 menu_browse[QUIT_KEY].label = (gmode&MDBRONLY) ? N_("Quit") : N_("Exit Brwsr");
2313 if(gmode & MDBRONLY){
2314 menu_browse[EXEC_KEY].name = "L";
2315 menu_browse[EXEC_KEY].label = N_("Launch");
2316 menu_browse[SELECT_KEY].name = "V";
2317 menu_browse[SELECT_KEY].label = "[" N_("View") "]";
2318 menu_browse[PICO_KEY].name = "E";
2319 menu_browse[PICO_KEY].label = N_("Edit");
2321 else{
2322 menu_browse[SELECT_KEY].name = "S";
2323 menu_browse[SELECT_KEY].label = "[" N_("Select") "]";
2324 menu_browse[PICO_KEY].name = "A";
2325 menu_browse[PICO_KEY].label = N_("Add");
2327 if(gmp && gmp->flags & FB_LMODEPOS){
2328 if(gmp && gmp->flags & FB_LMODE){ /* ListMode is already on */
2329 menu_browse[EXEC_KEY].name = "X";
2330 menu_browse[EXEC_KEY].label = N_("Set/Unset");
2332 else{ /* ListMode is possible */
2333 menu_browse[EXEC_KEY].name = "L";
2334 menu_browse[EXEC_KEY].label = N_("ListMode");
2337 else{ /* No ListMode possible */
2338 menu_browse[EXEC_KEY].name = NULL;
2339 menu_browse[EXEC_KEY].label = NULL;
2343 wkeyhelp(menu_browse);
2348 * layoutcells - figure out max length of cell and how many cells can
2349 * go on a line of the display
2351 void
2352 layoutcells(struct bmaster *mp)
2354 static int wid = -1;
2356 * Max chars/file. Actually this is max screen cells/file
2357 * Longest name + "(parent dir)"
2359 if(wid < 0)
2360 wid = MAX(utf8_width(PARENTDIR),utf8_width(SELECTWORD)) + utf8_width(DIRWORD);
2362 mp->cpf = mp->longest + wid + 3;
2364 if(mp->flags & FB_LMODEPOS) /* "[X] " */
2365 mp->cpf += 4;
2367 if(gmode & MDONECOL){
2368 mp->fpl = 1;
2370 else{
2371 int i = 1;
2373 while(i*mp->cpf - 2 <= term.t_ncol) /* no force... */
2374 i++; /* like brute force! */
2376 mp->fpl = i - 1; /* files per line */
2379 if(mp->fpl == 0)
2380 mp->fpl = 1;
2385 * percdircells - bubble all the directory cells to the top of the
2386 * list.
2388 void
2389 percdircells(struct bmaster *mp)
2391 struct fcell *dirlp, /* dir cell list pointer */
2392 *lp, *nlp; /* cell list ptr and next */
2394 dirlp = NULL;
2395 for(lp = mp->head; lp; lp = nlp){
2396 nlp = lp->next;
2397 if(lp->mode == FIODIR){
2398 if(lp->prev) /* clip from list */
2399 lp->prev->next = lp->next;
2401 if(lp->next)
2402 lp->next->prev = lp->prev;
2404 if((lp->prev = dirlp) != NULL){ /* tie it into dir portion */
2405 if((lp->next = dirlp->next) != NULL)
2406 lp->next->prev = lp;
2408 dirlp->next = lp;
2409 dirlp = lp;
2411 else{
2412 if((dirlp = lp) != mp->head)
2413 dirlp->next = mp->head;
2415 if(dirlp->next)
2416 dirlp->next->prev = dirlp;
2418 mp->head = mp->top = mp->current = dirlp;
2426 * PlaceCell - given a browser master and a cell, return row and col of the display that
2427 * it should go on.
2429 * return 1 if mp->top has changed, x,y relative to new page
2430 * return 0 if otherwise (same page)
2431 * return -1 on error
2434 PlaceCell(struct bmaster *mp, struct fcell *cp, int *x, int *y)
2436 int cl = COMPOSER_TOP_LINE; /* current line */
2437 int ci = 0; /* current index on line */
2438 int rv = 0;
2439 int secondtry = 0;
2440 struct fcell *tp;
2442 /* will cp fit on screen? */
2443 tp = mp->top;
2444 while(1){
2445 if(tp == cp){ /* bingo! */
2446 *x = cl;
2447 *y = ci * mp->cpf;
2448 break;
2451 if((tp = tp->next) == NULL){ /* above top? */
2452 if(secondtry++){
2453 emlwwrite(_("Internal error: can't find fname cell"), NULL);
2454 return(-1);
2456 else{
2457 tp = mp->top = mp->head; /* try from the top! */
2458 cl = COMPOSER_TOP_LINE;
2459 ci = 0;
2460 rv = 1;
2461 continue; /* start over! */
2465 if(++ci >= mp->fpl){ /* next line? */
2466 ci = 0;
2467 if(++cl > term.t_nrow-(term.t_mrow+1)){ /* next page? */
2468 ci = mp->fpl; /* tp is at bottom right */
2469 while(ci--) /* find new top */
2470 tp = tp->prev;
2471 mp->top = tp;
2472 ci = 0;
2473 cl = COMPOSER_TOP_LINE; /* keep checking */
2474 rv = 1;
2480 /* not on display! */
2481 return(rv);
2486 * zotfcells - clean up malloc'd cells of file names
2488 void
2489 zotfcells(struct fcell *hp)
2491 struct fcell *tp;
2493 while(hp){
2494 tp = hp;
2495 hp = hp->next;
2496 tp->next = NULL;
2497 free((char *) tp);
2503 * zotmaster - blast the browser master struct
2505 void
2506 zotmaster(struct bmaster **mp)
2508 if(mp && *mp){
2509 zotfcells((*mp)->head); /* free cells */
2510 zotlmlist((*mp)->lm); /* free lmlist */
2511 if((*mp)->names)
2512 free((char *)(*mp)->names); /* free file names */
2514 free((char *)*mp); /* free master */
2515 *mp = NULL; /* make double sure */
2521 * FindCell - starting from the current cell find the first occurance of
2522 * the given string wrapping around if necessary
2524 struct fcell *
2525 FindCell(struct bmaster *mp, char *utf8string, int bsearch)
2527 struct fcell *tp, *fp;
2529 if(*utf8string == '\0')
2530 return(NULL);
2532 fp = NULL;
2533 tp = bsearch ? mp->current->prev : mp->current->next;
2535 while(tp && !fp){
2536 if(sisin(tp->fname, utf8string))
2537 fp = tp;
2538 else
2539 tp = bsearch ? tp->prev : tp->next;
2542 tp = bsearch ? mp->bottom : mp->head;
2543 while(tp != mp->current && !fp){
2544 if(sisin(tp->fname, utf8string))
2545 fp = tp;
2546 else
2547 tp = bsearch ? tp->prev : tp->next;
2550 return(fp);
2555 * sisin - case insensitive substring matching function
2557 * We can't really do case-insensitive for non-ascii, so restrict
2558 * that to ascii characters. The strings will be utf8.
2561 sisin(char *bigstr, char *utf8substr)
2563 register int j;
2565 while(*bigstr){
2566 j = 0;
2567 while(bigstr[j] == utf8substr[j]
2568 || ((unsigned char)bigstr[j] < 0x80 && (unsigned char)utf8substr[j] < 0x80
2569 && toupper((unsigned char)bigstr[j]) == toupper((unsigned char)utf8substr[j])))
2570 if(utf8substr[++j] == '\0') /* bingo! */
2571 return(1);
2573 bigstr++;
2576 return(0);
2581 * set_browser_title -
2583 void
2584 set_browser_title(char *s)
2586 browser_title = s;
2591 * BrowserAnchor - draw the browser's anchor line.
2593 void
2594 BrowserAnchor(char *utf8dir)
2596 char *pdir;
2597 char titlebuf[NLINE];
2598 char buf[NLINE];
2599 char dirbuf[NLINE];
2600 char *dots = "...";
2601 char *br = "BROWSER";
2602 char *dir = "Dir: ";
2603 UCS *ucs;
2604 int need, extra, avail;
2605 int title_wid, b_wid, t_to_b_wid, b_to_d_wid, d_wid, dot_wid, dir_wid, after_dir_wid;
2606 COLOR_PAIR *lastc = NULL;
2608 if(!utf8dir)
2609 utf8dir = "";
2611 pdir = utf8dir;
2613 if(browser_title)
2614 snprintf(titlebuf, sizeof(buf), " %s", browser_title);
2615 else if(Pmaster)
2616 snprintf(titlebuf, sizeof(buf), " ALPINE %s", Pmaster->pine_version);
2617 else
2618 snprintf(titlebuf, sizeof(buf), " UW PICO %s", (gmode&MDBRONLY) ? "BROWSER" : version);
2620 title_wid = utf8_width(titlebuf);
2621 t_to_b_wid = 15;
2622 b_wid = utf8_width(br);
2623 b_to_d_wid = 4;
2624 d_wid = utf8_width(dir);
2625 dot_wid = 0;
2626 dir_wid = utf8_width(pdir);
2628 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2630 if(need > term.t_ncol){
2631 extra = need - term.t_ncol;
2632 t_to_b_wid -= MIN(extra, 10);
2633 need -= MIN(extra, 10);
2635 if(need > term.t_ncol){
2636 extra = need - term.t_ncol;
2637 b_to_d_wid -= MIN(extra, 2);
2638 need -= MIN(extra, 2);
2641 if(need > term.t_ncol){
2642 titlebuf[0] = titlebuf[1] = ' ';
2643 titlebuf[2] = '\0';
2644 title_wid = utf8_width(titlebuf);
2645 t_to_b_wid = 0;
2646 b_to_d_wid = 4;
2647 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2648 if(need > term.t_ncol){
2649 extra = need - term.t_ncol;
2650 b_to_d_wid -= MIN(extra, 2);
2651 need -= MIN(extra, 2);
2654 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2655 if(need > term.t_ncol){
2656 t_to_b_wid = 0;
2657 b_wid = 0;
2658 b_to_d_wid = 0;
2661 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2662 if(need > term.t_ncol)
2663 d_wid = 0;
2665 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2667 if(need > term.t_ncol && dir_wid > 0){
2668 dot_wid = utf8_width(dots);
2669 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2670 while(need > term.t_ncol && (pdir = strchr(pdir+1, C_FILESEP))){
2671 dir_wid = utf8_width(pdir);
2672 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2675 if(!pdir){ /* adjust other widths to fill up space */
2676 avail = term.t_ncol - (title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid);
2677 if(avail > 2)
2678 utf8_to_width_rhs(dirbuf, utf8dir, sizeof(dirbuf), avail);
2679 else
2680 dirbuf[0] = '\0';
2682 pdir = dirbuf;
2685 dir_wid = utf8_width(pdir);
2690 extra = term.t_ncol - (title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid);
2692 if(extra >= 0)
2693 after_dir_wid = extra;
2694 else
2695 after_dir_wid = 0;
2697 utf8_snprintf(buf, sizeof(buf), "%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w",
2698 title_wid, title_wid, titlebuf,
2699 t_to_b_wid, t_to_b_wid, "",
2700 b_wid, b_wid, br,
2701 b_to_d_wid, b_to_d_wid, "",
2702 d_wid, d_wid, dir,
2703 dot_wid, dot_wid, dots,
2704 dir_wid, dir_wid, pdir,
2705 after_dir_wid, after_dir_wid, "");
2707 /* just making sure */
2708 utf8_snprintf(titlebuf, sizeof(titlebuf), "%-*.*w", term.t_ncol, term.t_ncol, buf);
2710 movecursor(0, 0);
2711 if(Pmaster && Pmaster->colors && Pmaster->colors->tbcp
2712 && pico_is_good_colorpair(Pmaster->colors->tbcp)){
2713 lastc = pico_get_cur_color();
2714 (void) pico_set_colorp(Pmaster->colors->tbcp, PSC_NONE);
2716 else
2717 (*term.t_rev)(1);
2719 ucs = utf8_to_ucs4_cpystr(titlebuf);
2720 if(ucs){
2721 pputs(ucs, 0);
2722 fs_give((void **) &ucs);
2725 if(lastc){
2726 (void) pico_set_colorp(lastc, PSC_NONE);
2727 free_color_pair(&lastc);
2729 else
2730 (*term.t_rev)(0);
2735 * ResizeBrowser - handle a resize event
2738 ResizeBrowser(void)
2740 if(gmp){
2741 layoutcells(gmp);
2742 PaintBrowser(gmp, 0, NULL, NULL);
2743 return(1);
2745 else
2746 return(0);
2750 void
2751 ClearBrowserScreen(void)
2753 int i;
2755 for(i = 0; i <= term.t_nrow; i++){ /* clear screen */
2756 movecursor(i, 0);
2757 peeol();
2762 void
2763 BrowserRunChild(char *child, char *dir)
2765 int status;
2766 char tmp[NLINE];
2767 time_t t_in, t_out;
2769 ClearBrowserScreen();
2770 movecursor(0, 0);
2771 (*term.t_close)();
2772 if(!isdir(dir, NULL, &t_in))
2773 t_in = 0;
2775 fflush(stdout);
2776 status = system(child);
2777 (*term.t_open)();
2778 if(t_in && isdir(dir, NULL, &t_out) && t_in < t_out){
2779 struct bmaster *mp;
2781 if((mp = getfcells(dir, 0)) != NULL){
2782 zotmaster(&gmp);
2783 gmp = mp;
2785 /* else getfcells should explain what happened */
2788 /* complain about non-zero exit status */
2789 if((status >> 8) & 0xff){
2791 movecursor(term.t_nrow - 1, 0);
2792 snprintf(tmp, sizeof(tmp), "[ \"%.30s\" exit with error value: %d ]",
2793 child, (status >> 8) & 0xff);
2794 pputs_utf8(tmp, 1);
2795 movecursor(term.t_nrow, 0);
2796 pputs_utf8("[ Hit RETURN to continue ]", 1);
2797 fflush(stdout);
2799 while(GetKey() != (CTRL|'M')){
2800 (*term.t_beep)();
2801 fflush(stdout);
2808 * imitate pc-pine memory for where we last called the file browser.
2810 void
2811 p_chdir(struct bmaster *mp)
2813 if(mp && mp->dname)
2814 chdir(mp->dname);
2816 #else /* _WINDOWS */
2820 * pico_file_browse - Exported version of FileBrowse below.
2823 pico_file_browse(PICO *pdata, char *dir, size_t dirlen, char *fn, size_t fnlen,
2824 char *sz, size_t szlen, int flags)
2826 return(FileBrowse(dir, dirlen, fn, fnlen, sz, szlen, flags, NULL));
2830 ResizeBrowser (void)
2832 return (0);
2836 * FileBrowse - Windows version of above function
2838 int
2839 FileBrowse(char *dir, size_t dirlen, char *fn, size_t fnlen,
2840 char *sz, size_t szlen, int fb_flags, LMLIST **lmreturn)
2842 struct stat sbuf;
2843 int rc;
2844 char lfn[NLINE];
2846 if (fb_flags & FB_SAVE){
2847 rc = mswin_savefile(dir, dirlen, fn, MIN(dirlen,fnlen));
2849 else{
2850 *fn = '\0'; /* No initial file names for
2851 * open. */
2852 if(fb_flags & FB_LMODEPOS){
2854 * We're going to allow multiple filenames to be returned so
2855 * we don't want to use the passed in fn for that. Instead, make
2856 * a bigger space and use that.
2858 char f[20000];
2859 size_t flen, dlen;
2861 memset(f, 0, sizeof(f));
2863 rc = mswin_multopenfile(dir, dirlen, f, sizeof(f),
2864 (fb_flags & FB_ATTACH) ? NULL : "Text Files (*.txt)#*.txt");
2866 if(rc == 1){
2867 LMLIST *lmhead = NULL, *new;
2868 char *p;
2871 * Build an LMLIST to return to the caller.
2873 for(p = f; *p; p += strlen(p)+1){
2874 flen = strlen(p);
2875 dlen = strlen(dir ? dir : "");
2876 new = (LMLIST *) fs_get(sizeof(*new));
2877 new->fname = (char *) fs_get((flen+1) * sizeof(char));
2878 new->dir = (char *) fs_get((dlen+1) * sizeof(char));
2880 strncpy(new->fname, p, flen);
2881 new->fname[flen] = '\0';
2882 strncpy(new->dir, dir ? dir : "", dlen);
2883 new->dir[dlen] = '\0';
2885 /* build full path to stat file. */
2886 if((strlen(new->dir) + strlen(S_FILESEP) +
2887 strlen(new->fname) + 1) < sizeof(lfn)){
2888 strncpy(lfn, new->dir, sizeof(lfn));
2889 lfn[sizeof(lfn)-1] = '\0';
2890 strncat(lfn, S_FILESEP, sizeof(lfn)-strlen(lfn)-1);
2891 strncat(lfn, new->fname, sizeof(lfn)-strlen(lfn)-1);
2892 lfn[sizeof(lfn)-1] = '\0';
2893 if(our_stat(lfn, &sbuf) < 0)
2894 strncpy(new->size, "0", 32);
2895 else
2896 strncpy(new->size, prettysz((off_t)sbuf.st_size), 32);
2898 new->size[32-1] = '\0';
2901 new->next = lmhead;
2902 lmhead = new;
2905 *lmreturn = lmhead;
2906 return(1);
2909 else
2910 rc = mswin_openfile (dir, dirlen, fn, fnlen,
2911 (fb_flags & FB_ATTACH) ? NULL : "Text Files (*.txt)#*.txt");
2914 if(rc == 1){
2915 if(sz != NULL){
2916 /* build full path to stat file. */
2917 if((strlen(dir) + strlen(S_FILESEP) + strlen(fn) + 1) > NLINE)
2918 return -1;
2920 strncpy(lfn, dir, sizeof(lfn));
2921 lfn[sizeof(lfn)-1] = '\0';
2922 strncat(lfn, S_FILESEP, sizeof(lfn)-strlen(lfn)-1);
2923 lfn[sizeof(lfn)-1] = '\0';
2924 strncat(lfn, fn, sizeof(lfn)-strlen(lfn)-1);
2925 lfn[sizeof(lfn)-1] = '\0';
2926 if(our_stat(lfn, &sbuf) < 0){
2927 strncpy(sz, "0", szlen);
2928 sz[szlen-1] = '\0';
2930 else{
2931 strncpy(sz, prettysz ((off_t)sbuf.st_size), szlen);
2932 sz[szlen-1] = '\0';
2936 return(1);
2939 return(rc ? -1 : 0);
2942 #endif /* _WINDOWS */
2946 * LikelyASCII - make a rough guess as to the displayability of the
2947 * given file.
2950 LikelyASCII(char *file)
2952 #define LA_TEST_BUF 1024
2953 #define LA_LINE_LIMIT 300
2954 int n, i, line, rv = FALSE;
2955 unsigned char buf[LA_TEST_BUF];
2956 FILE *fp;
2957 EML eml;
2959 if((fp = our_fopen(file, "rb")) != NULL){
2960 clearerr(fp);
2961 if((n = fread(buf, sizeof(char), LA_TEST_BUF * sizeof(char), fp)) > 0
2962 || !ferror(fp)){
2964 * If we don't hit any newlines in a reasonable number,
2965 * LA_LINE_LIMIT, of characters or the file contains NULLs,
2966 * bag out...
2968 rv = TRUE;
2969 for(i = line = 0; i < n; i++)
2970 if((line = (buf[i] == '\n') ? 0 : line + 1) >= LA_LINE_LIMIT
2971 || !buf[i]){
2972 rv = FALSE;
2973 emlwrite(_("Can't display non-text file. Try \"Launch\"."),
2974 NULL);
2975 break;
2978 else{
2979 eml.s = file;
2980 emlwrite(_("Can't read file: %s"), &eml);
2983 fclose(fp);
2985 else{
2986 eml.s = file;
2987 emlwrite(_("Can't open file: %s"), &eml);
2990 return(rv);
2994 void
2995 zotlmlist(LMLIST *lm)
2997 LMLIST *tp;
2999 while(lm){
3000 if(lm->fname)
3001 free(lm->fname);
3003 if(lm->dir)
3004 free(lm->dir);
3006 tp = lm;
3007 lm = lm->next;
3008 tp->next = NULL;
3009 free((char *) tp);
3015 * time_to_check - checks the current time against the last time called
3016 * and returns true if the elapsed time is > below.
3017 * Newmail won't necessarily check, but we want to give it
3018 * a chance to check or do a keepalive.
3021 time_to_check(void)
3023 static time_t lasttime = 0L;
3025 if(!get_input_timeout())
3026 return(FALSE);
3028 if(time((time_t *) 0) - lasttime > (Pmaster ? (time_t)(FUDGE-10) : get_input_timeout())){
3029 lasttime = time((time_t *) 0);
3030 return(TRUE);
3032 else
3033 return(FALSE);