* New version 2.21
[alpine.git] / pico / browse.c
blob2869e8c0763feb05ac293b304b6fb17612b85854
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-2017 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 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 void BrowserKeys(void);
97 void layoutcells(struct bmaster *);
98 void percdircells(struct bmaster *);
99 int PlaceCell(struct bmaster *, struct fcell *, int *, int *);
100 void zotfcells(struct fcell *);
101 void zotmaster(struct bmaster **);
102 struct fcell *FindCell(struct bmaster *, char *, int);
103 int sisin(char *, char *);
104 void p_chdir(struct bmaster *);
105 void BrowserAnchor(char *);
106 void ClearBrowserScreen(void);
107 void BrowserRunChild(char *, char *);
108 int fcell_is_selected(struct fcell *, struct bmaster *);
109 void add_cell_to_lmlist(struct fcell *, struct bmaster *);
110 void del_cell_from_lmlist(struct fcell *, struct bmaster *);
113 static KEYMENU menu_browse[] = {
114 {"?", N_("Get Help"), KS_SCREENHELP}, {NULL, NULL, KS_NONE},
115 {NULL, NULL, KS_NONE}, {"-", N_("Prev Pg"), KS_PREVPAGE},
116 {"D", N_("Delete"), KS_NONE}, {"C",N_("Copy"), KS_NONE},
117 {NULL, NULL, KS_NONE}, {NULL, NULL, KS_NONE},
118 {"W", N_("Where is"), KS_NONE}, {"Spc", N_("Next Pg"), KS_NEXTPAGE},
119 {"R", N_("Rename"), KS_NONE}, {NULL, NULL, KS_NONE}
121 #define QUIT_KEY 1
122 #define EXEC_KEY 2
123 #define GOTO_KEY 6
124 #define SELECT_KEY 7
125 #define PICO_KEY 11
128 #define DIRWORD "dir"
129 #define PARENTDIR "parent"
130 #define SELECTWORD "SELECT"
134 * Default pager used by the stand-alone file browser.
136 #define BROWSER_PAGER ((gmode & MDFKEY) ? "pine -k -F" : "pine -F")
140 * function key mappings for callable browser
142 static UCS bfmappings[2][12][2] = { { { F1, '?'}, /* stand alone */
143 { F2, NODATA }, /* browser function */
144 { F3, 'q'}, /* key mappings... */
145 { F4, 'v'},
146 { F5, 'l'},
147 { F6, 'w'},
148 { F7, '-'},
149 { F8, ' '},
150 { F9, 'd'},
151 { F10, 'r'},
152 { F11, 'c'},
153 { F12, 'e'} },
154 { { F1, '?'}, /* callable browser */
155 { F2, NODATA }, /* function key */
156 { F3, 'e'}, /* mappings... */
157 { F4, 's'},
158 { F5, NODATA },
159 { F6, 'w'},
160 { F7, '-'},
161 { F8, ' '},
162 { F9, 'd'},
163 { F10, 'r'},
164 { F11, 'c'},
165 { F12, 'a'} } };
169 * Browser help for pico (pine composer help handed to us by pine)
171 static char *BrowseHelpText[] = {
172 /* TRANSLATORS: The next several lines go together. The ~ characters
173 should be left in front of the characters they cause to be bold. */
174 N_("Help for Browse Command"),
175 " ",
176 N_(" Pico's file browser is used to select a file from the"),
177 N_(" file system for inclusion in the edited text."),
178 " ",
179 N_("~ Both directories and files are displayed. Press ~S"),
180 N_("~ or ~R~e~t~u~r~n to select a file or directory. When a file"),
181 N_(" is selected during the \"Read File\" command, it is"),
182 N_(" inserted into edited text. Answering \"yes\" to the"),
183 N_(" verification question after a directory is selected causes"),
184 N_(" the contents of that directory to be displayed for selection."),
185 " ",
186 N_(" The file named \"..\" is special, and means the \"parent\""),
187 N_(" of the directory being displayed. Select this directory"),
188 N_(" to move upward in the directory tree."),
189 " ",
190 N_("End of Browser Help."),
191 " ",
192 NULL
196 * Help for standalone browser (pilot)
198 static char *sa_BrowseHelpText[] = {
199 /* TRANSLATORS: Some more help text */
200 N_("Help for Pilot (PIne's Looker-upper Of Things"),
201 " ",
202 N_(" Pilot is a simple, display-oriented file system browser based on the"),
203 N_("~ Alpine message system composer. As with Alpine, commands are displayed at"),
204 N_("~ the bottom of the screen, and context-sensitive help is provided."),
205 " ",
206 N_("~ Pilot displays the current working directory at the top of the screen."),
207 N_("~ The directory's contents are displayed in columns of file name, file"),
208 N_("~ size pairs. Names that are directories are indicated by the name"),
209 N_("~ ~(~d~i~r~) in place of the file size. The parent of the current working"),
210 N_("~ directory is indicated by the file name ~.~. and size of ~(~p~a~r~e~n~t~ ~d~i~r~)."),
211 N_("~ File names that are symbolic links to other files are displayed with a"),
212 N_("~ file size of ~-~-."),
213 " ",
214 N_(" The following function keys are available in Pilot:"),
215 " ",
216 N_("~ ~? Display this help text."),
217 N_("~ ~Q Quit Pilot."),
218 " ",
219 N_("~ ~V View the currently selected file or open the selected directory."),
220 N_("~ Note: Pilot invokes ~p~i~n~e ~-~F, or the program defined by the ~P~A~G~E~R"),
221 N_("~ environment variable, to view the file."),
222 N_("~ ~L Launch an external application program."),
223 " ",
224 N_("~ ~W Search for a file by name."),
225 N_("~ ~- Scroll up one page."),
226 N_("~ ~S~p~a~c~e Scroll down one page."),
227 N_("~ ~N,~^~F Move forward (right) one column."),
228 N_("~ ~P,~^~B Move back (left) one column."),
229 N_("~ ~^~N Move down one row."),
230 N_("~ ~^~P Move up one row."),
231 " ",
232 N_("~ ~D Delete the selected file."),
233 N_("~ ~R Rename the selected file or directory."),
234 N_("~ ~C Copy the selected file."),
235 N_("~ ~E Edit the selected file."),
236 N_("~ Note: Pilot invokes ~p~i~c~o, or the program defined by the ~E~D~I~T~O~R"),
237 N_("~ environment variable, to edit the file."),
238 " ",
239 N_("End of Pilot Help."),
240 NULL
246 * pico_file_browse - Exported version of FileBrowse below.
249 pico_file_browse(PICO *pdata, char *dir, size_t dirlen, char *fn, size_t fnlen,
250 char *sz, size_t szlen, int flags)
252 int rv;
253 char title_buf[64];
255 Pmaster = pdata;
256 gmode = pdata->pine_flags | MDEXTFB;
257 km_popped = 0;
259 /* only init screen bufs for display and winch handler */
260 if(!vtinit())
261 return(-1);
263 if(Pmaster){
264 term.t_mrow = Pmaster->menu_rows;
265 if(Pmaster->oper_dir)
266 strncpy(opertree, Pmaster->oper_dir, NLINE);
268 if(*opertree)
269 fixpath(opertree, sizeof(opertree));
272 /* install any necessary winch signal handler */
273 ttresize();
275 snprintf(title_buf, sizeof(title_buf), "%s FILE", pdata->pine_anchor);
276 set_browser_title(title_buf);
277 rv = FileBrowse(dir, dirlen, fn, fnlen, sz, szlen, flags, NULL);
278 set_browser_title(NULL);
279 vttidy(); /* clean up tty handling */
280 zotdisplay(); /* and display structures */
281 Pmaster = NULL; /* and global config structure */
282 return(rv);
288 * FileBrowse - display contents of given directory dir
290 * intput:
291 * dir points to initial dir to browse.
292 * dirlen- buffer size of dir
293 * fn initial file name.
294 * fnlen- buffer size of fn
296 * returns:
297 * dir points to currently selected directory (without
298 * trailing file system delimiter)
299 * fn points to currently selected file
300 * sz points to size of file if ptr passed was non-NULL
301 * flags
303 * Special dispensation for FB_LMODE. If the caller sets
304 * FB_LMODEPOS, and the caller passes a non-null lmreturn,
305 * then the return values will be in lmreturn instead of
306 * in dir and fn. The caller is responsible for freeing
307 * the contents of lmreturn.
309 * 1 if a file's been selected
310 * 0 if no files selected
311 * -1 if there were problems
314 FileBrowse(char *dir, size_t dirlen, char *fn, size_t fnlen,
315 char *sz, size_t szlen, int fb_flags, LMLIST **lmreturn)
317 UCS c, new_c;
318 int status, i, j;
319 int row, col, crow, ccol;
320 int flags;
321 int add_file;
322 char *p, *envp, child[NLINE], tmp[NLINE];
323 struct bmaster *mp;
324 struct fcell *tp;
325 EML eml;
326 #ifdef MOUSE
327 MOUSEPRESS mousep;
328 #endif
330 child[0] = '\0';
332 if((gmode&MDTREE) && !in_oper_tree(dir)){
333 eml.s = opertree;
334 emlwrite(_("\007Can't read outside of %s in restricted mode"), &eml);
335 sleep(2);
336 return(0);
339 if(gmode&MDGOTO){
340 /* fix up function key mapping table */
341 /* fix up key menu labels */
344 /* build contents of cell structures */
345 if((gmp = getfcells(dir, fb_flags)) == NULL)
346 return(-1);
348 tp = NULL;
349 if(fn && *fn){
350 if((tp = FindCell(gmp, fn, 0)) != NULL){
351 gmp->current = tp;
352 PlaceCell(gmp, gmp->current, &row, &col);
356 /* paint screen */
357 PaintBrowser(gmp, 0, &crow, &ccol);
359 while(1){ /* the big loop */
360 if(!(gmode&MDSHOCUR)){
361 crow = term.t_nrow-term.t_mrow;
362 ccol = 0;
365 if(!(gmode&MDSHOCUR))
366 movecursor(crow, ccol);
367 else if(gmp->flags & FB_LMODEPOS){
368 if(gmp->flags & FB_LMODE && gmp->current->mode != FIODIR)
369 movecursor(crow, ccol+1);
370 else
371 movecursor(crow, ccol+4);
373 else
374 movecursor(crow, ccol);
376 if(km_popped){
377 km_popped--;
378 if(km_popped == 0)
379 /* cause bottom three lines to repaint */
380 PaintBrowser(gmp, 0, &crow, &ccol);
383 if(km_popped){ /* temporarily change to cause menu to paint */
384 term.t_mrow = 2;
385 movecursor(term.t_nrow-2, 0); /* clear status line */
386 peeol();
387 BrowserKeys();
388 term.t_mrow = 0;
391 (*term.t_flush)();
393 #ifdef MOUSE
394 mouse_in_content(KEY_MOUSE, -1, -1, 0, 0);
395 register_mfunc(mouse_in_content,2,0,term.t_nrow-(term.t_mrow+1),
396 term.t_ncol);
397 #endif
398 c = GetKey();
399 #ifdef MOUSE
400 clear_mfunc(mouse_in_content);
401 #endif
403 if(Pmaster){
404 if(Pmaster->newmail && (c == NODATA || time_to_check())){
405 if((*Pmaster->newmail)(c == NODATA ? 0 : 2, 1) >= 0){
406 int rv;
408 if(km_popped){ /* restore display */
409 km_popped = 0;
410 PaintBrowser(gmp, 0, &crow, &ccol);
413 clearcursor();
414 mlerase();
415 rv = (*Pmaster->showmsg)(c);
416 ttresize();
417 picosigs();
418 if(rv) /* Did showmsg corrupt the display? */
419 PaintBrowser(gmp, 0, &crow, &ccol); /* Yes, repaint */
421 mpresf = 1;
424 clearcursor();
425 if(gmp->flags & FB_LMODEPOS){
426 if(gmp->flags & FB_LMODE && gmp->current->mode != FIODIR)
427 movecursor(crow, ccol+1);
428 else
429 movecursor(crow, ccol+4);
431 else
432 movecursor(crow, ccol);
435 else{
436 if(get_input_timeout() && (c == NODATA || time_to_check()))
437 if(pico_new_mail())
438 emlwrite(_("You may possibly have new mail."), NULL);
441 if(km_popped)
442 switch(c){
443 case NODATA:
444 case (CTRL|'L'):
445 km_popped++;
446 break;
448 default:
449 /* clear bottom three lines */
450 movecursor(term.t_nrow-2, 0);
451 peeol();
452 movecursor(term.t_nrow-1, 0);
453 peeol();
454 movecursor(term.t_nrow, 0);
455 peeol();
456 break;
459 if(c == NODATA) /* GetKey timed out */
460 continue;
461 else if(Pmaster)
462 (*Pmaster->keybinput)();
465 if(mpresf){ /* blast old messages */
466 if(mpresf++ > MESSDELAY){ /* every few keystrokes */
467 mlerase();
471 /* process commands */
472 switch(new_c = normalize_cmd(c,bfmappings[(gmode&MDBRONLY)?0:1],2)){
474 case KEY_RIGHT: /* move right */
475 case (CTRL|'@'):
476 case (CTRL|'F'): /* forward */
477 case 'n' :
478 case 'N' :
479 if(gmp->current->next == NULL){
480 (*term.t_beep)();
481 break;
484 PlaceCell(gmp, gmp->current, &row, &col);
485 PaintCell(row, col, gmp->cpf, gmp->current, 0);
486 gmp->current = gmp->current->next;
487 if(PlaceCell(gmp, gmp->current, &row, &col)){
488 PaintBrowser(gmp, 1, &crow, &ccol);
490 else{
491 PaintCell(row, col, gmp->cpf, gmp->current, 1);
492 crow = row;
493 ccol = col;
495 break;
497 case KEY_LEFT: /* move left */
498 case (CTRL|'B'): /* back */
499 case 'p' :
500 case 'P' :
501 if(gmp->current->prev == NULL){
502 (*term.t_beep)();
503 break;
506 PlaceCell(gmp, gmp->current, &row, &col);
507 PaintCell(row, col, gmp->cpf, gmp->current, 0);
508 gmp->current = gmp->current->prev;
509 if(PlaceCell(gmp, gmp->current, &row, &col)){
510 PaintBrowser(gmp, 1, &crow, &ccol);
512 else{
513 PaintCell(row, col, gmp->cpf, gmp->current, 1);
514 crow = row;
515 ccol = col;
517 break;
519 case (CTRL|'A'): /* beginning of line */
520 tp = gmp->current;
521 i = col;
522 while(i > 0){
523 i -= gmp->cpf;
524 if(tp->prev != NULL)
525 tp = tp->prev;
527 PlaceCell(gmp, gmp->current, &row, &col);
528 PaintCell(row, col, gmp->cpf, gmp->current, 0);
529 gmp->current = tp;
530 if(PlaceCell(gmp, tp, &row, &col)){
531 PaintBrowser(gmp, 1, &crow, &ccol);
533 else{
534 PaintCell(row, col, gmp->cpf, gmp->current, 1);
535 crow = row;
536 ccol = col;
538 break;
540 case (CTRL|'E'): /* end of line */
541 tp = gmp->current;
542 i = col + gmp->cpf;
543 while(i+gmp->cpf <= gmp->cpf * gmp->fpl){
544 i += gmp->cpf;
545 if(tp->next != NULL)
546 tp = tp->next;
549 PlaceCell(gmp, gmp->current, &row, &col);
550 PaintCell(row, col, gmp->cpf, gmp->current, 0);
551 gmp->current = tp;
552 if(PlaceCell(gmp, tp, &row, &col)){
553 PaintBrowser(gmp, 1, &crow, &ccol);
555 else{
556 PaintCell(row, col, gmp->cpf, gmp->current, 1);
557 crow = row;
558 ccol = col;
560 break;
562 case (CTRL|'V'): /* page forward */
563 case ' ':
564 case KEY_PGDN :
565 case KEY_END :
566 tp = gmp->top;
567 i = term.t_nrow - term.t_mrow - 2;
569 while(i-- && tp->next != NULL){
570 j = 0;
571 while(++j <= gmp->fpl && tp->next != NULL)
572 tp = tp->next;
575 if(tp == NULL)
576 continue;
578 PlaceCell(gmp, gmp->current, &row, &col);
579 PaintCell(row, col, gmp->cpf, gmp->current, 0);
580 gmp->current = tp;
581 if(PlaceCell(gmp, tp, &row, &col)){
582 PaintBrowser(gmp, 1, &crow, &ccol);
584 else{
585 PaintCell(row, col, gmp->cpf, gmp->current, 1);
586 crow = row;
587 ccol = col;
589 break;
591 case '-' :
592 case (CTRL|'Y'): /* page backward */
593 case KEY_PGUP :
594 case KEY_HOME :
595 tp = gmp->top;
596 i = term.t_nrow - term.t_mrow - 4;
597 while(i-- && tp != NULL){
598 j = gmp->fpl;
599 while(j-- && tp != NULL)
600 tp = tp->prev;
603 if(tp || (gmp->current != gmp->top)){ /* clear old hilite */
604 PlaceCell(gmp, gmp->current, &row, &col);
605 PaintCell(row, col, gmp->cpf, gmp->current, 0);
608 if(tp) /* new page ! */
609 gmp->current = tp;
610 else if(gmp->current != gmp->top) /* goto top of page */
611 gmp->current = gmp->top;
612 else /* do nothing */
613 continue;
615 if(PlaceCell(gmp, gmp->current, &row, &col)){
616 PaintBrowser(gmp, 1, &crow, &ccol);
618 else{
619 PaintCell(row, col, gmp->cpf, gmp->current, 1);
620 crow = row;
621 ccol = col;
624 break;
626 case KEY_DOWN :
627 case (CTRL|'N'): /* next */
628 tp = gmp->current;
629 i = gmp->fpl;
630 while(i--){
631 if(tp->next == NULL){
632 (*term.t_beep)();
633 break;
635 else
636 tp = tp->next;
638 if(i != -1) /* can't go down */
639 break;
641 PlaceCell(gmp, gmp->current, &row, &col);
642 PaintCell(row, col, gmp->cpf, gmp->current, 0);
643 gmp->current = tp;
644 if(PlaceCell(gmp, tp, &row, &col)){
645 PaintBrowser(gmp, 1, &crow, &ccol);
647 else{
648 PaintCell(row, col, gmp->cpf, gmp->current, 1);
649 crow = row;
650 ccol = col;
652 break;
654 case KEY_UP :
655 case (CTRL|'P'): /* previous */
656 tp = gmp->current;
657 i = gmp->fpl;
658 while(i-- && tp)
659 tp = tp->prev;
661 if(tp == NULL)
662 break;
664 PlaceCell(gmp, gmp->current, &row, &col);
665 PaintCell(row, col, gmp->cpf, gmp->current, 0);
666 gmp->current = tp;
667 if(PlaceCell(gmp, tp, &row, &col)){
668 PaintBrowser(gmp, 1, &crow, &ccol);
670 else{
671 PaintCell(row, col, gmp->cpf, gmp->current, 1);
672 crow = row;
673 ccol = col;
675 break;
677 #ifdef MOUSE
678 case KEY_MOUSE:
679 mouse_get_last (NULL, &mousep);
680 if (mousep.doubleclick) {
681 goto Selected;
683 else {
684 row = mousep.row -= 2; /* Adjust for header*/
685 col = mousep.col;
686 i = row * gmp->fpl + (col / gmp->cpf); /* Count from top */
687 tp = gmp->top; /* start at top. */
688 for (; i > 0 && tp != NULL; --i) /* Count cells. */
689 tp = tp->next;
690 if (tp != NULL) { /* Valid cell? */
691 PlaceCell(gmp, gmp->current, &row, &col);
692 PaintCell(row, col, gmp->cpf, gmp->current, 0);
693 gmp->current = tp;
694 if(PlaceCell(gmp, tp, &row, &col)){
695 PaintBrowser(gmp, 1, &crow, &ccol);
697 else{
698 PaintCell(row, col, gmp->cpf, gmp->current, 1);
699 crow = row;
700 ccol = col;
704 break;
705 #endif
707 case 'e': /* exit or edit */
708 case 'E':
709 if(gmode&MDBRONLY){ /* run "pico" */
710 snprintf(child, sizeof(child), "%s%c%s", gmp->dname, C_FILESEP,
711 gmp->current->fname);
712 /* make sure selected isn't a directory or executable */
713 if(!LikelyASCII(child)){
714 emlwrite(_("Can't edit non-text file. Try Launch."), NULL);
715 break;
718 if((envp = (char *) getenv("EDITOR")) != NULL)
719 snprintf(tmp, sizeof(tmp), "%s \'%s\'", envp, child);
720 else
721 snprintf(tmp, sizeof(tmp), "pico%s%s%s \'%s\'",
722 (gmode & MDFKEY) ? " -f" : "",
723 (gmode & MDSHOCUR) ? " -g" : "",
724 (gmode & MDMOUSE) ? " -m" : "",
725 child);
727 BrowserRunChild(tmp, gmp->dname); /* spawn pico */
728 PaintBrowser(gmp, 0, &crow, &ccol); /* redraw browser */
730 else{
731 zotmaster(&gmp);
732 return(0);
735 break;
737 case 'q': /* user exits wrong */
738 case 'Q':
739 if(gmode&MDBRONLY){
740 zotmaster(&gmp);
741 return(0);
744 unknown_command(c);
745 break;
747 case 'x': /* toggle selection */
748 case 'X':
749 if(!(gmp->flags & FB_LMODE)){
750 if(gmp->flags & FB_LMODEPOS)
751 emlwrite(_("\007Type L command to use ListMode"), NULL);
752 else
753 unknown_command(c);
755 break;
758 if(gmp->current->mode == FIODIR){
759 emlwrite(_("\007Can't Set directories"), NULL);
760 break;
763 if(fcell_is_selected(gmp->current, gmp))
764 del_cell_from_lmlist(gmp->current, gmp);
765 else
766 add_cell_to_lmlist(gmp->current, gmp);
768 PlaceCell(gmp, gmp->current, &row, &col);
769 PaintCell(row, col, gmp->cpf, gmp->current, 1);
770 break;
772 case 'l': /* run Command */
773 case 'L': /* or ListMode */
774 if(gmp->flags & FB_LMODEPOS){
775 if(gmp->flags & FB_LMODE){
777 * Unless we make it so you can get out of ListMode
778 * once you're in ListMode, this must be an error.
780 emlwrite(_("\007Already in ListMode"), NULL);
781 break;
783 else{
784 gmp->flags |= FB_LMODE;
785 PaintBrowser(gmp, 0, &crow, &ccol);
788 break;
791 if(!(gmode&MDBRONLY)){
792 unknown_command(c);
793 break;
796 /* add subcommands to invoke pico and insert selected filename */
797 /* perhaps: add subcmd to scroll command history */
799 tmp[0] = '\0';
800 i = 0;
801 snprintf(child, sizeof(child), "%s%c%s", gmp->dname, C_FILESEP,
802 gmp->current->fname);
803 while(!i){
804 static EXTRAKEYS opts[] = {
805 {"^X", N_("Add Name"), CTRL|'X', KS_NONE},
806 {NULL, NULL, 0, KS_NONE},
807 {NULL, NULL, 0, KS_NONE},
808 {NULL, NULL, 0, KS_NONE},
809 {NULL, NULL, 0, KS_NONE},
810 {NULL, NULL, 0, KS_NONE},
811 {NULL, NULL, 0, KS_NONE},
812 {NULL, NULL, 0, KS_NONE},
813 {NULL, NULL, 0, KS_NONE},
814 {NULL, NULL, 0, KS_NONE}
817 status = mlreply_utf8(_("Command to execute: "),
818 tmp, NLINE, QNORML, opts);
819 switch(status){
820 case HELPCH:
821 emlwrite(_("\007No help yet!"), NULL);
822 /* remove break and sleep after help text is installed */
823 sleep(3);
824 break;
825 case (CTRL|'X'):
826 strncat(tmp, child, sizeof(tmp)-strlen(tmp)-1);
827 tmp[sizeof(tmp)-1] = '\0';
828 break;
829 case (CTRL|'L'):
830 PaintBrowser(gmp, 0, &crow, &ccol);
831 break;
832 case ABORT:
833 emlwrite(_("Command cancelled"), NULL);
834 i++;
835 break;
836 case FALSE:
837 case TRUE:
838 i++;
840 if(tmp[0] == '\0'){
841 emlwrite(_("No command specified"), NULL);
842 break;
845 BrowserRunChild(tmp, gmp->dname);
846 PaintBrowser(gmp, 0, &crow, &ccol);
847 break;
848 default:
849 break;
853 BrowserKeys();
854 break;
856 case 'd': /* delete */
857 case 'D':
858 if(gmp->current->mode == FIODIR){
859 /* BUG: if dir is empty it should be deleted */
860 emlwrite(_("\007Can't delete a directory"), NULL);
861 break;
864 if(gmode&MDSCUR){ /* not allowed! */
865 emlwrite(_("Delete not allowed in restricted mode"),NULL);
866 break;
869 snprintf(child, sizeof(child), "%s%c%s", gmp->dname, C_FILESEP,
870 gmp->current->fname);
872 i = 0;
873 while(i++ < 2){ /* verify twice!! */
874 if(i == 1){
875 if(fexist(child, "w", (off_t *)NULL) != FIOSUC){
876 strncpy(tmp, _("File is write protected! OVERRIDE"), sizeof(tmp));
877 tmp[sizeof(tmp)-1] = '\0';
879 else
880 /* TRANSLATORS: This is a question, Delete file <filename> */
881 snprintf(tmp, sizeof(tmp), _("Delete file \"%.*s\""), NLINE - 20, child);
883 else{
884 strncpy(tmp, _("File CANNOT be UNdeleted! Really delete"), sizeof(tmp));
885 tmp[sizeof(tmp)-1] = '\0';
888 if((status = mlyesno_utf8(tmp, FALSE)) != TRUE){
889 emlwrite((status == ABORT)
890 ? _("Delete Cancelled")
891 : _("File Not Deleted"),
892 NULL);
893 break;
897 if(status == TRUE){
898 if(our_unlink(child) < 0){
899 eml.s = errstr(errno);
900 emlwrite(_("Delete Failed: %s"), &eml);
902 else{ /* fix up pointers and redraw */
903 tp = gmp->current;
904 if(tp->next){
905 gmp->current = tp->next;
906 if((tp->next->prev = tp->prev) != NULL)
907 tp->prev->next = tp->next;
909 else if(tp->prev) {
910 gmp->current = tp->prev;
911 if((tp->prev->next = tp->next) != NULL)
912 tp->next->prev = tp->prev;
915 if(tp == gmp->head)
916 gmp->head = tp->next;
918 if(tp == gmp->bottom)
919 gmp->bottom = tp->prev;
921 tp->fname = NULL;
922 tp->next = tp->prev = NULL;
923 if(tp != gmp->current)
924 free((char *) tp);
926 if((tp = FindCell(gmp, gmp->current->fname, 0)) != NULL){
927 gmp->current = tp;
928 PlaceCell(gmp, gmp->current, &row, &col);
931 PaintBrowser(gmp, 1, &crow, &ccol);
932 mlerase();
936 BrowserKeys();
937 break;
939 case '?': /* HELP! */
940 case (CTRL|'G'):
941 if(term.t_mrow == 0){
942 if(km_popped == 0){
943 km_popped = 2;
944 break;
948 if(Pmaster){
949 VARS_TO_SAVE *saved_state;
951 saved_state = save_pico_state();
952 (*Pmaster->helper)(Pmaster->browse_help,
953 _("Help for Browsing"), 1);
954 if(saved_state){
955 restore_pico_state(saved_state);
956 free_pico_state(saved_state);
959 else if(gmode&MDBRONLY)
960 pico_help(sa_BrowseHelpText, _("Browser Help"), 1);
961 else
962 pico_help(BrowseHelpText, _("Help for Browsing"), 1);
963 /* fall thru to repaint everything */
965 case (CTRL|'L'):
966 PaintBrowser(gmp, 0, &crow, &ccol);
967 break;
969 case 'g': /* jump to a directory */
970 case 'G':
972 if(!(gmode&MDGOTO))
973 goto Default;
975 i = 0;
976 child[0] = '\0';
978 while(!i){
980 /* TRANSLATORS: A prompt asking for a directory to
981 move into */
982 status = mlreply_utf8(_("Directory to go to: "), child, NLINE, QNORML,
983 NULL);
985 switch(status){
986 case HELPCH:
987 emlwrite(_("\007No help yet!"), NULL);
988 /* remove break and sleep after help text is installed */
989 sleep(3);
990 break;
991 case (CTRL|'L'):
992 PaintBrowser(gmp, 0, &crow, &ccol);
993 break;
994 case ABORT:
995 emlwrite(_("Goto cancelled"), NULL);
996 i++;
997 break;
998 case FALSE:
999 case TRUE:
1000 i++;
1002 if(*child == '\0'){
1003 strncpy(child, gethomedir(NULL), sizeof(child));
1004 child[sizeof(child)-1] = '\0';
1007 if(!compresspath(gmp->dname, child, sizeof(child))){
1008 eml.s = child;
1009 emlwrite(_("Invalid Directory: %s"), &eml);
1010 break;
1013 if((gmode&MDSCUR) && homeless(child)){
1014 emlwrite(_("Restricted mode browsing limited to home directory"),NULL);
1015 break;
1018 if((gmode&MDTREE) && !in_oper_tree(child)){
1019 eml.s = opertree;
1020 emlwrite(_("\007 Can't go outside of %s in restricted mode"),
1021 &eml);
1022 break;
1025 if(isdir(child, (long *) NULL, NULL)){
1026 if((mp = getfcells(child, fb_flags)) == NULL){
1027 /* getfcells should explain what happened */
1028 i++;
1029 break;
1032 zotmaster(&gmp);
1033 gmp = mp;
1034 PaintBrowser(gmp, 0, &crow, &ccol);
1036 else{
1037 eml.s = child;
1038 emlwrite(_("\007Not a directory: \"%s\""), &eml);
1041 break;
1042 default:
1043 break;
1046 BrowserKeys();
1047 break;
1049 case 'a': /* Add */
1050 case 'A':
1051 if(gmode&MDSCUR){ /* not allowed! */
1052 emlwrite(_("Add not allowed in restricted mode"),NULL);
1053 break;
1056 add_file = 1;
1057 i = 0;
1058 child[0] = '\0';
1059 /* pass in default filename */
1060 if(fn && *fn){
1061 strncpy(child, fn, sizeof(child) - 1);
1062 child[sizeof(child) - 1] = '\0';
1065 while(!i){
1066 int repaint = 0;
1067 EXTRAKEYS opts[10];
1069 memset((void *) &opts, 0, 10*sizeof(EXTRAKEYS));
1070 opts[0].name = "^X";
1071 opts[0].label = add_file ? N_("Add Dir") : N_("Add File");
1072 opts[0].key = (CTRL|'X');
1074 switch(status=mlreply_utf8(add_file ? _("Name of file to add: ") : _("Name of directory to add: "), child, NLINE,
1075 QFFILE, opts)){
1076 case HELPCH:
1077 emlwrite(_("\007No help yet!"), NULL);
1078 /* remove break and sleep after help text is installed */
1079 sleep(3);
1080 break;
1081 case (CTRL|'L'):
1082 PaintBrowser(gmp, 0, &crow, &ccol);
1083 break;
1084 case (CTRL|'X'):
1085 if(add_file > 0) add_file = 0; else add_file = 1;
1086 break;
1087 case ABORT:
1088 emlwrite(add_file > 0 ? _("Add File Cancelled") : _("Add Directory Cancelled"), NULL);
1089 i++;
1090 break;
1091 case FALSE:
1093 * Support default filename. A return of 'FALSE' means
1094 * 'No change in filename' which is fine if we have
1095 * provided a default.
1096 * John Berthels <john.berthels@nexor.co.uk>
1098 /* FALLTHROUGH */
1099 case TRUE:
1100 i++;
1102 if(child[0] == '\0'){
1103 emlwrite(add_file > 0 ? _("No file named. Add Cancelled.") : _("No directory named. Add Cancelled"), NULL);
1104 break;
1107 if(!compresspath(gmp->dname, child, sizeof(child))){
1108 emlwrite(_("Too many ..'s in name"), NULL);
1109 break;
1112 if((gmode&MDTREE) && !in_oper_tree(child)){
1113 eml.s = opertree;
1114 emlwrite(_("\007Restricted mode allows Add in %s only"),
1115 &eml);
1116 break;
1119 if((status = fexist(child, "w", (off_t *)NULL)) == FIOSUC){
1120 snprintf(tmp, sizeof(tmp), _("%s \"%.*s\" already exists!"),
1121 NLINE - 20, add_file > 0 ? "File" : "Directory", child);
1122 emlwrite(tmp, NULL);
1123 break;
1125 else if(status != FIOFNF){
1126 fioperr(status, child);
1127 break;
1130 if(add_file == 0){
1131 if(our_mkdir(child, (0700)) < 0){
1132 eml.s = child;
1133 emlwrite(_("Error adding Directory \"%s\""), &eml);
1135 else /* success! Directory added! */
1136 repaint = 1;
1138 else if(ffwopen(child, FALSE) != FIOSUC){
1139 /* ffwopen should've complained */
1140 break;
1142 else{ /* highlight new file */
1143 ffclose();
1144 eml.s = child;
1145 emlwrite(_("Added File \"%s\""), &eml);
1146 repaint = 1;
1149 if(repaint > 0){
1150 if((p = strrchr(child, C_FILESEP)) == NULL){
1151 emlwrite(_("Problems refiguring browser"), NULL);
1152 break;
1155 *p = '\0';
1156 if(p != child){
1157 strncpy(tmp, child, sizeof(tmp));
1158 tmp[sizeof(tmp)-1] = '\0';
1159 j = 0;
1160 while((child[j++] = *++p) != '\0')
1163 else{
1164 strncpy(tmp, S_FILESEP, sizeof(tmp));
1165 tmp[sizeof(tmp)-1] = '\0';
1169 * new file in same dir? if so, refigure files
1170 * and redraw...
1172 if(!strcmp(tmp, gmp->dname)){
1173 if((mp = getfcells(gmp->dname, fb_flags)) == NULL)
1174 /* getfcells should explain what happened */
1175 break;
1177 zotmaster(&gmp);
1178 gmp = mp;
1179 if((tp = FindCell(gmp, child, 0)) != NULL){
1180 gmp->current = tp;
1181 PlaceCell(gmp, gmp->current, &row, &col);
1184 PaintBrowser(gmp, 1, &crow, &ccol);
1187 break;
1188 default:
1189 break;
1193 BrowserKeys();
1194 break;
1196 case 'c': /* copy */
1197 case 'C':
1198 if(gmp->current->mode == FIODIR){
1199 emlwrite(_("\007Can't copy a directory"), NULL);
1200 break;
1203 if(gmode&MDSCUR){ /* not allowed! */
1204 emlwrite(_("Copy not allowed in restricted mode"),NULL);
1205 break;
1208 i = 0;
1209 child[0] = '\0';
1211 while(!i){
1213 switch(status=mlreply_utf8(_("Name of new copy: "), child, NLINE,
1214 QFFILE, NULL)){
1215 case HELPCH:
1216 emlwrite(_("\007No help yet!"), NULL);
1217 /* remove break and sleep after help text is installed */
1218 sleep(3);
1219 break;
1220 case (CTRL|'L'):
1221 PaintBrowser(gmp, 0, &crow, &ccol);
1222 break;
1223 case ABORT:
1224 emlwrite(_("Make Copy Cancelled"), NULL);
1225 i++;
1226 break;
1227 case FALSE:
1228 i++;
1229 mlerase();
1230 break;
1231 case TRUE:
1232 i++;
1234 if(child[0] == '\0'){
1235 emlwrite(_("No destination, file not copied"), NULL);
1236 break;
1239 if(!strcmp(gmp->current->fname, child)){
1240 emlwrite(_("\007Can't copy file on to itself!"), NULL);
1241 break;
1244 if(!compresspath(gmp->dname, child, sizeof(child))){
1245 emlwrite(_("Too many ..'s in name"), NULL);
1246 break;
1249 if((gmode&MDTREE) && !in_oper_tree(child)){
1250 eml.s = opertree;
1251 emlwrite(_("\007Restricted mode allows Copy in %s only"),
1252 &eml);
1253 break;
1256 if((status = fexist(child, "w", (off_t *)NULL)) == FIOSUC){
1257 /* TRANSLATORS: A question: File <filename> exists! Replace? */
1258 snprintf(tmp, sizeof(tmp), _("File \"%.*s\" exists! OVERWRITE"),
1259 NLINE - 20, child);
1260 if((status = mlyesno_utf8(tmp, 0)) != TRUE){
1261 emlwrite((status == ABORT)
1262 ? _("Make copy cancelled")
1263 : _("File Not Renamed"),
1264 NULL);
1265 break;
1268 else if(status != FIOFNF){
1269 fioperr(status, child);
1270 break;
1273 snprintf(tmp, sizeof(tmp), "%s%c%s", gmp->dname, C_FILESEP,
1274 gmp->current->fname);
1276 if(copy(tmp, child) < 0){
1277 /* copy() will report any error messages */
1278 break;
1280 else{ /* highlight new file */
1281 eml.s = child;
1282 emlwrite(_("File copied to %s"), &eml);
1284 if((p = strrchr(child, C_FILESEP)) == NULL){
1285 emlwrite(_("Problems refiguring browser"), NULL);
1286 break;
1289 *p = '\0';
1290 strncpy(tmp, (p == child) ? S_FILESEP: child, sizeof(tmp));
1291 tmp[sizeof(tmp)-1] = '\0';
1294 * new file in same dir? if so, refigure files
1295 * and redraw...
1297 if(!strcmp(tmp, gmp->dname)){
1298 strncpy(child, gmp->current->fname, sizeof(child));
1299 child[sizeof(child)-1] = '\0';
1300 if((mp = getfcells(gmp->dname, fb_flags)) == NULL)
1301 /* getfcells should explain what happened */
1302 break;
1304 zotmaster(&gmp);
1305 gmp = mp;
1306 if((tp = FindCell(gmp, child, 0)) != NULL){
1307 gmp->current = tp;
1308 PlaceCell(gmp, gmp->current, &row, &col);
1311 PaintBrowser(gmp, 1, &crow, &ccol);
1314 break;
1315 default:
1316 break;
1319 BrowserKeys();
1320 break;
1322 case 'r': /* rename */
1323 case 'R':
1324 i = 0;
1326 if(!strcmp(gmp->current->fname, "..")){
1327 emlwrite(_("\007Can't rename \"..\""), NULL);
1328 break;
1331 if(gmode&MDSCUR){ /* not allowed! */
1332 emlwrite(_("Rename not allowed in restricted mode"),NULL);
1333 break;
1336 strncpy(child, gmp->current->fname, sizeof(child));
1337 child[sizeof(child)-1] = '\0';
1339 while(!i){
1341 switch(status=mlreply_utf8(_("Rename file to: "), child, NLINE, QFFILE,
1342 NULL)){
1343 case HELPCH:
1344 emlwrite(_("\007No help yet!"), NULL);
1345 /* remove break and sleep after help text is installed */
1346 sleep(3);
1347 break;
1348 case (CTRL|'L'):
1349 PaintBrowser(gmp, 0, &crow, &ccol);
1350 break;
1351 case ABORT:
1352 emlwrite(_("Rename cancelled"), NULL);
1353 i++;
1354 break;
1355 case FALSE:
1356 case TRUE:
1357 i++;
1359 if(child[0] == '\0' || status == FALSE){
1360 mlerase();
1361 break;
1364 if(!compresspath(gmp->dname, child, sizeof(child))){
1365 emlwrite(_("Too many ..'s in name"), NULL);
1366 break;
1369 if((gmode&MDTREE) && !in_oper_tree(child)){
1370 eml.s = opertree;
1371 emlwrite(_("\007Restricted mode allows Rename in %s only"),
1372 &eml);
1373 break;
1376 status = fexist(child, "w", (off_t *)NULL);
1377 if(status == FIOSUC || status == FIOFNF){
1378 if(status == FIOSUC){
1379 snprintf(tmp, sizeof(tmp), _("File \"%.*s\" exists! OVERWRITE"),
1380 NLINE - 20, child);
1382 if((status = mlyesno_utf8(tmp, FALSE)) != TRUE){
1383 emlwrite((status == ABORT)
1384 ? _("Rename cancelled")
1385 : _("Not Renamed"),
1386 NULL);
1387 break;
1391 snprintf(tmp, sizeof(tmp), "%s%c%s", gmp->dname, C_FILESEP,
1392 gmp->current->fname);
1394 if(our_rename(tmp, child) < 0){
1395 eml.s = errstr(errno);
1396 emlwrite(_("Rename Failed: %s"), &eml);
1398 else{
1399 if((p = strrchr(child, C_FILESEP)) == NULL){
1400 emlwrite(_("Problems refiguring browser"), NULL);
1401 break;
1404 *p = '\0';
1405 strncpy(tmp, (p == child) ? S_FILESEP: child, sizeof(tmp));
1406 tmp[sizeof(tmp)-1] = '\0';
1408 if((mp = getfcells(tmp, fb_flags)) == NULL)
1409 /* getfcells should explain what happened */
1410 break;
1412 zotmaster(&gmp);
1413 gmp = mp;
1415 if((tp = FindCell(gmp, ++p, 0)) != NULL){
1416 gmp->current = tp;
1417 PlaceCell(gmp, gmp->current, &row, &col);
1420 PaintBrowser(gmp, 1, &crow, &ccol);
1421 mlerase();
1424 else{
1425 fioperr(status, child);
1427 break;
1428 default:
1429 break;
1432 BrowserKeys();
1433 break;
1435 case 'v': /* stand-alone */
1436 case 'V': /* browser "view" */
1437 case 's': /* user "select" */
1438 case 'S':
1439 case (CTRL|'M'):
1440 Selected:
1442 if(((new_c == 'S' || new_c == 's') && (gmode&MDBRONLY))
1443 || ((new_c == 'V' || new_c == 'v') && !(gmode&MDBRONLY)))
1444 goto Default;
1446 if(gmp->current->mode == FIODIR){
1447 *child = '\0';
1448 strncpy(tmp, gmp->dname, sizeof(tmp));
1449 tmp[sizeof(tmp)-1] = '\0';
1450 p = gmp->current->fname;
1451 if(p[0] == '.' && p[1] == '.' && p[2] == '\0'){
1452 if((p=strrchr(tmp, C_FILESEP)) != NULL){
1453 *p = '\0';
1455 if((gmode&MDTREE) && !in_oper_tree(tmp)){
1456 eml.s = PARENTDIR;
1457 emlwrite(
1458 _("\007Can't visit %s in restricted mode"),
1459 &eml);
1460 break;
1463 strncpy(child, &p[1], sizeof(child));
1464 child[sizeof(child)-1] = '\0';
1467 #if defined(DOS) || defined(OS2)
1468 (p == tmp || (tmp[1] == ':' && tmp[2] == '\0'))
1469 #else
1470 (p == tmp)
1471 #endif
1472 { /* is it root? */
1473 #if defined(DOS) || defined(OS2)
1474 if(*child){
1475 strncat(tmp, S_FILESEP, sizeof(tmp)-strlen(tmp)-1);
1476 tmp[sizeof(tmp)-1] = '\0';
1478 #else
1479 if(*child){
1480 strncpy(tmp, S_FILESEP, sizeof(tmp));
1481 tmp[sizeof(tmp)-1] = '\0';
1483 #endif
1484 else{
1485 emlwrite(_("\007Can't move up a directory"),
1486 NULL);
1487 break;
1492 else if((fb_flags&FB_SAVE) && p[0] == '.' && p[1] == '\0'){
1493 if ((strlen(gmp->dname) < dirlen) &&
1494 (strlen(gmp->current->fname) < fnlen)){
1495 strncpy(dir, gmp->dname, dirlen);
1496 dir[dirlen-1] = '\0';
1499 zotmaster(&gmp);
1500 return(0); /* just change the directory, still return no selection */
1502 else{
1503 if(tmp[1] != '\0'){ /* were in root? */
1504 strncat(tmp, S_FILESEP, sizeof(tmp)-strlen(tmp)-1);
1505 tmp[sizeof(tmp)-1] = '\0';
1508 strncat(tmp, gmp->current->fname, sizeof(tmp)-strlen(tmp)-1);
1509 tmp[sizeof(tmp)-1] = '\0';
1512 if((mp = getfcells(tmp, fb_flags)) == NULL)
1513 /* getfcells should explain what happened */
1514 break;
1516 if(gmp->flags & FB_LMODE){
1517 mp->flags |= FB_LMODE;
1518 mp->lm = gmp->lm;
1519 gmp->lm = NULL;
1522 zotmaster(&gmp);
1523 gmp = mp;
1524 tp = NULL;
1526 if(*child){
1527 if((tp = FindCell(gmp, child, 0)) != NULL){
1528 gmp->current = tp;
1529 PlaceCell(gmp, gmp->current, &row, &col);
1531 else{
1532 eml.s = child;
1533 emlwrite(_("\007Problem finding dir \"%s\""), &eml);
1537 PaintBrowser(gmp, 0, &crow, &ccol);
1538 if(!*child){
1539 char b[100];
1541 snprintf(b, sizeof(b), "Select/View \" .. %s %s\" to return to previous directory.", PARENTDIR, DIRWORD);
1542 emlwrite(b, NULL);
1545 break;
1547 else if(gmode&MDBRONLY){
1548 snprintf(child, sizeof(child), "%s%c%s", gmp->dname, C_FILESEP,
1549 gmp->current->fname);
1551 if(LikelyASCII(child)){
1552 snprintf(tmp, sizeof(tmp), "%s \'%s\'",
1553 (envp = (char *) getenv("PAGER"))
1554 ? envp : BROWSER_PAGER, child);
1555 BrowserRunChild(tmp, gmp->dname);
1556 PaintBrowser(gmp, 0, &crow, &ccol);
1559 break;
1561 else{ /* just return */
1562 if(gmp->flags & FB_LMODEPOS){
1564 if(!lmreturn){
1565 emlwrite("Programming error, called FileBrowse with LMODEPOS but no lmreturn", NULL);
1566 zotmaster(&gmp);
1567 return(-1);
1570 /* user actually used ListMode, the list is finished */
1571 if(gmp->flags & FB_LMODE){
1572 if(!gmp->lm){
1573 (*term.t_beep)();
1574 emlwrite(_("No files are selected, use \"X\" to mark files for selection"), NULL);
1575 break;
1578 *lmreturn = gmp->lm;
1579 gmp->lm = NULL;
1581 else{ /* construct an lmreturn for user */
1582 LMLIST *new;
1583 size_t flen, dlen;
1585 if((new=(LMLIST *)malloc(sizeof(*new))) == NULL
1586 || (new->fname=malloc(gmp->current->fname ? (flen=strlen(gmp->current->fname))+1 : 1)) == NULL
1587 || (new->dir=malloc((dlen=strlen(gmp->dname))+1)) == NULL){
1588 emlwrite("\007Can't malloc space for filename", NULL);
1589 return(-1);
1592 strncpy(new->fname,
1593 gmp->current->fname ? gmp->current->fname : "", flen);
1594 new->fname[flen] = '\0';
1595 strncpy(new->dir, gmp->dname, dlen);
1596 new->dir[dlen] = '\0';
1597 strncpy(new->size, gmp->current->size, sizeof(new->size));
1598 new->size[sizeof(new->size)-1] = '\0';
1599 new->next = NULL;
1600 *lmreturn = new;
1603 zotmaster(&gmp);
1604 return(1);
1607 if ((strlen(gmp->dname) < dirlen) &&
1608 (strlen(gmp->current->fname) < fnlen)){
1609 strncpy(dir, gmp->dname, dirlen);
1610 dir[dirlen-1] = '\0';
1611 strncpy(fn, gmp->current->fname, fnlen);
1612 fn[fnlen-1] = '\0';
1614 else {
1615 zotmaster(&gmp);
1616 return(-1);
1618 if(sz != NULL){ /* size uninteresting */
1619 strncpy(sz, gmp->current->size, szlen);
1620 sz[szlen-1] = '\0';
1623 zotmaster (&gmp);
1624 return (1);
1626 break;
1628 case 'w': /* Where is */
1629 case 'W':
1630 case (CTRL|'W'):
1631 i = 0;
1632 flags = SR_ORIGMEN | SR_FORWARD | SR_NOEXACT;
1634 while(!i){
1635 switch(readpattern(_("File name to find"), FALSE, flags)){
1636 case HELPCH:
1637 emlwrite(_("\007No help yet!"), NULL);
1638 /* remove break and sleep after help text is installed */
1639 sleep(3);
1640 break;
1641 case (CTRL|'L'):
1642 PaintBrowser(gmp, 0, &crow, &ccol);
1643 break;
1644 case (CTRL|'P'):
1645 if(flags & SR_FORWARD){
1646 flags &= ~SR_FORWARD;
1647 flags |= SR_BACKWRD;
1648 } else {
1649 flags &= ~SR_BACKWRD;
1650 flags |= SR_FORWARD;
1652 break;
1653 case (CTRL|'Y'): /* first first cell */
1654 for(tp = gmp->top; tp->prev; tp = tp->prev)
1657 i++;
1658 /* fall thru to repaint */
1659 case (CTRL|'V'):
1660 if(!i){
1662 tp = gmp->top;
1663 if((i = term.t_nrow - term.t_mrow - 2) <= 0)
1664 break;
1666 while(i-- && tp->next){
1667 j = 0;
1668 while(++j <= gmp->fpl && tp->next)
1669 tp = tp->next;
1672 if(i < 0)
1673 gmp->top = tp;
1675 while(tp->next);
1677 emlwrite(_("Searched to end of directory"), NULL);
1679 else
1680 emlwrite(_("Searched to start of directory"), NULL);
1682 if(tp){
1683 PlaceCell(gmp, gmp->current, &row, &col);
1684 PaintCell(row, col, gmp->cpf, gmp->current, 0);
1685 gmp->current = tp;
1686 if(PlaceCell(gmp, gmp->current, &row, &col)){
1687 PaintBrowser(gmp, 1, &crow, &ccol);
1689 else{
1690 PaintCell(row, col, gmp->cpf, gmp->current, 1);
1691 crow = row;
1692 ccol = col;
1696 i++; /* make sure we jump out */
1697 break;
1698 case ABORT:
1699 emlwrite(_("Whereis cancelled"), NULL);
1700 i++;
1701 break;
1702 case FALSE:
1703 mlerase();
1704 i++;
1705 break;
1706 case TRUE:
1708 char *utf8 = NULL;
1710 if(pat && pat[0])
1711 utf8 = ucs4_to_utf8_cpystr(pat);
1713 if(utf8 && (tp = FindCell(gmp, utf8, flags & SR_BACKWRD)) != NULL){
1714 PlaceCell(gmp, gmp->current, &row, &col);
1715 PaintCell(row, col, gmp->cpf, gmp->current, 0);
1716 gmp->current = tp;
1718 if(PlaceCell(gmp, tp, &row, &col)){ /* top changed */
1719 PaintBrowser(gmp, 1, &crow, &ccol);
1721 else{
1722 PaintCell(row, col, gmp->cpf, gmp->current, 1);
1723 crow = row;
1724 ccol = col;
1726 mlerase();
1728 else{
1729 eml.s = utf8;
1730 emlwrite(_("\"%s\" not found"), &eml);
1733 if(utf8)
1734 fs_give((void **) &utf8);
1737 i++;
1738 break;
1739 default:
1740 break;
1744 BrowserKeys();
1745 break;
1747 case (CTRL|'\\') :
1748 #if defined MOUSE && !defined(_WINDOWS)
1749 toggle_xterm_mouse(0,1);
1750 #else
1751 unknown_command(c);
1752 #endif
1753 break;
1755 case (CTRL|'Z'):
1756 if(gmode&MDSSPD){
1757 bktoshell(0, 1);
1758 PaintBrowser(gmp, 0, &crow, &ccol);
1759 break;
1760 } /* fall thru with error! */
1762 default: /* what? */
1763 Default:
1764 unknown_command(c);
1766 case NODATA: /* no op */
1767 break;
1774 * getfcells - make a master browser struct and fill it in
1775 * return NULL if there's a problem.
1777 struct bmaster *
1778 getfcells(char *dname, int fb_flags)
1780 int i, /* various return codes */
1781 flength,
1782 nentries = 0; /* number of dir ents */
1783 off_t attsz;
1784 char *np, /* names of files in dir */
1785 *dcp, /* to add file to path */
1786 *tmpstr,
1787 **filtnames, /* array filtered names */
1788 errbuf[NLINE];
1789 struct fcell *ncp, /* new cell pointer */
1790 *tcp; /* trailing cell ptr */
1791 struct bmaster *mp;
1792 EML eml;
1794 errbuf[0] = '\0';
1795 if((mp=(struct bmaster *)malloc(sizeof(struct bmaster))) == NULL){
1796 emlwrite("\007Can't malloc space for master filename cell", NULL);
1797 return(NULL);
1800 memset(mp, 0, sizeof(*mp));
1802 if(dname[0] == '.' && dname[1] == '\0'){ /* remember this dir */
1803 if(!getcwd(mp->dname, 256))
1804 mp->dname[0] = '\0';
1806 else if(dname[0] == '.' && dname[1] == '.' && dname[2] == '\0'){
1807 if(!getcwd(mp->dname, 256))
1808 mp->dname[0] = '\0';
1809 else{
1810 if((np = (char *)strrchr(mp->dname, C_FILESEP)) != NULL)
1811 if(np != mp->dname)
1812 *np = '\0';
1815 else{
1816 strncpy(mp->dname, dname, sizeof(mp->dname));
1817 mp->dname[sizeof(mp->dname)-1] = '\0';
1820 mp->bottom = mp->head = mp->top = NULL;
1821 mp->cpf = mp->fpl = 0;
1822 mp->longest = 5; /* .. must be labeled! */
1823 mp->flags = fb_flags;
1825 eml.s = mp->dname;
1826 emlwrite("Building file list of %s...", &eml);
1828 if((mp->names = getfnames(mp->dname, NULL, &nentries, errbuf, sizeof(errbuf))) == NULL){
1829 free((char *) mp);
1830 if(*errbuf)
1831 emlwrite(errbuf, NULL);
1833 return(NULL);
1837 * this is the fun part. build an array of pointers to the fnames we're
1838 * interested in (i.e., do any filtering), then pass that off to be
1839 * sorted before building list of cells...
1841 * right now default filtering on ".*" except "..", but this could
1842 * easily be made a user option later on...
1844 if((filtnames=(char **)malloc((nentries+1) * sizeof(char *))) == NULL){
1845 emlwrite("\007Can't malloc space for name array", NULL);
1846 zotmaster(&mp);
1847 return(NULL);
1850 i = 0; /* index of filt'd array */
1851 np = mp->names;
1852 while(nentries--){
1853 int ii;
1854 int width;
1857 * Filter dot files? Always filter ".", never filter "..",
1858 * and sometimes fitler ".*"...
1860 if(*np == '.' && (!(*(np+1) == '.' && *(np+2) == '\0')
1861 && !(*(np+1) == '\0' && (fb_flags&FB_SAVE)))
1862 && (*(np+1) == '\0' || !(gmode & MDDOTSOK))){
1863 np += strlen(np) + 1;
1864 continue;
1867 filtnames[i++] = np;
1869 ii = (int) strlen(np);
1870 if((width = (int) utf8_width(np)) > mp->longest)
1871 mp->longest = width; /* remember longest */
1873 np += ii + 1; /* advance name pointer */
1876 nentries = i; /* new # of entries */
1879 * sort files case independently
1881 qsort((qsort_t *)filtnames, (size_t)nentries, sizeof(char *), sstrcasecmp);
1884 * this is so we use absolute path names for stats.
1885 * remember: be careful using dname as directory name, and fix mp->dname
1886 * when we're done
1888 dcp = (char *) strchr(mp->dname, '\0');
1889 if(dcp == mp->dname || dcp[-1] != C_FILESEP){
1890 dcp[0] = C_FILESEP;
1891 dcp[1] = '\0';
1893 else
1894 dcp--;
1896 i = 0;
1897 while(nentries--){ /* stat filtered files */
1898 /* get a new cell */
1899 if((ncp=(struct fcell *)malloc(sizeof(struct fcell))) == NULL){
1900 emlwrite("\007Can't malloc cells for browser!", NULL);
1901 zotfcells(mp->head); /* clean up cells */
1902 free((char *) filtnames);
1903 free((char *) mp);
1904 return(NULL); /* bummer. */
1907 ncp->next = ncp->prev = NULL;
1909 if(mp->head == NULL){ /* tie it onto the list */
1910 mp->head = mp->top = mp->current = ncp;
1912 else{
1913 mp->bottom = ncp;
1914 tcp->next = ncp;
1915 ncp->prev = tcp;
1918 tcp = ncp;
1920 /* fill in the new cell */
1921 ncp->fname = filtnames[i++];
1923 /* fill in file's mode */
1924 if ((flength = strlen(ncp->fname) + 1 + strlen(dname)) < sizeof(mp->dname)){
1925 strncpy(&dcp[1], ncp->fname, sizeof(mp->dname)-(dcp+1-mp->dname)); /* use absolute path! */
1926 mp->dname[sizeof(mp->dname)-1] = '\0';
1927 tmpstr = mp->dname;
1929 else{
1930 if((tmpstr = (char *)malloc((flength+1)*sizeof(char))) == NULL){
1931 emlwrite("\007Can't malloc cells for temp buffer!", NULL);
1932 zotfcells(mp->head); /* clean up cells */
1933 free((char *) filtnames);
1934 free((char *) mp);
1935 return(NULL); /* bummer. */
1938 strncpy(tmpstr, dname, flength);
1939 tmpstr[flength] = '\0';
1940 tmpstr = strncat(tmpstr, S_FILESEP, flength+1-1-strlen(tmpstr));
1941 tmpstr[flength] = '\0';
1942 tmpstr = strncat(tmpstr, ncp->fname, flength+1-1-strlen(tmpstr));
1943 tmpstr[flength] = '\0';
1946 switch(fexist(tmpstr, "t", &attsz)){
1947 case FIODIR :
1948 ncp->mode = FIODIR;
1949 snprintf(ncp->size, sizeof(ncp->size), "(%s%s%s)",
1950 (ncp->fname[0] == '.' && ncp->fname[1] == '.'
1951 && ncp->fname[2] == '\0') ? PARENTDIR :
1952 ((fb_flags&FB_SAVE) && ncp->fname[0] == '.' && ncp->fname[1] == '\0'
1953 ? SELECTWORD : ""),
1954 (ncp->fname[0] == '.' && ncp->fname[1] == '.'
1955 && ncp->fname[2] == '\0') ? " " :
1956 ((fb_flags&FB_SAVE) && ncp->fname[0] == '.' && ncp->fname[1] == '\0'
1957 ? " " : ""),
1958 DIRWORD);
1959 break;
1961 case FIOSYM :
1962 ncp->mode = FIOSYM;
1963 strncpy(ncp->size, "--", sizeof(ncp->size));
1964 ncp->size[sizeof(ncp->size)-1] = '\0';
1965 break;
1967 default :
1968 ncp->mode = FIOSUC; /* regular file */
1969 strncpy(ncp->size, prettysz(attsz), sizeof(ncp->size));
1970 ncp->size[sizeof(ncp->size)-1] = '\0';
1971 break;
1974 if (flength >= NLINE)
1975 free((char *) tmpstr);
1978 dcp[(dcp == mp->dname) ? 1 : 0] = '\0'; /* remember to cap dname */
1979 free((char *) filtnames); /* 'n blast filt'd array*/
1981 percdircells(mp);
1982 layoutcells(mp);
1983 if(strlen(mp->dname) < sizeof(browse_dir)){
1984 strncpy(browse_dir, mp->dname, sizeof(browse_dir));
1985 browse_dir[sizeof(browse_dir)-1] = '\0';
1987 else
1988 browse_dir[0] = '\0';
1990 return(mp);
1995 fcell_is_selected(struct fcell *cell, struct bmaster *mp)
1997 LMLIST *lm;
1999 if(cell && cell->fname){
2000 for(lm = mp ? mp->lm : NULL; lm; lm = lm->next){
2001 /* directory has to match */
2002 if(!((mp->dname[0] == '\0' && (!lm->dir || lm->dir[0] =='\0'))
2003 || (mp->dname[0] != '\0' && lm->dir && lm->dir[0] !='\0'
2004 && !strcmp(mp->dname, lm->dir))))
2005 continue;
2007 if(lm->fname && !strcmp(cell->fname, lm->fname))
2008 return(1);
2012 return(0);
2017 * Adds a new name to the head of the lmlist
2019 void
2020 add_cell_to_lmlist(struct fcell *cell, struct bmaster *mp)
2022 LMLIST *new;
2023 size_t flen, dlen;
2025 if(mp && cell && cell->fname && cell->fname[0]){
2026 if((new=(LMLIST *)malloc(sizeof(*new))) == NULL ||
2027 (new->fname=malloc(sizeof(char)*((flen=strlen(cell->fname))+1))) == NULL ||
2028 (new->dir=malloc(sizeof(char)*((dlen=strlen(mp->dname))+1))) == NULL){
2029 emlwrite("\007Can't malloc space for filename", NULL);
2030 return;
2033 strncpy(new->fname, cell->fname, flen);
2034 new->fname[flen] = '\0';
2035 strncpy(new->dir, mp->dname, dlen);
2036 new->dir[dlen] = '\0';
2037 new->size[0] = '\0';
2038 if(cell->size[0]){
2039 strncpy(new->size, cell->size, sizeof(new->size));
2040 new->size[sizeof(new->size)-1] = '\0';
2043 new->next = mp->lm;
2044 mp->lm = new;
2050 * Deletes a name from the lmlist
2052 void
2053 del_cell_from_lmlist(struct fcell *cell, struct bmaster *mp)
2055 LMLIST *lm, *lmprev = NULL;
2057 if(mp && cell && cell->fname && cell->fname[0])
2058 for(lm = mp ? mp->lm : NULL; lm; lm = lm->next){
2059 if(lm->fname && strcmp(cell->fname, lm->fname) == 0){
2060 free((char *) lm->fname);
2061 if(lm->dir)
2062 free((char *) lm->dir);
2064 if(lmprev)
2065 lmprev->next = lm->next;
2066 else
2067 mp->lm = lm->next;
2069 free((char *) lm);
2071 break;
2074 lmprev = lm;
2080 * PaintCell - print the given cell at the given location on the display
2081 * the format of a printed cell is:
2083 * "<fname> <size> "
2085 void
2086 PaintCell(int row, int col,
2087 int sc, /* screen columns available for this cell */
2088 struct fcell *cell, int inverted)
2090 char buf1[NLINE], buf2[NLINE];
2091 char lbuf[5];
2092 int need, l_wid, f_wid, f_to_s_wid, s_wid;
2093 UCS *ucs;
2095 if(cell == NULL)
2096 return;
2098 l_wid = (gmp && ((gmp->flags & FB_LMODEPOS) > 4)) ? 4 : 0;
2099 f_wid = utf8_width(cell->fname ? cell->fname : "");
2100 f_to_s_wid = 1;
2101 s_wid = utf8_width(cell->size ? cell->size : "");
2103 /* the two is the space between cell columns */
2104 sc = MIN(sc-2, term.t_ncol-col);
2106 if(l_wid){
2107 if(gmp && gmp->flags & FB_LMODE && cell->mode != FIODIR){
2109 * We have to figure out here if it is selected or not
2110 * and use that to write the X or space.
2112 lbuf[0] = '[';
2113 if(fcell_is_selected(cell, gmp))
2114 lbuf[1] = 'X';
2115 else
2116 lbuf[1] = ' ';
2118 lbuf[2] = ']';
2119 lbuf[3] = ' ';
2121 else{
2122 lbuf[0] = lbuf[1] = lbuf[2] = lbuf[3] = ' ';
2125 lbuf[4] = '\0';
2128 sc -= l_wid;
2130 need = f_wid+f_to_s_wid+s_wid;
2132 /* space between name and size */
2133 if(need < sc)
2134 f_to_s_wid += (sc-need);
2137 * If the width isn't enough to handle everything we're just putting a single
2138 * space between fname and size and truncating on the right. That means that
2139 * the sizes in the right hand column won't line up correctly when there is
2140 * a lack of space. Instead, we opt for displaying as much info as possible
2141 * in a slightly discordant way.
2144 movecursor(row, col);
2145 if(l_wid){
2146 ucs = utf8_to_ucs4_cpystr(lbuf);
2147 if(ucs){
2148 pputs(ucs, 0);
2149 fs_give((void **) &ucs);
2153 utf8_snprintf(buf1, sizeof(buf1), "%*.*w%*.*w%*.*w",
2154 f_wid, f_wid, cell->fname ? cell->fname : "",
2155 f_to_s_wid, f_to_s_wid, "",
2156 s_wid, s_wid, cell->size ? cell->size : "");
2158 utf8_snprintf(buf2, sizeof(buf2), "%*.*w", sc, sc, buf1);
2160 if(inverted)
2161 (*term.t_rev)(1);
2163 ucs = utf8_to_ucs4_cpystr(buf2);
2164 if(ucs){
2165 pputs(ucs, 0);
2166 fs_give((void **) &ucs);
2169 if(inverted)
2170 (*term.t_rev)(0);
2175 * PaintBrowse - with the current data, display the browser. if level == 0
2176 * paint the whole thing, if level == 1 just paint the cells
2177 * themselves
2179 void
2180 PaintBrowser(struct bmaster *mp, int level, int *row, int *col)
2182 int i, cl;
2183 struct fcell *tp;
2185 if(!level){
2186 ClearBrowserScreen();
2187 BrowserAnchor(mp->dname);
2190 i = 0;
2191 tp = mp->top;
2192 cl = COMPOSER_TOP_LINE; /* current display line */
2193 while(tp){
2195 PaintCell(cl, mp->cpf * i, mp->cpf, tp, tp == mp->current);
2197 if(tp == mp->current){
2198 if(row)
2199 *row = cl;
2201 if(col)
2202 *col = mp->cpf * i;
2205 if(++i >= mp->fpl){
2206 i = 0;
2207 if(++cl > term.t_nrow-(term.t_mrow+1))
2208 break;
2211 tp = tp->next;
2214 if(level){
2215 while(cl <= term.t_nrow - (term.t_mrow+1)){
2216 if(!i)
2217 movecursor(cl, 0);
2218 peeol();
2219 movecursor(++cl, 0);
2222 else{
2223 BrowserKeys();
2229 * BrowserKeys - just paints the keyhelp at the bottom of the display
2231 void
2232 BrowserKeys(void)
2234 menu_browse[QUIT_KEY].name = (gmode&MDBRONLY) ? "Q" : "E";
2235 /* TRANSLATORS: Brwsr is an abbreviation for Browser, which is
2236 a command used to look through something */
2237 menu_browse[QUIT_KEY].label = (gmode&MDBRONLY) ? N_("Quit") : N_("Exit Brwsr");
2238 menu_browse[GOTO_KEY].name = (gmode&MDGOTO) ? "G" : NULL;
2239 menu_browse[GOTO_KEY].label = (gmode&MDGOTO) ? N_("Goto") : NULL;
2240 if(gmode & MDBRONLY){
2241 menu_browse[EXEC_KEY].name = "L";
2242 menu_browse[EXEC_KEY].label = N_("Launch");
2243 menu_browse[SELECT_KEY].name = "V";
2244 menu_browse[SELECT_KEY].label = "[" N_("View") "]";
2245 menu_browse[PICO_KEY].name = "E";
2246 menu_browse[PICO_KEY].label = N_("Edit");
2248 else{
2249 menu_browse[SELECT_KEY].name = "S";
2250 menu_browse[SELECT_KEY].label = "[" N_("Select") "]";
2251 menu_browse[PICO_KEY].name = "A";
2252 menu_browse[PICO_KEY].label = N_("Add");
2254 if(gmp && gmp->flags & FB_LMODEPOS){
2255 if(gmp && gmp->flags & FB_LMODE){ /* ListMode is already on */
2256 menu_browse[EXEC_KEY].name = "X";
2257 menu_browse[EXEC_KEY].label = N_("Set/Unset");
2259 else{ /* ListMode is possible */
2260 menu_browse[EXEC_KEY].name = "L";
2261 menu_browse[EXEC_KEY].label = N_("ListMode");
2264 else{ /* No ListMode possible */
2265 menu_browse[EXEC_KEY].name = NULL;
2266 menu_browse[EXEC_KEY].label = NULL;
2270 wkeyhelp(menu_browse);
2275 * layoutcells - figure out max length of cell and how many cells can
2276 * go on a line of the display
2278 void
2279 layoutcells(struct bmaster *mp)
2281 static int wid = -1;
2283 * Max chars/file. Actually this is max screen cells/file
2284 * Longest name + "(parent dir)"
2286 if(wid < 0)
2287 wid = MAX(utf8_width(PARENTDIR),utf8_width(SELECTWORD)) + utf8_width(DIRWORD);
2289 mp->cpf = mp->longest + wid + 3;
2291 if(mp->flags & FB_LMODEPOS) /* "[X] " */
2292 mp->cpf += 4;
2294 if(gmode & MDONECOL){
2295 mp->fpl = 1;
2297 else{
2298 int i = 1;
2300 while(i*mp->cpf - 2 <= term.t_ncol) /* no force... */
2301 i++; /* like brute force! */
2303 mp->fpl = i - 1; /* files per line */
2306 if(mp->fpl == 0)
2307 mp->fpl = 1;
2312 * percdircells - bubble all the directory cells to the top of the
2313 * list.
2315 void
2316 percdircells(struct bmaster *mp)
2318 struct fcell *dirlp, /* dir cell list pointer */
2319 *lp, *nlp; /* cell list ptr and next */
2321 dirlp = NULL;
2322 for(lp = mp->head; lp; lp = nlp){
2323 nlp = lp->next;
2324 if(lp->mode == FIODIR){
2325 if(lp->prev) /* clip from list */
2326 lp->prev->next = lp->next;
2328 if(lp->next)
2329 lp->next->prev = lp->prev;
2331 if((lp->prev = dirlp) != NULL){ /* tie it into dir portion */
2332 if((lp->next = dirlp->next) != NULL)
2333 lp->next->prev = lp;
2335 dirlp->next = lp;
2336 dirlp = lp;
2338 else{
2339 if((dirlp = lp) != mp->head)
2340 dirlp->next = mp->head;
2342 if(dirlp->next)
2343 dirlp->next->prev = dirlp;
2345 mp->head = mp->top = mp->current = dirlp;
2353 * PlaceCell - given a browser master and a cell, return row and col of the display that
2354 * it should go on.
2356 * return 1 if mp->top has changed, x,y relative to new page
2357 * return 0 if otherwise (same page)
2358 * return -1 on error
2361 PlaceCell(struct bmaster *mp, struct fcell *cp, int *x, int *y)
2363 int cl = COMPOSER_TOP_LINE; /* current line */
2364 int ci = 0; /* current index on line */
2365 int rv = 0;
2366 int secondtry = 0;
2367 struct fcell *tp;
2369 /* will cp fit on screen? */
2370 tp = mp->top;
2371 while(1){
2372 if(tp == cp){ /* bingo! */
2373 *x = cl;
2374 *y = ci * mp->cpf;
2375 break;
2378 if((tp = tp->next) == NULL){ /* above top? */
2379 if(secondtry++){
2380 emlwrite("\007Internal error: can't find fname cell", NULL);
2381 return(-1);
2383 else{
2384 tp = mp->top = mp->head; /* try from the top! */
2385 cl = COMPOSER_TOP_LINE;
2386 ci = 0;
2387 rv = 1;
2388 continue; /* start over! */
2392 if(++ci >= mp->fpl){ /* next line? */
2393 ci = 0;
2394 if(++cl > term.t_nrow-(term.t_mrow+1)){ /* next page? */
2395 ci = mp->fpl; /* tp is at bottom right */
2396 while(ci--) /* find new top */
2397 tp = tp->prev;
2398 mp->top = tp;
2399 ci = 0;
2400 cl = COMPOSER_TOP_LINE; /* keep checking */
2401 rv = 1;
2407 /* not on display! */
2408 return(rv);
2413 * zotfcells - clean up malloc'd cells of file names
2415 void
2416 zotfcells(struct fcell *hp)
2418 struct fcell *tp;
2420 while(hp){
2421 tp = hp;
2422 hp = hp->next;
2423 tp->next = NULL;
2424 free((char *) tp);
2430 * zotmaster - blast the browser master struct
2432 void
2433 zotmaster(struct bmaster **mp)
2435 if(mp && *mp){
2436 zotfcells((*mp)->head); /* free cells */
2437 zotlmlist((*mp)->lm); /* free lmlist */
2438 if((*mp)->names)
2439 free((char *)(*mp)->names); /* free file names */
2441 free((char *)*mp); /* free master */
2442 *mp = NULL; /* make double sure */
2448 * FindCell - starting from the current cell find the first occurance of
2449 * the given string wrapping around if necessary
2451 struct fcell *
2452 FindCell(struct bmaster *mp, char *utf8string, int bsearch)
2454 struct fcell *tp, *fp;
2456 if(*utf8string == '\0')
2457 return(NULL);
2459 fp = NULL;
2460 tp = bsearch ? mp->current->prev : mp->current->next;
2462 while(tp && !fp){
2463 if(sisin(tp->fname, utf8string))
2464 fp = tp;
2465 else
2466 tp = bsearch ? tp->prev : tp->next;
2469 tp = bsearch ? mp->bottom : mp->head;
2470 while(tp != mp->current && !fp){
2471 if(sisin(tp->fname, utf8string))
2472 fp = tp;
2473 else
2474 tp = bsearch ? tp->prev : tp->next;
2477 return(fp);
2482 * sisin - case insensitive substring matching function
2484 * We can't really do case-insensitive for non-ascii, so restrict
2485 * that to ascii characters. The strings will be utf8.
2488 sisin(char *bigstr, char *utf8substr)
2490 register int j;
2492 while(*bigstr){
2493 j = 0;
2494 while(bigstr[j] == utf8substr[j]
2495 || ((unsigned char)bigstr[j] < 0x80 && (unsigned char)utf8substr[j] < 0x80
2496 && toupper((unsigned char)bigstr[j]) == toupper((unsigned char)utf8substr[j])))
2497 if(utf8substr[++j] == '\0') /* bingo! */
2498 return(1);
2500 bigstr++;
2503 return(0);
2508 * set_browser_title -
2510 void
2511 set_browser_title(char *s)
2513 browser_title = s;
2518 * BrowserAnchor - draw the browser's anchor line.
2520 void
2521 BrowserAnchor(char *utf8dir)
2523 char *pdir;
2524 char titlebuf[NLINE];
2525 char buf[NLINE];
2526 char dirbuf[NLINE];
2527 char *dots = "...";
2528 char *br = "BROWSER";
2529 char *dir = "Dir: ";
2530 UCS *ucs;
2531 int need, extra, avail;
2532 int title_wid, b_wid, t_to_b_wid, b_to_d_wid, d_wid, dot_wid, dir_wid, after_dir_wid;
2533 COLOR_PAIR *lastc = NULL;
2535 if(!utf8dir)
2536 utf8dir = "";
2538 pdir = utf8dir;
2540 if(browser_title)
2541 snprintf(titlebuf, sizeof(buf), " %s", browser_title);
2542 else if(Pmaster)
2543 snprintf(titlebuf, sizeof(buf), " ALPINE %s", Pmaster->pine_version);
2544 else
2545 snprintf(titlebuf, sizeof(buf), " UW PICO %s", (gmode&MDBRONLY) ? "BROWSER" : version);
2547 title_wid = utf8_width(titlebuf);
2548 t_to_b_wid = 15;
2549 b_wid = utf8_width(br);
2550 b_to_d_wid = 4;
2551 d_wid = utf8_width(dir);
2552 dot_wid = 0;
2553 dir_wid = utf8_width(pdir);
2555 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2557 if(need > term.t_ncol){
2558 extra = need - term.t_ncol;
2559 t_to_b_wid -= MIN(extra, 10);
2560 need -= MIN(extra, 10);
2562 if(need > term.t_ncol){
2563 extra = need - term.t_ncol;
2564 b_to_d_wid -= MIN(extra, 2);
2565 need -= MIN(extra, 2);
2568 if(need > term.t_ncol){
2569 titlebuf[0] = titlebuf[1] = ' ';
2570 titlebuf[2] = '\0';
2571 title_wid = utf8_width(titlebuf);
2572 t_to_b_wid = 0;
2573 b_to_d_wid = 4;
2574 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2575 if(need > term.t_ncol){
2576 extra = need - term.t_ncol;
2577 b_to_d_wid -= MIN(extra, 2);
2578 need -= MIN(extra, 2);
2581 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2582 if(need > term.t_ncol){
2583 t_to_b_wid = 0;
2584 b_wid = 0;
2585 b_to_d_wid = 0;
2588 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2589 if(need > term.t_ncol)
2590 d_wid = 0;
2592 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2594 if(need > term.t_ncol && dir_wid > 0){
2595 dot_wid = utf8_width(dots);
2596 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2597 while(need > term.t_ncol && (pdir = strchr(pdir+1, C_FILESEP))){
2598 dir_wid = utf8_width(pdir);
2599 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2602 if(!pdir){ /* adjust other widths to fill up space */
2603 avail = term.t_ncol - (title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid);
2604 if(avail > 2)
2605 utf8_to_width_rhs(dirbuf, utf8dir, sizeof(dirbuf), avail);
2606 else
2607 dirbuf[0] = '\0';
2609 pdir = dirbuf;
2612 dir_wid = utf8_width(pdir);
2617 extra = term.t_ncol - (title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid);
2619 if(extra >= 0)
2620 after_dir_wid = extra;
2621 else
2622 after_dir_wid = 0;
2624 utf8_snprintf(buf, sizeof(buf), "%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w",
2625 title_wid, title_wid, titlebuf,
2626 t_to_b_wid, t_to_b_wid, "",
2627 b_wid, b_wid, br,
2628 b_to_d_wid, b_to_d_wid, "",
2629 d_wid, d_wid, dir,
2630 dot_wid, dot_wid, dots,
2631 dir_wid, dir_wid, pdir,
2632 after_dir_wid, after_dir_wid, "");
2634 /* just making sure */
2635 utf8_snprintf(titlebuf, sizeof(titlebuf), "%-*.*w", term.t_ncol, term.t_ncol, buf);
2637 movecursor(0, 0);
2638 if(Pmaster && Pmaster->colors && Pmaster->colors->tbcp
2639 && pico_is_good_colorpair(Pmaster->colors->tbcp)){
2640 lastc = pico_get_cur_color();
2641 (void) pico_set_colorp(Pmaster->colors->tbcp, PSC_NONE);
2643 else
2644 (*term.t_rev)(1);
2646 ucs = utf8_to_ucs4_cpystr(titlebuf);
2647 if(ucs){
2648 pputs(ucs, 0);
2649 fs_give((void **) &ucs);
2652 if(lastc){
2653 (void) pico_set_colorp(lastc, PSC_NONE);
2654 free_color_pair(&lastc);
2656 else
2657 (*term.t_rev)(0);
2662 * ResizeBrowser - handle a resize event
2665 ResizeBrowser(void)
2667 if(gmp){
2668 layoutcells(gmp);
2669 PaintBrowser(gmp, 0, NULL, NULL);
2670 return(1);
2672 else
2673 return(0);
2677 void
2678 ClearBrowserScreen(void)
2680 int i;
2682 for(i = 0; i <= term.t_nrow; i++){ /* clear screen */
2683 movecursor(i, 0);
2684 peeol();
2689 void
2690 BrowserRunChild(char *child, char *dir)
2692 int status;
2693 char tmp[NLINE];
2694 time_t t_in, t_out;
2696 ClearBrowserScreen();
2697 movecursor(0, 0);
2698 (*term.t_close)();
2699 if(!isdir(dir, NULL, &t_in))
2700 t_in = 0;
2702 fflush(stdout);
2703 status = system(child);
2704 (*term.t_open)();
2705 if(t_in && isdir(dir, NULL, &t_out) && t_in < t_out){
2706 struct bmaster *mp;
2708 if((mp = getfcells(dir, 0)) != NULL){
2709 zotmaster(&gmp);
2710 gmp = mp;
2712 /* else getfcells should explain what happened */
2715 /* complain about non-zero exit status */
2716 if((status >> 8) & 0xff){
2718 movecursor(term.t_nrow - 1, 0);
2719 snprintf(tmp, sizeof(tmp), "[ \"%.30s\" exit with error value: %d ]",
2720 child, (status >> 8) & 0xff);
2721 pputs_utf8(tmp, 1);
2722 movecursor(term.t_nrow, 0);
2723 pputs_utf8("[ Hit RETURN to continue ]", 1);
2724 fflush(stdout);
2726 while(GetKey() != (CTRL|'M')){
2727 (*term.t_beep)();
2728 fflush(stdout);
2735 * imitate pc-pine memory for where we last called the file browser.
2737 void
2738 p_chdir(struct bmaster *mp)
2740 if(mp && mp->dname)
2741 chdir(mp->dname);
2743 #else /* _WINDOWS */
2747 * pico_file_browse - Exported version of FileBrowse below.
2750 pico_file_browse(PICO *pdata, char *dir, size_t dirlen, char *fn, size_t fnlen,
2751 char *sz, size_t szlen, int flags)
2753 return(FileBrowse(dir, dirlen, fn, fnlen, sz, szlen, flags, NULL));
2757 ResizeBrowser (void)
2759 return (0);
2763 * FileBrowse - Windows version of above function
2765 int
2766 FileBrowse(char *dir, size_t dirlen, char *fn, size_t fnlen,
2767 char *sz, size_t szlen, int fb_flags, LMLIST **lmreturn)
2769 struct stat sbuf;
2770 int rc;
2771 char lfn[NLINE];
2773 if (fb_flags & FB_SAVE){
2774 rc = mswin_savefile(dir, dirlen, fn, MIN(dirlen,fnlen));
2776 else{
2777 *fn = '\0'; /* No initial file names for
2778 * open. */
2779 if(fb_flags & FB_LMODEPOS){
2781 * We're going to allow multiple filenames to be returned so
2782 * we don't want to use the passed in fn for that. Instead, make
2783 * a bigger space and use that.
2785 char f[20000];
2786 size_t flen, dlen;
2788 memset(f, 0, sizeof(f));
2790 rc = mswin_multopenfile(dir, dirlen, f, sizeof(f),
2791 (fb_flags & FB_ATTACH) ? NULL : "Text Files (*.txt)#*.txt");
2793 if(rc == 1){
2794 LMLIST *lmhead = NULL, *new;
2795 char *p;
2798 * Build an LMLIST to return to the caller.
2800 for(p = f; *p; p += strlen(p)+1){
2801 flen = strlen(p);
2802 dlen = strlen(dir ? dir : "");
2803 new = (LMLIST *) fs_get(sizeof(*new));
2804 new->fname = (char *) fs_get((flen+1) * sizeof(char));
2805 new->dir = (char *) fs_get((dlen+1) * sizeof(char));
2807 strncpy(new->fname, p, flen);
2808 new->fname[flen] = '\0';
2809 strncpy(new->dir, dir ? dir : "", dlen);
2810 new->dir[dlen] = '\0';
2812 /* build full path to stat file. */
2813 if((strlen(new->dir) + strlen(S_FILESEP) +
2814 strlen(new->fname) + 1) < sizeof(lfn)){
2815 strncpy(lfn, new->dir, sizeof(lfn));
2816 lfn[sizeof(lfn)-1] = '\0';
2817 strncat(lfn, S_FILESEP, sizeof(lfn)-strlen(lfn)-1);
2818 strncat(lfn, new->fname, sizeof(lfn)-strlen(lfn)-1);
2819 lfn[sizeof(lfn)-1] = '\0';
2820 if(our_stat(lfn, &sbuf) < 0)
2821 strncpy(new->size, "0", 32);
2822 else
2823 strncpy(new->size, prettysz((off_t)sbuf.st_size), 32);
2825 new->size[32-1] = '\0';
2828 new->next = lmhead;
2829 lmhead = new;
2832 *lmreturn = lmhead;
2833 return(1);
2836 else
2837 rc = mswin_openfile (dir, dirlen, fn, fnlen,
2838 (fb_flags & FB_ATTACH) ? NULL : "Text Files (*.txt)#*.txt");
2841 if(rc == 1){
2842 if(sz != NULL){
2843 /* build full path to stat file. */
2844 if((strlen(dir) + strlen(S_FILESEP) + strlen(fn) + 1) > NLINE)
2845 return -1;
2847 strncpy(lfn, dir, sizeof(lfn));
2848 lfn[sizeof(lfn)-1] = '\0';
2849 strncat(lfn, S_FILESEP, sizeof(lfn)-strlen(lfn)-1);
2850 lfn[sizeof(lfn)-1] = '\0';
2851 strncat(lfn, fn, sizeof(lfn)-strlen(lfn)-1);
2852 lfn[sizeof(lfn)-1] = '\0';
2853 if(our_stat(lfn, &sbuf) < 0){
2854 strncpy(sz, "0", szlen);
2855 sz[szlen-1] = '\0';
2857 else{
2858 strncpy(sz, prettysz ((off_t)sbuf.st_size), szlen);
2859 sz[szlen-1] = '\0';
2863 return(1);
2866 return(rc ? -1 : 0);
2869 #endif /* _WINDOWS */
2873 * LikelyASCII - make a rough guess as to the displayability of the
2874 * given file.
2877 LikelyASCII(char *file)
2879 #define LA_TEST_BUF 1024
2880 #define LA_LINE_LIMIT 300
2881 int n, i, line, rv = FALSE;
2882 unsigned char buf[LA_TEST_BUF];
2883 FILE *fp;
2884 EML eml;
2886 if((fp = our_fopen(file, "rb")) != NULL){
2887 clearerr(fp);
2888 if((n = fread(buf, sizeof(char), LA_TEST_BUF * sizeof(char), fp)) > 0
2889 || !ferror(fp)){
2891 * If we don't hit any newlines in a reasonable number,
2892 * LA_LINE_LIMIT, of characters or the file contains NULLs,
2893 * bag out...
2895 rv = TRUE;
2896 for(i = line = 0; i < n; i++)
2897 if((line = (buf[i] == '\n') ? 0 : line + 1) >= LA_LINE_LIMIT
2898 || !buf[i]){
2899 rv = FALSE;
2900 emlwrite(_("Can't display non-text file. Try \"Launch\"."),
2901 NULL);
2902 break;
2905 else{
2906 eml.s = file;
2907 emlwrite(_("Can't read file: %s"), &eml);
2910 fclose(fp);
2912 else{
2913 eml.s = file;
2914 emlwrite(_("Can't open file: %s"), &eml);
2917 return(rv);
2921 void
2922 zotlmlist(LMLIST *lm)
2924 LMLIST *tp;
2926 while(lm){
2927 if(lm->fname)
2928 free(lm->fname);
2930 if(lm->dir)
2931 free(lm->dir);
2933 tp = lm;
2934 lm = lm->next;
2935 tp->next = NULL;
2936 free((char *) tp);
2942 * time_to_check - checks the current time against the last time called
2943 * and returns true if the elapsed time is > below.
2944 * Newmail won't necessarily check, but we want to give it
2945 * a chance to check or do a keepalive.
2948 time_to_check(void)
2950 static time_t lasttime = 0L;
2952 if(!get_input_timeout())
2953 return(FALSE);
2955 if(time((time_t *) 0) - lasttime > (Pmaster ? (time_t)(FUDGE-10) : get_input_timeout())){
2956 lasttime = time((time_t *) 0);
2957 return(TRUE);
2959 else
2960 return(FALSE);