* Clean up some function definitions to comply with strict
[alpine.git] / pico / browse.c
blobc762742dc35feedc9e0a727f0633bdfffc5fbe80
2 /*
3 * ========================================================================
4 * Copyright 2006-2008 University of Washington
5 * Copyright 2013-2022 Eduardo Chappa
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * ========================================================================
15 * Program: Routines to support file browser in pico and Pine composer
18 * NOTES:
20 * Misc. thoughts (mss, 5 Apr 92)
22 * This is supposed to be just a general purpose browser, equally
23 * callable from either pico or the pine composer. Someday, it could
24 * even be used to "wrap" the unix file business for really novice
25 * users. The stubs are here for renaming, copying, creating directories,
26 * deleting, undeleting (thought is delete just moves files to
27 * ~/.pico.deleted directory or something and undelete offers the
28 * files in there for undeletion: kind of like the mac trashcan).
30 * Nice side effects
32 * Since the full path name is always maintained and referencing ".."
33 * stats the path stripped of its trailing name, the unpleasantness of
34 * symbolic links is hidden.
36 * Fleshed out the file managements stuff (mss, 24 June 92)
40 #include "headers.h"
41 #include "../c-client/mail.h"
42 #include "../c-client/rfc822.h"
43 #include "../pith/osdep/collate.h"
44 #include "../pith/charconv/filesys.h"
45 #include "../pith/conf.h"
47 #ifndef _WINDOWS
49 #if defined(bsd) || defined(lnx)
50 #ifndef errno
51 extern int errno;
52 #endif
53 #endif
57 * directory cell structure
59 struct fcell {
60 char *fname; /* file name */
61 unsigned mode; /* file's mode */
62 char size[16]; /* file's size in s */
63 struct fcell *next;
64 struct fcell *prev;
69 * master browser structure
71 static struct bmaster {
72 struct fcell *head; /* first cell in list */
73 struct fcell *bottom; /* last cell in list */
74 struct fcell *top; /* cell in top left */
75 struct fcell *current; /* currently selected */
76 int longest; /* longest file name (in screen width) */
77 int fpl; /* file names per line */
78 int cpf; /* chars / file / line */
79 int menu; /* current menu to display */
80 int flags;
81 char dname[NLINE]; /* this dir's name (UTF-8) */
82 char *names; /* malloc'd name array (UTF-8) */
83 LMLIST *lm;
84 } *gmp; /* global master ptr */
88 * title for exported browser display
90 static char *browser_title = NULL;
93 struct bmaster *getfcells(char *, int);
94 void PaintCell(int, int, int, struct fcell *, int);
95 void PaintBrowser(struct bmaster *, int, int *, int *);
96 struct bmaster *RepaintBrowser(struct bmaster *, int);
97 void BrowserKeys(void);
98 void layoutcells(struct bmaster *);
99 void percdircells(struct bmaster *);
100 int PlaceCell(struct bmaster *, struct fcell *, int *, int *);
101 void zotfcells(struct fcell *);
102 void zotmaster(struct bmaster **);
103 struct fcell *FindCell(struct bmaster *, char *, int);
104 int sisin(char *, char *);
105 void p_chdir(struct bmaster *);
106 void BrowserAnchor(char *);
107 void ClearBrowserScreen(void);
108 void BrowserRunChild(char *, char *);
109 int fcell_is_selected(struct fcell *, struct bmaster *);
110 void add_cell_to_lmlist(struct fcell *, struct bmaster *);
111 void del_cell_from_lmlist(struct fcell *, struct bmaster *);
114 #define HELP_MENU {"?", N_("Get Help"), KS_SCREENHELP}
115 #define OTHER_MENU {"O", N_("OTHER CMDS"), KS_NONE}
116 static KEYMENU menu_browse[] = {
117 HELP_MENU, {NULL, NULL, KS_NONE},
118 {NULL, NULL, KS_NONE}, {"-", N_("Prev Pg"), KS_PREVPAGE},
119 {"D", N_("Delete"), KS_NONE}, {"C",N_("Copy"), KS_NONE},
120 OTHER_MENU, {NULL, NULL, KS_NONE},
121 {"W", N_("Where is"), KS_NONE}, {"Spc", N_("Next Pg"), KS_NEXTPAGE},
122 {"R", N_("Rename"), KS_NONE}, {NULL, NULL, KS_NONE}
124 static KEYMENU menu_other[] = {
125 HELP_MENU, {NULL, NULL, KS_NONE},
126 {NULL, NULL, KS_NONE}, {NULL, NULL, KS_NONE},
127 {NULL, NULL, KS_NONE}, {"1", N_("One Col"), KS_NONE},
128 OTHER_MENU, {NULL, NULL, KS_NONE},
129 {NULL, NULL, KS_NONE}, {NULL, NULL, KS_NONE},
130 {NULL, NULL, KS_NONE}, {".", N_("Dot files"), KS_NONE}
132 #define QUIT_KEY 1
133 #define EXEC_KEY 2
134 #define GOTO_KEY 10
135 #define SELECT_KEY 7
136 #define PICO_KEY 11
139 #define DIRWORD "dir"
140 #define PARENTDIR "parent"
141 #define SELECTWORD "SELECT"
145 * Default pager used by the stand-alone file browser.
147 #define BROWSER_PAGER ((gmode & MDFKEY) ? "alpine -k -F" : "alpine -F")
151 * function key mappings for callable browser
153 static UCS bfmappings[2][12][2] = { { { F1, '?'}, /* stand alone */
154 { F2, NODATA }, /* browser function */
155 { F3, 'q'}, /* key mappings... */
156 { F4, 'v'},
157 { F5, 'l'},
158 { F6, 'w'},
159 { F7, '-'},
160 { F8, ' '},
161 { F9, 'd'},
162 { F10, 'r'},
163 { F11, 'c'},
164 { F12, 'e'} },
165 { { F1, '?'}, /* callable browser */
166 { F2, NODATA }, /* function key */
167 { F3, 'e'}, /* mappings... */
168 { F4, 's'},
169 { F5, NODATA },
170 { F6, 'w'},
171 { F7, '-'},
172 { F8, ' '},
173 { F9, 'd'},
174 { F10, 'r'},
175 { F11, 'c'},
176 { F12, 'a'} } };
180 * Browser help for pico (pine composer help handed to us by pine)
182 static char *BrowseHelpText[] = {
183 /* TRANSLATORS: The next several lines go together. The ~ characters
184 should be left in front of the characters they cause to be bold. */
185 N_("Help for Browse Command"),
186 " ",
187 N_(" Pico's file browser is used to select a file from the"),
188 N_(" file system for inclusion in the edited text."),
189 " ",
190 N_("~ Both directories and files are displayed. Press ~S"),
191 N_("~ or ~R~e~t~u~r~n to select a file or directory. When a file"),
192 N_(" is selected during the \"Read File\" command, it is"),
193 N_(" inserted into edited text. Answering \"yes\" to the"),
194 N_(" verification question after a directory is selected causes"),
195 N_(" the contents of that directory to be displayed for selection."),
196 " ",
197 N_(" The file named \"..\" is special, and means the \"parent\""),
198 N_(" of the directory being displayed. Select this directory"),
199 N_(" to move upward in the directory tree."),
200 " ",
201 N_("End of Browser Help."),
202 " ",
203 NULL
207 * Help for standalone browser (pilot)
209 static char *sa_BrowseHelpText[] = {
210 /* TRANSLATORS: Some more help text */
211 N_("Help for Pilot (PIne's Looker-upper Of Things"),
212 " ",
213 N_(" Pilot is a simple, display-oriented file system browser based on the"),
214 N_("~ Alpine message system composer. As with Alpine, commands are displayed at"),
215 N_("~ the bottom of the screen, and context-sensitive help is provided."),
216 " ",
217 N_("~ Pilot displays the current working directory at the top of the screen."),
218 N_("~ The directory's contents are displayed in columns of file name, file"),
219 N_("~ size pairs. Names that are directories are indicated by the name"),
220 N_("~ ~(~d~i~r~) in place of the file size. The parent of the current working"),
221 N_("~ directory is indicated by the file name ~.~. and size of ~(~p~a~r~e~n~t~ ~d~i~r~)."),
222 N_("~ File names that are symbolic links to other files are displayed with a"),
223 N_("~ file size of ~-~-."),
224 " ",
225 N_(" The following function keys are available in Pilot:"),
226 " ",
227 N_("~ ~? Display this help text."),
228 N_("~ ~Q Quit Pilot."),
229 " ",
230 N_("~ ~V View the currently selected file or open the selected directory."),
231 N_("~ Note: Pilot invokes ~a~l~p~i~n~e ~-~F, or the program defined by the ~P~A~G~E~R"),
232 N_("~ environment variable, to view the file."),
233 N_("~ ~L Launch an external application program."),
234 " ",
235 N_("~ ~W Search for a file by name."),
236 N_("~ ~- Scroll up one page."),
237 N_("~ ~S~p~a~c~e Scroll down one page."),
238 N_("~ ~N,~^~F Move forward (right) one column."),
239 N_("~ ~P,~^~B Move back (left) one column."),
240 N_("~ ~^~N Move down one row."),
241 N_("~ ~^~P Move up one row."),
242 N_("~ ~. Switch show dot files."),
243 N_("~ ~1 Switch single column display."),
244 " ",
245 N_("~ ~D Delete the selected file."),
246 N_("~ ~R Rename the selected file or directory."),
247 N_("~ ~C Copy the selected file."),
248 N_("~ ~E Edit the selected file."),
249 N_("~ Note: Pilot invokes ~p~i~c~o, or the program defined by the ~E~D~I~T~O~R"),
250 N_("~ environment variable, to edit the file."),
251 " ",
252 N_("End of Pilot Help."),
253 NULL
259 * pico_file_browse - Exported version of FileBrowse below.
262 pico_file_browse(PICO *pdata, char *dir, size_t dirlen, char *fn, size_t fnlen,
263 char *sz, size_t szlen, int flags)
265 int rv;
266 char title_buf[64];
268 Pmaster = pdata;
269 gmode = pdata->pine_flags | MDEXTFB;
270 km_popped = 0;
272 /* only init screen bufs for display and winch handler */
273 if(!vtinit())
274 return(-1);
276 if(Pmaster){
277 term.t_mrow = Pmaster->menu_rows;
278 if(Pmaster->oper_dir)
279 strncpy(opertree, Pmaster->oper_dir, NLINE);
281 if(*opertree)
282 fixpath(opertree, sizeof(opertree));
285 /* install any necessary winch signal handler */
286 ttresize();
288 snprintf(title_buf, sizeof(title_buf), "%s FILE", pdata->pine_anchor);
289 set_browser_title(title_buf);
290 rv = FileBrowse(dir, dirlen, fn, fnlen, sz, szlen, flags, NULL);
291 set_browser_title(NULL);
292 vttidy(); /* clean up tty handling */
293 zotdisplay(); /* and display structures */
294 Pmaster = NULL; /* and global config structure */
295 return(rv);
301 * FileBrowse - display contents of given directory dir
303 * input:
304 * dir points to initial dir to browse.
305 * dirlen- buffer size of dir
306 * fn initial file name.
307 * fnlen- buffer size of fn
309 * returns:
310 * dir points to currently selected directory (without
311 * trailing file system delimiter)
312 * fn points to currently selected file
313 * sz points to size of file if ptr passed was non-NULL
314 * flags
316 * Special dispensation for FB_LMODE. If the caller sets
317 * FB_LMODEPOS, and the caller passes a non-null lmreturn,
318 * then the return values will be in lmreturn instead of
319 * in dir and fn. The caller is responsible for freeing
320 * the contents of lmreturn.
322 * 1 if a file's been selected
323 * 0 if no files selected
324 * -1 if there were problems
327 FileBrowse(char *dir, size_t dirlen, char *fn, size_t fnlen,
328 char *sz, size_t szlen, int fb_flags, LMLIST **lmreturn)
330 UCS c, new_c;
331 int status, i, j;
332 int row, col, crow, ccol;
333 int flags;
334 int add_file;
335 char *p, *envp, child[2*NLINE+2], tmp[2*NLINE+1];
336 struct bmaster *mp;
337 struct fcell *tp;
338 EML eml;
339 #ifdef MOUSE
340 MOUSEPRESS mousep;
341 #endif
343 child[0] = '\0';
345 if((gmode&MDTREE) && !in_oper_tree(dir)){
346 eml.s = opertree;
347 emlwwrite(_("Can't read outside of %s in restricted mode"), &eml);
348 sleep(2);
349 return(0);
352 if(gmode&MDGOTO){
353 /* fix up function key mapping table */
354 /* fix up key menu labels */
357 /* build contents of cell structures */
358 if((gmp = getfcells(dir, fb_flags)) == NULL)
359 return(-1);
361 tp = NULL;
362 if(fn && *fn){
363 if((tp = FindCell(gmp, fn, 0)) != NULL){
364 gmp->current = tp;
365 PlaceCell(gmp, gmp->current, &row, &col);
369 /* paint screen */
370 PaintBrowser(gmp, 0, &crow, &ccol);
372 while(1){ /* the big loop */
373 if(!(gmode&MDSHOCUR)){
374 crow = term.t_nrow-term.t_mrow;
375 ccol = 0;
378 if(!(gmode&MDSHOCUR))
379 movecursor(crow, ccol);
380 else if(gmp->flags & FB_LMODEPOS){
381 if(gmp->flags & FB_LMODE && gmp->current->mode != FIODIR)
382 movecursor(crow, ccol+1);
383 else
384 movecursor(crow, ccol+4);
386 else
387 movecursor(crow, ccol);
389 if(km_popped){
390 km_popped--;
391 if(km_popped == 0)
392 /* cause bottom three lines to repaint */
393 PaintBrowser(gmp, 0, &crow, &ccol);
396 if(km_popped){ /* temporarily change to cause menu to paint */
397 term.t_mrow = 2;
398 movecursor(term.t_nrow-2, 0); /* clear status line */
399 peeol();
400 BrowserKeys();
401 term.t_mrow = 0;
404 (*term.t_flush)();
406 #ifdef MOUSE
407 mouse_in_content(KEY_MOUSE, -1, -1, 0, 0);
408 register_mfunc(mouse_in_content,2,0,term.t_nrow-(term.t_mrow+1),
409 term.t_ncol);
410 #endif
411 c = GetKey();
412 #ifdef MOUSE
413 clear_mfunc(mouse_in_content);
414 #endif
416 if(Pmaster){
417 if(Pmaster->newmail && (c == NODATA || time_to_check())){
418 if((*Pmaster->newmail)(c == NODATA ? 0 : 2, 1) >= 0){
419 int rv;
421 if(km_popped){ /* restore display */
422 km_popped = 0;
423 PaintBrowser(gmp, 0, &crow, &ccol);
426 clearcursor();
427 mlerase();
428 rv = (*Pmaster->showmsg)(c);
429 ttresize();
430 picosigs();
431 if(rv) /* Did showmsg corrupt the display? */
432 PaintBrowser(gmp, 0, &crow, &ccol); /* Yes, repaint */
434 mpresf = 1;
437 clearcursor();
438 if(gmp->flags & FB_LMODEPOS){
439 if(gmp->flags & FB_LMODE && gmp->current->mode != FIODIR)
440 movecursor(crow, ccol+1);
441 else
442 movecursor(crow, ccol+4);
444 else
445 movecursor(crow, ccol);
448 else{
449 if(get_input_timeout() && (c == NODATA || time_to_check()))
450 if(pico_new_mail())
451 emlwrite(_("You may possibly have new mail."), NULL);
454 if(km_popped)
455 switch(c){
456 case NODATA:
457 case (CTRL|'L'):
458 km_popped++;
459 break;
461 default:
462 /* clear bottom three lines */
463 movecursor(term.t_nrow-2, 0);
464 peeol();
465 movecursor(term.t_nrow-1, 0);
466 peeol();
467 movecursor(term.t_nrow, 0);
468 peeol();
469 break;
472 if(c == NODATA) /* GetKey timed out */
473 continue;
474 else if(Pmaster)
475 (*Pmaster->keybinput)();
478 if(mpresf){ /* blast old messages */
479 if(mpresf++ > MESSDELAY){ /* every few keystrokes */
480 mlerase();
484 /* process commands */
485 switch(new_c = normalize_cmd(c,bfmappings[(gmode&MDBRONLY)?0:1],2)){
487 case KEY_RIGHT: /* move right */
488 case (CTRL|'@'):
489 case (CTRL|'F'): /* forward */
490 case 'n' :
491 case 'N' :
492 if(gmp->current->next == NULL){
493 (*term.t_beep)();
494 break;
497 PlaceCell(gmp, gmp->current, &row, &col);
498 PaintCell(row, col, gmp->cpf, gmp->current, 0);
499 gmp->current = gmp->current->next;
500 if(PlaceCell(gmp, gmp->current, &row, &col)){
501 PaintBrowser(gmp, 1, &crow, &ccol);
503 else{
504 PaintCell(row, col, gmp->cpf, gmp->current, 1);
505 crow = row;
506 ccol = col;
508 break;
510 case KEY_LEFT: /* move left */
511 case (CTRL|'B'): /* back */
512 case 'p' :
513 case 'P' :
514 if(gmp->current->prev == NULL){
515 (*term.t_beep)();
516 break;
519 PlaceCell(gmp, gmp->current, &row, &col);
520 PaintCell(row, col, gmp->cpf, gmp->current, 0);
521 gmp->current = gmp->current->prev;
522 if(PlaceCell(gmp, gmp->current, &row, &col)){
523 PaintBrowser(gmp, 1, &crow, &ccol);
525 else{
526 PaintCell(row, col, gmp->cpf, gmp->current, 1);
527 crow = row;
528 ccol = col;
530 break;
532 case (CTRL|'A'): /* beginning of line */
533 tp = gmp->current;
534 i = col;
535 while(i > 0){
536 i -= gmp->cpf;
537 if(tp->prev != NULL)
538 tp = tp->prev;
540 PlaceCell(gmp, gmp->current, &row, &col);
541 PaintCell(row, col, gmp->cpf, gmp->current, 0);
542 gmp->current = tp;
543 if(PlaceCell(gmp, tp, &row, &col)){
544 PaintBrowser(gmp, 1, &crow, &ccol);
546 else{
547 PaintCell(row, col, gmp->cpf, gmp->current, 1);
548 crow = row;
549 ccol = col;
551 break;
553 case (CTRL|'E'): /* end of line */
554 tp = gmp->current;
555 i = col + gmp->cpf;
556 while(i+gmp->cpf <= gmp->cpf * gmp->fpl){
557 i += gmp->cpf;
558 if(tp->next != NULL)
559 tp = tp->next;
562 PlaceCell(gmp, gmp->current, &row, &col);
563 PaintCell(row, col, gmp->cpf, gmp->current, 0);
564 gmp->current = tp;
565 if(PlaceCell(gmp, tp, &row, &col)){
566 PaintBrowser(gmp, 1, &crow, &ccol);
568 else{
569 PaintCell(row, col, gmp->cpf, gmp->current, 1);
570 crow = row;
571 ccol = col;
573 break;
575 case (CTRL|'V'): /* page forward */
576 case ' ':
577 case KEY_PGDN :
578 case KEY_END :
579 tp = gmp->top;
580 i = term.t_nrow - term.t_mrow - 2;
582 while(i-- && tp->next != NULL){
583 j = 0;
584 while(++j <= gmp->fpl && tp->next != NULL)
585 tp = tp->next;
588 if(tp == NULL)
589 continue;
591 PlaceCell(gmp, gmp->current, &row, &col);
592 PaintCell(row, col, gmp->cpf, gmp->current, 0);
593 gmp->current = tp;
594 if(PlaceCell(gmp, tp, &row, &col)){
595 PaintBrowser(gmp, 1, &crow, &ccol);
597 else{
598 PaintCell(row, col, gmp->cpf, gmp->current, 1);
599 crow = row;
600 ccol = col;
602 break;
604 case '-' :
605 case (CTRL|'Y'): /* page backward */
606 case KEY_PGUP :
607 case KEY_HOME :
608 tp = gmp->top;
609 i = term.t_nrow - term.t_mrow - 4;
610 while(i-- && tp != NULL){
611 j = gmp->fpl;
612 while(j-- && tp != NULL)
613 tp = tp->prev;
616 if(tp || (gmp->current != gmp->top)){ /* clear old hilite */
617 PlaceCell(gmp, gmp->current, &row, &col);
618 PaintCell(row, col, gmp->cpf, gmp->current, 0);
621 if(tp) /* new page ! */
622 gmp->current = tp;
623 else if(gmp->current != gmp->top) /* goto top of page */
624 gmp->current = gmp->top;
625 else /* do nothing */
626 continue;
628 if(PlaceCell(gmp, gmp->current, &row, &col)){
629 PaintBrowser(gmp, 1, &crow, &ccol);
631 else{
632 PaintCell(row, col, gmp->cpf, gmp->current, 1);
633 crow = row;
634 ccol = col;
637 break;
639 case KEY_DOWN :
640 case (CTRL|'N'): /* next */
641 tp = gmp->current;
642 i = gmp->fpl;
643 while(i--){
644 if(tp->next == NULL){
645 (*term.t_beep)();
646 break;
648 else
649 tp = tp->next;
651 if(i != -1) /* can't go down */
652 break;
654 PlaceCell(gmp, gmp->current, &row, &col);
655 PaintCell(row, col, gmp->cpf, gmp->current, 0);
656 gmp->current = tp;
657 if(PlaceCell(gmp, tp, &row, &col)){
658 PaintBrowser(gmp, 1, &crow, &ccol);
660 else{
661 PaintCell(row, col, gmp->cpf, gmp->current, 1);
662 crow = row;
663 ccol = col;
665 break;
667 case KEY_UP :
668 case (CTRL|'P'): /* previous */
669 tp = gmp->current;
670 i = gmp->fpl;
671 while(i-- && tp)
672 tp = tp->prev;
674 if(tp == NULL)
675 break;
677 PlaceCell(gmp, gmp->current, &row, &col);
678 PaintCell(row, col, gmp->cpf, gmp->current, 0);
679 gmp->current = tp;
680 if(PlaceCell(gmp, tp, &row, &col)){
681 PaintBrowser(gmp, 1, &crow, &ccol);
683 else{
684 PaintCell(row, col, gmp->cpf, gmp->current, 1);
685 crow = row;
686 ccol = col;
688 break;
690 #ifdef MOUSE
691 case KEY_MOUSE:
692 mouse_get_last (NULL, &mousep);
693 if (mousep.doubleclick) {
694 goto Selected;
696 else {
697 row = mousep.row -= 2; /* Adjust for header*/
698 col = mousep.col;
699 i = row * gmp->fpl + (col / gmp->cpf); /* Count from top */
700 tp = gmp->top; /* start at top. */
701 for (; i > 0 && tp != NULL; --i) /* Count cells. */
702 tp = tp->next;
703 if (tp != NULL) { /* Valid cell? */
704 PlaceCell(gmp, gmp->current, &row, &col);
705 PaintCell(row, col, gmp->cpf, gmp->current, 0);
706 gmp->current = tp;
707 if(PlaceCell(gmp, tp, &row, &col)){
708 PaintBrowser(gmp, 1, &crow, &ccol);
710 else{
711 PaintCell(row, col, gmp->cpf, gmp->current, 1);
712 crow = row;
713 ccol = col;
717 break;
718 #endif
720 case 'e': /* exit or edit */
721 case 'E':
722 if(gmode&MDBRONLY){ /* run "pico" */
723 char *t;
724 snprintf(child, sizeof(child), "%.*s%c%.*s", NLINE, gmp->dname, C_FILESEP,
725 NLINE, 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 t = fs_get(strlen(envp) + strlen(child) + 3 + 1);
734 sprintf(t, "%s \'%s\'", envp, child);
736 else{
737 t = fs_get(strlen(child) + 16 + 1);
738 sprintf(t, "pico%s%s%s \'%s\'",
739 (gmode & MDFKEY) ? " -f" : "",
740 (gmode & MDSHOCUR) ? " -g" : "",
741 (gmode & MDMOUSE) ? " -m" : "",
742 child);
745 BrowserRunChild(t, gmp->dname); /* spawn pico */
746 PaintBrowser(gmp, 0, &crow, &ccol); /* redraw browser */
747 if(t) fs_give((void **) &t);
749 else{
750 zotmaster(&gmp);
751 return(0);
754 break;
756 case 'q': /* user exits wrong */
757 case 'Q':
758 if(gmode&MDBRONLY){
759 zotmaster(&gmp);
760 return(0);
763 unknown_command(c);
764 break;
766 case 'x': /* toggle selection */
767 case 'X':
768 if(!(gmp->flags & FB_LMODE)){
769 if(gmp->flags & FB_LMODEPOS)
770 emlwwrite(_("Type L command to use ListMode"), NULL);
771 else
772 unknown_command(c);
774 break;
777 if(gmp->current->mode == FIODIR){
778 emlwwrite(_("Can't Set directories"), NULL);
779 break;
782 if(fcell_is_selected(gmp->current, gmp))
783 del_cell_from_lmlist(gmp->current, gmp);
784 else
785 add_cell_to_lmlist(gmp->current, gmp);
787 PlaceCell(gmp, gmp->current, &row, &col);
788 PaintCell(row, col, gmp->cpf, gmp->current, 1);
789 break;
791 case 'l': /* run Command */
792 case 'L': /* or ListMode */
793 if(gmp->flags & FB_LMODEPOS){
794 if(gmp->flags & FB_LMODE){
796 * Unless we make it so you can get out of ListMode
797 * once you're in ListMode, this must be an error.
799 emlwwrite(_("Already in ListMode"), NULL);
800 break;
802 else{
803 gmp->flags |= FB_LMODE;
804 PaintBrowser(gmp, 0, &crow, &ccol);
807 break;
810 if(!(gmode&MDBRONLY)){
811 unknown_command(c);
812 break;
815 /* add subcommands to invoke pico and insert selected filename */
816 /* perhaps: add subcmd to scroll command history */
818 tmp[0] = '\0';
819 i = 0;
820 snprintf(child, sizeof(child), "%.*s%c%.*s", NLINE, gmp->dname, C_FILESEP,
821 NLINE, gmp->current->fname);
822 while(!i){
823 static EXTRAKEYS opts[] = {
824 {"^X", N_("Add Name"), CTRL|'X', 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},
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}
836 status = mlreply_utf8(_("Command to execute: "),
837 tmp, NLINE, QNORML, opts);
838 switch(status){
839 case HELPCH:
840 emlwwrite(_("No help yet!"), NULL);
841 /* remove break and sleep after help text is installed */
842 sleep(3);
843 break;
844 case (CTRL|'X'):
845 strncat(tmp, child, sizeof(tmp)-strlen(tmp)-1);
846 tmp[sizeof(tmp)-1] = '\0';
847 break;
848 case (CTRL|'L'):
849 PaintBrowser(gmp, 0, &crow, &ccol);
850 break;
851 case ABORT:
852 emlwrite(_("Command cancelled"), NULL);
853 i++;
854 break;
855 case FALSE:
856 case TRUE:
857 i++;
859 if(tmp[0] == '\0'){
860 emlwrite(_("No command specified"), NULL);
861 break;
864 BrowserRunChild(tmp, gmp->dname);
865 PaintBrowser(gmp, 0, &crow, &ccol);
866 break;
867 default:
868 break;
872 BrowserKeys();
873 break;
875 case 'd': /* delete */
876 case 'D':
877 if(gmp->current->mode == FIODIR){
878 /* BUG: if dir is empty it should be deleted */
879 emlwwrite(_("Can't delete a directory"), NULL);
880 break;
883 if(gmode&MDSCUR){ /* not allowed! */
884 emlwrite(_("Delete not allowed in restricted mode"),NULL);
885 break;
888 snprintf(child, sizeof(child), "%s%c%s", gmp->dname, C_FILESEP,
889 gmp->current->fname);
891 i = 0;
892 while(i++ < 2){ /* verify twice!! */
893 if(i == 1){
894 if(fexist(child, "w", (off_t *)NULL) != FIOSUC){
895 strncpy(tmp, _("File is write protected! OVERRIDE"), sizeof(tmp));
896 tmp[sizeof(tmp)-1] = '\0';
898 else
899 /* TRANSLATORS: This is a question, Delete file <filename> */
900 snprintf(tmp, sizeof(tmp), _("Delete file \"%.*s\""), NLINE - 20, child);
902 else{
903 strncpy(tmp, _("File CANNOT be UNdeleted! Really delete"), sizeof(tmp));
904 tmp[sizeof(tmp)-1] = '\0';
907 if((status = mlyesno_utf8(tmp, FALSE)) != TRUE){
908 emlwrite((status == ABORT)
909 ? _("Delete Cancelled")
910 : _("File Not Deleted"),
911 NULL);
912 break;
916 if(status == TRUE){
917 if(our_unlink(child) < 0){
918 eml.s = errstr(errno);
919 emlwrite(_("Delete Failed: %s"), &eml);
921 else{ /* fix up pointers and redraw */
922 tp = gmp->current;
923 if(tp->next){
924 gmp->current = tp->next;
925 if((tp->next->prev = tp->prev) != NULL)
926 tp->prev->next = tp->next;
928 else if(tp->prev) {
929 gmp->current = tp->prev;
930 if((tp->prev->next = tp->next) != NULL)
931 tp->next->prev = tp->prev;
934 if(tp == gmp->head)
935 gmp->head = tp->next;
937 if(tp == gmp->bottom)
938 gmp->bottom = tp->prev;
940 tp->fname = NULL;
941 tp->next = tp->prev = NULL;
942 if(tp != gmp->current)
943 free((char *) tp);
945 if((tp = FindCell(gmp, gmp->current->fname, 0)) != NULL){
946 gmp->current = tp;
947 PlaceCell(gmp, gmp->current, &row, &col);
950 PaintBrowser(gmp, 1, &crow, &ccol);
951 mlerase();
955 BrowserKeys();
956 break;
958 case '?': /* HELP! */
959 case (CTRL|'G'):
960 if(term.t_mrow == 0){
961 if(km_popped == 0){
962 km_popped = 2;
963 break;
967 if(Pmaster){
968 VARS_TO_SAVE *saved_state;
970 saved_state = save_pico_state();
971 (*Pmaster->helper)(Pmaster->browse_help,
972 _("Help for Browsing"), 1);
973 if(saved_state){
974 restore_pico_state(saved_state);
975 free_pico_state(saved_state);
978 else if(gmode&MDBRONLY)
979 pico_help(sa_BrowseHelpText, _("Browser Help"), 1);
980 else
981 pico_help(BrowseHelpText, _("Help for Browsing"), 1);
982 /* fall thru to repaint everything */
984 case (CTRL|'L'):
985 PaintBrowser(gmp, 0, &crow, &ccol);
986 break;
988 case 'g': /* jump to a directory */
989 case 'G':
991 if(!(gmode&MDGOTO))
992 goto Default;
994 i = 0;
995 child[0] = '\0';
997 while(!i){
999 /* TRANSLATORS: A prompt asking for a directory to
1000 move into */
1001 status = mlreply_utf8(_("Directory to go to: "), child, NLINE, QNORML,
1002 NULL);
1004 switch(status){
1005 case HELPCH:
1006 emlwwrite(_("No help yet!"), NULL);
1007 /* remove break and sleep after help text is installed */
1008 sleep(3);
1009 break;
1010 case (CTRL|'L'):
1011 PaintBrowser(gmp, 0, &crow, &ccol);
1012 break;
1013 case ABORT:
1014 emlwrite(_("Goto cancelled"), NULL);
1015 i++;
1016 break;
1017 case FALSE:
1018 case TRUE:
1019 i++;
1021 if(*child == '\0'){
1022 strncpy(child, gethomedir(NULL), sizeof(child));
1023 child[sizeof(child)-1] = '\0';
1026 if(!compresspath(gmp->dname, child, sizeof(child))){
1027 eml.s = child;
1028 emlwrite(_("Invalid Directory: %s"), &eml);
1029 break;
1032 if((gmode&MDSCUR) && homeless(child)){
1033 emlwrite(_("Restricted mode browsing limited to home directory"),NULL);
1034 break;
1037 if((gmode&MDTREE) && !in_oper_tree(child)){
1038 eml.s = opertree;
1039 emlwwrite(_("Can't go outside of %s in restricted mode"),
1040 &eml);
1041 break;
1044 if(isdir(child, (long *) NULL, NULL)){
1045 if((mp = getfcells(child, fb_flags)) == NULL){
1046 /* getfcells should explain what happened */
1047 i++;
1048 break;
1051 zotmaster(&gmp);
1052 gmp = mp;
1053 PaintBrowser(gmp, 0, &crow, &ccol);
1055 else{
1056 eml.s = child;
1057 emlwwrite(_("Not a directory: \"%s\""), &eml);
1060 break;
1061 default:
1062 break;
1065 BrowserKeys();
1066 break;
1068 case '.': /* display dot files */
1069 gmode ^= MDDOTSOK;
1070 gmp = RepaintBrowser(gmp, fb_flags);
1071 break;
1073 case '1': /* One column mode */
1074 gmode ^= MDONECOL;
1075 gmp = RepaintBrowser(gmp, fb_flags);
1076 break;
1078 case 'o': /* Other menu */
1079 case 'O':
1080 if (gmp){
1081 if(gmp->menu == 0){
1082 gmp->menu = 1;
1083 } else {
1084 gmp->menu = 0;
1086 BrowserKeys();
1088 break;
1090 case 'a': /* Add */
1091 case 'A':
1092 if(gmode&MDSCUR){ /* not allowed! */
1093 emlwrite(_("Add not allowed in restricted mode"),NULL);
1094 break;
1097 add_file = 1;
1098 i = 0;
1099 child[0] = '\0';
1100 /* pass in default filename */
1101 if(fn && *fn){
1102 strncpy(child, fn, sizeof(child) - 1);
1103 child[sizeof(child) - 1] = '\0';
1106 while(!i){
1107 int repaint = 0;
1108 EXTRAKEYS opts[10];
1110 memset((void *) &opts, 0, 10*sizeof(EXTRAKEYS));
1111 opts[0].name = "^X";
1112 opts[0].label = add_file ? N_("Add Dir") : N_("Add File");
1113 opts[0].key = (CTRL|'X');
1115 switch(status=mlreply_utf8(add_file ? _("Name of file to add: ") : _("Name of directory to add: "), child, NLINE,
1116 QFFILE, opts)){
1117 case HELPCH:
1118 emlwwrite(_("No help yet!"), NULL);
1119 /* remove break and sleep after help text is installed */
1120 sleep(3);
1121 break;
1122 case (CTRL|'L'):
1123 PaintBrowser(gmp, 0, &crow, &ccol);
1124 break;
1125 case (CTRL|'X'):
1126 if(add_file > 0) add_file = 0; else add_file = 1;
1127 break;
1128 case ABORT:
1129 emlwrite(add_file > 0 ? _("Add File Cancelled") : _("Add Directory Cancelled"), NULL);
1130 i++;
1131 break;
1132 case FALSE:
1134 * Support default filename. A return of 'FALSE' means
1135 * 'No change in filename' which is fine if we have
1136 * provided a default.
1137 * John Berthels <john.berthels@nexor.co.uk>
1139 /* FALLTHROUGH */
1140 case TRUE:
1141 i++;
1143 if(child[0] == '\0'){
1144 emlwrite(add_file > 0 ? _("No file named. Add Cancelled.") : _("No directory named. Add Cancelled"), NULL);
1145 break;
1148 if(!compresspath(gmp->dname, child, sizeof(child))){
1149 emlwrite(_("Too many ..'s in name"), NULL);
1150 break;
1153 if((gmode&MDTREE) && !in_oper_tree(child)){
1154 eml.s = opertree;
1155 emlwwrite(_("Restricted mode allows Add in %s only"),
1156 &eml);
1157 break;
1160 if((status = fexist(child, "w", (off_t *)NULL)) == FIOSUC){
1161 snprintf(tmp, sizeof(tmp), _("%s \"%.*s\" already exists!"),
1162 add_file > 0 ? "File" : "Directory", NLINE - 20, child);
1163 emlwrite(tmp, NULL);
1164 break;
1166 else if(status != FIOFNF){
1167 fioperr(status, child);
1168 break;
1171 if(add_file == 0){
1172 if(our_mkdir(child, (0700)) < 0){
1173 eml.s = child;
1174 emlwrite(_("Error adding Directory \"%s\""), &eml);
1176 else /* success! Directory added! */
1177 repaint = 1;
1179 else if(ffwopen(child, FALSE) != FIOSUC){
1180 /* ffwopen should've complained */
1181 break;
1183 else{ /* highlight new file */
1184 ffclose();
1185 eml.s = child;
1186 emlwrite(_("Added File \"%s\""), &eml);
1187 repaint = 1;
1190 if(repaint > 0){
1191 if((p = strrchr(child, C_FILESEP)) == NULL){
1192 emlwrite(_("Problems refiguring browser"), NULL);
1193 break;
1196 *p = '\0';
1197 if(p != child){
1198 strncpy(tmp, child, sizeof(tmp));
1199 tmp[sizeof(tmp)-1] = '\0';
1200 j = 0;
1201 while((child[j++] = *++p) != '\0')
1204 else{
1205 strncpy(tmp, S_FILESEP, sizeof(tmp));
1206 tmp[sizeof(tmp)-1] = '\0';
1210 * new file in same dir? if so, refigure files
1211 * and redraw...
1213 if(!strcmp(tmp, gmp->dname)){
1214 if((mp = getfcells(gmp->dname, fb_flags)) == NULL)
1215 /* getfcells should explain what happened */
1216 break;
1218 zotmaster(&gmp);
1219 gmp = mp;
1220 if((tp = FindCell(gmp, child, 0)) != NULL){
1221 gmp->current = tp;
1222 PlaceCell(gmp, gmp->current, &row, &col);
1225 PaintBrowser(gmp, 1, &crow, &ccol);
1228 break;
1229 default:
1230 break;
1234 BrowserKeys();
1235 break;
1237 case 'c': /* copy */
1238 case 'C':
1239 if(gmp->current->mode == FIODIR){
1240 emlwwrite(_("Can't copy a directory"), NULL);
1241 break;
1244 if(gmode&MDSCUR){ /* not allowed! */
1245 emlwrite(_("Copy not allowed in restricted mode"),NULL);
1246 break;
1249 i = 0;
1250 child[0] = '\0';
1252 while(!i){
1254 switch(status=mlreply_utf8(_("Name of new copy: "), child, NLINE,
1255 QFFILE, NULL)){
1256 case HELPCH:
1257 emlwwrite(_("No help yet!"), NULL);
1258 /* remove break and sleep after help text is installed */
1259 sleep(3);
1260 break;
1261 case (CTRL|'L'):
1262 PaintBrowser(gmp, 0, &crow, &ccol);
1263 break;
1264 case ABORT:
1265 emlwrite(_("Make Copy Cancelled"), NULL);
1266 i++;
1267 break;
1268 case FALSE:
1269 i++;
1270 mlerase();
1271 break;
1272 case TRUE:
1273 i++;
1275 if(child[0] == '\0'){
1276 emlwrite(_("No destination, file not copied"), NULL);
1277 break;
1280 if(!strcmp(gmp->current->fname, child)){
1281 emlwwrite(_("Can't copy file on to itself!"), NULL);
1282 break;
1285 if(!compresspath(gmp->dname, child, sizeof(child))){
1286 emlwrite(_("Too many ..'s in name"), NULL);
1287 break;
1290 if((gmode&MDTREE) && !in_oper_tree(child)){
1291 eml.s = opertree;
1292 emlwwrite(_("Restricted mode allows Copy in %s only"),
1293 &eml);
1294 break;
1297 if((status = fexist(child, "w", (off_t *)NULL)) == FIOSUC){
1298 /* TRANSLATORS: A question: File <filename> exists! Replace? */
1299 snprintf(tmp, sizeof(tmp), _("File \"%.*s\" exists! OVERWRITE"),
1300 NLINE - 20, child);
1301 if((status = mlyesno_utf8(tmp, 0)) != TRUE){
1302 emlwrite((status == ABORT)
1303 ? _("Make copy cancelled")
1304 : _("File Not Renamed"),
1305 NULL);
1306 break;
1309 else if(status != FIOFNF){
1310 fioperr(status, child);
1311 break;
1314 snprintf(tmp, sizeof(tmp), "%.*s%c%.*s", NLINE, gmp->dname, C_FILESEP,
1315 NLINE, gmp->current->fname);
1317 if(copy(tmp, child) < 0){
1318 /* copy() will report any error messages */
1319 break;
1321 else{ /* highlight new file */
1322 eml.s = child;
1323 emlwrite(_("File copied to %s"), &eml);
1325 if((p = strrchr(child, C_FILESEP)) == NULL){
1326 emlwrite(_("Problems refiguring browser"), NULL);
1327 break;
1330 *p = '\0';
1331 strncpy(tmp, (p == child) ? S_FILESEP: child, sizeof(tmp));
1332 tmp[sizeof(tmp)-1] = '\0';
1335 * new file in same dir? if so, refigure files
1336 * and redraw...
1338 if(!strcmp(tmp, gmp->dname)){
1339 strncpy(child, gmp->current->fname, sizeof(child));
1340 child[sizeof(child)-1] = '\0';
1341 if((mp = getfcells(gmp->dname, fb_flags)) == NULL)
1342 /* getfcells should explain what happened */
1343 break;
1345 zotmaster(&gmp);
1346 gmp = mp;
1347 if((tp = FindCell(gmp, child, 0)) != NULL){
1348 gmp->current = tp;
1349 PlaceCell(gmp, gmp->current, &row, &col);
1352 PaintBrowser(gmp, 1, &crow, &ccol);
1355 break;
1356 default:
1357 break;
1360 BrowserKeys();
1361 break;
1363 case 'r': /* rename */
1364 case 'R':
1365 i = 0;
1367 if(!strcmp(gmp->current->fname, "..")){
1368 emlwwrite(_("Can't rename \"..\""), NULL);
1369 break;
1372 if(gmode&MDSCUR){ /* not allowed! */
1373 emlwrite(_("Rename not allowed in restricted mode"),NULL);
1374 break;
1377 strncpy(child, gmp->current->fname, sizeof(child));
1378 child[sizeof(child)-1] = '\0';
1380 while(!i){
1382 switch(status=mlreply_utf8(_("Rename file to: "), child, NLINE, QFFILE,
1383 NULL)){
1384 case HELPCH:
1385 emlwwrite(_("No help yet!"), NULL);
1386 /* remove break and sleep after help text is installed */
1387 sleep(3);
1388 break;
1389 case (CTRL|'L'):
1390 PaintBrowser(gmp, 0, &crow, &ccol);
1391 break;
1392 case ABORT:
1393 emlwrite(_("Rename cancelled"), NULL);
1394 i++;
1395 break;
1396 case FALSE:
1397 case TRUE:
1398 i++;
1400 if(child[0] == '\0' || status == FALSE){
1401 mlerase();
1402 break;
1405 if(!compresspath(gmp->dname, child, sizeof(child))){
1406 emlwrite(_("Too many ..'s in name"), NULL);
1407 break;
1410 if((gmode&MDTREE) && !in_oper_tree(child)){
1411 eml.s = opertree;
1412 emlwwrite(_("Restricted mode allows Rename in %s only"),
1413 &eml);
1414 break;
1417 status = fexist(child, "w", (off_t *)NULL);
1418 if(status == FIOSUC || status == FIOFNF){
1419 if(status == FIOSUC){
1420 snprintf(tmp, sizeof(tmp), _("File \"%.*s\" exists! OVERWRITE"),
1421 NLINE - 20, child);
1423 if((status = mlyesno_utf8(tmp, FALSE)) != TRUE){
1424 emlwrite((status == ABORT)
1425 ? _("Rename cancelled")
1426 : _("Not Renamed"),
1427 NULL);
1428 break;
1432 snprintf(tmp, sizeof(tmp), "%.*s%c%.*s", NLINE, gmp->dname, C_FILESEP,
1433 NLINE, gmp->current->fname);
1435 if(our_rename(tmp, child) < 0){
1436 eml.s = errstr(errno);
1437 emlwrite(_("Rename Failed: %s"), &eml);
1439 else{
1440 if((p = strrchr(child, C_FILESEP)) == NULL){
1441 emlwrite(_("Problems refiguring browser"), NULL);
1442 break;
1445 *p = '\0';
1446 strncpy(tmp, (p == child) ? S_FILESEP: child, sizeof(tmp));
1447 tmp[sizeof(tmp)-1] = '\0';
1449 if((mp = getfcells(tmp, fb_flags)) == NULL)
1450 /* getfcells should explain what happened */
1451 break;
1453 zotmaster(&gmp);
1454 gmp = mp;
1456 if((tp = FindCell(gmp, ++p, 0)) != NULL){
1457 gmp->current = tp;
1458 PlaceCell(gmp, gmp->current, &row, &col);
1461 PaintBrowser(gmp, 1, &crow, &ccol);
1462 mlerase();
1465 else{
1466 fioperr(status, child);
1468 break;
1469 default:
1470 break;
1473 BrowserKeys();
1474 break;
1476 case 'v': /* stand-alone */
1477 case 'V': /* browser "view" */
1478 case 's': /* user "select" */
1479 case 'S':
1480 case (CTRL|'M'):
1481 Selected:
1483 if(((new_c == 'S' || new_c == 's') && (gmode&MDBRONLY))
1484 || ((new_c == 'V' || new_c == 'v') && !(gmode&MDBRONLY)))
1485 goto Default;
1487 if(gmp->current->mode == FIODIR){
1488 *child = '\0';
1489 strncpy(tmp, gmp->dname, sizeof(tmp));
1490 tmp[sizeof(tmp)-1] = '\0';
1491 p = gmp->current->fname;
1492 if(p[0] == '.' && p[1] == '.' && p[2] == '\0'){
1493 if((p=strrchr(tmp, C_FILESEP)) != NULL){
1494 *p = '\0';
1496 if((gmode&MDTREE) && !in_oper_tree(tmp)){
1497 eml.s = PARENTDIR;
1498 emlwwrite(
1499 _("Can't visit %s in restricted mode"),
1500 &eml);
1501 break;
1504 strncpy(child, &p[1], sizeof(child));
1505 child[sizeof(child)-1] = '\0';
1508 #if defined(DOS) || defined(OS2)
1509 (p == tmp || (tmp[1] == ':' && tmp[2] == '\0'))
1510 #else
1511 (p == tmp)
1512 #endif
1513 { /* is it root? */
1514 #if defined(DOS) || defined(OS2)
1515 if(*child){
1516 strncat(tmp, S_FILESEP, sizeof(tmp)-strlen(tmp)-1);
1517 tmp[sizeof(tmp)-1] = '\0';
1519 #else
1520 if(*child){
1521 strncpy(tmp, S_FILESEP, sizeof(tmp));
1522 tmp[sizeof(tmp)-1] = '\0';
1524 #endif
1525 else{
1526 emlwwrite(_("Can't move up a directory"),
1527 NULL);
1528 break;
1533 else if((fb_flags&FB_SAVE) && p[0] == '.' && p[1] == '\0'){
1534 if ((strlen(gmp->dname) < dirlen) &&
1535 (strlen(gmp->current->fname) < fnlen)){
1536 strncpy(dir, gmp->dname, dirlen);
1537 dir[dirlen-1] = '\0';
1540 zotmaster(&gmp);
1541 return(0); /* just change the directory, still return no selection */
1543 else{
1544 if(tmp[1] != '\0'){ /* were in root? */
1545 strncat(tmp, S_FILESEP, sizeof(tmp)-strlen(tmp)-1);
1546 tmp[sizeof(tmp)-1] = '\0';
1549 strncat(tmp, gmp->current->fname, sizeof(tmp)-strlen(tmp)-1);
1550 tmp[sizeof(tmp)-1] = '\0';
1553 if((mp = getfcells(tmp, fb_flags)) == NULL)
1554 /* getfcells should explain what happened */
1555 break;
1557 if(gmp->flags & FB_LMODE){
1558 mp->flags |= FB_LMODE;
1559 mp->lm = gmp->lm;
1560 gmp->lm = NULL;
1563 zotmaster(&gmp);
1564 gmp = mp;
1565 tp = NULL;
1567 if(*child){
1568 if((tp = FindCell(gmp, child, 0)) != NULL){
1569 gmp->current = tp;
1570 PlaceCell(gmp, gmp->current, &row, &col);
1572 else{
1573 eml.s = child;
1574 emlwwrite(_("Problem finding dir \"%s\""), &eml);
1578 PaintBrowser(gmp, 0, &crow, &ccol);
1579 if(!*child){
1580 char b[100];
1582 snprintf(b, sizeof(b), "Select/View \" .. %s %s\" to return to previous directory.", PARENTDIR, DIRWORD);
1583 emlwrite(b, NULL);
1586 break;
1588 else if(gmode&MDBRONLY){
1589 snprintf(child, sizeof(child), "%.*s%c%.*s", NLINE, gmp->dname, C_FILESEP,
1590 NLINE, gmp->current->fname);
1592 if(LikelyASCII(child)){
1593 char *t;
1594 envp = (char *) getenv("PAGER");
1595 t = fs_get((envp ? strlen(envp) : strlen(BROWSER_PAGER))
1596 + strlen(child) + 3 + 1);
1597 sprintf(t, "%s \'%s\'", envp ? envp : BROWSER_PAGER, child);
1598 BrowserRunChild(t, gmp->dname);
1599 PaintBrowser(gmp, 0, &crow, &ccol);
1600 if(t) fs_give((void **) &t);
1603 break;
1605 else{ /* just return */
1606 if(gmp->flags & FB_LMODEPOS){
1608 if(!lmreturn){
1609 emlwrite("Programming error, called FileBrowse with LMODEPOS but no lmreturn", NULL);
1610 zotmaster(&gmp);
1611 return(-1);
1614 /* user actually used ListMode, the list is finished */
1615 if(gmp->flags & FB_LMODE){
1616 if(!gmp->lm){
1617 (*term.t_beep)();
1618 emlwrite(_("No files are selected, use \"X\" to mark files for selection"), NULL);
1619 break;
1622 *lmreturn = gmp->lm;
1623 gmp->lm = NULL;
1625 else{ /* construct an lmreturn for user */
1626 LMLIST *new;
1627 size_t flen, dlen;
1629 if((new=(LMLIST *)malloc(sizeof(*new))) == NULL
1630 || (new->fname=malloc(gmp->current->fname ? (flen=strlen(gmp->current->fname))+1 : 1)) == NULL
1631 || (new->dir=malloc((dlen=strlen(gmp->dname))+1)) == NULL){
1632 emlwwrite(_("Can't malloc space for filename"), NULL);
1633 return(-1);
1636 strncpy(new->fname,
1637 gmp->current->fname ? gmp->current->fname : "", flen);
1638 new->fname[flen] = '\0';
1639 strncpy(new->dir, gmp->dname, dlen);
1640 new->dir[dlen] = '\0';
1641 strncpy(new->size, gmp->current->size, sizeof(new->size));
1642 new->size[sizeof(new->size)-1] = '\0';
1643 new->next = NULL;
1644 *lmreturn = new;
1647 zotmaster(&gmp);
1648 return(1);
1651 if ((strlen(gmp->dname) < dirlen) &&
1652 (strlen(gmp->current->fname) < fnlen)){
1653 strncpy(dir, gmp->dname, dirlen);
1654 dir[dirlen-1] = '\0';
1655 strncpy(fn, gmp->current->fname, fnlen);
1656 fn[fnlen-1] = '\0';
1658 else {
1659 zotmaster(&gmp);
1660 return(-1);
1662 if(sz != NULL){ /* size uninteresting */
1663 strncpy(sz, gmp->current->size, szlen);
1664 sz[szlen-1] = '\0';
1667 zotmaster (&gmp);
1668 return (1);
1670 break;
1672 case 'w': /* Where is */
1673 case 'W':
1674 case (CTRL|'W'):
1675 i = 0;
1676 flags = SR_ORIGMEN | SR_FORWARD | SR_NOEXACT;
1678 while(!i){
1679 switch(readpattern(_("File name to find"), FALSE, flags)){
1680 case HELPCH:
1681 emlwwrite(_("No help yet!"), NULL);
1682 /* remove break and sleep after help text is installed */
1683 sleep(3);
1684 break;
1685 case (CTRL|'L'):
1686 PaintBrowser(gmp, 0, &crow, &ccol);
1687 break;
1688 case (CTRL|'P'):
1689 if(flags & SR_FORWARD){
1690 flags &= ~SR_FORWARD;
1691 flags |= SR_BACKWRD;
1692 } else {
1693 flags &= ~SR_BACKWRD;
1694 flags |= SR_FORWARD;
1696 break;
1697 case (CTRL|'Y'): /* first first cell */
1698 for(tp = gmp->top; tp->prev; tp = tp->prev)
1701 i++;
1702 /* fall thru to repaint */
1703 case (CTRL|'V'):
1704 if(!i){
1706 tp = gmp->top;
1707 if((i = term.t_nrow - term.t_mrow - 2) <= 0)
1708 break;
1710 while(i-- && tp->next){
1711 j = 0;
1712 while(++j <= gmp->fpl && tp->next)
1713 tp = tp->next;
1716 if(i < 0)
1717 gmp->top = tp;
1719 while(tp->next);
1721 emlwrite(_("Searched to end of directory"), NULL);
1723 else
1724 emlwrite(_("Searched to start of directory"), NULL);
1726 if(tp){
1727 PlaceCell(gmp, gmp->current, &row, &col);
1728 PaintCell(row, col, gmp->cpf, gmp->current, 0);
1729 gmp->current = tp;
1730 if(PlaceCell(gmp, gmp->current, &row, &col)){
1731 PaintBrowser(gmp, 1, &crow, &ccol);
1733 else{
1734 PaintCell(row, col, gmp->cpf, gmp->current, 1);
1735 crow = row;
1736 ccol = col;
1740 i++; /* make sure we jump out */
1741 break;
1742 case ABORT:
1743 emlwrite(_("Whereis cancelled"), NULL);
1744 i++;
1745 break;
1746 case FALSE:
1747 mlerase();
1748 i++;
1749 break;
1750 case TRUE:
1752 char *utf8 = NULL;
1754 if(pat && pat[0])
1755 utf8 = ucs4_to_utf8_cpystr(pat);
1757 if(utf8 && (tp = FindCell(gmp, utf8, flags & SR_BACKWRD)) != NULL){
1758 PlaceCell(gmp, gmp->current, &row, &col);
1759 PaintCell(row, col, gmp->cpf, gmp->current, 0);
1760 gmp->current = tp;
1762 if(PlaceCell(gmp, tp, &row, &col)){ /* top changed */
1763 PaintBrowser(gmp, 1, &crow, &ccol);
1765 else{
1766 PaintCell(row, col, gmp->cpf, gmp->current, 1);
1767 crow = row;
1768 ccol = col;
1770 mlerase();
1772 else{
1773 eml.s = utf8;
1774 emlwrite(_("\"%s\" not found"), &eml);
1777 if(utf8)
1778 fs_give((void **) &utf8);
1781 i++;
1782 break;
1783 default:
1784 break;
1788 BrowserKeys();
1789 break;
1791 case (CTRL|'\\') :
1792 #if defined MOUSE && !defined(_WINDOWS)
1793 toggle_xterm_mouse(0,1);
1794 #else
1795 unknown_command(c);
1796 #endif
1797 break;
1799 case (CTRL|'Z'):
1800 if(gmode&MDSSPD){
1801 bktoshell(0, 1);
1802 PaintBrowser(gmp, 0, &crow, &ccol);
1803 break;
1804 } /* fall thru with error! */
1806 default: /* what? */
1807 Default:
1808 unknown_command(c);
1810 case NODATA: /* no op */
1811 break;
1818 * getfcells - make a master browser struct and fill it in
1819 * return NULL if there's a problem.
1821 struct bmaster *
1822 getfcells(char *dname, int fb_flags)
1824 int i, /* various return codes */
1825 flength,
1826 nentries = 0; /* number of dir ents */
1827 off_t attsz;
1828 char *np, /* names of files in dir */
1829 *dcp, /* to add file to path */
1830 *tmpstr,
1831 **filtnames, /* array filtered names */
1832 errbuf[NLINE];
1833 struct fcell *ncp, /* new cell pointer */
1834 *tcp = NULL; /* trailing cell ptr */
1835 struct bmaster *mp;
1836 EML eml;
1838 errbuf[0] = '\0';
1839 if((mp=(struct bmaster *)malloc(sizeof(struct bmaster))) == NULL){
1840 emlwwrite(_("Can't malloc space for master filename cell"), NULL);
1841 return(NULL);
1844 memset(mp, 0, sizeof(*mp));
1846 if(dname[0] == '.' && dname[1] == '\0'){ /* remember this dir */
1847 if(!getcwd(mp->dname, 256))
1848 mp->dname[0] = '\0';
1850 else if(dname[0] == '.' && dname[1] == '.' && dname[2] == '\0'){
1851 if(!getcwd(mp->dname, 256))
1852 mp->dname[0] = '\0';
1853 else{
1854 if((np = (char *)strrchr(mp->dname, C_FILESEP)) != NULL)
1855 if(np != mp->dname)
1856 *np = '\0';
1859 else{
1860 strncpy(mp->dname, dname, sizeof(mp->dname));
1861 mp->dname[sizeof(mp->dname)-1] = '\0';
1864 mp->bottom = mp->head = mp->top = NULL;
1865 mp->menu = mp->cpf = mp->fpl = 0;
1866 mp->longest = 5; /* .. must be labeled! */
1867 mp->flags = fb_flags;
1869 eml.s = mp->dname;
1870 emlwrite("Building file list of %s...", &eml);
1872 if((mp->names = getfnames(mp->dname, NULL, &nentries, errbuf, sizeof(errbuf))) == NULL){
1873 free((char *) mp);
1874 if(*errbuf)
1875 emlwwrite(errbuf, NULL);
1877 return(NULL);
1881 * this is the fun part. build an array of pointers to the fnames we're
1882 * interested in (i.e., do any filtering), then pass that off to be
1883 * sorted before building list of cells...
1885 * right now default filtering on ".*" except "..", but this could
1886 * easily be made a user option later on...
1888 if((filtnames=(char **)malloc((nentries+1) * sizeof(char *))) == NULL){
1889 emlwwrite(_("Can't malloc space for name array"), NULL);
1890 zotmaster(&mp);
1891 return(NULL);
1894 i = 0; /* index of filt'd array */
1895 np = mp->names;
1896 while(nentries--){
1897 int ii;
1898 int width;
1901 * Filter dot files? Always filter ".", never filter "..",
1902 * and sometimes filter ".*"...
1904 if(*np == '.' && (!(*(np+1) == '.' && *(np+2) == '\0')
1905 && !(*(np+1) == '\0' && (fb_flags&FB_SAVE)))
1906 && (*(np+1) == '\0' || !(gmode & MDDOTSOK))){
1907 np += strlen(np) + 1;
1908 continue;
1911 filtnames[i++] = np;
1913 ii = (int) strlen(np);
1914 if((width = (int) utf8_width(np)) > mp->longest)
1915 mp->longest = width; /* remember longest */
1917 np += ii + 1; /* advance name pointer */
1920 nentries = i; /* new # of entries */
1923 * sort files case independently
1925 qsort((qsort_t *)filtnames, (size_t)nentries, sizeof(char *), sstrcasecmp);
1928 * this is so we use absolute path names for stats.
1929 * remember: be careful using dname as directory name, and fix mp->dname
1930 * when we're done
1932 dcp = (char *) strchr(mp->dname, '\0');
1933 if(dcp == mp->dname || dcp[-1] != C_FILESEP){
1934 dcp[0] = C_FILESEP;
1935 dcp[1] = '\0';
1937 else
1938 dcp--;
1940 i = 0;
1941 while(nentries--){ /* stat filtered files */
1942 /* get a new cell */
1943 if((ncp=(struct fcell *)malloc(sizeof(struct fcell))) == NULL){
1944 emlwwrite(_("Can't malloc cells for browser!"), NULL);
1945 zotfcells(mp->head); /* clean up cells */
1946 free((char *) filtnames);
1947 free((char *) mp);
1948 return(NULL); /* bummer. */
1951 ncp->next = ncp->prev = NULL;
1953 if(mp->head == NULL){ /* tie it onto the list */
1954 mp->head = mp->top = mp->current = ncp;
1956 else{
1957 mp->bottom = ncp;
1958 tcp->next = ncp;
1959 ncp->prev = tcp;
1962 tcp = ncp;
1964 /* fill in the new cell */
1965 ncp->fname = filtnames[i++];
1967 /* fill in file's mode */
1968 if ((flength = strlen(ncp->fname) + 1 + strlen(dname)) < sizeof(mp->dname)){
1969 strncpy(&dcp[1], ncp->fname, sizeof(mp->dname)-(dcp+1-mp->dname)); /* use absolute path! */
1970 mp->dname[sizeof(mp->dname)-1] = '\0';
1971 tmpstr = mp->dname;
1973 else{
1974 if((tmpstr = (char *)malloc((flength+1)*sizeof(char))) == NULL){
1975 emlwwrite(_("Can't malloc cells for temp buffer!"), NULL);
1976 zotfcells(mp->head); /* clean up cells */
1977 free((char *) filtnames);
1978 free((char *) mp);
1979 return(NULL); /* bummer. */
1982 strncpy(tmpstr, dname, flength);
1983 tmpstr[flength] = '\0';
1984 tmpstr = strncat(tmpstr, S_FILESEP, flength+1-1-strlen(tmpstr));
1985 tmpstr[flength] = '\0';
1986 tmpstr = strncat(tmpstr, ncp->fname, flength+1-1-strlen(tmpstr));
1987 tmpstr[flength] = '\0';
1990 switch(fexist(tmpstr, "t", &attsz)){
1991 case FIODIR :
1992 ncp->mode = FIODIR;
1993 snprintf(ncp->size, sizeof(ncp->size), "(%s%s%s)",
1994 (ncp->fname[0] == '.' && ncp->fname[1] == '.'
1995 && ncp->fname[2] == '\0') ? PARENTDIR :
1996 ((fb_flags&FB_SAVE) && ncp->fname[0] == '.' && ncp->fname[1] == '\0'
1997 ? SELECTWORD : ""),
1998 (ncp->fname[0] == '.' && ncp->fname[1] == '.'
1999 && ncp->fname[2] == '\0') ? " " :
2000 ((fb_flags&FB_SAVE) && ncp->fname[0] == '.' && ncp->fname[1] == '\0'
2001 ? " " : ""),
2002 DIRWORD);
2003 break;
2005 case FIOSYM :
2006 ncp->mode = FIOSYM;
2007 strncpy(ncp->size, "--", sizeof(ncp->size));
2008 ncp->size[sizeof(ncp->size)-1] = '\0';
2009 break;
2011 default :
2012 ncp->mode = FIOSUC; /* regular file */
2013 strncpy(ncp->size, prettysz(attsz), sizeof(ncp->size));
2014 ncp->size[sizeof(ncp->size)-1] = '\0';
2015 break;
2018 if (flength >= NLINE)
2019 free((char *) tmpstr);
2022 dcp[(dcp == mp->dname) ? 1 : 0] = '\0'; /* remember to cap dname */
2023 free((char *) filtnames); /* 'n blast filt'd array*/
2025 percdircells(mp);
2026 layoutcells(mp);
2027 if(strlen(mp->dname) < sizeof(browse_dir)){
2028 strncpy(browse_dir, mp->dname, sizeof(browse_dir));
2029 browse_dir[sizeof(browse_dir)-1] = '\0';
2031 else
2032 browse_dir[0] = '\0';
2034 return(mp);
2039 fcell_is_selected(struct fcell *cell, struct bmaster *mp)
2041 LMLIST *lm;
2043 if(cell && cell->fname){
2044 for(lm = mp ? mp->lm : NULL; lm; lm = lm->next){
2045 /* directory has to match */
2046 if(!((mp->dname[0] == '\0' && (!lm->dir || lm->dir[0] =='\0'))
2047 || (mp->dname[0] != '\0' && lm->dir && lm->dir[0] !='\0'
2048 && !strcmp(mp->dname, lm->dir))))
2049 continue;
2051 if(lm->fname && !strcmp(cell->fname, lm->fname))
2052 return(1);
2056 return(0);
2061 * Adds a new name to the head of the lmlist
2063 void
2064 add_cell_to_lmlist(struct fcell *cell, struct bmaster *mp)
2066 LMLIST *new;
2067 size_t flen, dlen;
2069 if(mp && cell && cell->fname && cell->fname[0]){
2070 if((new=(LMLIST *)malloc(sizeof(*new))) == NULL ||
2071 (new->fname=malloc(sizeof(char)*((flen=strlen(cell->fname))+1))) == NULL ||
2072 (new->dir=malloc(sizeof(char)*((dlen=strlen(mp->dname))+1))) == NULL){
2073 emlwwrite(_("Can't malloc space for filename"), NULL);
2074 return;
2077 strncpy(new->fname, cell->fname, flen);
2078 new->fname[flen] = '\0';
2079 strncpy(new->dir, mp->dname, dlen);
2080 new->dir[dlen] = '\0';
2081 new->size[0] = '\0';
2082 if(cell->size[0]){
2083 strncpy(new->size, cell->size, sizeof(new->size));
2084 new->size[sizeof(new->size)-1] = '\0';
2087 new->next = mp->lm;
2088 mp->lm = new;
2094 * Deletes a name from the lmlist
2096 void
2097 del_cell_from_lmlist(struct fcell *cell, struct bmaster *mp)
2099 LMLIST *lm, *lmprev = NULL;
2101 if(mp && cell && cell->fname && cell->fname[0])
2102 for(lm = mp ? mp->lm : NULL; lm; lm = lm->next){
2103 if(lm->fname && strcmp(cell->fname, lm->fname) == 0){
2104 free((char *) lm->fname);
2105 if(lm->dir)
2106 free((char *) lm->dir);
2108 if(lmprev)
2109 lmprev->next = lm->next;
2110 else
2111 mp->lm = lm->next;
2113 free((char *) lm);
2115 break;
2118 lmprev = lm;
2124 * PaintCell - print the given cell at the given location on the display
2125 * the format of a printed cell is:
2127 * "<fname> <size> "
2129 void
2130 PaintCell(int row, int col,
2131 int sc, /* screen columns available for this cell */
2132 struct fcell *cell, int inverted)
2134 char buf1[NLINE], buf2[NLINE];
2135 char lbuf[5];
2136 int need, l_wid, f_wid, f_to_s_wid, s_wid;
2137 UCS *ucs;
2139 if(cell == NULL)
2140 return;
2142 l_wid = (gmp && ((gmp->flags & FB_LMODEPOS) > 4)) ? 4 : 0;
2143 f_wid = utf8_width(cell->fname ? cell->fname : "");
2144 f_to_s_wid = 1;
2145 s_wid = utf8_width(cell->size ? cell->size : "");
2147 /* the two is the space between cell columns */
2148 sc = MIN(sc-2, term.t_ncol-col);
2150 if(l_wid){
2151 if(gmp && gmp->flags & FB_LMODE && cell->mode != FIODIR){
2153 * We have to figure out here if it is selected or not
2154 * and use that to write the X or space.
2156 lbuf[0] = '[';
2157 if(fcell_is_selected(cell, gmp))
2158 lbuf[1] = 'X';
2159 else
2160 lbuf[1] = ' ';
2162 lbuf[2] = ']';
2163 lbuf[3] = ' ';
2165 else{
2166 lbuf[0] = lbuf[1] = lbuf[2] = lbuf[3] = ' ';
2169 lbuf[4] = '\0';
2172 sc -= l_wid;
2174 need = f_wid+f_to_s_wid+s_wid;
2176 /* space between name and size */
2177 if(need < sc)
2178 f_to_s_wid += (sc-need);
2181 * If the width isn't enough to handle everything we're just putting a single
2182 * space between fname and size and truncating on the right. That means that
2183 * the sizes in the right hand column won't line up correctly when there is
2184 * a lack of space. Instead, we opt for displaying as much info as possible
2185 * in a slightly discordant way.
2188 movecursor(row, col);
2189 if(l_wid){
2190 ucs = utf8_to_ucs4_cpystr(lbuf);
2191 if(ucs){
2192 pputs(ucs, 0);
2193 fs_give((void **) &ucs);
2197 utf8_snprintf(buf1, sizeof(buf1), "%*.*w%*.*w%*.*w",
2198 f_wid, f_wid, cell->fname ? cell->fname : "",
2199 f_to_s_wid, f_to_s_wid, "",
2200 s_wid, s_wid, cell->size ? cell->size : "");
2202 utf8_snprintf(buf2, sizeof(buf2), "%*.*w", sc, sc, buf1);
2204 if(inverted)
2205 (*term.t_rev)(1);
2206 else if (*term.t_eri)
2207 (*term.t_eri)();
2209 ucs = utf8_to_ucs4_cpystr(buf2);
2210 if(ucs){
2211 pputs(ucs, 0);
2212 fs_give((void **) &ucs);
2215 if(inverted)
2216 (*term.t_rev)(0);
2221 * PaintBrowse - with the current data, display the browser. if level == 0
2222 * paint the whole thing, if level == 1 just paint the cells
2223 * themselves
2225 void
2226 PaintBrowser(struct bmaster *mp, int level, int *row, int *col)
2228 int i, cl;
2229 struct fcell *tp;
2231 if(!level){
2232 ClearBrowserScreen();
2233 BrowserAnchor(mp->dname);
2236 i = 0;
2237 tp = mp->top;
2238 cl = COMPOSER_TOP_LINE; /* current display line */
2239 while(tp){
2241 PaintCell(cl, mp->cpf * i, mp->cpf, tp, tp == mp->current);
2243 if(tp == mp->current){
2244 if(row)
2245 *row = cl;
2247 if(col)
2248 *col = mp->cpf * i;
2251 if(++i >= mp->fpl){
2252 i = 0;
2253 if(++cl > term.t_nrow-(term.t_mrow+1))
2254 break;
2257 tp = tp->next;
2260 if(level){
2261 while(cl <= term.t_nrow - (term.t_mrow+1)){
2262 if(!i)
2263 movecursor(cl, 0);
2264 peeol();
2265 movecursor(++cl, 0);
2268 else{
2269 BrowserKeys();
2274 * RepaintBrowser - factorized method to just repaint everything
2276 struct bmaster *
2277 RepaintBrowser(struct bmaster *gmp, int fb_flags)
2279 struct bmaster *mp;
2280 struct fcell *tp;
2281 int row, col;
2282 char *fn;
2283 fn = gmp->current->fname;
2285 if((mp = getfcells(gmp->dname, fb_flags)) == NULL){
2286 /* getfcells should explain what happened */
2287 return(gmp);
2289 zotmaster(&gmp);
2290 gmp = mp;
2292 tp = NULL;
2293 if(*fn){
2294 if((tp = FindCell(gmp, fn, 0)) != NULL){
2295 gmp->current = tp;
2296 PlaceCell(gmp, gmp->current, &row, &col);
2300 PaintBrowser(gmp, 0, NULL, NULL);
2301 return(gmp);
2306 * BrowserKeys - just paints the keyhelp at the bottom of the display
2308 void
2309 BrowserKeys(void)
2311 if (gmp && gmp->menu == 1){
2312 menu_other[GOTO_KEY].name = (gmode&MDGOTO) ? "G" : NULL;
2313 menu_other[GOTO_KEY].label = (gmode&MDGOTO) ? N_("Goto") : NULL;
2314 wkeyhelp(menu_other);
2315 return;
2317 menu_browse[QUIT_KEY].name = (gmode&MDBRONLY) ? "Q" : "E";
2318 /* TRANSLATORS: Brwsr is an abbreviation for Browser, which is
2319 a command used to look through something */
2320 menu_browse[QUIT_KEY].label = (gmode&MDBRONLY) ? N_("Quit") : N_("Exit Brwsr");
2321 if(gmode & MDBRONLY){
2322 menu_browse[EXEC_KEY].name = "L";
2323 menu_browse[EXEC_KEY].label = N_("Launch");
2324 menu_browse[SELECT_KEY].name = "V";
2325 menu_browse[SELECT_KEY].label = "[" N_("View") "]";
2326 menu_browse[PICO_KEY].name = "E";
2327 menu_browse[PICO_KEY].label = N_("Edit");
2329 else{
2330 menu_browse[SELECT_KEY].name = "S";
2331 menu_browse[SELECT_KEY].label = "[" N_("Select") "]";
2332 menu_browse[PICO_KEY].name = "A";
2333 menu_browse[PICO_KEY].label = N_("Add");
2335 if(gmp && gmp->flags & FB_LMODEPOS){
2336 if(gmp && gmp->flags & FB_LMODE){ /* ListMode is already on */
2337 menu_browse[EXEC_KEY].name = "X";
2338 menu_browse[EXEC_KEY].label = N_("Set/Unset");
2340 else{ /* ListMode is possible */
2341 menu_browse[EXEC_KEY].name = "L";
2342 menu_browse[EXEC_KEY].label = N_("ListMode");
2345 else{ /* No ListMode possible */
2346 menu_browse[EXEC_KEY].name = NULL;
2347 menu_browse[EXEC_KEY].label = NULL;
2351 wkeyhelp(menu_browse);
2356 * layoutcells - figure out max length of cell and how many cells can
2357 * go on a line of the display
2359 void
2360 layoutcells(struct bmaster *mp)
2362 static int wid = -1;
2364 * Max chars/file. Actually this is max screen cells/file
2365 * Longest name + "(parent dir)"
2367 if(wid < 0)
2368 wid = MAX(utf8_width(PARENTDIR),utf8_width(SELECTWORD)) + utf8_width(DIRWORD);
2370 mp->cpf = mp->longest + wid + 3;
2372 if(mp->flags & FB_LMODEPOS) /* "[X] " */
2373 mp->cpf += 4;
2375 if(gmode & MDONECOL){
2376 mp->fpl = 1;
2378 else{
2379 int i = 1;
2381 while(i*mp->cpf - 2 <= term.t_ncol) /* no force... */
2382 i++; /* like brute force! */
2384 mp->fpl = i - 1; /* files per line */
2387 if(mp->fpl == 0)
2388 mp->fpl = 1;
2393 * percdircells - bubble all the directory cells to the top of the
2394 * list.
2396 void
2397 percdircells(struct bmaster *mp)
2399 struct fcell *dirlp, /* dir cell list pointer */
2400 *lp, *nlp; /* cell list ptr and next */
2402 dirlp = NULL;
2403 for(lp = mp->head; lp; lp = nlp){
2404 nlp = lp->next;
2405 if(lp->mode == FIODIR){
2406 if(lp->prev) /* clip from list */
2407 lp->prev->next = lp->next;
2409 if(lp->next)
2410 lp->next->prev = lp->prev;
2412 if((lp->prev = dirlp) != NULL){ /* tie it into dir portion */
2413 if((lp->next = dirlp->next) != NULL)
2414 lp->next->prev = lp;
2416 dirlp->next = lp;
2417 dirlp = lp;
2419 else{
2420 if((dirlp = lp) != mp->head)
2421 dirlp->next = mp->head;
2423 if(dirlp->next)
2424 dirlp->next->prev = dirlp;
2426 mp->head = mp->top = mp->current = dirlp;
2434 * PlaceCell - given a browser master and a cell, return row and col of the display that
2435 * it should go on.
2437 * return 1 if mp->top has changed, x,y relative to new page
2438 * return 0 if otherwise (same page)
2439 * return -1 on error
2442 PlaceCell(struct bmaster *mp, struct fcell *cp, int *x, int *y)
2444 int cl = COMPOSER_TOP_LINE; /* current line */
2445 int ci = 0; /* current index on line */
2446 int rv = 0;
2447 int secondtry = 0;
2448 struct fcell *tp;
2450 /* will cp fit on screen? */
2451 tp = mp->top;
2452 while(1){
2453 if(tp == cp){ /* bingo! */
2454 *x = cl;
2455 *y = ci * mp->cpf;
2456 break;
2459 if((tp = tp->next) == NULL){ /* above top? */
2460 if(secondtry++){
2461 emlwwrite(_("Internal error: can't find fname cell"), NULL);
2462 return(-1);
2464 else{
2465 tp = mp->top = mp->head; /* try from the top! */
2466 cl = COMPOSER_TOP_LINE;
2467 ci = 0;
2468 rv = 1;
2469 continue; /* start over! */
2473 if(++ci >= mp->fpl){ /* next line? */
2474 ci = 0;
2475 if(++cl > term.t_nrow-(term.t_mrow+1)){ /* next page? */
2476 ci = mp->fpl; /* tp is at bottom right */
2477 while(ci--) /* find new top */
2478 tp = tp->prev;
2479 mp->top = tp;
2480 ci = 0;
2481 cl = COMPOSER_TOP_LINE; /* keep checking */
2482 rv = 1;
2488 /* not on display! */
2489 return(rv);
2494 * zotfcells - clean up malloc'd cells of file names
2496 void
2497 zotfcells(struct fcell *hp)
2499 struct fcell *tp;
2501 while(hp){
2502 tp = hp;
2503 hp = hp->next;
2504 tp->next = NULL;
2505 free((char *) tp);
2511 * zotmaster - blast the browser master struct
2513 void
2514 zotmaster(struct bmaster **mp)
2516 if(mp && *mp){
2517 zotfcells((*mp)->head); /* free cells */
2518 zotlmlist((*mp)->lm); /* free lmlist */
2519 if((*mp)->names)
2520 free((char *)(*mp)->names); /* free file names */
2522 free((char *)*mp); /* free master */
2523 *mp = NULL; /* make double sure */
2529 * FindCell - starting from the current cell find the first occurance of
2530 * the given string wrapping around if necessary
2532 struct fcell *
2533 FindCell(struct bmaster *mp, char *utf8string, int bsearch)
2535 struct fcell *tp, *fp;
2537 if(*utf8string == '\0')
2538 return(NULL);
2540 fp = NULL;
2541 tp = bsearch ? mp->current->prev : mp->current->next;
2543 while(tp && !fp){
2544 if(sisin(tp->fname, utf8string))
2545 fp = tp;
2546 else
2547 tp = bsearch ? tp->prev : tp->next;
2550 tp = bsearch ? mp->bottom : mp->head;
2551 while(tp != mp->current && !fp){
2552 if(sisin(tp->fname, utf8string))
2553 fp = tp;
2554 else
2555 tp = bsearch ? tp->prev : tp->next;
2558 return(fp);
2563 * sisin - case insensitive substring matching function
2565 * We can't really do case-insensitive for non-ascii, so restrict
2566 * that to ascii characters. The strings will be utf8.
2569 sisin(char *bigstr, char *utf8substr)
2571 register int j;
2573 while(*bigstr){
2574 j = 0;
2575 while(bigstr[j] == utf8substr[j]
2576 || ((unsigned char)bigstr[j] < 0x80 && (unsigned char)utf8substr[j] < 0x80
2577 && toupper((unsigned char)bigstr[j]) == toupper((unsigned char)utf8substr[j])))
2578 if(utf8substr[++j] == '\0') /* bingo! */
2579 return(1);
2581 bigstr++;
2584 return(0);
2589 * set_browser_title -
2591 void
2592 set_browser_title(char *s)
2594 browser_title = s;
2599 * BrowserAnchor - draw the browser's anchor line.
2601 void
2602 BrowserAnchor(char *utf8dir)
2604 char *pdir;
2605 char titlebuf[NLINE];
2606 char buf[NLINE];
2607 char dirbuf[NLINE];
2608 char *dots = "...";
2609 char *br = "BROWSER";
2610 char *dir = "Dir: ";
2611 UCS *ucs;
2612 int need, extra, avail;
2613 int title_wid, b_wid, t_to_b_wid, b_to_d_wid, d_wid, dot_wid, dir_wid, after_dir_wid;
2614 COLOR_PAIR *lastc = NULL;
2616 if(!utf8dir)
2617 utf8dir = "";
2619 pdir = utf8dir;
2621 if(browser_title)
2622 snprintf(titlebuf, sizeof(buf), " %s", browser_title);
2623 else if(Pmaster)
2624 snprintf(titlebuf, sizeof(buf), " ALPINE %s", Pmaster->pine_version);
2625 else
2626 snprintf(titlebuf, sizeof(buf), " UW PICO %s", (gmode&MDBRONLY) ? "BROWSER" : version);
2628 title_wid = utf8_width(titlebuf);
2629 t_to_b_wid = 15;
2630 b_wid = utf8_width(br);
2631 b_to_d_wid = 4;
2632 d_wid = utf8_width(dir);
2633 dot_wid = 0;
2634 dir_wid = utf8_width(pdir);
2636 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2638 if(need > term.t_ncol){
2639 extra = need - term.t_ncol;
2640 t_to_b_wid -= MIN(extra, 10);
2641 need -= MIN(extra, 10);
2643 if(need > term.t_ncol){
2644 extra = need - term.t_ncol;
2645 b_to_d_wid -= MIN(extra, 2);
2646 need -= MIN(extra, 2);
2649 if(need > term.t_ncol){
2650 titlebuf[0] = titlebuf[1] = ' ';
2651 titlebuf[2] = '\0';
2652 title_wid = utf8_width(titlebuf);
2653 t_to_b_wid = 0;
2654 b_to_d_wid = 4;
2655 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2656 if(need > term.t_ncol){
2657 extra = need - term.t_ncol;
2658 b_to_d_wid -= MIN(extra, 2);
2659 need -= MIN(extra, 2);
2662 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2663 if(need > term.t_ncol){
2664 t_to_b_wid = 0;
2665 b_wid = 0;
2666 b_to_d_wid = 0;
2669 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2670 if(need > term.t_ncol)
2671 d_wid = 0;
2673 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2675 if(need > term.t_ncol && dir_wid > 0){
2676 dot_wid = utf8_width(dots);
2677 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2678 while(need > term.t_ncol && (pdir = strchr(pdir+1, C_FILESEP))){
2679 dir_wid = utf8_width(pdir);
2680 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2683 if(!pdir){ /* adjust other widths to fill up space */
2684 avail = term.t_ncol - (title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid);
2685 if(avail > 2)
2686 utf8_to_width_rhs(dirbuf, utf8dir, sizeof(dirbuf), avail);
2687 else
2688 dirbuf[0] = '\0';
2690 pdir = dirbuf;
2693 dir_wid = utf8_width(pdir);
2698 extra = term.t_ncol - (title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid);
2700 if(extra >= 0)
2701 after_dir_wid = extra;
2702 else
2703 after_dir_wid = 0;
2705 utf8_snprintf(buf, sizeof(buf), "%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w",
2706 title_wid, title_wid, titlebuf,
2707 t_to_b_wid, t_to_b_wid, "",
2708 b_wid, b_wid, br,
2709 b_to_d_wid, b_to_d_wid, "",
2710 d_wid, d_wid, dir,
2711 dot_wid, dot_wid, dots,
2712 dir_wid, dir_wid, pdir,
2713 after_dir_wid, after_dir_wid, "");
2715 /* just making sure */
2716 utf8_snprintf(titlebuf, sizeof(titlebuf), "%-*.*w", term.t_ncol, term.t_ncol, buf);
2718 movecursor(0, 0);
2719 if(Pmaster && Pmaster->colors && Pmaster->colors->tbcp
2720 && pico_is_good_colorpair(Pmaster->colors->tbcp)){
2721 lastc = pico_get_cur_color();
2722 (void) pico_set_colorp(Pmaster->colors->tbcp, PSC_NONE);
2724 else
2725 (*term.t_rev)(1);
2727 ucs = utf8_to_ucs4_cpystr(titlebuf);
2728 if(ucs){
2729 pputs(ucs, 0);
2730 fs_give((void **) &ucs);
2733 if(lastc){
2734 (void) pico_set_colorp(lastc, PSC_NONE);
2735 free_color_pair(&lastc);
2737 else
2738 (*term.t_rev)(0);
2743 * ResizeBrowser - handle a resize event
2746 ResizeBrowser(void)
2748 if(gmp){
2749 layoutcells(gmp);
2750 PaintBrowser(gmp, 0, NULL, NULL);
2751 return(1);
2753 else
2754 return(0);
2758 void
2759 ClearBrowserScreen(void)
2761 int i;
2763 for(i = 0; i <= term.t_nrow; i++){ /* clear screen */
2764 movecursor(i, 0);
2765 peeol();
2770 void
2771 BrowserRunChild(char *child, char *dir)
2773 int status;
2774 char tmp[NLINE];
2775 time_t t_in, t_out;
2777 ClearBrowserScreen();
2778 movecursor(0, 0);
2779 (*term.t_close)();
2780 if(!isdir(dir, NULL, &t_in))
2781 t_in = 0;
2783 fflush(stdout);
2784 status = system(child);
2785 (*term.t_open)();
2786 if(t_in && isdir(dir, NULL, &t_out) && t_in < t_out){
2787 struct bmaster *mp;
2789 if((mp = getfcells(dir, 0)) != NULL){
2790 zotmaster(&gmp);
2791 gmp = mp;
2793 /* else getfcells should explain what happened */
2796 /* complain about non-zero exit status */
2797 if((status >> 8) & 0xff){
2799 movecursor(term.t_nrow - 1, 0);
2800 snprintf(tmp, sizeof(tmp), "[ \"%.30s\" exit with error value: %d ]",
2801 child, (status >> 8) & 0xff);
2802 pputs_utf8(tmp, 1);
2803 movecursor(term.t_nrow, 0);
2804 pputs_utf8("[ Hit RETURN to continue ]", 1);
2805 fflush(stdout);
2807 while(GetKey() != (CTRL|'M')){
2808 (*term.t_beep)();
2809 fflush(stdout);
2816 * imitate pc-pine memory for where we last called the file browser.
2818 void
2819 p_chdir(struct bmaster *mp)
2821 if(mp && mp->dname)
2822 chdir(mp->dname);
2824 #else /* _WINDOWS */
2828 * pico_file_browse - Exported version of FileBrowse below.
2831 pico_file_browse(PICO *pdata, char *dir, size_t dirlen, char *fn, size_t fnlen,
2832 char *sz, size_t szlen, int flags)
2834 return(FileBrowse(dir, dirlen, fn, fnlen, sz, szlen, flags, NULL));
2838 ResizeBrowser (void)
2840 return (0);
2844 * FileBrowse - Windows version of above function
2846 int
2847 FileBrowse(char *dir, size_t dirlen, char *fn, size_t fnlen,
2848 char *sz, size_t szlen, int fb_flags, LMLIST **lmreturn)
2850 struct stat sbuf;
2851 int rc;
2852 char lfn[NLINE];
2854 if (fb_flags & FB_SAVE){
2855 rc = mswin_savefile(dir, dirlen, fn, MIN(dirlen,fnlen));
2857 else{
2858 *fn = '\0'; /* No initial file names for
2859 * open. */
2860 if(fb_flags & FB_LMODEPOS){
2862 * We're going to allow multiple filenames to be returned so
2863 * we don't want to use the passed in fn for that. Instead, make
2864 * a bigger space and use that.
2866 char f[20000];
2867 size_t flen, dlen;
2869 memset(f, 0, sizeof(f));
2871 rc = mswin_multopenfile(dir, dirlen, f, sizeof(f),
2872 (fb_flags & FB_ATTACH) ? NULL : "Text Files (*.txt)#*.txt");
2874 if(rc == 1){
2875 LMLIST *lmhead = NULL, *new;
2876 char *p;
2879 * Build an LMLIST to return to the caller.
2881 for(p = f; *p; p += strlen(p)+1){
2882 flen = strlen(p);
2883 dlen = strlen(dir ? dir : "");
2884 new = (LMLIST *) fs_get(sizeof(*new));
2885 new->fname = (char *) fs_get((flen+1) * sizeof(char));
2886 new->dir = (char *) fs_get((dlen+1) * sizeof(char));
2888 strncpy(new->fname, p, flen);
2889 new->fname[flen] = '\0';
2890 strncpy(new->dir, dir ? dir : "", dlen);
2891 new->dir[dlen] = '\0';
2893 /* build full path to stat file. */
2894 if((strlen(new->dir) + strlen(S_FILESEP) +
2895 strlen(new->fname) + 1) < sizeof(lfn)){
2896 strncpy(lfn, new->dir, sizeof(lfn));
2897 lfn[sizeof(lfn)-1] = '\0';
2898 strncat(lfn, S_FILESEP, sizeof(lfn)-strlen(lfn)-1);
2899 strncat(lfn, new->fname, sizeof(lfn)-strlen(lfn)-1);
2900 lfn[sizeof(lfn)-1] = '\0';
2901 if(our_stat(lfn, &sbuf) < 0)
2902 strncpy(new->size, "0", 32);
2903 else
2904 strncpy(new->size, prettysz((off_t)sbuf.st_size), 32);
2906 new->size[32-1] = '\0';
2909 new->next = lmhead;
2910 lmhead = new;
2913 *lmreturn = lmhead;
2914 return(1);
2917 else
2918 rc = mswin_openfile (dir, dirlen, fn, fnlen,
2919 (fb_flags & FB_ATTACH) ? NULL : "Text Files (*.txt)#*.txt");
2922 if(rc == 1){
2923 if(sz != NULL){
2924 /* build full path to stat file. */
2925 if((strlen(dir) + strlen(S_FILESEP) + strlen(fn) + 1) > NLINE)
2926 return -1;
2928 strncpy(lfn, dir, sizeof(lfn));
2929 lfn[sizeof(lfn)-1] = '\0';
2930 strncat(lfn, S_FILESEP, sizeof(lfn)-strlen(lfn)-1);
2931 lfn[sizeof(lfn)-1] = '\0';
2932 strncat(lfn, fn, sizeof(lfn)-strlen(lfn)-1);
2933 lfn[sizeof(lfn)-1] = '\0';
2934 if(our_stat(lfn, &sbuf) < 0){
2935 strncpy(sz, "0", szlen);
2936 sz[szlen-1] = '\0';
2938 else{
2939 strncpy(sz, prettysz ((off_t)sbuf.st_size), szlen);
2940 sz[szlen-1] = '\0';
2944 return(1);
2947 return(rc ? -1 : 0);
2950 #endif /* _WINDOWS */
2954 * LikelyASCII - make a rough guess as to the displayability of the
2955 * given file.
2958 LikelyASCII(char *file)
2960 #define LA_TEST_BUF 1024
2961 #define LA_LINE_LIMIT 300
2962 int n, i, line, rv = FALSE;
2963 unsigned char buf[LA_TEST_BUF];
2964 FILE *fp;
2965 EML eml;
2967 if((fp = our_fopen(file, "rb")) != NULL){
2968 clearerr(fp);
2969 if((n = fread(buf, sizeof(char), LA_TEST_BUF * sizeof(char), fp)) > 0
2970 || !ferror(fp)){
2972 * If we don't hit any newlines in a reasonable number,
2973 * LA_LINE_LIMIT, of characters or the file contains NULLs,
2974 * bag out...
2976 rv = TRUE;
2977 for(i = line = 0; i < n; i++)
2978 if((line = (buf[i] == '\n') ? 0 : line + 1) >= LA_LINE_LIMIT
2979 || !buf[i]){
2980 rv = FALSE;
2981 emlwrite(_("Can't display non-text file. Try \"Launch\"."),
2982 NULL);
2983 break;
2986 else{
2987 eml.s = file;
2988 emlwrite(_("Can't read file: %s"), &eml);
2991 fclose(fp);
2993 else{
2994 eml.s = file;
2995 emlwrite(_("Can't open file: %s"), &eml);
2998 return(rv);
3002 void
3003 zotlmlist(LMLIST *lm)
3005 LMLIST *tp;
3007 while(lm){
3008 if(lm->fname)
3009 free(lm->fname);
3011 if(lm->dir)
3012 free(lm->dir);
3014 tp = lm;
3015 lm = lm->next;
3016 tp->next = NULL;
3017 free((char *) tp);
3023 * time_to_check - checks the current time against the last time called
3024 * and returns true if the elapsed time is > below.
3025 * Newmail won't necessarily check, but we want to give it
3026 * a chance to check or do a keepalive.
3029 time_to_check(void)
3031 static time_t lasttime = 0L;
3033 if(!get_input_timeout())
3034 return(FALSE);
3036 if(time((time_t *) 0) - lasttime > (Pmaster ? (time_t)(FUDGE-10) : get_input_timeout())){
3037 lasttime = time((time_t *) 0);
3038 return(TRUE);
3040 else
3041 return(FALSE);