* new version 2.20.5
[alpine.git] / pico / browse.c
blob87f1ececff91668d2015dcc1b979df3b6bfb5178
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-2015 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 char *p, *envp, child[NLINE], tmp[NLINE];
322 struct bmaster *mp;
323 struct fcell *tp;
324 EML eml;
325 #ifdef MOUSE
326 MOUSEPRESS mousep;
327 #endif
329 child[0] = '\0';
331 if((gmode&MDTREE) && !in_oper_tree(dir)){
332 eml.s = opertree;
333 emlwrite(_("\007Can't read outside of %s in restricted mode"), &eml);
334 sleep(2);
335 return(0);
338 if(gmode&MDGOTO){
339 /* fix up function key mapping table */
340 /* fix up key menu labels */
343 /* build contents of cell structures */
344 if((gmp = getfcells(dir, fb_flags)) == NULL)
345 return(-1);
347 tp = NULL;
348 if(fn && *fn){
349 if((tp = FindCell(gmp, fn, 0)) != NULL){
350 gmp->current = tp;
351 PlaceCell(gmp, gmp->current, &row, &col);
355 /* paint screen */
356 PaintBrowser(gmp, 0, &crow, &ccol);
358 while(1){ /* the big loop */
359 if(!(gmode&MDSHOCUR)){
360 crow = term.t_nrow-term.t_mrow;
361 ccol = 0;
364 if(!(gmode&MDSHOCUR))
365 movecursor(crow, ccol);
366 else if(gmp->flags & FB_LMODEPOS){
367 if(gmp->flags & FB_LMODE && gmp->current->mode != FIODIR)
368 movecursor(crow, ccol+1);
369 else
370 movecursor(crow, ccol+4);
372 else
373 movecursor(crow, ccol);
375 if(km_popped){
376 km_popped--;
377 if(km_popped == 0)
378 /* cause bottom three lines to repaint */
379 PaintBrowser(gmp, 0, &crow, &ccol);
382 if(km_popped){ /* temporarily change to cause menu to paint */
383 term.t_mrow = 2;
384 movecursor(term.t_nrow-2, 0); /* clear status line */
385 peeol();
386 BrowserKeys();
387 term.t_mrow = 0;
390 (*term.t_flush)();
392 #ifdef MOUSE
393 mouse_in_content(KEY_MOUSE, -1, -1, 0, 0);
394 register_mfunc(mouse_in_content,2,0,term.t_nrow-(term.t_mrow+1),
395 term.t_ncol);
396 #endif
397 c = GetKey();
398 #ifdef MOUSE
399 clear_mfunc(mouse_in_content);
400 #endif
402 if(Pmaster){
403 if(Pmaster->newmail && (c == NODATA || time_to_check())){
404 if((*Pmaster->newmail)(c == NODATA ? 0 : 2, 1) >= 0){
405 int rv;
407 if(km_popped){ /* restore display */
408 km_popped = 0;
409 PaintBrowser(gmp, 0, &crow, &ccol);
412 clearcursor();
413 mlerase();
414 rv = (*Pmaster->showmsg)(c);
415 ttresize();
416 picosigs();
417 if(rv) /* Did showmsg corrupt the display? */
418 PaintBrowser(gmp, 0, &crow, &ccol); /* Yes, repaint */
420 mpresf = 1;
423 clearcursor();
424 if(gmp->flags & FB_LMODEPOS){
425 if(gmp->flags & FB_LMODE && gmp->current->mode != FIODIR)
426 movecursor(crow, ccol+1);
427 else
428 movecursor(crow, ccol+4);
430 else
431 movecursor(crow, ccol);
434 else{
435 if(get_input_timeout() && (c == NODATA || time_to_check()))
436 if(pico_new_mail())
437 emlwrite(_("You may possibly have new mail."), NULL);
440 if(km_popped)
441 switch(c){
442 case NODATA:
443 case (CTRL|'L'):
444 km_popped++;
445 break;
447 default:
448 /* clear bottom three lines */
449 movecursor(term.t_nrow-2, 0);
450 peeol();
451 movecursor(term.t_nrow-1, 0);
452 peeol();
453 movecursor(term.t_nrow, 0);
454 peeol();
455 break;
458 if(c == NODATA) /* GetKey timed out */
459 continue;
460 else if(Pmaster)
461 (*Pmaster->keybinput)();
464 if(mpresf){ /* blast old messages */
465 if(mpresf++ > MESSDELAY){ /* every few keystrokes */
466 mlerase();
470 /* process commands */
471 switch(new_c = normalize_cmd(c,bfmappings[(gmode&MDBRONLY)?0:1],2)){
473 case KEY_RIGHT: /* move right */
474 case (CTRL|'@'):
475 case (CTRL|'F'): /* forward */
476 case 'n' :
477 case 'N' :
478 if(gmp->current->next == NULL){
479 (*term.t_beep)();
480 break;
483 PlaceCell(gmp, gmp->current, &row, &col);
484 PaintCell(row, col, gmp->cpf, gmp->current, 0);
485 gmp->current = gmp->current->next;
486 if(PlaceCell(gmp, gmp->current, &row, &col)){
487 PaintBrowser(gmp, 1, &crow, &ccol);
489 else{
490 PaintCell(row, col, gmp->cpf, gmp->current, 1);
491 crow = row;
492 ccol = col;
494 break;
496 case KEY_LEFT: /* move left */
497 case (CTRL|'B'): /* back */
498 case 'p' :
499 case 'P' :
500 if(gmp->current->prev == NULL){
501 (*term.t_beep)();
502 break;
505 PlaceCell(gmp, gmp->current, &row, &col);
506 PaintCell(row, col, gmp->cpf, gmp->current, 0);
507 gmp->current = gmp->current->prev;
508 if(PlaceCell(gmp, gmp->current, &row, &col)){
509 PaintBrowser(gmp, 1, &crow, &ccol);
511 else{
512 PaintCell(row, col, gmp->cpf, gmp->current, 1);
513 crow = row;
514 ccol = col;
516 break;
518 case (CTRL|'A'): /* beginning of line */
519 tp = gmp->current;
520 i = col;
521 while(i > 0){
522 i -= gmp->cpf;
523 if(tp->prev != NULL)
524 tp = tp->prev;
526 PlaceCell(gmp, gmp->current, &row, &col);
527 PaintCell(row, col, gmp->cpf, gmp->current, 0);
528 gmp->current = tp;
529 if(PlaceCell(gmp, tp, &row, &col)){
530 PaintBrowser(gmp, 1, &crow, &ccol);
532 else{
533 PaintCell(row, col, gmp->cpf, gmp->current, 1);
534 crow = row;
535 ccol = col;
537 break;
539 case (CTRL|'E'): /* end of line */
540 tp = gmp->current;
541 i = col + gmp->cpf;
542 while(i+gmp->cpf <= gmp->cpf * gmp->fpl){
543 i += gmp->cpf;
544 if(tp->next != NULL)
545 tp = tp->next;
548 PlaceCell(gmp, gmp->current, &row, &col);
549 PaintCell(row, col, gmp->cpf, gmp->current, 0);
550 gmp->current = tp;
551 if(PlaceCell(gmp, tp, &row, &col)){
552 PaintBrowser(gmp, 1, &crow, &ccol);
554 else{
555 PaintCell(row, col, gmp->cpf, gmp->current, 1);
556 crow = row;
557 ccol = col;
559 break;
561 case (CTRL|'V'): /* page forward */
562 case ' ':
563 case KEY_PGDN :
564 case KEY_END :
565 tp = gmp->top;
566 i = term.t_nrow - term.t_mrow - 2;
568 while(i-- && tp->next != NULL){
569 j = 0;
570 while(++j <= gmp->fpl && tp->next != NULL)
571 tp = tp->next;
574 if(tp == NULL)
575 continue;
577 PlaceCell(gmp, gmp->current, &row, &col);
578 PaintCell(row, col, gmp->cpf, gmp->current, 0);
579 gmp->current = tp;
580 if(PlaceCell(gmp, tp, &row, &col)){
581 PaintBrowser(gmp, 1, &crow, &ccol);
583 else{
584 PaintCell(row, col, gmp->cpf, gmp->current, 1);
585 crow = row;
586 ccol = col;
588 break;
590 case '-' :
591 case (CTRL|'Y'): /* page backward */
592 case KEY_PGUP :
593 case KEY_HOME :
594 tp = gmp->top;
595 i = term.t_nrow - term.t_mrow - 4;
596 while(i-- && tp != NULL){
597 j = gmp->fpl;
598 while(j-- && tp != NULL)
599 tp = tp->prev;
602 if(tp || (gmp->current != gmp->top)){ /* clear old hilite */
603 PlaceCell(gmp, gmp->current, &row, &col);
604 PaintCell(row, col, gmp->cpf, gmp->current, 0);
607 if(tp) /* new page ! */
608 gmp->current = tp;
609 else if(gmp->current != gmp->top) /* goto top of page */
610 gmp->current = gmp->top;
611 else /* do nothing */
612 continue;
614 if(PlaceCell(gmp, gmp->current, &row, &col)){
615 PaintBrowser(gmp, 1, &crow, &ccol);
617 else{
618 PaintCell(row, col, gmp->cpf, gmp->current, 1);
619 crow = row;
620 ccol = col;
623 break;
625 case KEY_DOWN :
626 case (CTRL|'N'): /* next */
627 tp = gmp->current;
628 i = gmp->fpl;
629 while(i--){
630 if(tp->next == NULL){
631 (*term.t_beep)();
632 break;
634 else
635 tp = tp->next;
637 if(i != -1) /* can't go down */
638 break;
640 PlaceCell(gmp, gmp->current, &row, &col);
641 PaintCell(row, col, gmp->cpf, gmp->current, 0);
642 gmp->current = tp;
643 if(PlaceCell(gmp, tp, &row, &col)){
644 PaintBrowser(gmp, 1, &crow, &ccol);
646 else{
647 PaintCell(row, col, gmp->cpf, gmp->current, 1);
648 crow = row;
649 ccol = col;
651 break;
653 case KEY_UP :
654 case (CTRL|'P'): /* previous */
655 tp = gmp->current;
656 i = gmp->fpl;
657 while(i-- && tp)
658 tp = tp->prev;
660 if(tp == NULL)
661 break;
663 PlaceCell(gmp, gmp->current, &row, &col);
664 PaintCell(row, col, gmp->cpf, gmp->current, 0);
665 gmp->current = tp;
666 if(PlaceCell(gmp, tp, &row, &col)){
667 PaintBrowser(gmp, 1, &crow, &ccol);
669 else{
670 PaintCell(row, col, gmp->cpf, gmp->current, 1);
671 crow = row;
672 ccol = col;
674 break;
676 #ifdef MOUSE
677 case KEY_MOUSE:
678 mouse_get_last (NULL, &mousep);
679 if (mousep.doubleclick) {
680 goto Selected;
682 else {
683 row = mousep.row -= 2; /* Adjust for header*/
684 col = mousep.col;
685 i = row * gmp->fpl + (col / gmp->cpf); /* Count from top */
686 tp = gmp->top; /* start at top. */
687 for (; i > 0 && tp != NULL; --i) /* Count cells. */
688 tp = tp->next;
689 if (tp != NULL) { /* Valid cell? */
690 PlaceCell(gmp, gmp->current, &row, &col);
691 PaintCell(row, col, gmp->cpf, gmp->current, 0);
692 gmp->current = tp;
693 if(PlaceCell(gmp, tp, &row, &col)){
694 PaintBrowser(gmp, 1, &crow, &ccol);
696 else{
697 PaintCell(row, col, gmp->cpf, gmp->current, 1);
698 crow = row;
699 ccol = col;
703 break;
704 #endif
706 case 'e': /* exit or edit */
707 case 'E':
708 if(gmode&MDBRONLY){ /* run "pico" */
709 snprintf(child, sizeof(child), "%s%c%s", gmp->dname, C_FILESEP,
710 gmp->current->fname);
711 /* make sure selected isn't a directory or executable */
712 if(!LikelyASCII(child)){
713 emlwrite(_("Can't edit non-text file. Try Launch."), NULL);
714 break;
717 if((envp = (char *) getenv("EDITOR")) != NULL)
718 snprintf(tmp, sizeof(tmp), "%s \'%s\'", envp, child);
719 else
720 snprintf(tmp, sizeof(tmp), "pico%s%s%s \'%s\'",
721 (gmode & MDFKEY) ? " -f" : "",
722 (gmode & MDSHOCUR) ? " -g" : "",
723 (gmode & MDMOUSE) ? " -m" : "",
724 child);
726 BrowserRunChild(tmp, gmp->dname); /* spawn pico */
727 PaintBrowser(gmp, 0, &crow, &ccol); /* redraw browser */
729 else{
730 zotmaster(&gmp);
731 return(0);
734 break;
736 case 'q': /* user exits wrong */
737 case 'Q':
738 if(gmode&MDBRONLY){
739 zotmaster(&gmp);
740 return(0);
743 unknown_command(c);
744 break;
746 case 'x': /* toggle selection */
747 case 'X':
748 if(!(gmp->flags & FB_LMODE)){
749 if(gmp->flags & FB_LMODEPOS)
750 emlwrite(_("\007Type L command to use ListMode"), NULL);
751 else
752 unknown_command(c);
754 break;
757 if(gmp->current->mode == FIODIR){
758 emlwrite(_("\007Can't Set directories"), NULL);
759 break;
762 if(fcell_is_selected(gmp->current, gmp))
763 del_cell_from_lmlist(gmp->current, gmp);
764 else
765 add_cell_to_lmlist(gmp->current, gmp);
767 PlaceCell(gmp, gmp->current, &row, &col);
768 PaintCell(row, col, gmp->cpf, gmp->current, 1);
769 break;
771 case 'l': /* run Command */
772 case 'L': /* or ListMode */
773 if(gmp->flags & FB_LMODEPOS){
774 if(gmp->flags & FB_LMODE){
776 * Unless we make it so you can get out of ListMode
777 * once you're in ListMode, this must be an error.
779 emlwrite(_("\007Already in ListMode"), NULL);
780 break;
782 else{
783 gmp->flags |= FB_LMODE;
784 PaintBrowser(gmp, 0, &crow, &ccol);
787 break;
790 if(!(gmode&MDBRONLY)){
791 unknown_command(c);
792 break;
795 /* add subcommands to invoke pico and insert selected filename */
796 /* perhaps: add subcmd to scroll command history */
798 tmp[0] = '\0';
799 i = 0;
800 snprintf(child, sizeof(child), "%s%c%s", gmp->dname, C_FILESEP,
801 gmp->current->fname);
802 while(!i){
803 static EXTRAKEYS opts[] = {
804 {"^X", N_("Add Name"), CTRL|'X', KS_NONE},
805 {NULL, NULL, 0, KS_NONE},
808 status = mlreply_utf8(_("Command to execute: "),
809 tmp, NLINE, QNORML, opts);
810 switch(status){
811 case HELPCH:
812 emlwrite(_("\007No help yet!"), NULL);
813 /* remove break and sleep after help text is installed */
814 sleep(3);
815 break;
816 case (CTRL|'X'):
817 strncat(tmp, child, sizeof(tmp)-strlen(tmp)-1);
818 tmp[sizeof(tmp)-1] = '\0';
819 break;
820 case (CTRL|'L'):
821 PaintBrowser(gmp, 0, &crow, &ccol);
822 break;
823 case ABORT:
824 emlwrite(_("Command cancelled"), NULL);
825 i++;
826 break;
827 case FALSE:
828 case TRUE:
829 i++;
831 if(tmp[0] == '\0'){
832 emlwrite(_("No command specified"), NULL);
833 break;
836 BrowserRunChild(tmp, gmp->dname);
837 PaintBrowser(gmp, 0, &crow, &ccol);
838 break;
839 default:
840 break;
844 BrowserKeys();
845 break;
847 case 'd': /* delete */
848 case 'D':
849 if(gmp->current->mode == FIODIR){
850 /* BUG: if dir is empty it should be deleted */
851 emlwrite(_("\007Can't delete a directory"), NULL);
852 break;
855 if(gmode&MDSCUR){ /* not allowed! */
856 emlwrite(_("Delete not allowed in restricted mode"),NULL);
857 break;
860 snprintf(child, sizeof(child), "%s%c%s", gmp->dname, C_FILESEP,
861 gmp->current->fname);
863 i = 0;
864 while(i++ < 2){ /* verify twice!! */
865 if(i == 1){
866 if(fexist(child, "w", (off_t *)NULL) != FIOSUC){
867 strncpy(tmp, _("File is write protected! OVERRIDE"), sizeof(tmp));
868 tmp[sizeof(tmp)-1] = '\0';
870 else
871 /* TRANSLATORS: This is a question, Delete file <filename> */
872 snprintf(tmp, sizeof(tmp), _("Delete file \"%.*s\""), NLINE - 20, child);
874 else{
875 strncpy(tmp, _("File CANNOT be UNdeleted! Really delete"), sizeof(tmp));
876 tmp[sizeof(tmp)-1] = '\0';
879 if((status = mlyesno_utf8(tmp, FALSE)) != TRUE){
880 emlwrite((status == ABORT)
881 ? _("Delete Cancelled")
882 : _("File Not Deleted"),
883 NULL);
884 break;
888 if(status == TRUE){
889 if(our_unlink(child) < 0){
890 eml.s = errstr(errno);
891 emlwrite(_("Delete Failed: %s"), &eml);
893 else{ /* fix up pointers and redraw */
894 tp = gmp->current;
895 if(tp->next){
896 gmp->current = tp->next;
897 if((tp->next->prev = tp->prev) != NULL)
898 tp->prev->next = tp->next;
900 else if(tp->prev) {
901 gmp->current = tp->prev;
902 if((tp->prev->next = tp->next) != NULL)
903 tp->next->prev = tp->prev;
906 if(tp == gmp->head)
907 gmp->head = tp->next;
909 if(tp == gmp->bottom)
910 gmp->bottom = tp->prev;
912 tp->fname = NULL;
913 tp->next = tp->prev = NULL;
914 if(tp != gmp->current)
915 free((char *) tp);
917 if((tp = FindCell(gmp, gmp->current->fname, 0)) != NULL){
918 gmp->current = tp;
919 PlaceCell(gmp, gmp->current, &row, &col);
922 PaintBrowser(gmp, 1, &crow, &ccol);
923 mlerase();
927 BrowserKeys();
928 break;
930 case '?': /* HELP! */
931 case (CTRL|'G'):
932 if(term.t_mrow == 0){
933 if(km_popped == 0){
934 km_popped = 2;
935 break;
939 if(Pmaster){
940 VARS_TO_SAVE *saved_state;
942 saved_state = save_pico_state();
943 (*Pmaster->helper)(Pmaster->browse_help,
944 _("Help for Browsing"), 1);
945 if(saved_state){
946 restore_pico_state(saved_state);
947 free_pico_state(saved_state);
950 else if(gmode&MDBRONLY)
951 pico_help(sa_BrowseHelpText, _("Browser Help"), 1);
952 else
953 pico_help(BrowseHelpText, _("Help for Browsing"), 1);
954 /* fall thru to repaint everything */
956 case (CTRL|'L'):
957 PaintBrowser(gmp, 0, &crow, &ccol);
958 break;
960 case 'g': /* jump to a directory */
961 case 'G':
963 if(!(gmode&MDGOTO))
964 goto Default;
966 i = 0;
967 child[0] = '\0';
969 while(!i){
971 /* TRANSLATORS: A prompt asking for a directory to
972 move into */
973 status = mlreply_utf8(_("Directory to go to: "), child, NLINE, QNORML,
974 NULL);
976 switch(status){
977 case HELPCH:
978 emlwrite(_("\007No help yet!"), NULL);
979 /* remove break and sleep after help text is installed */
980 sleep(3);
981 break;
982 case (CTRL|'L'):
983 PaintBrowser(gmp, 0, &crow, &ccol);
984 break;
985 case ABORT:
986 emlwrite(_("Goto cancelled"), NULL);
987 i++;
988 break;
989 case FALSE:
990 case TRUE:
991 i++;
993 if(*child == '\0'){
994 strncpy(child, gethomedir(NULL), sizeof(child));
995 child[sizeof(child)-1] = '\0';
998 if(!compresspath(gmp->dname, child, sizeof(child))){
999 eml.s = child;
1000 emlwrite(_("Invalid Directory: %s"), &eml);
1001 break;
1004 if((gmode&MDSCUR) && homeless(child)){
1005 emlwrite(_("Restricted mode browsing limited to home directory"),NULL);
1006 break;
1009 if((gmode&MDTREE) && !in_oper_tree(child)){
1010 eml.s = opertree;
1011 emlwrite(_("\007 Can't go outside of %s in restricted mode"),
1012 &eml);
1013 break;
1016 if(isdir(child, (long *) NULL, NULL)){
1017 if((mp = getfcells(child, fb_flags)) == NULL){
1018 /* getfcells should explain what happened */
1019 i++;
1020 break;
1023 zotmaster(&gmp);
1024 gmp = mp;
1025 PaintBrowser(gmp, 0, &crow, &ccol);
1027 else{
1028 eml.s = child;
1029 emlwrite(_("\007Not a directory: \"%s\""), &eml);
1032 break;
1033 default:
1034 break;
1037 BrowserKeys();
1038 break;
1040 case 'a': /* Add */
1041 case 'A':
1042 if(gmode&MDSCUR){ /* not allowed! */
1043 emlwrite(_("Add not allowed in restricted mode"),NULL);
1044 break;
1047 i = 0;
1048 child[0] = '\0';
1049 /* pass in default filename */
1050 if(fn && *fn){
1051 strncpy(child, fn, sizeof(child) - 1);
1052 child[sizeof(child) - 1] = '\0';
1055 while(!i){
1057 switch(status=mlreply_utf8(_("Name of file to add: "), child, NLINE,
1058 QFFILE, NULL)){
1059 case HELPCH:
1060 emlwrite(_("\007No help yet!"), NULL);
1061 /* remove break and sleep after help text is installed */
1062 sleep(3);
1063 break;
1064 case (CTRL|'L'):
1065 PaintBrowser(gmp, 0, &crow, &ccol);
1066 break;
1067 case ABORT:
1068 emlwrite(_("Add File Cancelled"), NULL);
1069 i++;
1070 break;
1071 case FALSE:
1073 * Support default filename. A return of 'FALSE' means
1074 * 'No change in filename' which is fine if we have
1075 * provided a default.
1076 * John Berthels <john.berthels@nexor.co.uk>
1078 /* FALLTHROUGH */
1079 case TRUE:
1080 i++;
1082 if(child[0] == '\0'){
1083 emlwrite(_("No file named. Add Cancelled."), NULL);
1084 break;
1087 if(!compresspath(gmp->dname, child, sizeof(child))){
1088 emlwrite(_("Too many ..'s in name"), NULL);
1089 break;
1092 if((gmode&MDTREE) && !in_oper_tree(child)){
1093 eml.s = opertree;
1094 emlwrite(_("\007Restricted mode allows Add in %s only"),
1095 &eml);
1096 break;
1099 if((status = fexist(child, "w", (off_t *)NULL)) == FIOSUC){
1100 snprintf(tmp, sizeof(tmp), _("File \"%.*s\" already exists!"),
1101 NLINE - 20, child);
1102 emlwrite(tmp, NULL);
1103 break;
1105 else if(status != FIOFNF){
1106 fioperr(status, child);
1107 break;
1110 if(ffwopen(child, FALSE) != FIOSUC){
1111 /* ffwopen should've complained */
1112 break;
1114 else{ /* highlight new file */
1115 ffclose();
1116 eml.s = child;
1117 emlwrite(_("Added File \"%s\""), &eml);
1119 if((p = strrchr(child, C_FILESEP)) == NULL){
1120 emlwrite(_("Problems refiguring browser"), NULL);
1121 break;
1124 *p = '\0';
1125 if(p != child){
1126 strncpy(tmp, child, sizeof(tmp));
1127 tmp[sizeof(tmp)-1] = '\0';
1128 j = 0;
1129 while((child[j++] = *++p) != '\0')
1132 else{
1133 strncpy(tmp, S_FILESEP, sizeof(tmp));
1134 tmp[sizeof(tmp)-1] = '\0';
1138 * new file in same dir? if so, refigure files
1139 * and redraw...
1141 if(!strcmp(tmp, gmp->dname)){
1142 if((mp = getfcells(gmp->dname, fb_flags)) == NULL)
1143 /* getfcells should explain what happened */
1144 break;
1146 zotmaster(&gmp);
1147 gmp = mp;
1148 if((tp = FindCell(gmp, child, 0)) != NULL){
1149 gmp->current = tp;
1150 PlaceCell(gmp, gmp->current, &row, &col);
1153 PaintBrowser(gmp, 1, &crow, &ccol);
1156 break;
1157 default:
1158 break;
1162 BrowserKeys();
1163 break;
1165 case 'c': /* copy */
1166 case 'C':
1167 if(gmp->current->mode == FIODIR){
1168 emlwrite(_("\007Can't copy a directory"), NULL);
1169 break;
1172 if(gmode&MDSCUR){ /* not allowed! */
1173 emlwrite(_("Copy not allowed in restricted mode"),NULL);
1174 break;
1177 i = 0;
1178 child[0] = '\0';
1180 while(!i){
1182 switch(status=mlreply_utf8(_("Name of new copy: "), child, NLINE,
1183 QFFILE, NULL)){
1184 case HELPCH:
1185 emlwrite(_("\007No help yet!"), NULL);
1186 /* remove break and sleep after help text is installed */
1187 sleep(3);
1188 break;
1189 case (CTRL|'L'):
1190 PaintBrowser(gmp, 0, &crow, &ccol);
1191 break;
1192 case ABORT:
1193 emlwrite(_("Make Copy Cancelled"), NULL);
1194 i++;
1195 break;
1196 case FALSE:
1197 i++;
1198 mlerase();
1199 break;
1200 case TRUE:
1201 i++;
1203 if(child[0] == '\0'){
1204 emlwrite(_("No destination, file not copied"), NULL);
1205 break;
1208 if(!strcmp(gmp->current->fname, child)){
1209 emlwrite(_("\007Can't copy file on to itself!"), NULL);
1210 break;
1213 if(!compresspath(gmp->dname, child, sizeof(child))){
1214 emlwrite(_("Too many ..'s in name"), NULL);
1215 break;
1218 if((gmode&MDTREE) && !in_oper_tree(child)){
1219 eml.s = opertree;
1220 emlwrite(_("\007Restricted mode allows Copy in %s only"),
1221 &eml);
1222 break;
1225 if((status = fexist(child, "w", (off_t *)NULL)) == FIOSUC){
1226 /* TRANSLATORS: A question: File <filename> exists! Replace? */
1227 snprintf(tmp, sizeof(tmp), _("File \"%.*s\" exists! OVERWRITE"),
1228 NLINE - 20, child);
1229 if((status = mlyesno_utf8(tmp, 0)) != TRUE){
1230 emlwrite((status == ABORT)
1231 ? _("Make copy cancelled")
1232 : _("File Not Renamed"),
1233 NULL);
1234 break;
1237 else if(status != FIOFNF){
1238 fioperr(status, child);
1239 break;
1242 snprintf(tmp, sizeof(tmp), "%s%c%s", gmp->dname, C_FILESEP,
1243 gmp->current->fname);
1245 if(copy(tmp, child) < 0){
1246 /* copy() will report any error messages */
1247 break;
1249 else{ /* highlight new file */
1250 eml.s = child;
1251 emlwrite(_("File copied to %s"), &eml);
1253 if((p = strrchr(child, C_FILESEP)) == NULL){
1254 emlwrite(_("Problems refiguring browser"), NULL);
1255 break;
1258 *p = '\0';
1259 strncpy(tmp, (p == child) ? S_FILESEP: child, sizeof(tmp));
1260 tmp[sizeof(tmp)-1] = '\0';
1263 * new file in same dir? if so, refigure files
1264 * and redraw...
1266 if(!strcmp(tmp, gmp->dname)){
1267 strncpy(child, gmp->current->fname, sizeof(child));
1268 child[sizeof(child)-1] = '\0';
1269 if((mp = getfcells(gmp->dname, fb_flags)) == NULL)
1270 /* getfcells should explain what happened */
1271 break;
1273 zotmaster(&gmp);
1274 gmp = mp;
1275 if((tp = FindCell(gmp, child, 0)) != NULL){
1276 gmp->current = tp;
1277 PlaceCell(gmp, gmp->current, &row, &col);
1280 PaintBrowser(gmp, 1, &crow, &ccol);
1283 break;
1284 default:
1285 break;
1288 BrowserKeys();
1289 break;
1291 case 'r': /* rename */
1292 case 'R':
1293 i = 0;
1295 if(!strcmp(gmp->current->fname, "..")){
1296 emlwrite(_("\007Can't rename \"..\""), NULL);
1297 break;
1300 if(gmode&MDSCUR){ /* not allowed! */
1301 emlwrite(_("Rename not allowed in restricted mode"),NULL);
1302 break;
1305 strncpy(child, gmp->current->fname, sizeof(child));
1306 child[sizeof(child)-1] = '\0';
1308 while(!i){
1310 switch(status=mlreply_utf8(_("Rename file to: "), child, NLINE, QFFILE,
1311 NULL)){
1312 case HELPCH:
1313 emlwrite(_("\007No help yet!"), NULL);
1314 /* remove break and sleep after help text is installed */
1315 sleep(3);
1316 break;
1317 case (CTRL|'L'):
1318 PaintBrowser(gmp, 0, &crow, &ccol);
1319 break;
1320 case ABORT:
1321 emlwrite(_("Rename cancelled"), NULL);
1322 i++;
1323 break;
1324 case FALSE:
1325 case TRUE:
1326 i++;
1328 if(child[0] == '\0' || status == FALSE){
1329 mlerase();
1330 break;
1333 if(!compresspath(gmp->dname, child, sizeof(child))){
1334 emlwrite(_("Too many ..'s in name"), NULL);
1335 break;
1338 if((gmode&MDTREE) && !in_oper_tree(child)){
1339 eml.s = opertree;
1340 emlwrite(_("\007Restricted mode allows Rename in %s only"),
1341 &eml);
1342 break;
1345 status = fexist(child, "w", (off_t *)NULL);
1346 if(status == FIOSUC || status == FIOFNF){
1347 if(status == FIOSUC){
1348 snprintf(tmp, sizeof(tmp), _("File \"%.*s\" exists! OVERWRITE"),
1349 NLINE - 20, child);
1351 if((status = mlyesno_utf8(tmp, FALSE)) != TRUE){
1352 emlwrite((status == ABORT)
1353 ? _("Rename cancelled")
1354 : _("Not Renamed"),
1355 NULL);
1356 break;
1360 snprintf(tmp, sizeof(tmp), "%s%c%s", gmp->dname, C_FILESEP,
1361 gmp->current->fname);
1363 if(our_rename(tmp, child) < 0){
1364 eml.s = errstr(errno);
1365 emlwrite(_("Rename Failed: %s"), &eml);
1367 else{
1368 if((p = strrchr(child, C_FILESEP)) == NULL){
1369 emlwrite(_("Problems refiguring browser"), NULL);
1370 break;
1373 *p = '\0';
1374 strncpy(tmp, (p == child) ? S_FILESEP: child, sizeof(tmp));
1375 tmp[sizeof(tmp)-1] = '\0';
1377 if((mp = getfcells(tmp, fb_flags)) == NULL)
1378 /* getfcells should explain what happened */
1379 break;
1381 zotmaster(&gmp);
1382 gmp = mp;
1384 if((tp = FindCell(gmp, ++p, 0)) != NULL){
1385 gmp->current = tp;
1386 PlaceCell(gmp, gmp->current, &row, &col);
1389 PaintBrowser(gmp, 1, &crow, &ccol);
1390 mlerase();
1393 else{
1394 fioperr(status, child);
1396 break;
1397 default:
1398 break;
1401 BrowserKeys();
1402 break;
1404 case 'v': /* stand-alone */
1405 case 'V': /* browser "view" */
1406 case 's': /* user "select" */
1407 case 'S':
1408 case (CTRL|'M'):
1409 Selected:
1411 if(((new_c == 'S' || new_c == 's') && (gmode&MDBRONLY))
1412 || ((new_c == 'V' || new_c == 'v') && !(gmode&MDBRONLY)))
1413 goto Default;
1415 if(gmp->current->mode == FIODIR){
1416 *child = '\0';
1417 strncpy(tmp, gmp->dname, sizeof(tmp));
1418 tmp[sizeof(tmp)-1] = '\0';
1419 p = gmp->current->fname;
1420 if(p[0] == '.' && p[1] == '.' && p[2] == '\0'){
1421 if((p=strrchr(tmp, C_FILESEP)) != NULL){
1422 *p = '\0';
1424 if((gmode&MDTREE) && !in_oper_tree(tmp)){
1425 eml.s = PARENTDIR;
1426 emlwrite(
1427 _("\007Can't visit %s in restricted mode"),
1428 &eml);
1429 break;
1432 strncpy(child, &p[1], sizeof(child));
1433 child[sizeof(child)-1] = '\0';
1436 #if defined(DOS) || defined(OS2)
1437 (p == tmp || (tmp[1] == ':' && tmp[2] == '\0'))
1438 #else
1439 (p == tmp)
1440 #endif
1441 { /* is it root? */
1442 #if defined(DOS) || defined(OS2)
1443 if(*child){
1444 strncat(tmp, S_FILESEP, sizeof(tmp)-strlen(tmp)-1);
1445 tmp[sizeof(tmp)-1] = '\0';
1447 #else
1448 if(*child){
1449 strncpy(tmp, S_FILESEP, sizeof(tmp));
1450 tmp[sizeof(tmp)-1] = '\0';
1452 #endif
1453 else{
1454 emlwrite(_("\007Can't move up a directory"),
1455 NULL);
1456 break;
1461 else if((fb_flags&FB_SAVE) && p[0] == '.' && p[1] == '\0'){
1462 if ((strlen(gmp->dname) < dirlen) &&
1463 (strlen(gmp->current->fname) < fnlen)){
1464 strncpy(dir, gmp->dname, dirlen);
1465 dir[dirlen-1] = '\0';
1468 zotmaster(&gmp);
1469 return(0); /* just change the directory, still return no selection */
1471 else{
1472 if(tmp[1] != '\0'){ /* were in root? */
1473 strncat(tmp, S_FILESEP, sizeof(tmp)-strlen(tmp)-1);
1474 tmp[sizeof(tmp)-1] = '\0';
1477 strncat(tmp, gmp->current->fname, sizeof(tmp)-strlen(tmp));
1478 tmp[sizeof(tmp)-1] = '\0';
1481 if((mp = getfcells(tmp, fb_flags)) == NULL)
1482 /* getfcells should explain what happened */
1483 break;
1485 if(gmp->flags & FB_LMODE){
1486 mp->flags |= FB_LMODE;
1487 mp->lm = gmp->lm;
1488 gmp->lm = NULL;
1491 zotmaster(&gmp);
1492 gmp = mp;
1493 tp = NULL;
1495 if(*child){
1496 if((tp = FindCell(gmp, child, 0)) != NULL){
1497 gmp->current = tp;
1498 PlaceCell(gmp, gmp->current, &row, &col);
1500 else{
1501 eml.s = child;
1502 emlwrite(_("\007Problem finding dir \"%s\""), &eml);
1506 PaintBrowser(gmp, 0, &crow, &ccol);
1507 if(!*child){
1508 char b[100];
1510 snprintf(b, sizeof(b), "Select/View \" .. %s %s\" to return to previous directory.", PARENTDIR, DIRWORD);
1511 emlwrite(b, NULL);
1514 break;
1516 else if(gmode&MDBRONLY){
1517 snprintf(child, sizeof(child), "%s%c%s", gmp->dname, C_FILESEP,
1518 gmp->current->fname);
1520 if(LikelyASCII(child)){
1521 snprintf(tmp, sizeof(tmp), "%s \'%s\'",
1522 (envp = (char *) getenv("PAGER"))
1523 ? envp : BROWSER_PAGER, child);
1524 BrowserRunChild(tmp, gmp->dname);
1525 PaintBrowser(gmp, 0, &crow, &ccol);
1528 break;
1530 else{ /* just return */
1531 if(gmp->flags & FB_LMODEPOS){
1533 if(!lmreturn){
1534 emlwrite("Programming error, called FileBrowse with LMODEPOS but no lmreturn", NULL);
1535 zotmaster(&gmp);
1536 return(-1);
1539 /* user actually used ListMode, the list is finished */
1540 if(gmp->flags & FB_LMODE){
1541 if(!gmp->lm){
1542 (*term.t_beep)();
1543 emlwrite(_("No files are selected, use \"X\" to mark files for selection"), NULL);
1544 break;
1547 *lmreturn = gmp->lm;
1548 gmp->lm = NULL;
1550 else{ /* construct an lmreturn for user */
1551 LMLIST *new;
1552 size_t flen, dlen;
1554 if((new=(LMLIST *)malloc(sizeof(*new))) == NULL
1555 || (new->fname=malloc(gmp->current->fname ? (flen=strlen(gmp->current->fname))+1 : 1)) == NULL
1556 || (new->dir=malloc((dlen=strlen(gmp->dname))+1)) == NULL){
1557 emlwrite("\007Can't malloc space for filename", NULL);
1558 return(-1);
1561 strncpy(new->fname,
1562 gmp->current->fname ? gmp->current->fname : "", flen);
1563 new->fname[flen] = '\0';
1564 strncpy(new->dir, gmp->dname, dlen);
1565 new->dir[dlen] = '\0';
1566 strncpy(new->size, gmp->current->size, sizeof(new->size));
1567 new->size[sizeof(new->size)-1] = '\0';
1568 new->next = NULL;
1569 *lmreturn = new;
1572 zotmaster(&gmp);
1573 return(1);
1576 if ((strlen(gmp->dname) < dirlen) &&
1577 (strlen(gmp->current->fname) < fnlen)){
1578 strncpy(dir, gmp->dname, dirlen);
1579 dir[dirlen-1] = '\0';
1580 strncpy(fn, gmp->current->fname, fnlen);
1581 fn[fnlen-1] = '\0';
1583 else {
1584 zotmaster(&gmp);
1585 return(-1);
1587 if(sz != NULL){ /* size uninteresting */
1588 strncpy(sz, gmp->current->size, szlen);
1589 sz[szlen-1] = '\0';
1592 zotmaster (&gmp);
1593 return (1);
1595 break;
1597 case 'w': /* Where is */
1598 case 'W':
1599 case (CTRL|'W'):
1600 i = 0;
1601 flags = SR_ORIGMEN | SR_FORWARD | SR_NOEXACT;
1603 while(!i){
1604 switch(readpattern(_("File name to find"), FALSE, flags)){
1605 case HELPCH:
1606 emlwrite(_("\007No help yet!"), NULL);
1607 /* remove break and sleep after help text is installed */
1608 sleep(3);
1609 break;
1610 case (CTRL|'L'):
1611 PaintBrowser(gmp, 0, &crow, &ccol);
1612 break;
1613 case (CTRL|'P'):
1614 if(flags & SR_FORWARD){
1615 flags &= ~SR_FORWARD;
1616 flags |= SR_BACKWRD;
1617 } else {
1618 flags &= ~SR_BACKWRD;
1619 flags |= SR_FORWARD;
1621 break;
1622 case (CTRL|'Y'): /* first first cell */
1623 for(tp = gmp->top; tp->prev; tp = tp->prev)
1626 i++;
1627 /* fall thru to repaint */
1628 case (CTRL|'V'):
1629 if(!i){
1631 tp = gmp->top;
1632 if((i = term.t_nrow - term.t_mrow - 2) <= 0)
1633 break;
1635 while(i-- && tp->next){
1636 j = 0;
1637 while(++j <= gmp->fpl && tp->next)
1638 tp = tp->next;
1641 if(i < 0)
1642 gmp->top = tp;
1644 while(tp->next);
1646 emlwrite(_("Searched to end of directory"), NULL);
1648 else
1649 emlwrite(_("Searched to start of directory"), NULL);
1651 if(tp){
1652 PlaceCell(gmp, gmp->current, &row, &col);
1653 PaintCell(row, col, gmp->cpf, gmp->current, 0);
1654 gmp->current = tp;
1655 if(PlaceCell(gmp, gmp->current, &row, &col)){
1656 PaintBrowser(gmp, 1, &crow, &ccol);
1658 else{
1659 PaintCell(row, col, gmp->cpf, gmp->current, 1);
1660 crow = row;
1661 ccol = col;
1665 i++; /* make sure we jump out */
1666 break;
1667 case ABORT:
1668 emlwrite(_("Whereis cancelled"), NULL);
1669 i++;
1670 break;
1671 case FALSE:
1672 mlerase();
1673 i++;
1674 break;
1675 case TRUE:
1677 char *utf8 = NULL;
1679 if(pat && pat[0])
1680 utf8 = ucs4_to_utf8_cpystr(pat);
1682 if(utf8 && (tp = FindCell(gmp, utf8, flags & SR_BACKWRD)) != NULL){
1683 PlaceCell(gmp, gmp->current, &row, &col);
1684 PaintCell(row, col, gmp->cpf, gmp->current, 0);
1685 gmp->current = tp;
1687 if(PlaceCell(gmp, tp, &row, &col)){ /* top changed */
1688 PaintBrowser(gmp, 1, &crow, &ccol);
1690 else{
1691 PaintCell(row, col, gmp->cpf, gmp->current, 1);
1692 crow = row;
1693 ccol = col;
1695 mlerase();
1697 else{
1698 eml.s = utf8;
1699 emlwrite(_("\"%s\" not found"), &eml);
1702 if(utf8)
1703 fs_give((void **) &utf8);
1706 i++;
1707 break;
1708 default:
1709 break;
1713 BrowserKeys();
1714 break;
1716 case (CTRL|'\\') :
1717 #if defined MOUSE && !defined(_WINDOWS)
1718 toggle_xterm_mouse(0,1);
1719 #else
1720 unknown_command(c);
1721 #endif
1722 break;
1724 case (CTRL|'Z'):
1725 if(gmode&MDSSPD){
1726 bktoshell(0, 1);
1727 PaintBrowser(gmp, 0, &crow, &ccol);
1728 break;
1729 } /* fall thru with error! */
1731 default: /* what? */
1732 Default:
1733 unknown_command(c);
1735 case NODATA: /* no op */
1736 break;
1743 * getfcells - make a master browser struct and fill it in
1744 * return NULL if there's a problem.
1746 struct bmaster *
1747 getfcells(char *dname, int fb_flags)
1749 int i, /* various return codes */
1750 flength,
1751 nentries = 0; /* number of dir ents */
1752 off_t attsz;
1753 char *np, /* names of files in dir */
1754 *dcp, /* to add file to path */
1755 *tmpstr,
1756 **filtnames, /* array filtered names */
1757 errbuf[NLINE];
1758 struct fcell *ncp, /* new cell pointer */
1759 *tcp; /* trailing cell ptr */
1760 struct bmaster *mp;
1761 EML eml;
1763 errbuf[0] = '\0';
1764 if((mp=(struct bmaster *)malloc(sizeof(struct bmaster))) == NULL){
1765 emlwrite("\007Can't malloc space for master filename cell", NULL);
1766 return(NULL);
1769 memset(mp, 0, sizeof(*mp));
1771 if(dname[0] == '.' && dname[1] == '\0'){ /* remember this dir */
1772 if(!getcwd(mp->dname, 256))
1773 mp->dname[0] = '\0';
1775 else if(dname[0] == '.' && dname[1] == '.' && dname[2] == '\0'){
1776 if(!getcwd(mp->dname, 256))
1777 mp->dname[0] = '\0';
1778 else{
1779 if((np = (char *)strrchr(mp->dname, C_FILESEP)) != NULL)
1780 if(np != mp->dname)
1781 *np = '\0';
1784 else{
1785 strncpy(mp->dname, dname, sizeof(mp->dname));
1786 mp->dname[sizeof(mp->dname)-1] = '\0';
1789 mp->bottom = mp->head = mp->top = NULL;
1790 mp->cpf = mp->fpl = 0;
1791 mp->longest = 5; /* .. must be labeled! */
1792 mp->flags = fb_flags;
1794 eml.s = mp->dname;
1795 emlwrite("Building file list of %s...", &eml);
1797 if((mp->names = getfnames(mp->dname, NULL, &nentries, errbuf, sizeof(errbuf))) == NULL){
1798 free((char *) mp);
1799 if(*errbuf)
1800 emlwrite(errbuf, NULL);
1802 return(NULL);
1806 * this is the fun part. build an array of pointers to the fnames we're
1807 * interested in (i.e., do any filtering), then pass that off to be
1808 * sorted before building list of cells...
1810 * right now default filtering on ".*" except "..", but this could
1811 * easily be made a user option later on...
1813 if((filtnames=(char **)malloc((nentries+1) * sizeof(char *))) == NULL){
1814 emlwrite("\007Can't malloc space for name array", NULL);
1815 zotmaster(&mp);
1816 return(NULL);
1819 i = 0; /* index of filt'd array */
1820 np = mp->names;
1821 while(nentries--){
1822 int ii;
1823 int width;
1826 * Filter dot files? Always filter ".", never filter "..",
1827 * and sometimes fitler ".*"...
1829 if(*np == '.' && (!(*(np+1) == '.' && *(np+2) == '\0')
1830 && !(*(np+1) == '\0' && (fb_flags&FB_SAVE)))
1831 && (*(np+1) == '\0' || !(gmode & MDDOTSOK))){
1832 np += strlen(np) + 1;
1833 continue;
1836 filtnames[i++] = np;
1838 ii = (int) strlen(np);
1839 if((width = (int) utf8_width(np)) > mp->longest)
1840 mp->longest = width; /* remember longest */
1842 np += ii + 1; /* advance name pointer */
1845 nentries = i; /* new # of entries */
1848 * sort files case independently
1850 qsort((qsort_t *)filtnames, (size_t)nentries, sizeof(char *), sstrcasecmp);
1853 * this is so we use absolute path names for stats.
1854 * remember: be careful using dname as directory name, and fix mp->dname
1855 * when we're done
1857 dcp = (char *) strchr(mp->dname, '\0');
1858 if(dcp == mp->dname || dcp[-1] != C_FILESEP){
1859 dcp[0] = C_FILESEP;
1860 dcp[1] = '\0';
1862 else
1863 dcp--;
1865 i = 0;
1866 while(nentries--){ /* stat filtered files */
1867 /* get a new cell */
1868 if((ncp=(struct fcell *)malloc(sizeof(struct fcell))) == NULL){
1869 emlwrite("\007Can't malloc cells for browser!", NULL);
1870 zotfcells(mp->head); /* clean up cells */
1871 free((char *) filtnames);
1872 free((char *) mp);
1873 return(NULL); /* bummer. */
1876 ncp->next = ncp->prev = NULL;
1878 if(mp->head == NULL){ /* tie it onto the list */
1879 mp->head = mp->top = mp->current = ncp;
1881 else{
1882 mp->bottom = ncp;
1883 tcp->next = ncp;
1884 ncp->prev = tcp;
1887 tcp = ncp;
1889 /* fill in the new cell */
1890 ncp->fname = filtnames[i++];
1892 /* fill in file's mode */
1893 if ((flength = strlen(ncp->fname) + 1 + strlen(dname)) < sizeof(mp->dname)){
1894 strncpy(&dcp[1], ncp->fname, sizeof(mp->dname)-(dcp+1-mp->dname)); /* use absolute path! */
1895 mp->dname[sizeof(mp->dname)-1] = '\0';
1896 tmpstr = mp->dname;
1898 else{
1899 if((tmpstr = (char *)malloc((flength+1)*sizeof(char))) == NULL){
1900 emlwrite("\007Can't malloc cells for temp buffer!", NULL);
1901 zotfcells(mp->head); /* clean up cells */
1902 free((char *) filtnames);
1903 free((char *) mp);
1904 return(NULL); /* bummer. */
1907 strncpy(tmpstr, dname, flength);
1908 tmpstr[flength] = '\0';
1909 tmpstr = strncat(tmpstr, S_FILESEP, flength+1-1-strlen(tmpstr));
1910 tmpstr[flength] = '\0';
1911 tmpstr = strncat(tmpstr, ncp->fname, flength+1-1-strlen(tmpstr));
1912 tmpstr[flength] = '\0';
1915 switch(fexist(tmpstr, "t", &attsz)){
1916 case FIODIR :
1917 ncp->mode = FIODIR;
1918 snprintf(ncp->size, sizeof(ncp->size), "(%s%s%s)",
1919 (ncp->fname[0] == '.' && ncp->fname[1] == '.'
1920 && ncp->fname[2] == '\0') ? PARENTDIR :
1921 ((fb_flags&FB_SAVE) && ncp->fname[0] == '.' && ncp->fname[1] == '\0'
1922 ? SELECTWORD : ""),
1923 (ncp->fname[0] == '.' && ncp->fname[1] == '.'
1924 && ncp->fname[2] == '\0') ? " " :
1925 ((fb_flags&FB_SAVE) && ncp->fname[0] == '.' && ncp->fname[1] == '\0'
1926 ? " " : ""),
1927 DIRWORD);
1928 break;
1930 case FIOSYM :
1931 ncp->mode = FIOSYM;
1932 strncpy(ncp->size, "--", sizeof(ncp->size));
1933 ncp->size[sizeof(ncp->size)-1] = '\0';
1934 break;
1936 default :
1937 ncp->mode = FIOSUC; /* regular file */
1938 strncpy(ncp->size, prettysz(attsz), sizeof(ncp->size));
1939 ncp->size[sizeof(ncp->size)-1] = '\0';
1940 break;
1943 if (flength >= NLINE)
1944 free((char *) tmpstr);
1947 dcp[(dcp == mp->dname) ? 1 : 0] = '\0'; /* remember to cap dname */
1948 free((char *) filtnames); /* 'n blast filt'd array*/
1950 percdircells(mp);
1951 layoutcells(mp);
1952 if(strlen(mp->dname) < sizeof(browse_dir)){
1953 strncpy(browse_dir, mp->dname, sizeof(browse_dir));
1954 browse_dir[sizeof(browse_dir)-1] = '\0';
1956 else
1957 browse_dir[0] = '\0';
1959 return(mp);
1964 fcell_is_selected(struct fcell *cell, struct bmaster *mp)
1966 LMLIST *lm;
1968 if(cell && cell->fname){
1969 for(lm = mp ? mp->lm : NULL; lm; lm = lm->next){
1970 /* directory has to match */
1971 if(!((mp->dname[0] == '\0' && (!lm->dir || lm->dir[0] =='\0'))
1972 || (mp->dname[0] != '\0' && lm->dir && lm->dir[0] !='\0'
1973 && !strcmp(mp->dname, lm->dir))))
1974 continue;
1976 if(lm->fname && !strcmp(cell->fname, lm->fname))
1977 return(1);
1981 return(0);
1986 * Adds a new name to the head of the lmlist
1988 void
1989 add_cell_to_lmlist(struct fcell *cell, struct bmaster *mp)
1991 LMLIST *new;
1992 size_t flen, dlen;
1994 if(mp && cell && cell->fname && cell->fname[0]){
1995 if((new=(LMLIST *)malloc(sizeof(*new))) == NULL ||
1996 (new->fname=malloc(sizeof(char)*((flen=strlen(cell->fname))+1))) == NULL ||
1997 (new->dir=malloc(sizeof(char)*((dlen=strlen(mp->dname))+1))) == NULL){
1998 emlwrite("\007Can't malloc space for filename", NULL);
1999 return;
2002 strncpy(new->fname, cell->fname, flen);
2003 new->fname[flen] = '\0';
2004 strncpy(new->dir, mp->dname, dlen);
2005 new->dir[dlen] = '\0';
2006 new->size[0] = '\0';
2007 if(cell->size[0]){
2008 strncpy(new->size, cell->size, sizeof(new->size));
2009 new->size[sizeof(new->size)-1] = '\0';
2012 new->next = mp->lm;
2013 mp->lm = new;
2019 * Deletes a name from the lmlist
2021 void
2022 del_cell_from_lmlist(struct fcell *cell, struct bmaster *mp)
2024 LMLIST *lm, *lmprev = NULL;
2026 if(mp && cell && cell->fname && cell->fname[0])
2027 for(lm = mp ? mp->lm : NULL; lm; lm = lm->next){
2028 if(lm->fname && strcmp(cell->fname, lm->fname) == 0){
2029 free((char *) lm->fname);
2030 if(lm->dir)
2031 free((char *) lm->dir);
2033 if(lmprev)
2034 lmprev->next = lm->next;
2035 else
2036 mp->lm = lm->next;
2038 free((char *) lm);
2040 break;
2043 lmprev = lm;
2049 * PaintCell - print the given cell at the given location on the display
2050 * the format of a printed cell is:
2052 * "<fname> <size> "
2054 void
2055 PaintCell(int row, int col,
2056 int sc, /* screen columns available for this cell */
2057 struct fcell *cell, int inverted)
2059 char buf1[NLINE], buf2[NLINE];
2060 char lbuf[5];
2061 int need, l_wid, f_wid, f_to_s_wid, s_wid;
2062 UCS *ucs;
2064 if(cell == NULL)
2065 return;
2067 l_wid = (gmp && ((gmp->flags & FB_LMODEPOS) > 4)) ? 4 : 0;
2068 f_wid = utf8_width(cell->fname ? cell->fname : "");
2069 f_to_s_wid = 1;
2070 s_wid = utf8_width(cell->size ? cell->size : "");
2072 /* the two is the space between cell columns */
2073 sc = MIN(sc-2, term.t_ncol-col);
2075 if(l_wid){
2076 if(gmp && gmp->flags & FB_LMODE && cell->mode != FIODIR){
2078 * We have to figure out here if it is selected or not
2079 * and use that to write the X or space.
2081 lbuf[0] = '[';
2082 if(fcell_is_selected(cell, gmp))
2083 lbuf[1] = 'X';
2084 else
2085 lbuf[1] = ' ';
2087 lbuf[2] = ']';
2088 lbuf[3] = ' ';
2090 else{
2091 lbuf[0] = lbuf[1] = lbuf[2] = lbuf[3] = ' ';
2094 lbuf[4] = '\0';
2097 sc -= l_wid;
2099 need = f_wid+f_to_s_wid+s_wid;
2101 /* space between name and size */
2102 if(need < sc)
2103 f_to_s_wid += (sc-need);
2106 * If the width isn't enough to handle everything we're just putting a single
2107 * space between fname and size and truncating on the right. That means that
2108 * the sizes in the right hand column won't line up correctly when there is
2109 * a lack of space. Instead, we opt for displaying as much info as possible
2110 * in a slightly discordant way.
2113 movecursor(row, col);
2114 if(l_wid){
2115 ucs = utf8_to_ucs4_cpystr(lbuf);
2116 if(ucs){
2117 pputs(ucs, 0);
2118 fs_give((void **) &ucs);
2122 utf8_snprintf(buf1, sizeof(buf1), "%*.*w%*.*w%*.*w",
2123 f_wid, f_wid, cell->fname ? cell->fname : "",
2124 f_to_s_wid, f_to_s_wid, "",
2125 s_wid, s_wid, cell->size ? cell->size : "");
2127 utf8_snprintf(buf2, sizeof(buf2), "%*.*w", sc, sc, buf1);
2129 if(inverted)
2130 (*term.t_rev)(1);
2132 ucs = utf8_to_ucs4_cpystr(buf2);
2133 if(ucs){
2134 pputs(ucs, 0);
2135 fs_give((void **) &ucs);
2138 if(inverted)
2139 (*term.t_rev)(0);
2144 * PaintBrowse - with the current data, display the browser. if level == 0
2145 * paint the whole thing, if level == 1 just paint the cells
2146 * themselves
2148 void
2149 PaintBrowser(struct bmaster *mp, int level, int *row, int *col)
2151 int i, cl;
2152 struct fcell *tp;
2154 if(!level){
2155 ClearBrowserScreen();
2156 BrowserAnchor(mp->dname);
2159 i = 0;
2160 tp = mp->top;
2161 cl = COMPOSER_TOP_LINE; /* current display line */
2162 while(tp){
2164 PaintCell(cl, mp->cpf * i, mp->cpf, tp, tp == mp->current);
2166 if(tp == mp->current){
2167 if(row)
2168 *row = cl;
2170 if(col)
2171 *col = mp->cpf * i;
2174 if(++i >= mp->fpl){
2175 i = 0;
2176 if(++cl > term.t_nrow-(term.t_mrow+1))
2177 break;
2180 tp = tp->next;
2183 if(level){
2184 while(cl <= term.t_nrow - (term.t_mrow+1)){
2185 if(!i)
2186 movecursor(cl, 0);
2187 peeol();
2188 movecursor(++cl, 0);
2191 else{
2192 BrowserKeys();
2198 * BrowserKeys - just paints the keyhelp at the bottom of the display
2200 void
2201 BrowserKeys(void)
2203 menu_browse[QUIT_KEY].name = (gmode&MDBRONLY) ? "Q" : "E";
2204 /* TRANSLATORS: Brwsr is an abbreviation for Browser, which is
2205 a command used to look through something */
2206 menu_browse[QUIT_KEY].label = (gmode&MDBRONLY) ? N_("Quit") : N_("Exit Brwsr");
2207 menu_browse[GOTO_KEY].name = (gmode&MDGOTO) ? "G" : NULL;
2208 menu_browse[GOTO_KEY].label = (gmode&MDGOTO) ? N_("Goto") : NULL;
2209 if(gmode & MDBRONLY){
2210 menu_browse[EXEC_KEY].name = "L";
2211 menu_browse[EXEC_KEY].label = N_("Launch");
2212 menu_browse[SELECT_KEY].name = "V";
2213 menu_browse[SELECT_KEY].label = "[" N_("View") "]";
2214 menu_browse[PICO_KEY].name = "E";
2215 menu_browse[PICO_KEY].label = N_("Edit");
2217 else{
2218 menu_browse[SELECT_KEY].name = "S";
2219 menu_browse[SELECT_KEY].label = "[" N_("Select") "]";
2220 menu_browse[PICO_KEY].name = "A";
2221 menu_browse[PICO_KEY].label = N_("Add");
2223 if(gmp && gmp->flags & FB_LMODEPOS){
2224 if(gmp && gmp->flags & FB_LMODE){ /* ListMode is already on */
2225 menu_browse[EXEC_KEY].name = "X";
2226 menu_browse[EXEC_KEY].label = N_("Set/Unset");
2228 else{ /* ListMode is possible */
2229 menu_browse[EXEC_KEY].name = "L";
2230 menu_browse[EXEC_KEY].label = N_("ListMode");
2233 else{ /* No ListMode possible */
2234 menu_browse[EXEC_KEY].name = NULL;
2235 menu_browse[EXEC_KEY].label = NULL;
2239 wkeyhelp(menu_browse);
2244 * layoutcells - figure out max length of cell and how many cells can
2245 * go on a line of the display
2247 void
2248 layoutcells(struct bmaster *mp)
2250 static int wid = -1;
2252 * Max chars/file. Actually this is max screen cells/file
2253 * Longest name + "(parent dir)"
2255 if(wid < 0)
2256 wid = MAX(utf8_width(PARENTDIR),utf8_width(SELECTWORD)) + utf8_width(DIRWORD);
2258 mp->cpf = mp->longest + wid + 3;
2260 if(mp->flags & FB_LMODEPOS) /* "[X] " */
2261 mp->cpf += 4;
2263 if(gmode & MDONECOL){
2264 mp->fpl = 1;
2266 else{
2267 int i = 1;
2269 while(i*mp->cpf - 2 <= term.t_ncol) /* no force... */
2270 i++; /* like brute force! */
2272 mp->fpl = i - 1; /* files per line */
2275 if(mp->fpl == 0)
2276 mp->fpl = 1;
2281 * percdircells - bubble all the directory cells to the top of the
2282 * list.
2284 void
2285 percdircells(struct bmaster *mp)
2287 struct fcell *dirlp, /* dir cell list pointer */
2288 *lp, *nlp; /* cell list ptr and next */
2290 dirlp = NULL;
2291 for(lp = mp->head; lp; lp = nlp){
2292 nlp = lp->next;
2293 if(lp->mode == FIODIR){
2294 if(lp->prev) /* clip from list */
2295 lp->prev->next = lp->next;
2297 if(lp->next)
2298 lp->next->prev = lp->prev;
2300 if((lp->prev = dirlp) != NULL){ /* tie it into dir portion */
2301 if((lp->next = dirlp->next) != NULL)
2302 lp->next->prev = lp;
2304 dirlp->next = lp;
2305 dirlp = lp;
2307 else{
2308 if((dirlp = lp) != mp->head)
2309 dirlp->next = mp->head;
2311 if(dirlp->next)
2312 dirlp->next->prev = dirlp;
2314 mp->head = mp->top = mp->current = dirlp;
2322 * PlaceCell - given a browser master and a cell, return row and col of the display that
2323 * it should go on.
2325 * return 1 if mp->top has changed, x,y relative to new page
2326 * return 0 if otherwise (same page)
2327 * return -1 on error
2330 PlaceCell(struct bmaster *mp, struct fcell *cp, int *x, int *y)
2332 int cl = COMPOSER_TOP_LINE; /* current line */
2333 int ci = 0; /* current index on line */
2334 int rv = 0;
2335 int secondtry = 0;
2336 struct fcell *tp;
2338 /* will cp fit on screen? */
2339 tp = mp->top;
2340 while(1){
2341 if(tp == cp){ /* bingo! */
2342 *x = cl;
2343 *y = ci * mp->cpf;
2344 break;
2347 if((tp = tp->next) == NULL){ /* above top? */
2348 if(secondtry++){
2349 emlwrite("\007Internal error: can't find fname cell", NULL);
2350 return(-1);
2352 else{
2353 tp = mp->top = mp->head; /* try from the top! */
2354 cl = COMPOSER_TOP_LINE;
2355 ci = 0;
2356 rv = 1;
2357 continue; /* start over! */
2361 if(++ci >= mp->fpl){ /* next line? */
2362 ci = 0;
2363 if(++cl > term.t_nrow-(term.t_mrow+1)){ /* next page? */
2364 ci = mp->fpl; /* tp is at bottom right */
2365 while(ci--) /* find new top */
2366 tp = tp->prev;
2367 mp->top = tp;
2368 ci = 0;
2369 cl = COMPOSER_TOP_LINE; /* keep checking */
2370 rv = 1;
2376 /* not on display! */
2377 return(rv);
2382 * zotfcells - clean up malloc'd cells of file names
2384 void
2385 zotfcells(struct fcell *hp)
2387 struct fcell *tp;
2389 while(hp){
2390 tp = hp;
2391 hp = hp->next;
2392 tp->next = NULL;
2393 free((char *) tp);
2399 * zotmaster - blast the browser master struct
2401 void
2402 zotmaster(struct bmaster **mp)
2404 if(mp && *mp){
2405 zotfcells((*mp)->head); /* free cells */
2406 zotlmlist((*mp)->lm); /* free lmlist */
2407 if((*mp)->names)
2408 free((char *)(*mp)->names); /* free file names */
2410 free((char *)*mp); /* free master */
2411 *mp = NULL; /* make double sure */
2417 * FindCell - starting from the current cell find the first occurance of
2418 * the given string wrapping around if necessary
2420 struct fcell *
2421 FindCell(struct bmaster *mp, char *utf8string, int bsearch)
2423 struct fcell *tp, *fp;
2425 if(*utf8string == '\0')
2426 return(NULL);
2428 fp = NULL;
2429 tp = bsearch ? mp->current->prev : mp->current->next;
2431 while(tp && !fp){
2432 if(sisin(tp->fname, utf8string))
2433 fp = tp;
2434 else
2435 tp = bsearch ? tp->prev : tp->next;
2438 tp = bsearch ? mp->bottom : mp->head;
2439 while(tp != mp->current && !fp){
2440 if(sisin(tp->fname, utf8string))
2441 fp = tp;
2442 else
2443 tp = bsearch ? tp->prev : tp->next;
2446 return(fp);
2451 * sisin - case insensitive substring matching function
2453 * We can't really do case-insensitive for non-ascii, so restrict
2454 * that to ascii characters. The strings will be utf8.
2457 sisin(char *bigstr, char *utf8substr)
2459 register int j;
2461 while(*bigstr){
2462 j = 0;
2463 while(bigstr[j] == utf8substr[j]
2464 || ((unsigned char)bigstr[j] < 0x80 && (unsigned char)utf8substr[j] < 0x80
2465 && toupper((unsigned char)bigstr[j]) == toupper((unsigned char)utf8substr[j])))
2466 if(utf8substr[++j] == '\0') /* bingo! */
2467 return(1);
2469 bigstr++;
2472 return(0);
2477 * set_browser_title -
2479 void
2480 set_browser_title(char *s)
2482 browser_title = s;
2487 * BrowserAnchor - draw the browser's anchor line.
2489 void
2490 BrowserAnchor(char *utf8dir)
2492 char *pdir;
2493 char titlebuf[NLINE];
2494 char buf[NLINE];
2495 char dirbuf[NLINE];
2496 char *dots = "...";
2497 char *br = "BROWSER";
2498 char *dir = "Dir: ";
2499 UCS *ucs;
2500 int need, extra, avail;
2501 int title_wid, b_wid, t_to_b_wid, b_to_d_wid, d_wid, dot_wid, dir_wid, after_dir_wid;
2502 COLOR_PAIR *lastc = NULL;
2504 if(!utf8dir)
2505 utf8dir = "";
2507 pdir = utf8dir;
2509 if(browser_title)
2510 snprintf(titlebuf, sizeof(buf), " %s", browser_title);
2511 else if(Pmaster)
2512 snprintf(titlebuf, sizeof(buf), " ALPINE %s", Pmaster->pine_version);
2513 else
2514 snprintf(titlebuf, sizeof(buf), " UW PICO %s", (gmode&MDBRONLY) ? "BROWSER" : version);
2516 title_wid = utf8_width(titlebuf);
2517 t_to_b_wid = 15;
2518 b_wid = utf8_width(br);
2519 b_to_d_wid = 4;
2520 d_wid = utf8_width(dir);
2521 dot_wid = 0;
2522 dir_wid = utf8_width(pdir);
2524 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2526 if(need > term.t_ncol){
2527 extra = need - term.t_ncol;
2528 t_to_b_wid -= MIN(extra, 10);
2529 need -= MIN(extra, 10);
2531 if(need > term.t_ncol){
2532 extra = need - term.t_ncol;
2533 b_to_d_wid -= MIN(extra, 2);
2534 need -= MIN(extra, 2);
2537 if(need > term.t_ncol){
2538 titlebuf[0] = titlebuf[1] = ' ';
2539 titlebuf[2] = '\0';
2540 title_wid = utf8_width(titlebuf);
2541 t_to_b_wid = 0;
2542 b_to_d_wid = 4;
2543 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2544 if(need > term.t_ncol){
2545 extra = need - term.t_ncol;
2546 b_to_d_wid -= MIN(extra, 2);
2547 need -= MIN(extra, 2);
2550 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2551 if(need > term.t_ncol){
2552 t_to_b_wid = 0;
2553 b_wid = 0;
2554 b_to_d_wid = 0;
2557 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2558 if(need > term.t_ncol)
2559 d_wid = 0;
2561 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2563 if(need > term.t_ncol && dir_wid > 0){
2564 dot_wid = utf8_width(dots);
2565 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2566 while(need > term.t_ncol && (pdir = strchr(pdir+1, C_FILESEP))){
2567 dir_wid = utf8_width(pdir);
2568 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2571 if(!pdir){ /* adjust other widths to fill up space */
2572 avail = term.t_ncol - (title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid);
2573 if(avail > 2)
2574 utf8_to_width_rhs(dirbuf, utf8dir, sizeof(dirbuf), avail);
2575 else
2576 dirbuf[0] = '\0';
2578 pdir = dirbuf;
2581 dir_wid = utf8_width(pdir);
2586 extra = term.t_ncol - (title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid);
2588 if(extra >= 0)
2589 after_dir_wid = extra;
2590 else
2591 after_dir_wid = 0;
2593 utf8_snprintf(buf, sizeof(buf), "%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w",
2594 title_wid, title_wid, titlebuf,
2595 t_to_b_wid, t_to_b_wid, "",
2596 b_wid, b_wid, br,
2597 b_to_d_wid, b_to_d_wid, "",
2598 d_wid, d_wid, dir,
2599 dot_wid, dot_wid, dots,
2600 dir_wid, dir_wid, pdir,
2601 after_dir_wid, after_dir_wid, "");
2603 /* just making sure */
2604 utf8_snprintf(titlebuf, sizeof(titlebuf), "%-*.*w", term.t_ncol, term.t_ncol, buf);
2606 movecursor(0, 0);
2607 if(Pmaster && Pmaster->colors && Pmaster->colors->tbcp
2608 && pico_is_good_colorpair(Pmaster->colors->tbcp)){
2609 lastc = pico_get_cur_color();
2610 (void) pico_set_colorp(Pmaster->colors->tbcp, PSC_NONE);
2612 else
2613 (*term.t_rev)(1);
2615 ucs = utf8_to_ucs4_cpystr(titlebuf);
2616 if(ucs){
2617 pputs(ucs, 0);
2618 fs_give((void **) &ucs);
2621 if(lastc){
2622 (void) pico_set_colorp(lastc, PSC_NONE);
2623 free_color_pair(&lastc);
2625 else
2626 (*term.t_rev)(0);
2631 * ResizeBrowser - handle a resize event
2634 ResizeBrowser(void)
2636 if(gmp){
2637 layoutcells(gmp);
2638 PaintBrowser(gmp, 0, NULL, NULL);
2639 return(1);
2641 else
2642 return(0);
2646 void
2647 ClearBrowserScreen(void)
2649 int i;
2651 for(i = 0; i <= term.t_nrow; i++){ /* clear screen */
2652 movecursor(i, 0);
2653 peeol();
2658 void
2659 BrowserRunChild(char *child, char *dir)
2661 int status;
2662 char tmp[NLINE];
2663 time_t t_in, t_out;
2665 ClearBrowserScreen();
2666 movecursor(0, 0);
2667 (*term.t_close)();
2668 if(!isdir(dir, NULL, &t_in))
2669 t_in = 0;
2671 fflush(stdout);
2672 status = system(child);
2673 (*term.t_open)();
2674 if(t_in && isdir(dir, NULL, &t_out) && t_in < t_out){
2675 struct bmaster *mp;
2677 if((mp = getfcells(dir, 0)) != NULL){
2678 zotmaster(&gmp);
2679 gmp = mp;
2681 /* else getfcells should explain what happened */
2684 /* complain about non-zero exit status */
2685 if((status >> 8) & 0xff){
2687 movecursor(term.t_nrow - 1, 0);
2688 snprintf(tmp, sizeof(tmp), "[ \"%.30s\" exit with error value: %d ]",
2689 child, (status >> 8) & 0xff);
2690 pputs_utf8(tmp, 1);
2691 movecursor(term.t_nrow, 0);
2692 pputs_utf8("[ Hit RETURN to continue ]", 1);
2693 fflush(stdout);
2695 while(GetKey() != (CTRL|'M')){
2696 (*term.t_beep)();
2697 fflush(stdout);
2704 * imitate pc-pine memory for where we last called the file browser.
2706 void
2707 p_chdir(struct bmaster *mp)
2709 if(mp && mp->dname)
2710 chdir(mp->dname);
2712 #else /* _WINDOWS */
2716 * pico_file_browse - Exported version of FileBrowse below.
2719 pico_file_browse(PICO *pdata, char *dir, size_t dirlen, char *fn, size_t fnlen,
2720 char *sz, size_t szlen, int flags)
2722 return(FileBrowse(dir, dirlen, fn, fnlen, sz, szlen, flags, NULL));
2726 ResizeBrowser (void)
2728 return (0);
2732 * FileBrowse - Windows version of above function
2734 int
2735 FileBrowse(char *dir, size_t dirlen, char *fn, size_t fnlen,
2736 char *sz, size_t szlen, int fb_flags, LMLIST **lmreturn)
2738 struct stat sbuf;
2739 int rc;
2740 char lfn[NLINE];
2742 if (fb_flags & FB_SAVE){
2743 rc = mswin_savefile(dir, dirlen, fn, MIN(dirlen,fnlen));
2745 else{
2746 *fn = '\0'; /* No initial file names for
2747 * open. */
2748 if(fb_flags & FB_LMODEPOS){
2750 * We're going to allow multiple filenames to be returned so
2751 * we don't want to use the passed in fn for that. Instead, make
2752 * a bigger space and use that.
2754 char f[20000];
2755 size_t flen, dlen;
2757 memset(f, 0, sizeof(f));
2759 rc = mswin_multopenfile(dir, dirlen, f, sizeof(f),
2760 (fb_flags & FB_ATTACH) ? NULL : "Text Files (*.txt)#*.txt");
2762 if(rc == 1){
2763 LMLIST *lmhead = NULL, *new;
2764 char *p;
2767 * Build an LMLIST to return to the caller.
2769 for(p = f; *p; p += strlen(p)+1){
2770 flen = strlen(p);
2771 dlen = strlen(dir ? dir : "");
2772 new = (LMLIST *) fs_get(sizeof(*new));
2773 new->fname = (char *) fs_get((flen+1) * sizeof(char));
2774 new->dir = (char *) fs_get((dlen+1) * sizeof(char));
2776 strncpy(new->fname, p, flen);
2777 new->fname[flen] = '\0';
2778 strncpy(new->dir, dir ? dir : "", dlen);
2779 new->dir[dlen] = '\0';
2781 /* build full path to stat file. */
2782 if((strlen(new->dir) + strlen(S_FILESEP) +
2783 strlen(new->fname) + 1) < sizeof(lfn)){
2784 strncpy(lfn, new->dir, sizeof(lfn));
2785 lfn[sizeof(lfn)-1] = '\0';
2786 strncat(lfn, S_FILESEP, sizeof(lfn)-strlen(lfn)-1);
2787 strncat(lfn, new->fname, sizeof(lfn)-strlen(lfn)-1);
2788 lfn[sizeof(lfn)-1] = '\0';
2789 if(our_stat(lfn, &sbuf) < 0)
2790 strncpy(new->size, "0", 32);
2791 else
2792 strncpy(new->size, prettysz((off_t)sbuf.st_size), 32);
2794 new->size[32-1] = '\0';
2797 new->next = lmhead;
2798 lmhead = new;
2801 *lmreturn = lmhead;
2802 return(1);
2805 else
2806 rc = mswin_openfile (dir, dirlen, fn, fnlen,
2807 (fb_flags & FB_ATTACH) ? NULL : "Text Files (*.txt)#*.txt");
2810 if(rc == 1){
2811 if(sz != NULL){
2812 /* build full path to stat file. */
2813 if((strlen(dir) + strlen(S_FILESEP) + strlen(fn) + 1) > NLINE)
2814 return -1;
2816 strncpy(lfn, dir, sizeof(lfn));
2817 lfn[sizeof(lfn)-1] = '\0';
2818 strncat(lfn, S_FILESEP, sizeof(lfn)-strlen(lfn)-1);
2819 lfn[sizeof(lfn)-1] = '\0';
2820 strncat(lfn, fn, sizeof(lfn)-strlen(lfn)-1);
2821 lfn[sizeof(lfn)-1] = '\0';
2822 if(our_stat(lfn, &sbuf) < 0){
2823 strncpy(sz, "0", szlen);
2824 sz[szlen-1] = '\0';
2826 else{
2827 strncpy(sz, prettysz ((off_t)sbuf.st_size), szlen);
2828 sz[szlen-1] = '\0';
2832 return(1);
2835 return(rc ? -1 : 0);
2838 #endif /* _WINDOWS */
2842 * LikelyASCII - make a rough guess as to the displayability of the
2843 * given file.
2846 LikelyASCII(char *file)
2848 #define LA_TEST_BUF 1024
2849 #define LA_LINE_LIMIT 300
2850 int n, i, line, rv = FALSE;
2851 unsigned char buf[LA_TEST_BUF];
2852 FILE *fp;
2853 EML eml;
2855 if((fp = our_fopen(file, "rb")) != NULL){
2856 clearerr(fp);
2857 if((n = fread(buf, sizeof(char), LA_TEST_BUF * sizeof(char), fp)) > 0
2858 || !ferror(fp)){
2860 * If we don't hit any newlines in a reasonable number,
2861 * LA_LINE_LIMIT, of characters or the file contains NULLs,
2862 * bag out...
2864 rv = TRUE;
2865 for(i = line = 0; i < n; i++)
2866 if((line = (buf[i] == '\n') ? 0 : line + 1) >= LA_LINE_LIMIT
2867 || !buf[i]){
2868 rv = FALSE;
2869 emlwrite(_("Can't display non-text file. Try \"Launch\"."),
2870 NULL);
2871 break;
2874 else{
2875 eml.s = file;
2876 emlwrite(_("Can't read file: %s"), &eml);
2879 fclose(fp);
2881 else{
2882 eml.s = file;
2883 emlwrite(_("Can't open file: %s"), &eml);
2886 return(rv);
2890 void
2891 zotlmlist(LMLIST *lm)
2893 LMLIST *tp;
2895 while(lm){
2896 if(lm->fname)
2897 free(lm->fname);
2899 if(lm->dir)
2900 free(lm->dir);
2902 tp = lm;
2903 lm = lm->next;
2904 tp->next = NULL;
2905 free((char *) tp);
2911 * time_to_check - checks the current time against the last time called
2912 * and returns true if the elapsed time is > below.
2913 * Newmail won't necessarily check, but we want to give it
2914 * a chance to check or do a keepalive.
2917 time_to_check(void)
2919 static time_t lasttime = 0L;
2921 if(!get_input_timeout())
2922 return(FALSE);
2924 if(time((time_t *) 0) - lasttime > (Pmaster ? (time_t)(FUDGE-10) : get_input_timeout())){
2925 lasttime = time((time_t *) 0);
2926 return(TRUE);
2928 else
2929 return(FALSE);