* New version 2.10.99999
[alpine.git] / pico / browse.c
blob93de806674f2e7cadc92d08b67fe1fe62fbe1c19
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 2013 Eduardo Chappa
8 * Copyright 2006-2008 University of Washington
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 *top; /* cell in top left */
75 struct fcell *current; /* currently selected */
76 int longest; /* longest file name (in screen width) */
77 int fpl; /* file names per line */
78 int cpf; /* chars / file / line */
79 int flags;
80 char dname[NLINE]; /* this dir's name (UTF-8) */
81 char *names; /* malloc'd name array (UTF-8) */
82 LMLIST *lm;
83 } *gmp; /* global master ptr */
87 * title for exported browser display
89 static char *browser_title = NULL;
92 struct bmaster *getfcells(char *, int);
93 void PaintCell(int, int, int, struct fcell *, int);
94 void PaintBrowser(struct bmaster *, int, int *, int *);
95 void BrowserKeys(void);
96 void layoutcells(struct bmaster *);
97 void percdircells(struct bmaster *);
98 int PlaceCell(struct bmaster *, struct fcell *, int *, int *);
99 void zotfcells(struct fcell *);
100 void zotmaster(struct bmaster **);
101 struct fcell *FindCell(struct bmaster *, char *);
102 int sisin(char *, char *);
103 void p_chdir(struct bmaster *);
104 void BrowserAnchor(char *);
105 void ClearBrowserScreen(void);
106 void BrowserRunChild(char *, char *);
107 int fcell_is_selected(struct fcell *, struct bmaster *);
108 void add_cell_to_lmlist(struct fcell *, struct bmaster *);
109 void del_cell_from_lmlist(struct fcell *, struct bmaster *);
112 static KEYMENU menu_browse[] = {
113 {"?", N_("Get Help"), KS_SCREENHELP}, {NULL, NULL, KS_NONE},
114 {NULL, NULL, KS_NONE}, {"-", N_("Prev Pg"), KS_PREVPAGE},
115 {"D", N_("Delete"), KS_NONE}, {"C",N_("Copy"), KS_NONE},
116 {NULL, NULL, KS_NONE}, {NULL, NULL, KS_NONE},
117 {"W", N_("Where is"), KS_NONE}, {"Spc", N_("Next Pg"), KS_NEXTPAGE},
118 {"R", N_("Rename"), KS_NONE}, {NULL, NULL, KS_NONE}
120 #define QUIT_KEY 1
121 #define EXEC_KEY 2
122 #define GOTO_KEY 6
123 #define SELECT_KEY 7
124 #define PICO_KEY 11
127 #define DIRWORD "dir"
128 #define PARENTDIR "parent"
129 #define SELECTWORD "SELECT"
133 * Default pager used by the stand-alone file browser.
135 #define BROWSER_PAGER ((gmode & MDFKEY) ? "pine -k -F" : "pine -F")
139 * function key mappings for callable browser
141 static UCS bfmappings[2][12][2] = { { { F1, '?'}, /* stand alone */
142 { F2, NODATA }, /* browser function */
143 { F3, 'q'}, /* key mappings... */
144 { F4, 'v'},
145 { F5, 'l'},
146 { F6, 'w'},
147 { F7, '-'},
148 { F8, ' '},
149 { F9, 'd'},
150 { F10, 'r'},
151 { F11, 'c'},
152 { F12, 'e'} },
153 { { F1, '?'}, /* callable browser */
154 { F2, NODATA }, /* function key */
155 { F3, 'e'}, /* mappings... */
156 { F4, 's'},
157 { F5, NODATA },
158 { F6, 'w'},
159 { F7, '-'},
160 { F8, ' '},
161 { F9, 'd'},
162 { F10, 'r'},
163 { F11, 'c'},
164 { F12, 'a'} } };
168 * Browser help for pico (pine composer help handed to us by pine)
170 static char *BrowseHelpText[] = {
171 /* TRANSLATORS: The next several lines go together. The ~ characters
172 should be left in front of the characters they cause to be bold. */
173 N_("Help for Browse Command"),
174 " ",
175 N_(" Pico's file browser is used to select a file from the"),
176 N_(" file system for inclusion in the edited text."),
177 " ",
178 N_("~ Both directories and files are displayed. Press ~S"),
179 N_("~ or ~R~e~t~u~r~n to select a file or directory. When a file"),
180 N_(" is selected during the \"Read File\" command, it is"),
181 N_(" inserted into edited text. Answering \"yes\" to the"),
182 N_(" verification question after a directory is selected causes"),
183 N_(" the contents of that directory to be displayed for selection."),
184 " ",
185 N_(" The file named \"..\" is special, and means the \"parent\""),
186 N_(" of the directory being displayed. Select this directory"),
187 N_(" to move upward in the directory tree."),
188 " ",
189 N_("End of Browser Help."),
190 " ",
191 NULL
195 * Help for standalone browser (pilot)
197 static char *sa_BrowseHelpText[] = {
198 /* TRANSLATORS: Some more help text */
199 N_("Help for Pilot (PIne's Looker-upper Of Things"),
200 " ",
201 N_(" Pilot is a simple, display-oriented file system browser based on the"),
202 N_("~ Alpine message system composer. As with Alpine, commands are displayed at"),
203 N_("~ the bottom of the screen, and context-sensitive help is provided."),
204 " ",
205 N_("~ Pilot displays the current working directory at the top of the screen."),
206 N_("~ The directory's contents are displayed in columns of file name, file"),
207 N_("~ size pairs. Names that are directories are indicated by the name"),
208 N_("~ ~(~d~i~r~) in place of the file size. The parent of the current working"),
209 N_("~ directory is indicated by the file name ~.~. and size of ~(~p~a~r~e~n~t~ ~d~i~r~)."),
210 N_("~ File names that are symbolic links to other files are displayed with a"),
211 N_("~ file size of ~-~-."),
212 " ",
213 N_(" The following function keys are available in Pilot:"),
214 " ",
215 N_("~ ~? Display this help text."),
216 N_("~ ~Q Quit Pilot."),
217 " ",
218 N_("~ ~V View the currently selected file or open the selected directory."),
219 N_("~ Note: Pilot invokes ~p~i~n~e ~-~F, or the program defined by the ~P~A~G~E~R"),
220 N_("~ environment variable, to view the file."),
221 N_("~ ~L Launch an external application program."),
222 " ",
223 N_("~ ~W Search for a file by name."),
224 N_("~ ~- Scroll up one page."),
225 N_("~ ~S~p~a~c~e Scroll down one page."),
226 N_("~ ~N,~^~F Move forward (right) one column."),
227 N_("~ ~P,~^~B Move back (left) one column."),
228 N_("~ ~^~N Move down one row."),
229 N_("~ ~^~P Move up one row."),
230 " ",
231 N_("~ ~D Delete the selected file."),
232 N_("~ ~R Rename the selected file or directory."),
233 N_("~ ~C Copy the selected file."),
234 N_("~ ~E Edit the selected file."),
235 N_("~ Note: Pilot invokes ~p~i~c~o, or the program defined by the ~E~D~I~T~O~R"),
236 N_("~ environment variable, to edit the file."),
237 " ",
238 N_("End of Pilot Help."),
239 NULL
245 * pico_file_browse - Exported version of FileBrowse below.
248 pico_file_browse(PICO *pdata, char *dir, size_t dirlen, char *fn, size_t fnlen,
249 char *sz, size_t szlen, int flags)
251 int rv;
252 char title_buf[64];
254 Pmaster = pdata;
255 gmode = pdata->pine_flags | MDEXTFB;
256 km_popped = 0;
258 /* only init screen bufs for display and winch handler */
259 if(!vtinit())
260 return(-1);
262 if(Pmaster){
263 term.t_mrow = Pmaster->menu_rows;
264 if(Pmaster->oper_dir)
265 strncpy(opertree, Pmaster->oper_dir, NLINE);
267 if(*opertree)
268 fixpath(opertree, sizeof(opertree));
271 /* install any necessary winch signal handler */
272 ttresize();
274 snprintf(title_buf, sizeof(title_buf), "%s FILE", pdata->pine_anchor);
275 set_browser_title(title_buf);
276 rv = FileBrowse(dir, dirlen, fn, fnlen, sz, szlen, flags, NULL);
277 set_browser_title(NULL);
278 vttidy(); /* clean up tty handling */
279 zotdisplay(); /* and display structures */
280 Pmaster = NULL; /* and global config structure */
281 return(rv);
287 * FileBrowse - display contents of given directory dir
289 * intput:
290 * dir points to initial dir to browse.
291 * dirlen- buffer size of dir
292 * fn initial file name.
293 * fnlen- buffer size of fn
295 * returns:
296 * dir points to currently selected directory (without
297 * trailing file system delimiter)
298 * fn points to currently selected file
299 * sz points to size of file if ptr passed was non-NULL
300 * flags
302 * Special dispensation for FB_LMODE. If the caller sets
303 * FB_LMODEPOS, and the caller passes a non-null lmreturn,
304 * then the return values will be in lmreturn instead of
305 * in dir and fn. The caller is responsible for freeing
306 * the contents of lmreturn.
308 * 1 if a file's been selected
309 * 0 if no files selected
310 * -1 if there were problems
313 FileBrowse(char *dir, size_t dirlen, char *fn, size_t fnlen,
314 char *sz, size_t szlen, int fb_flags, LMLIST **lmreturn)
316 UCS c, new_c;
317 int status, i, j;
318 int row, col, crow, ccol;
319 char *p, *envp, child[NLINE], tmp[NLINE];
320 struct bmaster *mp;
321 struct fcell *tp;
322 EML eml;
323 #ifdef MOUSE
324 MOUSEPRESS mousep;
325 #endif
327 child[0] = '\0';
329 if((gmode&MDTREE) && !in_oper_tree(dir)){
330 eml.s = opertree;
331 emlwrite(_("\007Can't read outside of %s in restricted mode"), &eml);
332 sleep(2);
333 return(0);
336 if(gmode&MDGOTO){
337 /* fix up function key mapping table */
338 /* fix up key menu labels */
341 /* build contents of cell structures */
342 if((gmp = getfcells(dir, fb_flags)) == NULL)
343 return(-1);
345 tp = NULL;
346 if(fn && *fn){
347 if((tp = FindCell(gmp, fn)) != NULL){
348 gmp->current = tp;
349 PlaceCell(gmp, gmp->current, &row, &col);
353 /* paint screen */
354 PaintBrowser(gmp, 0, &crow, &ccol);
356 while(1){ /* the big loop */
357 if(!(gmode&MDSHOCUR)){
358 crow = term.t_nrow-term.t_mrow;
359 ccol = 0;
362 if(!(gmode&MDSHOCUR))
363 movecursor(crow, ccol);
364 else if(gmp->flags & FB_LMODEPOS){
365 if(gmp->flags & FB_LMODE && gmp->current->mode != FIODIR)
366 movecursor(crow, ccol+1);
367 else
368 movecursor(crow, ccol+4);
370 else
371 movecursor(crow, ccol);
373 if(km_popped){
374 km_popped--;
375 if(km_popped == 0)
376 /* cause bottom three lines to repaint */
377 PaintBrowser(gmp, 0, &crow, &ccol);
380 if(km_popped){ /* temporarily change to cause menu to paint */
381 term.t_mrow = 2;
382 movecursor(term.t_nrow-2, 0); /* clear status line */
383 peeol();
384 BrowserKeys();
385 term.t_mrow = 0;
388 (*term.t_flush)();
390 #ifdef MOUSE
391 mouse_in_content(KEY_MOUSE, -1, -1, 0, 0);
392 register_mfunc(mouse_in_content,2,0,term.t_nrow-(term.t_mrow+1),
393 term.t_ncol);
394 #endif
395 c = GetKey();
396 #ifdef MOUSE
397 clear_mfunc(mouse_in_content);
398 #endif
400 if(Pmaster){
401 if(Pmaster->newmail && (c == NODATA || time_to_check())){
402 if((*Pmaster->newmail)(c == NODATA ? 0 : 2, 1) >= 0){
403 int rv;
405 if(km_popped){ /* restore display */
406 km_popped = 0;
407 PaintBrowser(gmp, 0, &crow, &ccol);
410 clearcursor();
411 mlerase();
412 rv = (*Pmaster->showmsg)(c);
413 ttresize();
414 picosigs();
415 if(rv) /* Did showmsg corrupt the display? */
416 PaintBrowser(gmp, 0, &crow, &ccol); /* Yes, repaint */
418 mpresf = 1;
421 clearcursor();
422 if(gmp->flags & FB_LMODEPOS){
423 if(gmp->flags & FB_LMODE && gmp->current->mode != FIODIR)
424 movecursor(crow, ccol+1);
425 else
426 movecursor(crow, ccol+4);
428 else
429 movecursor(crow, ccol);
432 else{
433 if(get_input_timeout() && (c == NODATA || time_to_check()))
434 if(pico_new_mail())
435 emlwrite(_("You may possibly have new mail."), NULL);
438 if(km_popped)
439 switch(c){
440 case NODATA:
441 case (CTRL|'L'):
442 km_popped++;
443 break;
445 default:
446 /* clear bottom three lines */
447 movecursor(term.t_nrow-2, 0);
448 peeol();
449 movecursor(term.t_nrow-1, 0);
450 peeol();
451 movecursor(term.t_nrow, 0);
452 peeol();
453 break;
456 if(c == NODATA) /* GetKey timed out */
457 continue;
458 else if(Pmaster)
459 (*Pmaster->keybinput)();
462 if(mpresf){ /* blast old messages */
463 if(mpresf++ > MESSDELAY){ /* every few keystrokes */
464 mlerase();
468 /* process commands */
469 switch(new_c = normalize_cmd(c,bfmappings[(gmode&MDBRONLY)?0:1],2)){
471 case KEY_RIGHT: /* move right */
472 case (CTRL|'@'):
473 case (CTRL|'F'): /* forward */
474 case 'n' :
475 case 'N' :
476 if(gmp->current->next == NULL){
477 (*term.t_beep)();
478 break;
481 PlaceCell(gmp, gmp->current, &row, &col);
482 PaintCell(row, col, gmp->cpf, gmp->current, 0);
483 gmp->current = gmp->current->next;
484 if(PlaceCell(gmp, gmp->current, &row, &col)){
485 PaintBrowser(gmp, 1, &crow, &ccol);
487 else{
488 PaintCell(row, col, gmp->cpf, gmp->current, 1);
489 crow = row;
490 ccol = col;
492 break;
494 case KEY_LEFT: /* move left */
495 case (CTRL|'B'): /* back */
496 case 'p' :
497 case 'P' :
498 if(gmp->current->prev == NULL){
499 (*term.t_beep)();
500 break;
503 PlaceCell(gmp, gmp->current, &row, &col);
504 PaintCell(row, col, gmp->cpf, gmp->current, 0);
505 gmp->current = gmp->current->prev;
506 if(PlaceCell(gmp, gmp->current, &row, &col)){
507 PaintBrowser(gmp, 1, &crow, &ccol);
509 else{
510 PaintCell(row, col, gmp->cpf, gmp->current, 1);
511 crow = row;
512 ccol = col;
514 break;
516 case (CTRL|'A'): /* beginning of line */
517 tp = gmp->current;
518 i = col;
519 while(i > 0){
520 i -= gmp->cpf;
521 if(tp->prev != NULL)
522 tp = tp->prev;
524 PlaceCell(gmp, gmp->current, &row, &col);
525 PaintCell(row, col, gmp->cpf, gmp->current, 0);
526 gmp->current = tp;
527 if(PlaceCell(gmp, tp, &row, &col)){
528 PaintBrowser(gmp, 1, &crow, &ccol);
530 else{
531 PaintCell(row, col, gmp->cpf, gmp->current, 1);
532 crow = row;
533 ccol = col;
535 break;
537 case (CTRL|'E'): /* end of line */
538 tp = gmp->current;
539 i = col + gmp->cpf;
540 while(i+gmp->cpf <= gmp->cpf * gmp->fpl){
541 i += gmp->cpf;
542 if(tp->next != NULL)
543 tp = tp->next;
546 PlaceCell(gmp, gmp->current, &row, &col);
547 PaintCell(row, col, gmp->cpf, gmp->current, 0);
548 gmp->current = tp;
549 if(PlaceCell(gmp, tp, &row, &col)){
550 PaintBrowser(gmp, 1, &crow, &ccol);
552 else{
553 PaintCell(row, col, gmp->cpf, gmp->current, 1);
554 crow = row;
555 ccol = col;
557 break;
559 case (CTRL|'V'): /* page forward */
560 case ' ':
561 case KEY_PGDN :
562 case KEY_END :
563 tp = gmp->top;
564 i = term.t_nrow - term.t_mrow - 2;
566 while(i-- && tp->next != NULL){
567 j = 0;
568 while(++j <= gmp->fpl && tp->next != NULL)
569 tp = tp->next;
572 if(tp == NULL)
573 continue;
575 PlaceCell(gmp, gmp->current, &row, &col);
576 PaintCell(row, col, gmp->cpf, gmp->current, 0);
577 gmp->current = tp;
578 if(PlaceCell(gmp, tp, &row, &col)){
579 PaintBrowser(gmp, 1, &crow, &ccol);
581 else{
582 PaintCell(row, col, gmp->cpf, gmp->current, 1);
583 crow = row;
584 ccol = col;
586 break;
588 case '-' :
589 case (CTRL|'Y'): /* page backward */
590 case KEY_PGUP :
591 case KEY_HOME :
592 tp = gmp->top;
593 i = term.t_nrow - term.t_mrow - 4;
594 while(i-- && tp != NULL){
595 j = gmp->fpl;
596 while(j-- && tp != NULL)
597 tp = tp->prev;
600 if(tp || (gmp->current != gmp->top)){ /* clear old hilite */
601 PlaceCell(gmp, gmp->current, &row, &col);
602 PaintCell(row, col, gmp->cpf, gmp->current, 0);
605 if(tp) /* new page ! */
606 gmp->current = tp;
607 else if(gmp->current != gmp->top) /* goto top of page */
608 gmp->current = gmp->top;
609 else /* do nothing */
610 continue;
612 if(PlaceCell(gmp, gmp->current, &row, &col)){
613 PaintBrowser(gmp, 1, &crow, &ccol);
615 else{
616 PaintCell(row, col, gmp->cpf, gmp->current, 1);
617 crow = row;
618 ccol = col;
621 break;
623 case KEY_DOWN :
624 case (CTRL|'N'): /* next */
625 tp = gmp->current;
626 i = gmp->fpl;
627 while(i--){
628 if(tp->next == NULL){
629 (*term.t_beep)();
630 break;
632 else
633 tp = tp->next;
635 if(i != -1) /* can't go down */
636 break;
638 PlaceCell(gmp, gmp->current, &row, &col);
639 PaintCell(row, col, gmp->cpf, gmp->current, 0);
640 gmp->current = tp;
641 if(PlaceCell(gmp, tp, &row, &col)){
642 PaintBrowser(gmp, 1, &crow, &ccol);
644 else{
645 PaintCell(row, col, gmp->cpf, gmp->current, 1);
646 crow = row;
647 ccol = col;
649 break;
651 case KEY_UP :
652 case (CTRL|'P'): /* previous */
653 tp = gmp->current;
654 i = gmp->fpl;
655 while(i-- && tp)
656 tp = tp->prev;
658 if(tp == NULL)
659 break;
661 PlaceCell(gmp, gmp->current, &row, &col);
662 PaintCell(row, col, gmp->cpf, gmp->current, 0);
663 gmp->current = tp;
664 if(PlaceCell(gmp, tp, &row, &col)){
665 PaintBrowser(gmp, 1, &crow, &ccol);
667 else{
668 PaintCell(row, col, gmp->cpf, gmp->current, 1);
669 crow = row;
670 ccol = col;
672 break;
674 #ifdef MOUSE
675 case KEY_MOUSE:
676 mouse_get_last (NULL, &mousep);
677 if (mousep.doubleclick) {
678 goto Selected;
680 else {
681 row = mousep.row -= 2; /* Adjust for header*/
682 col = mousep.col;
683 i = row * gmp->fpl + (col / gmp->cpf); /* Count from top */
684 tp = gmp->top; /* start at top. */
685 for (; i > 0 && tp != NULL; --i) /* Count cells. */
686 tp = tp->next;
687 if (tp != NULL) { /* Valid cell? */
688 PlaceCell(gmp, gmp->current, &row, &col);
689 PaintCell(row, col, gmp->cpf, gmp->current, 0);
690 gmp->current = tp;
691 if(PlaceCell(gmp, tp, &row, &col)){
692 PaintBrowser(gmp, 1, &crow, &ccol);
694 else{
695 PaintCell(row, col, gmp->cpf, gmp->current, 1);
696 crow = row;
697 ccol = col;
701 break;
702 #endif
704 case 'e': /* exit or edit */
705 case 'E':
706 if(gmode&MDBRONLY){ /* run "pico" */
707 snprintf(child, sizeof(child), "%s%c%s", gmp->dname, C_FILESEP,
708 gmp->current->fname);
709 /* make sure selected isn't a directory or executable */
710 if(!LikelyASCII(child)){
711 emlwrite(_("Can't edit non-text file. Try Launch."), NULL);
712 break;
715 if((envp = (char *) getenv("EDITOR")) != NULL)
716 snprintf(tmp, sizeof(tmp), "%s \'%s\'", envp, child);
717 else
718 snprintf(tmp, sizeof(tmp), "pico%s%s%s \'%s\'",
719 (gmode & MDFKEY) ? " -f" : "",
720 (gmode & MDSHOCUR) ? " -g" : "",
721 (gmode & MDMOUSE) ? " -m" : "",
722 child);
724 BrowserRunChild(tmp, gmp->dname); /* spawn pico */
725 PaintBrowser(gmp, 0, &crow, &ccol); /* redraw browser */
727 else{
728 zotmaster(&gmp);
729 return(0);
732 break;
734 case 'q': /* user exits wrong */
735 case 'Q':
736 if(gmode&MDBRONLY){
737 zotmaster(&gmp);
738 return(0);
741 unknown_command(c);
742 break;
744 case 'x': /* toggle selection */
745 case 'X':
746 if(!(gmp->flags & FB_LMODE)){
747 if(gmp->flags & FB_LMODEPOS)
748 emlwrite(_("\007Type L command to use ListMode"), NULL);
749 else
750 unknown_command(c);
752 break;
755 if(gmp->current->mode == FIODIR){
756 emlwrite(_("\007Can't Set directories"), NULL);
757 break;
760 if(fcell_is_selected(gmp->current, gmp))
761 del_cell_from_lmlist(gmp->current, gmp);
762 else
763 add_cell_to_lmlist(gmp->current, gmp);
765 PlaceCell(gmp, gmp->current, &row, &col);
766 PaintCell(row, col, gmp->cpf, gmp->current, 1);
767 break;
769 case 'l': /* run Command */
770 case 'L': /* or ListMode */
771 if(gmp->flags & FB_LMODEPOS){
772 if(gmp->flags & FB_LMODE){
774 * Unless we make it so you can get out of ListMode
775 * once you're in ListMode, this must be an error.
777 emlwrite(_("\007Already in ListMode"), NULL);
778 break;
780 else{
781 gmp->flags |= FB_LMODE;
782 PaintBrowser(gmp, 0, &crow, &ccol);
785 break;
788 if(!(gmode&MDBRONLY)){
789 unknown_command(c);
790 break;
793 /* add subcommands to invoke pico and insert selected filename */
794 /* perhaps: add subcmd to scroll command history */
796 tmp[0] = '\0';
797 i = 0;
798 snprintf(child, sizeof(child), "%s%c%s", gmp->dname, C_FILESEP,
799 gmp->current->fname);
800 while(!i){
801 static EXTRAKEYS opts[] = {
802 {"^X", N_("Add Name"), CTRL|'X', KS_NONE},
803 {NULL, NULL, 0, KS_NONE},
806 status = mlreply_utf8(_("Command to execute: "),
807 tmp, NLINE, QNORML, opts);
808 switch(status){
809 case HELPCH:
810 emlwrite(_("\007No help yet!"), NULL);
811 /* remove break and sleep after help text is installed */
812 sleep(3);
813 break;
814 case (CTRL|'X'):
815 strncat(tmp, child, sizeof(tmp)-strlen(tmp)-1);
816 tmp[sizeof(tmp)-1] = '\0';
817 break;
818 case (CTRL|'L'):
819 PaintBrowser(gmp, 0, &crow, &ccol);
820 break;
821 case ABORT:
822 emlwrite(_("Command cancelled"), NULL);
823 i++;
824 break;
825 case FALSE:
826 case TRUE:
827 i++;
829 if(tmp[0] == '\0'){
830 emlwrite(_("No command specified"), NULL);
831 break;
834 BrowserRunChild(tmp, gmp->dname);
835 PaintBrowser(gmp, 0, &crow, &ccol);
836 break;
837 default:
838 break;
842 BrowserKeys();
843 break;
845 case 'd': /* delete */
846 case 'D':
847 if(gmp->current->mode == FIODIR){
848 /* BUG: if dir is empty it should be deleted */
849 emlwrite(_("\007Can't delete a directory"), NULL);
850 break;
853 if(gmode&MDSCUR){ /* not allowed! */
854 emlwrite(_("Delete not allowed in restricted mode"),NULL);
855 break;
858 snprintf(child, sizeof(child), "%s%c%s", gmp->dname, C_FILESEP,
859 gmp->current->fname);
861 i = 0;
862 while(i++ < 2){ /* verify twice!! */
863 if(i == 1){
864 if(fexist(child, "w", (off_t *)NULL) != FIOSUC){
865 strncpy(tmp, _("File is write protected! OVERRIDE"), sizeof(tmp));
866 tmp[sizeof(tmp)-1] = '\0';
868 else
869 /* TRANSLATORS: This is a question, Delete file <filename> */
870 snprintf(tmp, sizeof(tmp), _("Delete file \"%.*s\""), NLINE - 20, child);
872 else{
873 strncpy(tmp, _("File CANNOT be UNdeleted! Really delete"), sizeof(tmp));
874 tmp[sizeof(tmp)-1] = '\0';
877 if((status = mlyesno_utf8(tmp, FALSE)) != TRUE){
878 emlwrite((status == ABORT)
879 ? _("Delete Cancelled")
880 : _("File Not Deleted"),
881 NULL);
882 break;
886 if(status == TRUE){
887 if(our_unlink(child) < 0){
888 eml.s = errstr(errno);
889 emlwrite(_("Delete Failed: %s"), &eml);
891 else{ /* fix up pointers and redraw */
892 tp = gmp->current;
893 if(tp->next){
894 gmp->current = tp->next;
895 if((tp->next->prev = tp->prev) != NULL)
896 tp->prev->next = tp->next;
898 else if(tp->prev) {
899 gmp->current = tp->prev;
900 if((tp->prev->next = tp->next) != NULL)
901 tp->next->prev = tp->prev;
904 if(tp == gmp->head)
905 gmp->head = tp->next;
907 tp->fname = NULL;
908 tp->next = tp->prev = NULL;
909 if(tp != gmp->current)
910 free((char *) tp);
912 if((tp = FindCell(gmp, gmp->current->fname)) != NULL){
913 gmp->current = tp;
914 PlaceCell(gmp, gmp->current, &row, &col);
917 PaintBrowser(gmp, 1, &crow, &ccol);
918 mlerase();
922 BrowserKeys();
923 break;
925 case '?': /* HELP! */
926 case (CTRL|'G'):
927 if(term.t_mrow == 0){
928 if(km_popped == 0){
929 km_popped = 2;
930 break;
934 if(Pmaster){
935 VARS_TO_SAVE *saved_state;
937 saved_state = save_pico_state();
938 (*Pmaster->helper)(Pmaster->browse_help,
939 _("Help for Browsing"), 1);
940 if(saved_state){
941 restore_pico_state(saved_state);
942 free_pico_state(saved_state);
945 else if(gmode&MDBRONLY)
946 pico_help(sa_BrowseHelpText, _("Browser Help"), 1);
947 else
948 pico_help(BrowseHelpText, _("Help for Browsing"), 1);
949 /* fall thru to repaint everything */
951 case (CTRL|'L'):
952 PaintBrowser(gmp, 0, &crow, &ccol);
953 break;
955 case 'g': /* jump to a directory */
956 case 'G':
958 if(!(gmode&MDGOTO))
959 goto Default;
961 i = 0;
962 child[0] = '\0';
964 while(!i){
966 /* TRANSLATORS: A prompt asking for a directory to
967 move into */
968 status = mlreply_utf8(_("Directory to go to: "), child, NLINE, QNORML,
969 NULL);
971 switch(status){
972 case HELPCH:
973 emlwrite(_("\007No help yet!"), NULL);
974 /* remove break and sleep after help text is installed */
975 sleep(3);
976 break;
977 case (CTRL|'L'):
978 PaintBrowser(gmp, 0, &crow, &ccol);
979 break;
980 case ABORT:
981 emlwrite(_("Goto cancelled"), NULL);
982 i++;
983 break;
984 case FALSE:
985 case TRUE:
986 i++;
988 if(*child == '\0'){
989 strncpy(child, gethomedir(NULL), sizeof(child));
990 child[sizeof(child)-1] = '\0';
993 if(!compresspath(gmp->dname, child, sizeof(child))){
994 eml.s = child;
995 emlwrite(_("Invalid Directory: %s"), &eml);
996 break;
999 if((gmode&MDSCUR) && homeless(child)){
1000 emlwrite(_("Restricted mode browsing limited to home directory"),NULL);
1001 break;
1004 if((gmode&MDTREE) && !in_oper_tree(child)){
1005 eml.s = opertree;
1006 emlwrite(_("\007 Can't go outside of %s in restricted mode"),
1007 &eml);
1008 break;
1011 if(isdir(child, (long *) NULL, NULL)){
1012 if((mp = getfcells(child, fb_flags)) == NULL){
1013 /* getfcells should explain what happened */
1014 i++;
1015 break;
1018 zotmaster(&gmp);
1019 gmp = mp;
1020 PaintBrowser(gmp, 0, &crow, &ccol);
1022 else{
1023 eml.s = child;
1024 emlwrite(_("\007Not a directory: \"%s\""), &eml);
1027 break;
1028 default:
1029 break;
1032 BrowserKeys();
1033 break;
1035 case 'a': /* Add */
1036 case 'A':
1037 if(gmode&MDSCUR){ /* not allowed! */
1038 emlwrite(_("Add not allowed in restricted mode"),NULL);
1039 break;
1042 i = 0;
1043 child[0] = '\0';
1044 /* pass in default filename */
1045 if(fn && *fn){
1046 strncpy(child, fn, sizeof(child) - 1);
1047 child[sizeof(child) - 1] = '\0';
1050 while(!i){
1052 switch(status=mlreply_utf8(_("Name of file to add: "), child, NLINE,
1053 QFFILE, NULL)){
1054 case HELPCH:
1055 emlwrite(_("\007No help yet!"), NULL);
1056 /* remove break and sleep after help text is installed */
1057 sleep(3);
1058 break;
1059 case (CTRL|'L'):
1060 PaintBrowser(gmp, 0, &crow, &ccol);
1061 break;
1062 case ABORT:
1063 emlwrite(_("Add File Cancelled"), NULL);
1064 i++;
1065 break;
1066 case FALSE:
1068 * Support default filename. A return of 'FALSE' means
1069 * 'No change in filename' which is fine if we have
1070 * provided a default.
1071 * John Berthels <john.berthels@nexor.co.uk>
1073 /* FALLTHROUGH */
1074 case TRUE:
1075 i++;
1077 if(child[0] == '\0'){
1078 emlwrite(_("No file named. Add Cancelled."), NULL);
1079 break;
1082 if(!compresspath(gmp->dname, child, sizeof(child))){
1083 emlwrite(_("Too many ..'s in name"), NULL);
1084 break;
1087 if((gmode&MDTREE) && !in_oper_tree(child)){
1088 eml.s = opertree;
1089 emlwrite(_("\007Restricted mode allows Add in %s only"),
1090 &eml);
1091 break;
1094 if((status = fexist(child, "w", (off_t *)NULL)) == FIOSUC){
1095 snprintf(tmp, sizeof(tmp), _("File \"%.*s\" already exists!"),
1096 NLINE - 20, child);
1097 emlwrite(tmp, NULL);
1098 break;
1100 else if(status != FIOFNF){
1101 fioperr(status, child);
1102 break;
1105 if(ffwopen(child, FALSE) != FIOSUC){
1106 /* ffwopen should've complained */
1107 break;
1109 else{ /* highlight new file */
1110 ffclose();
1111 eml.s = child;
1112 emlwrite(_("Added File \"%s\""), &eml);
1114 if((p = strrchr(child, C_FILESEP)) == NULL){
1115 emlwrite(_("Problems refiguring browser"), NULL);
1116 break;
1119 *p = '\0';
1120 if(p != child){
1121 strncpy(tmp, child, sizeof(tmp));
1122 tmp[sizeof(tmp)-1] = '\0';
1123 j = 0;
1124 while((child[j++] = *++p) != '\0')
1127 else{
1128 strncpy(tmp, S_FILESEP, sizeof(tmp));
1129 tmp[sizeof(tmp)-1] = '\0';
1133 * new file in same dir? if so, refigure files
1134 * and redraw...
1136 if(!strcmp(tmp, gmp->dname)){
1137 if((mp = getfcells(gmp->dname, fb_flags)) == NULL)
1138 /* getfcells should explain what happened */
1139 break;
1141 zotmaster(&gmp);
1142 gmp = mp;
1143 if((tp = FindCell(gmp, child)) != NULL){
1144 gmp->current = tp;
1145 PlaceCell(gmp, gmp->current, &row, &col);
1148 PaintBrowser(gmp, 1, &crow, &ccol);
1151 break;
1152 default:
1153 break;
1157 BrowserKeys();
1158 break;
1160 case 'c': /* copy */
1161 case 'C':
1162 if(gmp->current->mode == FIODIR){
1163 emlwrite(_("\007Can't copy a directory"), NULL);
1164 break;
1167 if(gmode&MDSCUR){ /* not allowed! */
1168 emlwrite(_("Copy not allowed in restricted mode"),NULL);
1169 break;
1172 i = 0;
1173 child[0] = '\0';
1175 while(!i){
1177 switch(status=mlreply_utf8(_("Name of new copy: "), child, NLINE,
1178 QFFILE, NULL)){
1179 case HELPCH:
1180 emlwrite(_("\007No help yet!"), NULL);
1181 /* remove break and sleep after help text is installed */
1182 sleep(3);
1183 break;
1184 case (CTRL|'L'):
1185 PaintBrowser(gmp, 0, &crow, &ccol);
1186 break;
1187 case ABORT:
1188 emlwrite(_("Make Copy Cancelled"), NULL);
1189 i++;
1190 break;
1191 case FALSE:
1192 i++;
1193 mlerase();
1194 break;
1195 case TRUE:
1196 i++;
1198 if(child[0] == '\0'){
1199 emlwrite(_("No destination, file not copied"), NULL);
1200 break;
1203 if(!strcmp(gmp->current->fname, child)){
1204 emlwrite(_("\007Can't copy file on to itself!"), NULL);
1205 break;
1208 if(!compresspath(gmp->dname, child, sizeof(child))){
1209 emlwrite(_("Too many ..'s in name"), NULL);
1210 break;
1213 if((gmode&MDTREE) && !in_oper_tree(child)){
1214 eml.s = opertree;
1215 emlwrite(_("\007Restricted mode allows Copy in %s only"),
1216 &eml);
1217 break;
1220 if((status = fexist(child, "w", (off_t *)NULL)) == FIOSUC){
1221 /* TRANSLATORS: A question: File <filename> exists! Replace? */
1222 snprintf(tmp, sizeof(tmp), _("File \"%.*s\" exists! OVERWRITE"),
1223 NLINE - 20, child);
1224 if((status = mlyesno_utf8(tmp, 0)) != TRUE){
1225 emlwrite((status == ABORT)
1226 ? _("Make copy cancelled")
1227 : _("File Not Renamed"),
1228 NULL);
1229 break;
1232 else if(status != FIOFNF){
1233 fioperr(status, child);
1234 break;
1237 snprintf(tmp, sizeof(tmp), "%s%c%s", gmp->dname, C_FILESEP,
1238 gmp->current->fname);
1240 if(copy(tmp, child) < 0){
1241 /* copy() will report any error messages */
1242 break;
1244 else{ /* highlight new file */
1245 eml.s = child;
1246 emlwrite(_("File copied to %s"), &eml);
1248 if((p = strrchr(child, C_FILESEP)) == NULL){
1249 emlwrite(_("Problems refiguring browser"), NULL);
1250 break;
1253 *p = '\0';
1254 strncpy(tmp, (p == child) ? S_FILESEP: child, sizeof(tmp));
1255 tmp[sizeof(tmp)-1] = '\0';
1258 * new file in same dir? if so, refigure files
1259 * and redraw...
1261 if(!strcmp(tmp, gmp->dname)){
1262 strncpy(child, gmp->current->fname, sizeof(child));
1263 child[sizeof(child)-1] = '\0';
1264 if((mp = getfcells(gmp->dname, fb_flags)) == NULL)
1265 /* getfcells should explain what happened */
1266 break;
1268 zotmaster(&gmp);
1269 gmp = mp;
1270 if((tp = FindCell(gmp, child)) != NULL){
1271 gmp->current = tp;
1272 PlaceCell(gmp, gmp->current, &row, &col);
1275 PaintBrowser(gmp, 1, &crow, &ccol);
1278 break;
1279 default:
1280 break;
1283 BrowserKeys();
1284 break;
1286 case 'r': /* rename */
1287 case 'R':
1288 i = 0;
1290 if(!strcmp(gmp->current->fname, "..")){
1291 emlwrite(_("\007Can't rename \"..\""), NULL);
1292 break;
1295 if(gmode&MDSCUR){ /* not allowed! */
1296 emlwrite(_("Rename not allowed in restricted mode"),NULL);
1297 break;
1300 strncpy(child, gmp->current->fname, sizeof(child));
1301 child[sizeof(child)-1] = '\0';
1303 while(!i){
1305 switch(status=mlreply_utf8(_("Rename file to: "), child, NLINE, QFFILE,
1306 NULL)){
1307 case HELPCH:
1308 emlwrite(_("\007No help yet!"), NULL);
1309 /* remove break and sleep after help text is installed */
1310 sleep(3);
1311 break;
1312 case (CTRL|'L'):
1313 PaintBrowser(gmp, 0, &crow, &ccol);
1314 break;
1315 case ABORT:
1316 emlwrite(_("Rename cancelled"), NULL);
1317 i++;
1318 break;
1319 case FALSE:
1320 case TRUE:
1321 i++;
1323 if(child[0] == '\0' || status == FALSE){
1324 mlerase();
1325 break;
1328 if(!compresspath(gmp->dname, child, sizeof(child))){
1329 emlwrite(_("Too many ..'s in name"), NULL);
1330 break;
1333 if((gmode&MDTREE) && !in_oper_tree(child)){
1334 eml.s = opertree;
1335 emlwrite(_("\007Restricted mode allows Rename in %s only"),
1336 &eml);
1337 break;
1340 status = fexist(child, "w", (off_t *)NULL);
1341 if(status == FIOSUC || status == FIOFNF){
1342 if(status == FIOSUC){
1343 snprintf(tmp, sizeof(tmp), _("File \"%.*s\" exists! OVERWRITE"),
1344 NLINE - 20, child);
1346 if((status = mlyesno_utf8(tmp, FALSE)) != TRUE){
1347 emlwrite((status == ABORT)
1348 ? _("Rename cancelled")
1349 : _("Not Renamed"),
1350 NULL);
1351 break;
1355 snprintf(tmp, sizeof(tmp), "%s%c%s", gmp->dname, C_FILESEP,
1356 gmp->current->fname);
1358 if(our_rename(tmp, child) < 0){
1359 eml.s = errstr(errno);
1360 emlwrite(_("Rename Failed: %s"), &eml);
1362 else{
1363 if((p = strrchr(child, C_FILESEP)) == NULL){
1364 emlwrite(_("Problems refiguring browser"), NULL);
1365 break;
1368 *p = '\0';
1369 strncpy(tmp, (p == child) ? S_FILESEP: child, sizeof(tmp));
1370 tmp[sizeof(tmp)-1] = '\0';
1372 if((mp = getfcells(tmp, fb_flags)) == NULL)
1373 /* getfcells should explain what happened */
1374 break;
1376 zotmaster(&gmp);
1377 gmp = mp;
1379 if((tp = FindCell(gmp, ++p)) != NULL){
1380 gmp->current = tp;
1381 PlaceCell(gmp, gmp->current, &row, &col);
1384 PaintBrowser(gmp, 1, &crow, &ccol);
1385 mlerase();
1388 else{
1389 fioperr(status, child);
1391 break;
1392 default:
1393 break;
1396 BrowserKeys();
1397 break;
1399 case 'v': /* stand-alone */
1400 case 'V': /* browser "view" */
1401 case 's': /* user "select" */
1402 case 'S':
1403 case (CTRL|'M'):
1404 Selected:
1406 if(((new_c == 'S' || new_c == 's') && (gmode&MDBRONLY))
1407 || ((new_c == 'V' || new_c == 'v') && !(gmode&MDBRONLY)))
1408 goto Default;
1410 if(gmp->current->mode == FIODIR){
1411 *child = '\0';
1412 strncpy(tmp, gmp->dname, sizeof(tmp));
1413 tmp[sizeof(tmp)-1] = '\0';
1414 p = gmp->current->fname;
1415 if(p[0] == '.' && p[1] == '.' && p[2] == '\0'){
1416 if((p=strrchr(tmp, C_FILESEP)) != NULL){
1417 *p = '\0';
1419 if((gmode&MDTREE) && !in_oper_tree(tmp)){
1420 eml.s = PARENTDIR;
1421 emlwrite(
1422 _("\007Can't visit %s in restricted mode"),
1423 &eml);
1424 break;
1427 strncpy(child, &p[1], sizeof(child));
1428 child[sizeof(child)-1] = '\0';
1431 #if defined(DOS) || defined(OS2)
1432 (p == tmp || (tmp[1] == ':' && tmp[2] == '\0'))
1433 #else
1434 (p == tmp)
1435 #endif
1436 { /* is it root? */
1437 #if defined(DOS) || defined(OS2)
1438 if(*child){
1439 strncat(tmp, S_FILESEP, sizeof(tmp)-strlen(tmp)-1);
1440 tmp[sizeof(tmp)-1] = '\0';
1442 #else
1443 if(*child){
1444 strncpy(tmp, S_FILESEP, sizeof(tmp));
1445 tmp[sizeof(tmp)-1] = '\0';
1447 #endif
1448 else{
1449 emlwrite(_("\007Can't move up a directory"),
1450 NULL);
1451 break;
1456 else if((fb_flags&FB_SAVE) && p[0] == '.' && p[1] == '\0'){
1457 if ((strlen(gmp->dname) < dirlen) &&
1458 (strlen(gmp->current->fname) < fnlen)){
1459 strncpy(dir, gmp->dname, dirlen);
1460 dir[dirlen-1] = '\0';
1463 zotmaster(&gmp);
1464 return(0); /* just change the directory, still return no selection */
1466 else{
1467 if(tmp[1] != '\0'){ /* were in root? */
1468 strncat(tmp, S_FILESEP, sizeof(tmp)-strlen(tmp)-1);
1469 tmp[sizeof(tmp)-1] = '\0';
1472 strncat(tmp, gmp->current->fname, sizeof(tmp)-strlen(tmp));
1473 tmp[sizeof(tmp)-1] = '\0';
1476 if((mp = getfcells(tmp, fb_flags)) == NULL)
1477 /* getfcells should explain what happened */
1478 break;
1480 if(gmp->flags & FB_LMODE){
1481 mp->flags |= FB_LMODE;
1482 mp->lm = gmp->lm;
1483 gmp->lm = NULL;
1486 zotmaster(&gmp);
1487 gmp = mp;
1488 tp = NULL;
1490 if(*child){
1491 if((tp = FindCell(gmp, child)) != NULL){
1492 gmp->current = tp;
1493 PlaceCell(gmp, gmp->current, &row, &col);
1495 else{
1496 eml.s = child;
1497 emlwrite(_("\007Problem finding dir \"%s\""), &eml);
1501 PaintBrowser(gmp, 0, &crow, &ccol);
1502 if(!*child){
1503 char b[100];
1505 snprintf(b, sizeof(b), "Select/View \" .. %s %s\" to return to previous directory.", PARENTDIR, DIRWORD);
1506 emlwrite(b, NULL);
1509 break;
1511 else if(gmode&MDBRONLY){
1512 snprintf(child, sizeof(child), "%s%c%s", gmp->dname, C_FILESEP,
1513 gmp->current->fname);
1515 if(LikelyASCII(child)){
1516 snprintf(tmp, sizeof(tmp), "%s \'%s\'",
1517 (envp = (char *) getenv("PAGER"))
1518 ? envp : BROWSER_PAGER, child);
1519 BrowserRunChild(tmp, gmp->dname);
1520 PaintBrowser(gmp, 0, &crow, &ccol);
1523 break;
1525 else{ /* just return */
1526 if(gmp->flags & FB_LMODEPOS){
1528 if(!lmreturn){
1529 emlwrite("Programming error, called FileBrowse with LMODEPOS but no lmreturn", NULL);
1530 zotmaster(&gmp);
1531 return(-1);
1534 /* user actually used ListMode, the list is finished */
1535 if(gmp->flags & FB_LMODE){
1536 if(!gmp->lm){
1537 (*term.t_beep)();
1538 emlwrite(_("No files are selected, use \"X\" to mark files for selection"), NULL);
1539 break;
1542 *lmreturn = gmp->lm;
1543 gmp->lm = NULL;
1545 else{ /* construct an lmreturn for user */
1546 LMLIST *new;
1547 size_t flen, dlen;
1549 if((new=(LMLIST *)malloc(sizeof(*new))) == NULL
1550 || (new->fname=malloc(gmp->current->fname ? (flen=strlen(gmp->current->fname))+1 : 1)) == NULL
1551 || (new->dir=malloc((dlen=strlen(gmp->dname))+1)) == NULL){
1552 emlwrite("\007Can't malloc space for filename", NULL);
1553 return(-1);
1556 strncpy(new->fname,
1557 gmp->current->fname ? gmp->current->fname : "", flen);
1558 new->fname[flen] = '\0';
1559 strncpy(new->dir, gmp->dname, dlen);
1560 new->dir[dlen] = '\0';
1561 strncpy(new->size, gmp->current->size, sizeof(new->size));
1562 new->size[sizeof(new->size)-1] = '\0';
1563 new->next = NULL;
1564 *lmreturn = new;
1567 zotmaster(&gmp);
1568 return(1);
1571 if ((strlen(gmp->dname) < dirlen) &&
1572 (strlen(gmp->current->fname) < fnlen)){
1573 strncpy(dir, gmp->dname, dirlen);
1574 dir[dirlen-1] = '\0';
1575 strncpy(fn, gmp->current->fname, fnlen);
1576 fn[fnlen-1] = '\0';
1578 else {
1579 zotmaster(&gmp);
1580 return(-1);
1582 if(sz != NULL){ /* size uninteresting */
1583 strncpy(sz, gmp->current->size, szlen);
1584 sz[szlen-1] = '\0';
1587 zotmaster (&gmp);
1588 return (1);
1590 break;
1592 case 'w': /* Where is */
1593 case 'W':
1594 case (CTRL|'W'):
1595 i = 0;
1597 while(!i){
1598 int bsearch = 0;
1600 switch(readpattern(_("File name to find"), FALSE, bsearch)){
1601 case HELPCH:
1602 emlwrite(_("\007No help yet!"), NULL);
1603 /* remove break and sleep after help text is installed */
1604 sleep(3);
1605 break;
1606 case (CTRL|'L'):
1607 PaintBrowser(gmp, 0, &crow, &ccol);
1608 break;
1609 case (CTRL|'P'):
1610 bsearch = bsearch == 0 ? 1 : 0;
1611 break;
1612 case (CTRL|'Y'): /* first first cell */
1613 for(tp = gmp->top; tp->prev; tp = tp->prev)
1616 i++;
1617 /* fall thru to repaint */
1618 case (CTRL|'V'):
1619 if(!i){
1621 tp = gmp->top;
1622 if((i = term.t_nrow - term.t_mrow - 2) <= 0)
1623 break;
1625 while(i-- && tp->next){
1626 j = 0;
1627 while(++j <= gmp->fpl && tp->next)
1628 tp = tp->next;
1631 if(i < 0)
1632 gmp->top = tp;
1634 while(tp->next);
1636 emlwrite(_("Searched to end of directory"), NULL);
1638 else
1639 emlwrite(_("Searched to start of directory"), NULL);
1641 if(tp){
1642 PlaceCell(gmp, gmp->current, &row, &col);
1643 PaintCell(row, col, gmp->cpf, gmp->current, 0);
1644 gmp->current = tp;
1645 if(PlaceCell(gmp, gmp->current, &row, &col)){
1646 PaintBrowser(gmp, 1, &crow, &ccol);
1648 else{
1649 PaintCell(row, col, gmp->cpf, gmp->current, 1);
1650 crow = row;
1651 ccol = col;
1655 i++; /* make sure we jump out */
1656 break;
1657 case ABORT:
1658 emlwrite(_("Whereis cancelled"), NULL);
1659 i++;
1660 break;
1661 case FALSE:
1662 mlerase();
1663 i++;
1664 break;
1665 case TRUE:
1667 char *utf8 = NULL;
1669 if(pat && pat[0])
1670 utf8 = ucs4_to_utf8_cpystr(pat);
1672 if(utf8 && (tp = FindCell(gmp, utf8)) != NULL){
1673 PlaceCell(gmp, gmp->current, &row, &col);
1674 PaintCell(row, col, gmp->cpf, gmp->current, 0);
1675 gmp->current = tp;
1677 if(PlaceCell(gmp, tp, &row, &col)){ /* top changed */
1678 PaintBrowser(gmp, 1, &crow, &ccol);
1680 else{
1681 PaintCell(row, col, gmp->cpf, gmp->current, 1);
1682 crow = row;
1683 ccol = col;
1685 mlerase();
1687 else{
1688 eml.s = utf8;
1689 emlwrite(_("\"%s\" not found"), &eml);
1692 if(utf8)
1693 fs_give((void **) &utf8);
1696 i++;
1697 break;
1698 default:
1699 break;
1703 BrowserKeys();
1704 break;
1706 case (CTRL|'\\') :
1707 #if defined MOUSE && !defined(_WINDOWS)
1708 toggle_xterm_mouse(0,1);
1709 #else
1710 unknown_command(c);
1711 #endif
1712 break;
1714 case (CTRL|'Z'):
1715 if(gmode&MDSSPD){
1716 bktoshell(0, 1);
1717 PaintBrowser(gmp, 0, &crow, &ccol);
1718 break;
1719 } /* fall thru with error! */
1721 default: /* what? */
1722 Default:
1723 unknown_command(c);
1725 case NODATA: /* no op */
1726 break;
1733 * getfcells - make a master browser struct and fill it in
1734 * return NULL if there's a problem.
1736 struct bmaster *
1737 getfcells(char *dname, int fb_flags)
1739 int i, /* various return codes */
1740 flength,
1741 nentries = 0; /* number of dir ents */
1742 off_t attsz;
1743 char *np, /* names of files in dir */
1744 *dcp, /* to add file to path */
1745 *tmpstr,
1746 **filtnames, /* array filtered names */
1747 errbuf[NLINE];
1748 struct fcell *ncp, /* new cell pointer */
1749 *tcp; /* trailing cell ptr */
1750 struct bmaster *mp;
1751 EML eml;
1753 errbuf[0] = '\0';
1754 if((mp=(struct bmaster *)malloc(sizeof(struct bmaster))) == NULL){
1755 emlwrite("\007Can't malloc space for master filename cell", NULL);
1756 return(NULL);
1759 memset(mp, 0, sizeof(*mp));
1761 if(dname[0] == '.' && dname[1] == '\0'){ /* remember this dir */
1762 if(!getcwd(mp->dname, 256))
1763 mp->dname[0] = '\0';
1765 else if(dname[0] == '.' && dname[1] == '.' && dname[2] == '\0'){
1766 if(!getcwd(mp->dname, 256))
1767 mp->dname[0] = '\0';
1768 else{
1769 if((np = (char *)strrchr(mp->dname, C_FILESEP)) != NULL)
1770 if(np != mp->dname)
1771 *np = '\0';
1774 else{
1775 strncpy(mp->dname, dname, sizeof(mp->dname));
1776 mp->dname[sizeof(mp->dname)-1] = '\0';
1779 mp->head = mp->top = NULL;
1780 mp->cpf = mp->fpl = 0;
1781 mp->longest = 5; /* .. must be labeled! */
1782 mp->flags = fb_flags;
1784 eml.s = mp->dname;
1785 emlwrite("Building file list of %s...", &eml);
1787 if((mp->names = getfnames(mp->dname, NULL, &nentries, errbuf, sizeof(errbuf))) == NULL){
1788 free((char *) mp);
1789 if(*errbuf)
1790 emlwrite(errbuf, NULL);
1792 return(NULL);
1796 * this is the fun part. build an array of pointers to the fnames we're
1797 * interested in (i.e., do any filtering), then pass that off to be
1798 * sorted before building list of cells...
1800 * right now default filtering on ".*" except "..", but this could
1801 * easily be made a user option later on...
1803 if((filtnames=(char **)malloc((nentries+1) * sizeof(char *))) == NULL){
1804 emlwrite("\007Can't malloc space for name array", NULL);
1805 zotmaster(&mp);
1806 return(NULL);
1809 i = 0; /* index of filt'd array */
1810 np = mp->names;
1811 while(nentries--){
1812 int ii;
1813 int width;
1816 * Filter dot files? Always filter ".", never filter "..",
1817 * and sometimes fitler ".*"...
1819 if(*np == '.' && (!(*(np+1) == '.' && *(np+2) == '\0')
1820 && !(*(np+1) == '\0' && (fb_flags&FB_SAVE)))
1821 && (*(np+1) == '\0' || !(gmode & MDDOTSOK))){
1822 np += strlen(np) + 1;
1823 continue;
1826 filtnames[i++] = np;
1828 ii = (int) strlen(np);
1829 if((width = (int) utf8_width(np)) > mp->longest)
1830 mp->longest = width; /* remember longest */
1832 np += ii + 1; /* advance name pointer */
1835 nentries = i; /* new # of entries */
1838 * sort files case independently
1840 qsort((qsort_t *)filtnames, (size_t)nentries, sizeof(char *), sstrcasecmp);
1843 * this is so we use absolute path names for stats.
1844 * remember: be careful using dname as directory name, and fix mp->dname
1845 * when we're done
1847 dcp = (char *) strchr(mp->dname, '\0');
1848 if(dcp == mp->dname || dcp[-1] != C_FILESEP){
1849 dcp[0] = C_FILESEP;
1850 dcp[1] = '\0';
1852 else
1853 dcp--;
1855 i = 0;
1856 while(nentries--){ /* stat filtered files */
1857 /* get a new cell */
1858 if((ncp=(struct fcell *)malloc(sizeof(struct fcell))) == NULL){
1859 emlwrite("\007Can't malloc cells for browser!", NULL);
1860 zotfcells(mp->head); /* clean up cells */
1861 free((char *) filtnames);
1862 free((char *) mp);
1863 return(NULL); /* bummer. */
1866 ncp->next = ncp->prev = NULL;
1868 if(mp->head == NULL){ /* tie it onto the list */
1869 mp->head = mp->top = mp->current = ncp;
1871 else{
1872 tcp->next = ncp;
1873 ncp->prev = tcp;
1876 tcp = ncp;
1878 /* fill in the new cell */
1879 ncp->fname = filtnames[i++];
1881 /* fill in file's mode */
1882 if ((flength = strlen(ncp->fname) + 1 + strlen(dname)) < sizeof(mp->dname)){
1883 strncpy(&dcp[1], ncp->fname, sizeof(mp->dname)-(dcp+1-mp->dname)); /* use absolute path! */
1884 mp->dname[sizeof(mp->dname)-1] = '\0';
1885 tmpstr = mp->dname;
1887 else{
1888 if((tmpstr = (char *)malloc((flength+1)*sizeof(char))) == NULL){
1889 emlwrite("\007Can't malloc cells for temp buffer!", NULL);
1890 zotfcells(mp->head); /* clean up cells */
1891 free((char *) filtnames);
1892 free((char *) mp);
1893 return(NULL); /* bummer. */
1896 strncpy(tmpstr, dname, flength);
1897 tmpstr[flength] = '\0';
1898 tmpstr = strncat(tmpstr, S_FILESEP, flength+1-1-strlen(tmpstr));
1899 tmpstr[flength] = '\0';
1900 tmpstr = strncat(tmpstr, ncp->fname, flength+1-1-strlen(tmpstr));
1901 tmpstr[flength] = '\0';
1904 switch(fexist(tmpstr, "t", &attsz)){
1905 case FIODIR :
1906 ncp->mode = FIODIR;
1907 snprintf(ncp->size, sizeof(ncp->size), "(%s%s%s)",
1908 (ncp->fname[0] == '.' && ncp->fname[1] == '.'
1909 && ncp->fname[2] == '\0') ? PARENTDIR :
1910 ((fb_flags&FB_SAVE) && ncp->fname[0] == '.' && ncp->fname[1] == '\0'
1911 ? SELECTWORD : ""),
1912 (ncp->fname[0] == '.' && ncp->fname[1] == '.'
1913 && ncp->fname[2] == '\0') ? " " :
1914 ((fb_flags&FB_SAVE) && ncp->fname[0] == '.' && ncp->fname[1] == '\0'
1915 ? " " : ""),
1916 DIRWORD);
1917 break;
1919 case FIOSYM :
1920 ncp->mode = FIOSYM;
1921 strncpy(ncp->size, "--", sizeof(ncp->size));
1922 ncp->size[sizeof(ncp->size)-1] = '\0';
1923 break;
1925 default :
1926 ncp->mode = FIOSUC; /* regular file */
1927 strncpy(ncp->size, prettysz(attsz), sizeof(ncp->size));
1928 ncp->size[sizeof(ncp->size)-1] = '\0';
1929 break;
1932 if (flength >= NLINE)
1933 free((char *) tmpstr);
1936 dcp[(dcp == mp->dname) ? 1 : 0] = '\0'; /* remember to cap dname */
1937 free((char *) filtnames); /* 'n blast filt'd array*/
1939 percdircells(mp);
1940 layoutcells(mp);
1941 if(strlen(mp->dname) < sizeof(browse_dir)){
1942 strncpy(browse_dir, mp->dname, sizeof(browse_dir));
1943 browse_dir[sizeof(browse_dir)-1] = '\0';
1945 else
1946 browse_dir[0] = '\0';
1948 return(mp);
1953 fcell_is_selected(struct fcell *cell, struct bmaster *mp)
1955 LMLIST *lm;
1957 if(cell && cell->fname){
1958 for(lm = mp ? mp->lm : NULL; lm; lm = lm->next){
1959 /* directory has to match */
1960 if(!((mp->dname[0] == '\0' && (!lm->dir || lm->dir[0] =='\0'))
1961 || (mp->dname[0] != '\0' && lm->dir && lm->dir[0] !='\0'
1962 && !strcmp(mp->dname, lm->dir))))
1963 continue;
1965 if(lm->fname && !strcmp(cell->fname, lm->fname))
1966 return(1);
1970 return(0);
1975 * Adds a new name to the head of the lmlist
1977 void
1978 add_cell_to_lmlist(struct fcell *cell, struct bmaster *mp)
1980 LMLIST *new;
1981 size_t flen, dlen;
1983 if(mp && cell && cell->fname && cell->fname[0]){
1984 if((new=(LMLIST *)malloc(sizeof(*new))) == NULL ||
1985 (new->fname=malloc(sizeof(char)*((flen=strlen(cell->fname))+1))) == NULL ||
1986 (new->dir=malloc(sizeof(char)*((dlen=strlen(mp->dname))+1))) == NULL){
1987 emlwrite("\007Can't malloc space for filename", NULL);
1988 return;
1991 strncpy(new->fname, cell->fname, flen);
1992 new->fname[flen] = '\0';
1993 strncpy(new->dir, mp->dname, dlen);
1994 new->dir[dlen] = '\0';
1995 new->size[0] = '\0';
1996 if(cell->size[0]){
1997 strncpy(new->size, cell->size, sizeof(new->size));
1998 new->size[sizeof(new->size)-1] = '\0';
2001 new->next = mp->lm;
2002 mp->lm = new;
2008 * Deletes a name from the lmlist
2010 void
2011 del_cell_from_lmlist(struct fcell *cell, struct bmaster *mp)
2013 LMLIST *lm, *lmprev = NULL;
2015 if(mp && cell && cell->fname && cell->fname[0])
2016 for(lm = mp ? mp->lm : NULL; lm; lm = lm->next){
2017 if(lm->fname && strcmp(cell->fname, lm->fname) == 0){
2018 free((char *) lm->fname);
2019 if(lm->dir)
2020 free((char *) lm->dir);
2022 if(lmprev)
2023 lmprev->next = lm->next;
2024 else
2025 mp->lm = lm->next;
2027 free((char *) lm);
2029 break;
2032 lmprev = lm;
2038 * PaintCell - print the given cell at the given location on the display
2039 * the format of a printed cell is:
2041 * "<fname> <size> "
2043 void
2044 PaintCell(int row, int col,
2045 int sc, /* screen columns available for this cell */
2046 struct fcell *cell, int inverted)
2048 char buf1[NLINE], buf2[NLINE];
2049 char lbuf[5];
2050 int need, l_wid, f_wid, f_to_s_wid, s_wid;
2051 UCS *ucs;
2053 if(cell == NULL)
2054 return;
2056 l_wid = (gmp && ((gmp->flags & FB_LMODEPOS) > 4)) ? 4 : 0;
2057 f_wid = utf8_width(cell->fname ? cell->fname : "");
2058 f_to_s_wid = 1;
2059 s_wid = utf8_width(cell->size ? cell->size : "");
2061 /* the two is the space between cell columns */
2062 sc = MIN(sc-2, term.t_ncol-col);
2064 if(l_wid){
2065 if(gmp && gmp->flags & FB_LMODE && cell->mode != FIODIR){
2067 * We have to figure out here if it is selected or not
2068 * and use that to write the X or space.
2070 lbuf[0] = '[';
2071 if(fcell_is_selected(cell, gmp))
2072 lbuf[1] = 'X';
2073 else
2074 lbuf[1] = ' ';
2076 lbuf[2] = ']';
2077 lbuf[3] = ' ';
2079 else{
2080 lbuf[0] = lbuf[1] = lbuf[2] = lbuf[3] = ' ';
2083 lbuf[4] = '\0';
2086 sc -= l_wid;
2088 need = f_wid+f_to_s_wid+s_wid;
2090 /* space between name and size */
2091 if(need < sc)
2092 f_to_s_wid += (sc-need);
2095 * If the width isn't enough to handle everything we're just putting a single
2096 * space between fname and size and truncating on the right. That means that
2097 * the sizes in the right hand column won't line up correctly when there is
2098 * a lack of space. Instead, we opt for displaying as much info as possible
2099 * in a slightly discordant way.
2102 movecursor(row, col);
2103 if(l_wid){
2104 ucs = utf8_to_ucs4_cpystr(lbuf);
2105 if(ucs){
2106 pputs(ucs, 0);
2107 fs_give((void **) &ucs);
2111 utf8_snprintf(buf1, sizeof(buf1), "%*.*w%*.*w%*.*w",
2112 f_wid, f_wid, cell->fname ? cell->fname : "",
2113 f_to_s_wid, f_to_s_wid, "",
2114 s_wid, s_wid, cell->size ? cell->size : "");
2116 utf8_snprintf(buf2, sizeof(buf2), "%*.*w", sc, sc, buf1);
2118 if(inverted)
2119 (*term.t_rev)(1);
2121 ucs = utf8_to_ucs4_cpystr(buf2);
2122 if(ucs){
2123 pputs(ucs, 0);
2124 fs_give((void **) &ucs);
2127 if(inverted)
2128 (*term.t_rev)(0);
2133 * PaintBrowse - with the current data, display the browser. if level == 0
2134 * paint the whole thing, if level == 1 just paint the cells
2135 * themselves
2137 void
2138 PaintBrowser(struct bmaster *mp, int level, int *row, int *col)
2140 int i, cl;
2141 struct fcell *tp;
2143 if(!level){
2144 ClearBrowserScreen();
2145 BrowserAnchor(mp->dname);
2148 i = 0;
2149 tp = mp->top;
2150 cl = COMPOSER_TOP_LINE; /* current display line */
2151 while(tp){
2153 PaintCell(cl, mp->cpf * i, mp->cpf, tp, tp == mp->current);
2155 if(tp == mp->current){
2156 if(row)
2157 *row = cl;
2159 if(col)
2160 *col = mp->cpf * i;
2163 if(++i >= mp->fpl){
2164 i = 0;
2165 if(++cl > term.t_nrow-(term.t_mrow+1))
2166 break;
2169 tp = tp->next;
2172 if(level){
2173 while(cl <= term.t_nrow - (term.t_mrow+1)){
2174 if(!i)
2175 movecursor(cl, 0);
2176 peeol();
2177 movecursor(++cl, 0);
2180 else{
2181 BrowserKeys();
2187 * BrowserKeys - just paints the keyhelp at the bottom of the display
2189 void
2190 BrowserKeys(void)
2192 menu_browse[QUIT_KEY].name = (gmode&MDBRONLY) ? "Q" : "E";
2193 /* TRANSLATORS: Brwsr is an abbreviation for Browser, which is
2194 a command used to look through something */
2195 menu_browse[QUIT_KEY].label = (gmode&MDBRONLY) ? N_("Quit") : N_("Exit Brwsr");
2196 menu_browse[GOTO_KEY].name = (gmode&MDGOTO) ? "G" : NULL;
2197 menu_browse[GOTO_KEY].label = (gmode&MDGOTO) ? N_("Goto") : NULL;
2198 if(gmode & MDBRONLY){
2199 menu_browse[EXEC_KEY].name = "L";
2200 menu_browse[EXEC_KEY].label = N_("Launch");
2201 menu_browse[SELECT_KEY].name = "V";
2202 menu_browse[SELECT_KEY].label = "[" N_("View") "]";
2203 menu_browse[PICO_KEY].name = "E";
2204 menu_browse[PICO_KEY].label = N_("Edit");
2206 else{
2207 menu_browse[SELECT_KEY].name = "S";
2208 menu_browse[SELECT_KEY].label = "[" N_("Select") "]";
2209 menu_browse[PICO_KEY].name = "A";
2210 menu_browse[PICO_KEY].label = N_("Add");
2212 if(gmp && gmp->flags & FB_LMODEPOS){
2213 if(gmp && gmp->flags & FB_LMODE){ /* ListMode is already on */
2214 menu_browse[EXEC_KEY].name = "X";
2215 menu_browse[EXEC_KEY].label = N_("Set/Unset");
2217 else{ /* ListMode is possible */
2218 menu_browse[EXEC_KEY].name = "L";
2219 menu_browse[EXEC_KEY].label = N_("ListMode");
2222 else{ /* No ListMode possible */
2223 menu_browse[EXEC_KEY].name = NULL;
2224 menu_browse[EXEC_KEY].label = NULL;
2228 wkeyhelp(menu_browse);
2233 * layoutcells - figure out max length of cell and how many cells can
2234 * go on a line of the display
2236 void
2237 layoutcells(struct bmaster *mp)
2239 static int wid = -1;
2241 * Max chars/file. Actually this is max screen cells/file
2242 * Longest name + "(parent dir)"
2244 if(wid < 0)
2245 wid = MAX(utf8_width(PARENTDIR),utf8_width(SELECTWORD)) + utf8_width(DIRWORD);
2247 mp->cpf = mp->longest + wid + 3;
2249 if(mp->flags & FB_LMODEPOS) /* "[X] " */
2250 mp->cpf += 4;
2252 if(gmode & MDONECOL){
2253 mp->fpl = 1;
2255 else{
2256 int i = 1;
2258 while(i*mp->cpf - 2 <= term.t_ncol) /* no force... */
2259 i++; /* like brute force! */
2261 mp->fpl = i - 1; /* files per line */
2264 if(mp->fpl == 0)
2265 mp->fpl = 1;
2270 * percdircells - bubble all the directory cells to the top of the
2271 * list.
2273 void
2274 percdircells(struct bmaster *mp)
2276 struct fcell *dirlp, /* dir cell list pointer */
2277 *lp, *nlp; /* cell list ptr and next */
2279 dirlp = NULL;
2280 for(lp = mp->head; lp; lp = nlp){
2281 nlp = lp->next;
2282 if(lp->mode == FIODIR){
2283 if(lp->prev) /* clip from list */
2284 lp->prev->next = lp->next;
2286 if(lp->next)
2287 lp->next->prev = lp->prev;
2289 if((lp->prev = dirlp) != NULL){ /* tie it into dir portion */
2290 if((lp->next = dirlp->next) != NULL)
2291 lp->next->prev = lp;
2293 dirlp->next = lp;
2294 dirlp = lp;
2296 else{
2297 if((dirlp = lp) != mp->head)
2298 dirlp->next = mp->head;
2300 if(dirlp->next)
2301 dirlp->next->prev = dirlp;
2303 mp->head = mp->top = mp->current = dirlp;
2311 * PlaceCell - given a browser master and a cell, return row and col of the display that
2312 * it should go on.
2314 * return 1 if mp->top has changed, x,y relative to new page
2315 * return 0 if otherwise (same page)
2316 * return -1 on error
2319 PlaceCell(struct bmaster *mp, struct fcell *cp, int *x, int *y)
2321 int cl = COMPOSER_TOP_LINE; /* current line */
2322 int ci = 0; /* current index on line */
2323 int rv = 0;
2324 int secondtry = 0;
2325 struct fcell *tp;
2327 /* will cp fit on screen? */
2328 tp = mp->top;
2329 while(1){
2330 if(tp == cp){ /* bingo! */
2331 *x = cl;
2332 *y = ci * mp->cpf;
2333 break;
2336 if((tp = tp->next) == NULL){ /* above top? */
2337 if(secondtry++){
2338 emlwrite("\007Internal error: can't find fname cell", NULL);
2339 return(-1);
2341 else{
2342 tp = mp->top = mp->head; /* try from the top! */
2343 cl = COMPOSER_TOP_LINE;
2344 ci = 0;
2345 rv = 1;
2346 continue; /* start over! */
2350 if(++ci >= mp->fpl){ /* next line? */
2351 ci = 0;
2352 if(++cl > term.t_nrow-(term.t_mrow+1)){ /* next page? */
2353 ci = mp->fpl; /* tp is at bottom right */
2354 while(ci--) /* find new top */
2355 tp = tp->prev;
2356 mp->top = tp;
2357 ci = 0;
2358 cl = COMPOSER_TOP_LINE; /* keep checking */
2359 rv = 1;
2365 /* not on display! */
2366 return(rv);
2371 * zotfcells - clean up malloc'd cells of file names
2373 void
2374 zotfcells(struct fcell *hp)
2376 struct fcell *tp;
2378 while(hp){
2379 tp = hp;
2380 hp = hp->next;
2381 tp->next = NULL;
2382 free((char *) tp);
2388 * zotmaster - blast the browser master struct
2390 void
2391 zotmaster(struct bmaster **mp)
2393 if(mp && *mp){
2394 zotfcells((*mp)->head); /* free cells */
2395 zotlmlist((*mp)->lm); /* free lmlist */
2396 if((*mp)->names)
2397 free((char *)(*mp)->names); /* free file names */
2399 free((char *)*mp); /* free master */
2400 *mp = NULL; /* make double sure */
2406 * FindCell - starting from the current cell find the first occurance of
2407 * the given string wrapping around if necessary
2409 struct fcell *
2410 FindCell(struct bmaster *mp, char *utf8string)
2412 struct fcell *tp, *fp;
2414 if(*utf8string == '\0')
2415 return(NULL);
2417 fp = NULL;
2418 tp = mp->current->next;
2420 while(tp && !fp){
2421 if(sisin(tp->fname, utf8string))
2422 fp = tp;
2423 else
2424 tp = tp->next;
2427 tp = mp->head;
2428 while(tp != mp->current && !fp){
2429 if(sisin(tp->fname, utf8string))
2430 fp = tp;
2431 else
2432 tp = tp->next;
2435 return(fp);
2440 * sisin - case insensitive substring matching function
2442 * We can't really do case-insensitive for non-ascii, so restrict
2443 * that to ascii characters. The strings will be utf8.
2446 sisin(char *bigstr, char *utf8substr)
2448 register int j;
2450 while(*bigstr){
2451 j = 0;
2452 while(bigstr[j] == utf8substr[j]
2453 || ((unsigned char)bigstr[j] < 0x80 && (unsigned char)utf8substr[j] < 0x80
2454 && toupper((unsigned char)bigstr[j]) == toupper((unsigned char)utf8substr[j])))
2455 if(utf8substr[++j] == '\0') /* bingo! */
2456 return(1);
2458 bigstr++;
2461 return(0);
2466 * set_browser_title -
2468 void
2469 set_browser_title(char *s)
2471 browser_title = s;
2476 * BrowserAnchor - draw the browser's anchor line.
2478 void
2479 BrowserAnchor(char *utf8dir)
2481 char *pdir;
2482 char titlebuf[NLINE];
2483 char buf[NLINE];
2484 char dirbuf[NLINE];
2485 char *dots = "...";
2486 char *br = "BROWSER";
2487 char *dir = "Dir: ";
2488 UCS *ucs;
2489 int need, extra, avail;
2490 int title_wid, b_wid, t_to_b_wid, b_to_d_wid, d_wid, dot_wid, dir_wid, after_dir_wid;
2491 COLOR_PAIR *lastc = NULL;
2493 if(!utf8dir)
2494 utf8dir = "";
2496 pdir = utf8dir;
2498 if(browser_title)
2499 snprintf(titlebuf, sizeof(buf), " %s", browser_title);
2500 else if(Pmaster)
2501 snprintf(titlebuf, sizeof(buf), " ALPINE %s", Pmaster->pine_version);
2502 else
2503 snprintf(titlebuf, sizeof(buf), " UW PICO %s", (gmode&MDBRONLY) ? "BROWSER" : version);
2505 title_wid = utf8_width(titlebuf);
2506 t_to_b_wid = 15;
2507 b_wid = utf8_width(br);
2508 b_to_d_wid = 4;
2509 d_wid = utf8_width(dir);
2510 dot_wid = 0;
2511 dir_wid = utf8_width(pdir);
2513 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2515 if(need > term.t_ncol){
2516 extra = need - term.t_ncol;
2517 t_to_b_wid -= MIN(extra, 10);
2518 need -= MIN(extra, 10);
2520 if(need > term.t_ncol){
2521 extra = need - term.t_ncol;
2522 b_to_d_wid -= MIN(extra, 2);
2523 need -= MIN(extra, 2);
2526 if(need > term.t_ncol){
2527 titlebuf[0] = titlebuf[1] = ' ';
2528 titlebuf[2] = '\0';
2529 title_wid = utf8_width(titlebuf);
2530 t_to_b_wid = 0;
2531 b_to_d_wid = 4;
2532 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2533 if(need > term.t_ncol){
2534 extra = need - term.t_ncol;
2535 b_to_d_wid -= MIN(extra, 2);
2536 need -= MIN(extra, 2);
2539 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2540 if(need > term.t_ncol){
2541 t_to_b_wid = 0;
2542 b_wid = 0;
2543 b_to_d_wid = 0;
2546 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2547 if(need > term.t_ncol)
2548 d_wid = 0;
2550 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2552 if(need > term.t_ncol && dir_wid > 0){
2553 dot_wid = utf8_width(dots);
2554 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2555 while(need > term.t_ncol && (pdir = strchr(pdir+1, C_FILESEP))){
2556 dir_wid = utf8_width(pdir);
2557 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2560 if(!pdir){ /* adjust other widths to fill up space */
2561 avail = term.t_ncol - (title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid);
2562 if(avail > 2)
2563 utf8_to_width_rhs(dirbuf, utf8dir, sizeof(dirbuf), avail);
2564 else
2565 dirbuf[0] = '\0';
2567 pdir = dirbuf;
2570 dir_wid = utf8_width(pdir);
2575 extra = term.t_ncol - (title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid);
2577 if(extra >= 0)
2578 after_dir_wid = extra;
2579 else
2580 after_dir_wid = 0;
2582 utf8_snprintf(buf, sizeof(buf), "%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w",
2583 title_wid, title_wid, titlebuf,
2584 t_to_b_wid, t_to_b_wid, "",
2585 b_wid, b_wid, br,
2586 b_to_d_wid, b_to_d_wid, "",
2587 d_wid, d_wid, dir,
2588 dot_wid, dot_wid, dots,
2589 dir_wid, dir_wid, pdir,
2590 after_dir_wid, after_dir_wid, "");
2592 /* just making sure */
2593 utf8_snprintf(titlebuf, sizeof(titlebuf), "%-*.*w", term.t_ncol, term.t_ncol, buf);
2595 movecursor(0, 0);
2596 if(Pmaster && Pmaster->colors && Pmaster->colors->tbcp
2597 && pico_is_good_colorpair(Pmaster->colors->tbcp)){
2598 lastc = pico_get_cur_color();
2599 (void) pico_set_colorp(Pmaster->colors->tbcp, PSC_NONE);
2601 else
2602 (*term.t_rev)(1);
2604 ucs = utf8_to_ucs4_cpystr(titlebuf);
2605 if(ucs){
2606 pputs(ucs, 0);
2607 fs_give((void **) &ucs);
2610 if(lastc){
2611 (void) pico_set_colorp(lastc, PSC_NONE);
2612 free_color_pair(&lastc);
2614 else
2615 (*term.t_rev)(0);
2620 * ResizeBrowser - handle a resize event
2623 ResizeBrowser(void)
2625 if(gmp){
2626 layoutcells(gmp);
2627 PaintBrowser(gmp, 0, NULL, NULL);
2628 return(1);
2630 else
2631 return(0);
2635 void
2636 ClearBrowserScreen(void)
2638 int i;
2640 for(i = 0; i <= term.t_nrow; i++){ /* clear screen */
2641 movecursor(i, 0);
2642 peeol();
2647 void
2648 BrowserRunChild(char *child, char *dir)
2650 int status;
2651 char tmp[NLINE];
2652 time_t t_in, t_out;
2654 ClearBrowserScreen();
2655 movecursor(0, 0);
2656 (*term.t_close)();
2657 if(!isdir(dir, NULL, &t_in))
2658 t_in = 0;
2660 fflush(stdout);
2661 status = system(child);
2662 (*term.t_open)();
2663 if(t_in && isdir(dir, NULL, &t_out) && t_in < t_out){
2664 struct bmaster *mp;
2666 if((mp = getfcells(dir, 0)) != NULL){
2667 zotmaster(&gmp);
2668 gmp = mp;
2670 /* else getfcells should explain what happened */
2673 /* complain about non-zero exit status */
2674 if((status >> 8) & 0xff){
2676 movecursor(term.t_nrow - 1, 0);
2677 snprintf(tmp, sizeof(tmp), "[ \"%.30s\" exit with error value: %d ]",
2678 child, (status >> 8) & 0xff);
2679 pputs_utf8(tmp, 1);
2680 movecursor(term.t_nrow, 0);
2681 pputs_utf8("[ Hit RETURN to continue ]", 1);
2682 fflush(stdout);
2684 while(GetKey() != (CTRL|'M')){
2685 (*term.t_beep)();
2686 fflush(stdout);
2693 * imitate pc-pine memory for where we last called the file browser.
2695 void
2696 p_chdir(struct bmaster *mp)
2698 if(mp && mp->dname)
2699 chdir(mp->dname);
2701 #else /* _WINDOWS */
2705 * pico_file_browse - Exported version of FileBrowse below.
2708 pico_file_browse(PICO *pdata, char *dir, size_t dirlen, char *fn, size_t fnlen,
2709 char *sz, size_t szlen, int flags)
2711 return(FileBrowse(dir, dirlen, fn, fnlen, sz, szlen, flags, NULL));
2715 ResizeBrowser (void)
2717 return (0);
2721 * FileBrowse - Windows version of above function
2723 int
2724 FileBrowse(char *dir, size_t dirlen, char *fn, size_t fnlen,
2725 char *sz, size_t szlen, int fb_flags, LMLIST **lmreturn)
2727 struct stat sbuf;
2728 int rc;
2729 char lfn[NLINE];
2731 if (fb_flags & FB_SAVE){
2732 rc = mswin_savefile(dir, dirlen, fn, MIN(dirlen,fnlen));
2734 else{
2735 *fn = '\0'; /* No initial file names for
2736 * open. */
2737 if(fb_flags & FB_LMODEPOS){
2739 * We're going to allow multiple filenames to be returned so
2740 * we don't want to use the passed in fn for that. Instead, make
2741 * a bigger space and use that.
2743 char f[20000];
2744 size_t flen, dlen;
2746 memset(f, 0, sizeof(f));
2748 rc = mswin_multopenfile(dir, dirlen, f, sizeof(f),
2749 (fb_flags & FB_ATTACH) ? NULL : "Text Files (*.txt)#*.txt");
2751 if(rc == 1){
2752 LMLIST *lmhead = NULL, *new;
2753 char *p;
2756 * Build an LMLIST to return to the caller.
2758 for(p = f; *p; p += strlen(p)+1){
2759 flen = strlen(p);
2760 dlen = strlen(dir ? dir : "");
2761 new = (LMLIST *) fs_get(sizeof(*new));
2762 new->fname = (char *) fs_get((flen+1) * sizeof(char));
2763 new->dir = (char *) fs_get((dlen+1) * sizeof(char));
2765 strncpy(new->fname, p, flen);
2766 new->fname[flen] = '\0';
2767 strncpy(new->dir, dir ? dir : "", dlen);
2768 new->dir[dlen] = '\0';
2770 /* build full path to stat file. */
2771 if((strlen(new->dir) + strlen(S_FILESEP) +
2772 strlen(new->fname) + 1) < sizeof(lfn)){
2773 strncpy(lfn, new->dir, sizeof(lfn));
2774 lfn[sizeof(lfn)-1] = '\0';
2775 strncat(lfn, S_FILESEP, sizeof(lfn)-strlen(lfn)-1);
2776 strncat(lfn, new->fname, sizeof(lfn)-strlen(lfn)-1);
2777 lfn[sizeof(lfn)-1] = '\0';
2778 if(our_stat(lfn, &sbuf) < 0)
2779 strncpy(new->size, "0", 32);
2780 else
2781 strncpy(new->size, prettysz((off_t)sbuf.st_size), 32);
2783 new->size[32-1] = '\0';
2786 new->next = lmhead;
2787 lmhead = new;
2790 *lmreturn = lmhead;
2791 return(1);
2794 else
2795 rc = mswin_openfile (dir, dirlen, fn, fnlen,
2796 (fb_flags & FB_ATTACH) ? NULL : "Text Files (*.txt)#*.txt");
2799 if(rc == 1){
2800 if(sz != NULL){
2801 /* build full path to stat file. */
2802 if((strlen(dir) + strlen(S_FILESEP) + strlen(fn) + 1) > NLINE)
2803 return -1;
2805 strncpy(lfn, dir, sizeof(lfn));
2806 lfn[sizeof(lfn)-1] = '\0';
2807 strncat(lfn, S_FILESEP, sizeof(lfn)-strlen(lfn)-1);
2808 lfn[sizeof(lfn)-1] = '\0';
2809 strncat(lfn, fn, sizeof(lfn)-strlen(lfn)-1);
2810 lfn[sizeof(lfn)-1] = '\0';
2811 if(our_stat(lfn, &sbuf) < 0){
2812 strncpy(sz, "0", szlen);
2813 sz[szlen-1] = '\0';
2815 else{
2816 strncpy(sz, prettysz ((off_t)sbuf.st_size), szlen);
2817 sz[szlen-1] = '\0';
2821 return(1);
2824 return(rc ? -1 : 0);
2827 #endif /* _WINDOWS */
2831 * LikelyASCII - make a rough guess as to the displayability of the
2832 * given file.
2835 LikelyASCII(char *file)
2837 #define LA_TEST_BUF 1024
2838 #define LA_LINE_LIMIT 300
2839 int n, i, line, rv = FALSE;
2840 unsigned char buf[LA_TEST_BUF];
2841 FILE *fp;
2842 EML eml;
2844 if((fp = our_fopen(file, "rb")) != NULL){
2845 clearerr(fp);
2846 if((n = fread(buf, sizeof(char), LA_TEST_BUF * sizeof(char), fp)) > 0
2847 || !ferror(fp)){
2849 * If we don't hit any newlines in a reasonable number,
2850 * LA_LINE_LIMIT, of characters or the file contains NULLs,
2851 * bag out...
2853 rv = TRUE;
2854 for(i = line = 0; i < n; i++)
2855 if((line = (buf[i] == '\n') ? 0 : line + 1) >= LA_LINE_LIMIT
2856 || !buf[i]){
2857 rv = FALSE;
2858 emlwrite(_("Can't display non-text file. Try \"Launch\"."),
2859 NULL);
2860 break;
2863 else{
2864 eml.s = file;
2865 emlwrite(_("Can't read file: %s"), &eml);
2868 fclose(fp);
2870 else{
2871 eml.s = file;
2872 emlwrite(_("Can't open file: %s"), &eml);
2875 return(rv);
2879 void
2880 zotlmlist(LMLIST *lm)
2882 LMLIST *tp;
2884 while(lm){
2885 if(lm->fname)
2886 free(lm->fname);
2888 if(lm->dir)
2889 free(lm->dir);
2891 tp = lm;
2892 lm = lm->next;
2893 tp->next = NULL;
2894 free((char *) tp);
2900 * time_to_check - checks the current time against the last time called
2901 * and returns true if the elapsed time is > below.
2902 * Newmail won't necessarily check, but we want to give it
2903 * a chance to check or do a keepalive.
2906 time_to_check(void)
2908 static time_t lasttime = 0L;
2910 if(!get_input_timeout())
2911 return(FALSE);
2913 if(time((time_t *) 0) - lasttime > (Pmaster ? (time_t)(FUDGE-10) : get_input_timeout())){
2914 lasttime = time((time_t *) 0);
2915 return(TRUE);
2917 else
2918 return(FALSE);