summaryrefslogtreecommitdiff
path: root/pico/browse.c
diff options
context:
space:
mode:
authorEduardo Chappa <echappa@gmx.com>2013-02-03 00:59:38 -0700
committerEduardo Chappa <echappa@gmx.com>2013-02-03 00:59:38 -0700
commit094ca96844842928810f14844413109fc6cdd890 (patch)
treee60efbb980f38ba9308ccb4fb2b77b87bbc115f3 /pico/browse.c
downloadalpine-094ca96844842928810f14844413109fc6cdd890.tar.xz
Initial Alpine Version
Diffstat (limited to 'pico/browse.c')
-rw-r--r--pico/browse.c2915
1 files changed, 2915 insertions, 0 deletions
diff --git a/pico/browse.c b/pico/browse.c
new file mode 100644
index 00000000..46e01fd0
--- /dev/null
+++ b/pico/browse.c
@@ -0,0 +1,2915 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: browse.c 942 2008-03-04 18:21:33Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ *
+ * Program: Routines to support file browser in pico and Pine composer
+ *
+ *
+ * NOTES:
+ *
+ * Misc. thoughts (mss, 5 Apr 92)
+ *
+ * This is supposed to be just a general purpose browser, equally
+ * callable from either pico or the pine composer. Someday, it could
+ * even be used to "wrap" the unix file business for really novice
+ * users. The stubs are here for renaming, copying, creating directories,
+ * deleting, undeleting (thought is delete just moves files to
+ * ~/.pico.deleted directory or something and undelete offers the
+ * files in there for undeletion: kind of like the mac trashcan).
+ *
+ * Nice side effects
+ *
+ * Since the full path name is always maintained and referencing ".."
+ * stats the path stripped of its trailing name, the unpleasantness of
+ * symbolic links is hidden.
+ *
+ * Fleshed out the file managements stuff (mss, 24 June 92)
+ *
+ *
+ */
+#include "headers.h"
+#include "../c-client/mail.h"
+#include "../c-client/rfc822.h"
+#include "../pith/osdep/collate.h"
+#include "../pith/charconv/filesys.h"
+#include "../pith/conf.h"
+
+#ifndef _WINDOWS
+
+#if defined(bsd) || defined(lnx)
+extern int errno;
+#endif
+
+
+/*
+ * directory cell structure
+ */
+struct fcell {
+ char *fname; /* file name */
+ unsigned mode; /* file's mode */
+ char size[16]; /* file's size in s */
+ struct fcell *next;
+ struct fcell *prev;
+};
+
+
+/*
+ * master browser structure
+ */
+static struct bmaster {
+ struct fcell *head; /* first cell in list */
+ struct fcell *top; /* cell in top left */
+ struct fcell *current; /* currently selected */
+ int longest; /* longest file name (in screen width) */
+ int fpl; /* file names per line */
+ int cpf; /* chars / file / line */
+ int flags;
+ char dname[NLINE]; /* this dir's name (UTF-8) */
+ char *names; /* malloc'd name array (UTF-8) */
+ LMLIST *lm;
+} *gmp; /* global master ptr */
+
+
+/*
+ * title for exported browser display
+ */
+static char *browser_title = NULL;
+
+
+struct bmaster *getfcells(char *, int);
+void PaintCell(int, int, int, struct fcell *, int);
+void PaintBrowser(struct bmaster *, int, int *, int *);
+void BrowserKeys(void);
+void layoutcells(struct bmaster *);
+void percdircells(struct bmaster *);
+int PlaceCell(struct bmaster *, struct fcell *, int *, int *);
+void zotfcells(struct fcell *);
+void zotmaster(struct bmaster **);
+struct fcell *FindCell(struct bmaster *, char *);
+int sisin(char *, char *);
+void p_chdir(struct bmaster *);
+void BrowserAnchor(char *);
+void ClearBrowserScreen(void);
+void BrowserRunChild(char *, char *);
+int fcell_is_selected(struct fcell *, struct bmaster *);
+void add_cell_to_lmlist(struct fcell *, struct bmaster *);
+void del_cell_from_lmlist(struct fcell *, struct bmaster *);
+
+
+static KEYMENU menu_browse[] = {
+ {"?", N_("Get Help"), KS_SCREENHELP}, {NULL, NULL, KS_NONE},
+ {NULL, NULL, KS_NONE}, {"-", N_("Prev Pg"), KS_PREVPAGE},
+ {"D", N_("Delete"), KS_NONE}, {"C",N_("Copy"), KS_NONE},
+ {NULL, NULL, KS_NONE}, {NULL, NULL, KS_NONE},
+ {"W", N_("Where is"), KS_NONE}, {"Spc", N_("Next Pg"), KS_NEXTPAGE},
+ {"R", N_("Rename"), KS_NONE}, {NULL, NULL, KS_NONE}
+};
+#define QUIT_KEY 1
+#define EXEC_KEY 2
+#define GOTO_KEY 6
+#define SELECT_KEY 7
+#define PICO_KEY 11
+
+
+#define DIRWORD "dir"
+#define PARENTDIR "parent"
+#define SELECTWORD "SELECT"
+
+
+/*
+ * Default pager used by the stand-alone file browser.
+ */
+#define BROWSER_PAGER ((gmode & MDFKEY) ? "pine -k -F" : "pine -F")
+
+
+/*
+ * function key mappings for callable browser
+ */
+static UCS bfmappings[2][12][2] = { { { F1, '?'}, /* stand alone */
+ { F2, NODATA }, /* browser function */
+ { F3, 'q'}, /* key mappings... */
+ { F4, 'v'},
+ { F5, 'l'},
+ { F6, 'w'},
+ { F7, '-'},
+ { F8, ' '},
+ { F9, 'd'},
+ { F10, 'r'},
+ { F11, 'c'},
+ { F12, 'e'} },
+ { { F1, '?'}, /* callable browser */
+ { F2, NODATA }, /* function key */
+ { F3, 'e'}, /* mappings... */
+ { F4, 's'},
+ { F5, NODATA },
+ { F6, 'w'},
+ { F7, '-'},
+ { F8, ' '},
+ { F9, 'd'},
+ { F10, 'r'},
+ { F11, 'c'},
+ { F12, 'a'} } };
+
+
+/*
+ * Browser help for pico (pine composer help handed to us by pine)
+ */
+static char *BrowseHelpText[] = {
+/* TRANSLATORS: The next several lines go together. The ~ characters
+ should be left in front of the characters they cause to be bold. */
+N_("Help for Browse Command"),
+" ",
+N_(" Pico's file browser is used to select a file from the"),
+N_(" file system for inclusion in the edited text."),
+" ",
+N_("~ Both directories and files are displayed. Press ~S"),
+N_("~ or ~R~e~t~u~r~n to select a file or directory. When a file"),
+N_(" is selected during the \"Read File\" command, it is"),
+N_(" inserted into edited text. Answering \"yes\" to the"),
+N_(" verification question after a directory is selected causes"),
+N_(" the contents of that directory to be displayed for selection."),
+" ",
+N_(" The file named \"..\" is special, and means the \"parent\""),
+N_(" of the directory being displayed. Select this directory"),
+N_(" to move upward in the directory tree."),
+" ",
+N_("End of Browser Help."),
+" ",
+NULL
+};
+
+/*
+ * Help for standalone browser (pilot)
+ */
+static char *sa_BrowseHelpText[] = {
+/* TRANSLATORS: Some more help text */
+N_("Help for Pilot (PIne's Looker-upper Of Things"),
+" ",
+N_(" Pilot is a simple, display-oriented file system browser based on the"),
+N_("~ Alpine message system composer. As with Alpine, commands are displayed at"),
+N_("~ the bottom of the screen, and context-sensitive help is provided."),
+" ",
+N_("~ Pilot displays the current working directory at the top of the screen."),
+N_("~ The directory's contents are displayed in columns of file name, file"),
+N_("~ size pairs. Names that are directories are indicated by the name"),
+N_("~ ~(~d~i~r~) in place of the file size. The parent of the current working"),
+N_("~ directory is indicated by the file name ~.~. and size of ~(~p~a~r~e~n~t~ ~d~i~r~)."),
+N_("~ File names that are symbolic links to other files are displayed with a"),
+N_("~ file size of ~-~-."),
+" ",
+N_(" The following function keys are available in Pilot:"),
+" ",
+N_("~ ~? Display this help text."),
+N_("~ ~Q Quit Pilot."),
+" ",
+N_("~ ~V View the currently selected file or open the selected directory."),
+N_("~ Note: Pilot invokes ~p~i~n~e ~-~F, or the program defined by the ~P~A~G~E~R"),
+N_("~ environment variable, to view the file."),
+N_("~ ~L Launch an external application program."),
+" ",
+N_("~ ~W Search for a file by name."),
+N_("~ ~- Scroll up one page."),
+N_("~ ~S~p~a~c~e Scroll down one page."),
+N_("~ ~N,~^~F Move forward (right) one column."),
+N_("~ ~P,~^~B Move back (left) one column."),
+N_("~ ~^~N Move down one row."),
+N_("~ ~^~P Move up one row."),
+" ",
+N_("~ ~D Delete the selected file."),
+N_("~ ~R Rename the selected file or directory."),
+N_("~ ~C Copy the selected file."),
+N_("~ ~E Edit the selected file."),
+N_("~ Note: Pilot invokes ~p~i~c~o, or the program defined by the ~E~D~I~T~O~R"),
+N_("~ environment variable, to edit the file."),
+" ",
+N_("End of Pilot Help."),
+NULL
+};
+
+
+
+/*
+ * pico_file_browse - Exported version of FileBrowse below.
+ */
+int
+pico_file_browse(PICO *pdata, char *dir, size_t dirlen, char *fn, size_t fnlen,
+ char *sz, size_t szlen, int flags)
+{
+ int rv;
+ char title_buf[64];
+
+ Pmaster = pdata;
+ gmode = pdata->pine_flags | MDEXTFB;
+ km_popped = 0;
+
+ /* only init screen bufs for display and winch handler */
+ if(!vtinit())
+ return(-1);
+
+ if(Pmaster){
+ term.t_mrow = Pmaster->menu_rows;
+ if(Pmaster->oper_dir)
+ strncpy(opertree, Pmaster->oper_dir, NLINE);
+
+ if(*opertree)
+ fixpath(opertree, sizeof(opertree));
+ }
+
+ /* install any necessary winch signal handler */
+ ttresize();
+
+ snprintf(title_buf, sizeof(title_buf), "%s FILE", pdata->pine_anchor);
+ set_browser_title(title_buf);
+ rv = FileBrowse(dir, dirlen, fn, fnlen, sz, szlen, flags, NULL);
+ set_browser_title(NULL);
+ vttidy(); /* clean up tty handling */
+ zotdisplay(); /* and display structures */
+ Pmaster = NULL; /* and global config structure */
+ return(rv);
+}
+
+
+
+/*
+ * FileBrowse - display contents of given directory dir
+ *
+ * intput:
+ * dir points to initial dir to browse.
+ * dirlen- buffer size of dir
+ * fn initial file name.
+ * fnlen- buffer size of fn
+ *
+ * returns:
+ * dir points to currently selected directory (without
+ * trailing file system delimiter)
+ * fn points to currently selected file
+ * sz points to size of file if ptr passed was non-NULL
+ * flags
+ *
+ * Special dispensation for FB_LMODE. If the caller sets
+ * FB_LMODEPOS, and the caller passes a non-null lmreturn,
+ * then the return values will be in lmreturn instead of
+ * in dir and fn. The caller is responsible for freeing
+ * the contents of lmreturn.
+ *
+ * 1 if a file's been selected
+ * 0 if no files selected
+ * -1 if there were problems
+ */
+int
+FileBrowse(char *dir, size_t dirlen, char *fn, size_t fnlen,
+ char *sz, size_t szlen, int fb_flags, LMLIST **lmreturn)
+{
+ UCS c, new_c;
+ int status, i, j;
+ int row, col, crow, ccol;
+ char *p, *envp, child[NLINE], tmp[NLINE];
+ struct bmaster *mp;
+ struct fcell *tp;
+ EML eml;
+#ifdef MOUSE
+ MOUSEPRESS mousep;
+#endif
+
+ child[0] = '\0';
+
+ if((gmode&MDTREE) && !in_oper_tree(dir)){
+ eml.s = opertree;
+ emlwrite(_("\007Can't read outside of %s in restricted mode"), &eml);
+ sleep(2);
+ return(0);
+ }
+
+ if(gmode&MDGOTO){
+ /* fix up function key mapping table */
+ /* fix up key menu labels */
+ }
+
+ /* build contents of cell structures */
+ if((gmp = getfcells(dir, fb_flags)) == NULL)
+ return(-1);
+
+ tp = NULL;
+ if(fn && *fn){
+ if((tp = FindCell(gmp, fn)) != NULL){
+ gmp->current = tp;
+ PlaceCell(gmp, gmp->current, &row, &col);
+ }
+ }
+
+ /* paint screen */
+ PaintBrowser(gmp, 0, &crow, &ccol);
+
+ while(1){ /* the big loop */
+ if(!(gmode&MDSHOCUR)){
+ crow = term.t_nrow-term.t_mrow;
+ ccol = 0;
+ }
+
+ if(!(gmode&MDSHOCUR))
+ movecursor(crow, ccol);
+ else if(gmp->flags & FB_LMODEPOS){
+ if(gmp->flags & FB_LMODE && gmp->current->mode != FIODIR)
+ movecursor(crow, ccol+1);
+ else
+ movecursor(crow, ccol+4);
+ }
+ else
+ movecursor(crow, ccol);
+
+ if(km_popped){
+ km_popped--;
+ if(km_popped == 0)
+ /* cause bottom three lines to repaint */
+ PaintBrowser(gmp, 0, &crow, &ccol);
+ }
+
+ if(km_popped){ /* temporarily change to cause menu to paint */
+ term.t_mrow = 2;
+ movecursor(term.t_nrow-2, 0); /* clear status line */
+ peeol();
+ BrowserKeys();
+ term.t_mrow = 0;
+ }
+
+ (*term.t_flush)();
+
+#ifdef MOUSE
+ mouse_in_content(KEY_MOUSE, -1, -1, 0, 0);
+ register_mfunc(mouse_in_content,2,0,term.t_nrow-(term.t_mrow+1),
+ term.t_ncol);
+#endif
+ c = GetKey();
+#ifdef MOUSE
+ clear_mfunc(mouse_in_content);
+#endif
+
+ if(Pmaster){
+ if(Pmaster->newmail && (c == NODATA || time_to_check())){
+ if((*Pmaster->newmail)(c == NODATA ? 0 : 2, 1) >= 0){
+ int rv;
+
+ if(km_popped){ /* restore display */
+ km_popped = 0;
+ PaintBrowser(gmp, 0, &crow, &ccol);
+ }
+
+ clearcursor();
+ mlerase();
+ rv = (*Pmaster->showmsg)(c);
+ ttresize();
+ picosigs();
+ if(rv) /* Did showmsg corrupt the display? */
+ PaintBrowser(gmp, 0, &crow, &ccol); /* Yes, repaint */
+
+ mpresf = 1;
+ }
+
+ clearcursor();
+ if(gmp->flags & FB_LMODEPOS){
+ if(gmp->flags & FB_LMODE && gmp->current->mode != FIODIR)
+ movecursor(crow, ccol+1);
+ else
+ movecursor(crow, ccol+4);
+ }
+ else
+ movecursor(crow, ccol);
+ }
+ }
+ else{
+ if(get_input_timeout() && (c == NODATA || time_to_check()))
+ if(pico_new_mail())
+ emlwrite(_("You may possibly have new mail."), NULL);
+ }
+
+ if(km_popped)
+ switch(c){
+ case NODATA:
+ case (CTRL|'L'):
+ km_popped++;
+ break;
+
+ default:
+ /* clear bottom three lines */
+ movecursor(term.t_nrow-2, 0);
+ peeol();
+ movecursor(term.t_nrow-1, 0);
+ peeol();
+ movecursor(term.t_nrow, 0);
+ peeol();
+ break;
+ }
+
+ if(c == NODATA) /* GetKey timed out */
+ continue;
+ else if(Pmaster)
+ (*Pmaster->keybinput)();
+
+
+ if(mpresf){ /* blast old messages */
+ if(mpresf++ > MESSDELAY){ /* every few keystrokes */
+ mlerase();
+ }
+ }
+
+ /* process commands */
+ switch(new_c = normalize_cmd(c,bfmappings[(gmode&MDBRONLY)?0:1],2)){
+
+ case KEY_RIGHT: /* move right */
+ case (CTRL|'@'):
+ case (CTRL|'F'): /* forward */
+ case 'n' :
+ case 'N' :
+ if(gmp->current->next == NULL){
+ (*term.t_beep)();
+ break;
+ }
+
+ PlaceCell(gmp, gmp->current, &row, &col);
+ PaintCell(row, col, gmp->cpf, gmp->current, 0);
+ gmp->current = gmp->current->next;
+ if(PlaceCell(gmp, gmp->current, &row, &col)){
+ PaintBrowser(gmp, 1, &crow, &ccol);
+ }
+ else{
+ PaintCell(row, col, gmp->cpf, gmp->current, 1);
+ crow = row;
+ ccol = col;
+ }
+ break;
+
+ case KEY_LEFT: /* move left */
+ case (CTRL|'B'): /* back */
+ case 'p' :
+ case 'P' :
+ if(gmp->current->prev == NULL){
+ (*term.t_beep)();
+ break;
+ }
+
+ PlaceCell(gmp, gmp->current, &row, &col);
+ PaintCell(row, col, gmp->cpf, gmp->current, 0);
+ gmp->current = gmp->current->prev;
+ if(PlaceCell(gmp, gmp->current, &row, &col)){
+ PaintBrowser(gmp, 1, &crow, &ccol);
+ }
+ else{
+ PaintCell(row, col, gmp->cpf, gmp->current, 1);
+ crow = row;
+ ccol = col;
+ }
+ break;
+
+ case (CTRL|'A'): /* beginning of line */
+ tp = gmp->current;
+ i = col;
+ while(i > 0){
+ i -= gmp->cpf;
+ if(tp->prev != NULL)
+ tp = tp->prev;
+ }
+ PlaceCell(gmp, gmp->current, &row, &col);
+ PaintCell(row, col, gmp->cpf, gmp->current, 0);
+ gmp->current = tp;
+ if(PlaceCell(gmp, tp, &row, &col)){
+ PaintBrowser(gmp, 1, &crow, &ccol);
+ }
+ else{
+ PaintCell(row, col, gmp->cpf, gmp->current, 1);
+ crow = row;
+ ccol = col;
+ }
+ break;
+
+ case (CTRL|'E'): /* end of line */
+ tp = gmp->current;
+ i = col + gmp->cpf;
+ while(i+gmp->cpf <= gmp->cpf * gmp->fpl){
+ i += gmp->cpf;
+ if(tp->next != NULL)
+ tp = tp->next;
+ }
+
+ PlaceCell(gmp, gmp->current, &row, &col);
+ PaintCell(row, col, gmp->cpf, gmp->current, 0);
+ gmp->current = tp;
+ if(PlaceCell(gmp, tp, &row, &col)){
+ PaintBrowser(gmp, 1, &crow, &ccol);
+ }
+ else{
+ PaintCell(row, col, gmp->cpf, gmp->current, 1);
+ crow = row;
+ ccol = col;
+ }
+ break;
+
+ case (CTRL|'V'): /* page forward */
+ case ' ':
+ case KEY_PGDN :
+ case KEY_END :
+ tp = gmp->top;
+ i = term.t_nrow - term.t_mrow - 2;
+
+ while(i-- && tp->next != NULL){
+ j = 0;
+ while(++j <= gmp->fpl && tp->next != NULL)
+ tp = tp->next;
+ }
+
+ if(tp == NULL)
+ continue;
+
+ PlaceCell(gmp, gmp->current, &row, &col);
+ PaintCell(row, col, gmp->cpf, gmp->current, 0);
+ gmp->current = tp;
+ if(PlaceCell(gmp, tp, &row, &col)){
+ PaintBrowser(gmp, 1, &crow, &ccol);
+ }
+ else{
+ PaintCell(row, col, gmp->cpf, gmp->current, 1);
+ crow = row;
+ ccol = col;
+ }
+ break;
+
+ case '-' :
+ case (CTRL|'Y'): /* page backward */
+ case KEY_PGUP :
+ case KEY_HOME :
+ tp = gmp->top;
+ i = term.t_nrow - term.t_mrow - 4;
+ while(i-- && tp != NULL){
+ j = gmp->fpl;
+ while(j-- && tp != NULL)
+ tp = tp->prev;
+ }
+
+ if(tp || (gmp->current != gmp->top)){ /* clear old hilite */
+ PlaceCell(gmp, gmp->current, &row, &col);
+ PaintCell(row, col, gmp->cpf, gmp->current, 0);
+ }
+
+ if(tp) /* new page ! */
+ gmp->current = tp;
+ else if(gmp->current != gmp->top) /* goto top of page */
+ gmp->current = gmp->top;
+ else /* do nothing */
+ continue;
+
+ if(PlaceCell(gmp, gmp->current, &row, &col)){
+ PaintBrowser(gmp, 1, &crow, &ccol);
+ }
+ else{
+ PaintCell(row, col, gmp->cpf, gmp->current, 1);
+ crow = row;
+ ccol = col;
+ }
+
+ break;
+
+ case KEY_DOWN :
+ case (CTRL|'N'): /* next */
+ tp = gmp->current;
+ i = gmp->fpl;
+ while(i--){
+ if(tp->next == NULL){
+ (*term.t_beep)();
+ break;
+ }
+ else
+ tp = tp->next;
+ }
+ if(i != -1) /* can't go down */
+ break;
+
+ PlaceCell(gmp, gmp->current, &row, &col);
+ PaintCell(row, col, gmp->cpf, gmp->current, 0);
+ gmp->current = tp;
+ if(PlaceCell(gmp, tp, &row, &col)){
+ PaintBrowser(gmp, 1, &crow, &ccol);
+ }
+ else{
+ PaintCell(row, col, gmp->cpf, gmp->current, 1);
+ crow = row;
+ ccol = col;
+ }
+ break;
+
+ case KEY_UP :
+ case (CTRL|'P'): /* previous */
+ tp = gmp->current;
+ i = gmp->fpl;
+ while(i-- && tp)
+ tp = tp->prev;
+
+ if(tp == NULL)
+ break;
+
+ PlaceCell(gmp, gmp->current, &row, &col);
+ PaintCell(row, col, gmp->cpf, gmp->current, 0);
+ gmp->current = tp;
+ if(PlaceCell(gmp, tp, &row, &col)){
+ PaintBrowser(gmp, 1, &crow, &ccol);
+ }
+ else{
+ PaintCell(row, col, gmp->cpf, gmp->current, 1);
+ crow = row;
+ ccol = col;
+ }
+ break;
+
+#ifdef MOUSE
+ case KEY_MOUSE:
+ mouse_get_last (NULL, &mousep);
+ if (mousep.doubleclick) {
+ goto Selected;
+ }
+ else {
+ row = mousep.row -= 2; /* Adjust for header*/
+ col = mousep.col;
+ i = row * gmp->fpl + (col / gmp->cpf); /* Count from top */
+ tp = gmp->top; /* start at top. */
+ for (; i > 0 && tp != NULL; --i) /* Count cells. */
+ tp = tp->next;
+ if (tp != NULL) { /* Valid cell? */
+ PlaceCell(gmp, gmp->current, &row, &col);
+ PaintCell(row, col, gmp->cpf, gmp->current, 0);
+ gmp->current = tp;
+ if(PlaceCell(gmp, tp, &row, &col)){
+ PaintBrowser(gmp, 1, &crow, &ccol);
+ }
+ else{
+ PaintCell(row, col, gmp->cpf, gmp->current, 1);
+ crow = row;
+ ccol = col;
+ }
+ }
+ }
+ break;
+#endif
+
+ case 'e': /* exit or edit */
+ case 'E':
+ if(gmode&MDBRONLY){ /* run "pico" */
+ snprintf(child, sizeof(child), "%s%c%s", gmp->dname, C_FILESEP,
+ gmp->current->fname);
+ /* make sure selected isn't a directory or executable */
+ if(!LikelyASCII(child)){
+ emlwrite(_("Can't edit non-text file. Try Launch."), NULL);
+ break;
+ }
+
+ if((envp = (char *) getenv("EDITOR")) != NULL)
+ snprintf(tmp, sizeof(tmp), "%s \'%s\'", envp, child);
+ else
+ snprintf(tmp, sizeof(tmp), "pico%s%s%s \'%s\'",
+ (gmode & MDFKEY) ? " -f" : "",
+ (gmode & MDSHOCUR) ? " -g" : "",
+ (gmode & MDMOUSE) ? " -m" : "",
+ child);
+
+ BrowserRunChild(tmp, gmp->dname); /* spawn pico */
+ PaintBrowser(gmp, 0, &crow, &ccol); /* redraw browser */
+ }
+ else{
+ zotmaster(&gmp);
+ return(0);
+ }
+
+ break;
+
+ case 'q': /* user exits wrong */
+ case 'Q':
+ if(gmode&MDBRONLY){
+ zotmaster(&gmp);
+ return(0);
+ }
+
+ unknown_command(c);
+ break;
+
+ case 'x': /* toggle selection */
+ case 'X':
+ if(!(gmp->flags & FB_LMODE)){
+ if(gmp->flags & FB_LMODEPOS)
+ emlwrite(_("\007Type L command to use ListMode"), NULL);
+ else
+ unknown_command(c);
+
+ break;
+ }
+
+ if(gmp->current->mode == FIODIR){
+ emlwrite(_("\007Can't Set directories"), NULL);
+ break;
+ }
+
+ if(fcell_is_selected(gmp->current, gmp))
+ del_cell_from_lmlist(gmp->current, gmp);
+ else
+ add_cell_to_lmlist(gmp->current, gmp);
+
+ PlaceCell(gmp, gmp->current, &row, &col);
+ PaintCell(row, col, gmp->cpf, gmp->current, 1);
+ break;
+
+ case 'l': /* run Command */
+ case 'L': /* or ListMode */
+ if(gmp->flags & FB_LMODEPOS){
+ if(gmp->flags & FB_LMODE){
+ /*
+ * Unless we make it so you can get out of ListMode
+ * once you're in ListMode, this must be an error.
+ */
+ emlwrite(_("\007Already in ListMode"), NULL);
+ break;
+ }
+ else{
+ gmp->flags |= FB_LMODE;
+ PaintBrowser(gmp, 0, &crow, &ccol);
+ }
+
+ break;
+ }
+
+ if(!(gmode&MDBRONLY)){
+ unknown_command(c);
+ break;
+ }
+
+/* add subcommands to invoke pico and insert selected filename */
+/* perhaps: add subcmd to scroll command history */
+
+ tmp[0] = '\0';
+ i = 0;
+ snprintf(child, sizeof(child), "%s%c%s", gmp->dname, C_FILESEP,
+ gmp->current->fname);
+ while(!i){
+ static EXTRAKEYS opts[] = {
+ {"^X", N_("Add Name"), CTRL|'X', KS_NONE},
+ {NULL, NULL, 0, KS_NONE},
+ };
+
+ status = mlreply_utf8(_("Command to execute: "),
+ tmp, NLINE, QNORML, opts);
+ switch(status){
+ case HELPCH:
+ emlwrite(_("\007No help yet!"), NULL);
+/* remove break and sleep after help text is installed */
+ sleep(3);
+ break;
+ case (CTRL|'X'):
+ strncat(tmp, child, sizeof(tmp)-strlen(tmp)-1);
+ tmp[sizeof(tmp)-1] = '\0';
+ break;
+ case (CTRL|'L'):
+ PaintBrowser(gmp, 0, &crow, &ccol);
+ break;
+ case ABORT:
+ emlwrite(_("Command cancelled"), NULL);
+ i++;
+ break;
+ case FALSE:
+ case TRUE:
+ i++;
+
+ if(tmp[0] == '\0'){
+ emlwrite(_("No command specified"), NULL);
+ break;
+ }
+
+ BrowserRunChild(tmp, gmp->dname);
+ PaintBrowser(gmp, 0, &crow, &ccol);
+ break;
+ default:
+ break;
+ }
+ }
+
+ BrowserKeys();
+ break;
+
+ case 'd': /* delete */
+ case 'D':
+ if(gmp->current->mode == FIODIR){
+/* BUG: if dir is empty it should be deleted */
+ emlwrite(_("\007Can't delete a directory"), NULL);
+ break;
+ }
+
+ if(gmode&MDSCUR){ /* not allowed! */
+ emlwrite(_("Delete not allowed in restricted mode"),NULL);
+ break;
+ }
+
+ snprintf(child, sizeof(child), "%s%c%s", gmp->dname, C_FILESEP,
+ gmp->current->fname);
+
+ i = 0;
+ while(i++ < 2){ /* verify twice!! */
+ if(i == 1){
+ if(fexist(child, "w", (off_t *)NULL) != FIOSUC){
+ strncpy(tmp, _("File is write protected! OVERRIDE"), sizeof(tmp));
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+ else
+ /* TRANSLATORS: This is a question, Delete file <filename> */
+ snprintf(tmp, sizeof(tmp), _("Delete file \"%.*s\""), NLINE - 20, child);
+ }
+ else{
+ strncpy(tmp, _("File CANNOT be UNdeleted! Really delete"), sizeof(tmp));
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+
+ if((status = mlyesno_utf8(tmp, FALSE)) != TRUE){
+ emlwrite((status == ABORT)
+ ? _("Delete Cancelled")
+ : _("File Not Deleted"),
+ NULL);
+ break;
+ }
+ }
+
+ if(status == TRUE){
+ if(our_unlink(child) < 0){
+ eml.s = errstr(errno);
+ emlwrite(_("Delete Failed: %s"), &eml);
+ }
+ else{ /* fix up pointers and redraw */
+ tp = gmp->current;
+ if(tp->next){
+ gmp->current = tp->next;
+ if((tp->next->prev = tp->prev) != NULL)
+ tp->prev->next = tp->next;
+ }
+ else if(tp->prev) {
+ gmp->current = tp->prev;
+ if((tp->prev->next = tp->next) != NULL)
+ tp->next->prev = tp->prev;
+ }
+
+ if(tp == gmp->head)
+ gmp->head = tp->next;
+
+ tp->fname = NULL;
+ tp->next = tp->prev = NULL;
+ if(tp != gmp->current)
+ free((char *) tp);
+
+ if((tp = FindCell(gmp, gmp->current->fname)) != NULL){
+ gmp->current = tp;
+ PlaceCell(gmp, gmp->current, &row, &col);
+ }
+
+ PaintBrowser(gmp, 1, &crow, &ccol);
+ mlerase();
+ }
+ }
+
+ BrowserKeys();
+ break;
+
+ case '?': /* HELP! */
+ case (CTRL|'G'):
+ if(term.t_mrow == 0){
+ if(km_popped == 0){
+ km_popped = 2;
+ break;
+ }
+ }
+
+ if(Pmaster){
+ VARS_TO_SAVE *saved_state;
+
+ saved_state = save_pico_state();
+ (*Pmaster->helper)(Pmaster->browse_help,
+ _("Help for Browsing"), 1);
+ if(saved_state){
+ restore_pico_state(saved_state);
+ free_pico_state(saved_state);
+ }
+ }
+ else if(gmode&MDBRONLY)
+ pico_help(sa_BrowseHelpText, _("Browser Help"), 1);
+ else
+ pico_help(BrowseHelpText, _("Help for Browsing"), 1);
+ /* fall thru to repaint everything */
+
+ case (CTRL|'L'):
+ PaintBrowser(gmp, 0, &crow, &ccol);
+ break;
+
+ case 'g': /* jump to a directory */
+ case 'G':
+
+ if(!(gmode&MDGOTO))
+ goto Default;
+
+ i = 0;
+ child[0] = '\0';
+
+ while(!i){
+
+ /* TRANSLATORS: A prompt asking for a directory to
+ move into */
+ status = mlreply_utf8(_("Directory to go to: "), child, NLINE, QNORML,
+ NULL);
+
+ switch(status){
+ case HELPCH:
+ emlwrite(_("\007No help yet!"), NULL);
+ /* remove break and sleep after help text is installed */
+ sleep(3);
+ break;
+ case (CTRL|'L'):
+ PaintBrowser(gmp, 0, &crow, &ccol);
+ break;
+ case ABORT:
+ emlwrite(_("Goto cancelled"), NULL);
+ i++;
+ break;
+ case FALSE:
+ case TRUE:
+ i++;
+
+ if(*child == '\0'){
+ strncpy(child, gethomedir(NULL), sizeof(child));
+ child[sizeof(child)-1] = '\0';
+ }
+
+ if(!compresspath(gmp->dname, child, sizeof(child))){
+ eml.s = child;
+ emlwrite(_("Invalid Directory: %s"), &eml);
+ break;
+ }
+
+ if((gmode&MDSCUR) && homeless(child)){
+ emlwrite(_("Restricted mode browsing limited to home directory"),NULL);
+ break;
+ }
+
+ if((gmode&MDTREE) && !in_oper_tree(child)){
+ eml.s = opertree;
+ emlwrite(_("\007 Can't go outside of %s in restricted mode"),
+ &eml);
+ break;
+ }
+
+ if(isdir(child, (long *) NULL, NULL)){
+ if((mp = getfcells(child, fb_flags)) == NULL){
+ /* getfcells should explain what happened */
+ i++;
+ break;
+ }
+
+ zotmaster(&gmp);
+ gmp = mp;
+ PaintBrowser(gmp, 0, &crow, &ccol);
+ }
+ else{
+ eml.s = child;
+ emlwrite(_("\007Not a directory: \"%s\""), &eml);
+ }
+
+ break;
+ default:
+ break;
+ }
+ }
+ BrowserKeys();
+ break;
+
+ case 'a': /* Add */
+ case 'A':
+ if(gmode&MDSCUR){ /* not allowed! */
+ emlwrite(_("Add not allowed in restricted mode"),NULL);
+ break;
+ }
+
+ i = 0;
+ child[0] = '\0';
+ /* pass in default filename */
+ if(fn && *fn){
+ strncpy(child, fn, sizeof(child) - 1);
+ child[sizeof(child) - 1] = '\0';
+ }
+
+ while(!i){
+
+ switch(status=mlreply_utf8(_("Name of file to add: "), child, NLINE,
+ QFFILE, NULL)){
+ case HELPCH:
+ emlwrite(_("\007No help yet!"), NULL);
+/* remove break and sleep after help text is installed */
+ sleep(3);
+ break;
+ case (CTRL|'L'):
+ PaintBrowser(gmp, 0, &crow, &ccol);
+ break;
+ case ABORT:
+ emlwrite(_("Add File Cancelled"), NULL);
+ i++;
+ break;
+ case FALSE:
+ /*
+ * Support default filename. A return of 'FALSE' means
+ * 'No change in filename' which is fine if we have
+ * provided a default.
+ * John Berthels <john.berthels@nexor.co.uk>
+ */
+ /* FALLTHROUGH */
+ case TRUE:
+ i++;
+
+ if(child[0] == '\0'){
+ emlwrite(_("No file named. Add Cancelled."), NULL);
+ break;
+ }
+
+ if(!compresspath(gmp->dname, child, sizeof(child))){
+ emlwrite(_("Too many ..'s in name"), NULL);
+ break;
+ }
+
+ if((gmode&MDTREE) && !in_oper_tree(child)){
+ eml.s = opertree;
+ emlwrite(_("\007Restricted mode allows Add in %s only"),
+ &eml);
+ break;
+ }
+
+ if((status = fexist(child, "w", (off_t *)NULL)) == FIOSUC){
+ snprintf(tmp, sizeof(tmp), _("File \"%.*s\" already exists!"),
+ NLINE - 20, child);
+ emlwrite(tmp, NULL);
+ break;
+ }
+ else if(status != FIOFNF){
+ fioperr(status, child);
+ break;
+ }
+
+ if(ffwopen(child, FALSE) != FIOSUC){
+ /* ffwopen should've complained */
+ break;
+ }
+ else{ /* highlight new file */
+ ffclose();
+ eml.s = child;
+ emlwrite(_("Added File \"%s\""), &eml);
+
+ if((p = strrchr(child, C_FILESEP)) == NULL){
+ emlwrite(_("Problems refiguring browser"), NULL);
+ break;
+ }
+
+ *p = '\0';
+ if(p != child){
+ strncpy(tmp, child, sizeof(tmp));
+ tmp[sizeof(tmp)-1] = '\0';
+ j = 0;
+ while((child[j++] = *++p) != '\0')
+ ;
+ }
+ else{
+ strncpy(tmp, S_FILESEP, sizeof(tmp));
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+
+ /*
+ * new file in same dir? if so, refigure files
+ * and redraw...
+ */
+ if(!strcmp(tmp, gmp->dname)){
+ if((mp = getfcells(gmp->dname, fb_flags)) == NULL)
+ /* getfcells should explain what happened */
+ break;
+
+ zotmaster(&gmp);
+ gmp = mp;
+ if((tp = FindCell(gmp, child)) != NULL){
+ gmp->current = tp;
+ PlaceCell(gmp, gmp->current, &row, &col);
+ }
+
+ PaintBrowser(gmp, 1, &crow, &ccol);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ BrowserKeys();
+ break;
+
+ case 'c': /* copy */
+ case 'C':
+ if(gmp->current->mode == FIODIR){
+ emlwrite(_("\007Can't copy a directory"), NULL);
+ break;
+ }
+
+ if(gmode&MDSCUR){ /* not allowed! */
+ emlwrite(_("Copy not allowed in restricted mode"),NULL);
+ break;
+ }
+
+ i = 0;
+ child[0] = '\0';
+
+ while(!i){
+
+ switch(status=mlreply_utf8(_("Name of new copy: "), child, NLINE,
+ QFFILE, NULL)){
+ case HELPCH:
+ emlwrite(_("\007No help yet!"), NULL);
+/* remove break and sleep after help text is installed */
+ sleep(3);
+ break;
+ case (CTRL|'L'):
+ PaintBrowser(gmp, 0, &crow, &ccol);
+ break;
+ case ABORT:
+ emlwrite(_("Make Copy Cancelled"), NULL);
+ i++;
+ break;
+ case FALSE:
+ i++;
+ mlerase();
+ break;
+ case TRUE:
+ i++;
+
+ if(child[0] == '\0'){
+ emlwrite(_("No destination, file not copied"), NULL);
+ break;
+ }
+
+ if(!strcmp(gmp->current->fname, child)){
+ emlwrite(_("\007Can't copy file on to itself!"), NULL);
+ break;
+ }
+
+ if(!compresspath(gmp->dname, child, sizeof(child))){
+ emlwrite(_("Too many ..'s in name"), NULL);
+ break;
+ }
+
+ if((gmode&MDTREE) && !in_oper_tree(child)){
+ eml.s = opertree;
+ emlwrite(_("\007Restricted mode allows Copy in %s only"),
+ &eml);
+ break;
+ }
+
+ if((status = fexist(child, "w", (off_t *)NULL)) == FIOSUC){
+ /* TRANSLATORS: A question: File <filename> exists! Replace? */
+ snprintf(tmp, sizeof(tmp), _("File \"%.*s\" exists! OVERWRITE"),
+ NLINE - 20, child);
+ if((status = mlyesno_utf8(tmp, 0)) != TRUE){
+ emlwrite((status == ABORT)
+ ? _("Make copy cancelled")
+ : _("File Not Renamed"),
+ NULL);
+ break;
+ }
+ }
+ else if(status != FIOFNF){
+ fioperr(status, child);
+ break;
+ }
+
+ snprintf(tmp, sizeof(tmp), "%s%c%s", gmp->dname, C_FILESEP,
+ gmp->current->fname);
+
+ if(copy(tmp, child) < 0){
+ /* copy() will report any error messages */
+ break;
+ }
+ else{ /* highlight new file */
+ eml.s = child;
+ emlwrite(_("File copied to %s"), &eml);
+
+ if((p = strrchr(child, C_FILESEP)) == NULL){
+ emlwrite(_("Problems refiguring browser"), NULL);
+ break;
+ }
+
+ *p = '\0';
+ strncpy(tmp, (p == child) ? S_FILESEP: child, sizeof(tmp));
+ tmp[sizeof(tmp)-1] = '\0';
+
+ /*
+ * new file in same dir? if so, refigure files
+ * and redraw...
+ */
+ if(!strcmp(tmp, gmp->dname)){
+ strncpy(child, gmp->current->fname, sizeof(child));
+ child[sizeof(child)-1] = '\0';
+ if((mp = getfcells(gmp->dname, fb_flags)) == NULL)
+ /* getfcells should explain what happened */
+ break;
+
+ zotmaster(&gmp);
+ gmp = mp;
+ if((tp = FindCell(gmp, child)) != NULL){
+ gmp->current = tp;
+ PlaceCell(gmp, gmp->current, &row, &col);
+ }
+
+ PaintBrowser(gmp, 1, &crow, &ccol);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ BrowserKeys();
+ break;
+
+ case 'r': /* rename */
+ case 'R':
+ i = 0;
+
+ if(!strcmp(gmp->current->fname, "..")){
+ emlwrite(_("\007Can't rename \"..\""), NULL);
+ break;
+ }
+
+ if(gmode&MDSCUR){ /* not allowed! */
+ emlwrite(_("Rename not allowed in restricted mode"),NULL);
+ break;
+ }
+
+ strncpy(child, gmp->current->fname, sizeof(child));
+ child[sizeof(child)-1] = '\0';
+
+ while(!i){
+
+ switch(status=mlreply_utf8(_("Rename file to: "), child, NLINE, QFFILE,
+ NULL)){
+ case HELPCH:
+ emlwrite(_("\007No help yet!"), NULL);
+/* remove break and sleep after help text is installed */
+ sleep(3);
+ break;
+ case (CTRL|'L'):
+ PaintBrowser(gmp, 0, &crow, &ccol);
+ break;
+ case ABORT:
+ emlwrite(_("Rename cancelled"), NULL);
+ i++;
+ break;
+ case FALSE:
+ case TRUE:
+ i++;
+
+ if(child[0] == '\0' || status == FALSE){
+ mlerase();
+ break;
+ }
+
+ if(!compresspath(gmp->dname, child, sizeof(child))){
+ emlwrite(_("Too many ..'s in name"), NULL);
+ break;
+ }
+
+ if((gmode&MDTREE) && !in_oper_tree(child)){
+ eml.s = opertree;
+ emlwrite(_("\007Restricted mode allows Rename in %s only"),
+ &eml);
+ break;
+ }
+
+ status = fexist(child, "w", (off_t *)NULL);
+ if(status == FIOSUC || status == FIOFNF){
+ if(status == FIOSUC){
+ snprintf(tmp, sizeof(tmp), _("File \"%.*s\" exists! OVERWRITE"),
+ NLINE - 20, child);
+
+ if((status = mlyesno_utf8(tmp, FALSE)) != TRUE){
+ emlwrite((status == ABORT)
+ ? _("Rename cancelled")
+ : _("Not Renamed"),
+ NULL);
+ break;
+ }
+ }
+
+ snprintf(tmp, sizeof(tmp), "%s%c%s", gmp->dname, C_FILESEP,
+ gmp->current->fname);
+
+ if(our_rename(tmp, child) < 0){
+ eml.s = errstr(errno);
+ emlwrite(_("Rename Failed: %s"), &eml);
+ }
+ else{
+ if((p = strrchr(child, C_FILESEP)) == NULL){
+ emlwrite(_("Problems refiguring browser"), NULL);
+ break;
+ }
+
+ *p = '\0';
+ strncpy(tmp, (p == child) ? S_FILESEP: child, sizeof(tmp));
+ tmp[sizeof(tmp)-1] = '\0';
+
+ if((mp = getfcells(tmp, fb_flags)) == NULL)
+ /* getfcells should explain what happened */
+ break;
+
+ zotmaster(&gmp);
+ gmp = mp;
+
+ if((tp = FindCell(gmp, ++p)) != NULL){
+ gmp->current = tp;
+ PlaceCell(gmp, gmp->current, &row, &col);
+ }
+
+ PaintBrowser(gmp, 1, &crow, &ccol);
+ mlerase();
+ }
+ }
+ else{
+ fioperr(status, child);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ BrowserKeys();
+ break;
+
+ case 'v': /* stand-alone */
+ case 'V': /* browser "view" */
+ case 's': /* user "select" */
+ case 'S':
+ case (CTRL|'M'):
+ Selected:
+
+ if(((new_c == 'S' || new_c == 's') && (gmode&MDBRONLY))
+ || ((new_c == 'V' || new_c == 'v') && !(gmode&MDBRONLY)))
+ goto Default;
+
+ if(gmp->current->mode == FIODIR){
+ *child = '\0';
+ strncpy(tmp, gmp->dname, sizeof(tmp));
+ tmp[sizeof(tmp)-1] = '\0';
+ p = gmp->current->fname;
+ if(p[0] == '.' && p[1] == '.' && p[2] == '\0'){
+ if((p=strrchr(tmp, C_FILESEP)) != NULL){
+ *p = '\0';
+
+ if((gmode&MDTREE) && !in_oper_tree(tmp)){
+ eml.s = PARENTDIR;
+ emlwrite(
+ _("\007Can't visit %s in restricted mode"),
+ &eml);
+ break;
+ }
+
+ strncpy(child, &p[1], sizeof(child));
+ child[sizeof(child)-1] = '\0';
+
+ if
+#if defined(DOS) || defined(OS2)
+ (p == tmp || (tmp[1] == ':' && tmp[2] == '\0'))
+#else
+ (p == tmp)
+#endif
+ { /* is it root? */
+#if defined(DOS) || defined(OS2)
+ if(*child){
+ strncat(tmp, S_FILESEP, sizeof(tmp)-strlen(tmp)-1);
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+#else
+ if(*child){
+ strncpy(tmp, S_FILESEP, sizeof(tmp));
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+#endif
+ else{
+ emlwrite(_("\007Can't move up a directory"),
+ NULL);
+ break;
+ }
+ }
+ }
+ }
+ else if((fb_flags&FB_SAVE) && p[0] == '.' && p[1] == '\0'){
+ if ((strlen(gmp->dname) < dirlen) &&
+ (strlen(gmp->current->fname) < fnlen)){
+ strncpy(dir, gmp->dname, dirlen);
+ dir[dirlen-1] = '\0';
+ }
+
+ zotmaster(&gmp);
+ return(0); /* just change the directory, still return no selection */
+ }
+ else{
+ if(tmp[1] != '\0'){ /* were in root? */
+ strncat(tmp, S_FILESEP, sizeof(tmp)-strlen(tmp)-1);
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+
+ strncat(tmp, gmp->current->fname, sizeof(tmp)-strlen(tmp));
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+
+ if((mp = getfcells(tmp, fb_flags)) == NULL)
+ /* getfcells should explain what happened */
+ break;
+
+ if(gmp->flags & FB_LMODE){
+ mp->flags |= FB_LMODE;
+ mp->lm = gmp->lm;
+ gmp->lm = NULL;
+ }
+
+ zotmaster(&gmp);
+ gmp = mp;
+ tp = NULL;
+
+ if(*child){
+ if((tp = FindCell(gmp, child)) != NULL){
+ gmp->current = tp;
+ PlaceCell(gmp, gmp->current, &row, &col);
+ }
+ else{
+ eml.s = child;
+ emlwrite(_("\007Problem finding dir \"%s\""), &eml);
+ }
+ }
+
+ PaintBrowser(gmp, 0, &crow, &ccol);
+ if(!*child){
+ char b[100];
+
+ snprintf(b, sizeof(b), "Select/View \" .. %s %s\" to return to previous directory.", PARENTDIR, DIRWORD);
+ emlwrite(b, NULL);
+ }
+
+ break;
+ }
+ else if(gmode&MDBRONLY){
+ snprintf(child, sizeof(child), "%s%c%s", gmp->dname, C_FILESEP,
+ gmp->current->fname);
+
+ if(LikelyASCII(child)){
+ snprintf(tmp, sizeof(tmp), "%s \'%s\'",
+ (envp = (char *) getenv("PAGER"))
+ ? envp : BROWSER_PAGER, child);
+ BrowserRunChild(tmp, gmp->dname);
+ PaintBrowser(gmp, 0, &crow, &ccol);
+ }
+
+ break;
+ }
+ else{ /* just return */
+ if(gmp->flags & FB_LMODEPOS){
+
+ if(!lmreturn){
+ emlwrite("Programming error, called FileBrowse with LMODEPOS but no lmreturn", NULL);
+ zotmaster(&gmp);
+ return(-1);
+ }
+
+ /* user actually used ListMode, the list is finished */
+ if(gmp->flags & FB_LMODE){
+ if(!gmp->lm){
+ (*term.t_beep)();
+ emlwrite(_("No files are selected, use \"X\" to mark files for selection"), NULL);
+ break;
+ }
+
+ *lmreturn = gmp->lm;
+ gmp->lm = NULL;
+ }
+ else{ /* construct an lmreturn for user */
+ LMLIST *new;
+ size_t flen, dlen;
+
+ if((new=(LMLIST *)malloc(sizeof(*new))) == NULL
+ || (new->fname=malloc(gmp->current->fname ? (flen=strlen(gmp->current->fname))+1 : 1)) == NULL
+ || (new->dir=malloc((dlen=strlen(gmp->dname))+1)) == NULL){
+ emlwrite("\007Can't malloc space for filename", NULL);
+ return(-1);
+ }
+
+ strncpy(new->fname,
+ gmp->current->fname ? gmp->current->fname : "", flen);
+ new->fname[flen] = '\0';
+ strncpy(new->dir, gmp->dname, dlen);
+ new->dir[dlen] = '\0';
+ strncpy(new->size, gmp->current->size, sizeof(new->size));
+ new->size[sizeof(new->size)-1] = '\0';
+ new->next = NULL;
+ *lmreturn = new;
+ }
+
+ zotmaster(&gmp);
+ return(1);
+ }
+
+ if ((strlen(gmp->dname) < dirlen) &&
+ (strlen(gmp->current->fname) < fnlen)){
+ strncpy(dir, gmp->dname, dirlen);
+ dir[dirlen-1] = '\0';
+ strncpy(fn, gmp->current->fname, fnlen);
+ fn[fnlen-1] = '\0';
+ }
+ else {
+ zotmaster(&gmp);
+ return(-1);
+ }
+ if(sz != NULL){ /* size uninteresting */
+ strncpy(sz, gmp->current->size, szlen);
+ sz[szlen-1] = '\0';
+ }
+
+ zotmaster (&gmp);
+ return (1);
+ }
+ break;
+
+ case 'w': /* Where is */
+ case 'W':
+ case (CTRL|'W'):
+ i = 0;
+
+ while(!i){
+
+ switch(readpattern(_("File name to find"), FALSE)){
+ case HELPCH:
+ emlwrite(_("\007No help yet!"), NULL);
+/* remove break and sleep after help text is installed */
+ sleep(3);
+ break;
+ case (CTRL|'L'):
+ PaintBrowser(gmp, 0, &crow, &ccol);
+ break;
+ case (CTRL|'Y'): /* first first cell */
+ for(tp = gmp->top; tp->prev; tp = tp->prev)
+ ;
+
+ i++;
+ /* fall thru to repaint */
+ case (CTRL|'V'):
+ if(!i){
+ do{
+ tp = gmp->top;
+ if((i = term.t_nrow - term.t_mrow - 2) <= 0)
+ break;
+
+ while(i-- && tp->next){
+ j = 0;
+ while(++j <= gmp->fpl && tp->next)
+ tp = tp->next;
+ }
+
+ if(i < 0)
+ gmp->top = tp;
+ }
+ while(tp->next);
+
+ emlwrite(_("Searched to end of directory"), NULL);
+ }
+ else
+ emlwrite(_("Searched to start of directory"), NULL);
+
+ if(tp){
+ PlaceCell(gmp, gmp->current, &row, &col);
+ PaintCell(row, col, gmp->cpf, gmp->current, 0);
+ gmp->current = tp;
+ if(PlaceCell(gmp, gmp->current, &row, &col)){
+ PaintBrowser(gmp, 1, &crow, &ccol);
+ }
+ else{
+ PaintCell(row, col, gmp->cpf, gmp->current, 1);
+ crow = row;
+ ccol = col;
+ }
+ }
+
+ i++; /* make sure we jump out */
+ break;
+ case ABORT:
+ emlwrite(_("Whereis cancelled"), NULL);
+ i++;
+ break;
+ case FALSE:
+ mlerase();
+ i++;
+ break;
+ case TRUE:
+ {
+ char *utf8 = NULL;
+
+ if(pat && pat[0])
+ utf8 = ucs4_to_utf8_cpystr(pat);
+
+ if(utf8 && (tp = FindCell(gmp, utf8)) != NULL){
+ PlaceCell(gmp, gmp->current, &row, &col);
+ PaintCell(row, col, gmp->cpf, gmp->current, 0);
+ gmp->current = tp;
+
+ if(PlaceCell(gmp, tp, &row, &col)){ /* top changed */
+ PaintBrowser(gmp, 1, &crow, &ccol);
+ }
+ else{
+ PaintCell(row, col, gmp->cpf, gmp->current, 1);
+ crow = row;
+ ccol = col;
+ }
+ mlerase();
+ }
+ else{
+ eml.s = utf8;
+ emlwrite(_("\"%s\" not found"), &eml);
+ }
+
+ if(utf8)
+ fs_give((void **) &utf8);
+ }
+
+ i++;
+ break;
+ default:
+ break;
+ }
+ }
+
+ BrowserKeys();
+ break;
+
+ case (CTRL|'\\') :
+#if defined MOUSE && !defined(_WINDOWS)
+ toggle_xterm_mouse(0,1);
+#else
+ unknown_command(c);
+#endif
+ break;
+
+ case (CTRL|'Z'):
+ if(gmode&MDSSPD){
+ bktoshell(0, 1);
+ PaintBrowser(gmp, 0, &crow, &ccol);
+ break;
+ } /* fall thru with error! */
+
+ default: /* what? */
+ Default:
+ unknown_command(c);
+
+ case NODATA: /* no op */
+ break;
+ }
+ }
+}
+
+
+/*
+ * getfcells - make a master browser struct and fill it in
+ * return NULL if there's a problem.
+ */
+struct bmaster *
+getfcells(char *dname, int fb_flags)
+{
+ int i, /* various return codes */
+ flength,
+ nentries = 0; /* number of dir ents */
+ off_t attsz;
+ char *np, /* names of files in dir */
+ *dcp, /* to add file to path */
+ *tmpstr,
+ **filtnames, /* array filtered names */
+ errbuf[NLINE];
+ struct fcell *ncp, /* new cell pointer */
+ *tcp; /* trailing cell ptr */
+ struct bmaster *mp;
+ EML eml;
+
+ errbuf[0] = '\0';
+ if((mp=(struct bmaster *)malloc(sizeof(struct bmaster))) == NULL){
+ emlwrite("\007Can't malloc space for master filename cell", NULL);
+ return(NULL);
+ }
+
+ memset(mp, 0, sizeof(*mp));
+
+ if(dname[0] == '.' && dname[1] == '\0'){ /* remember this dir */
+ if(!getcwd(mp->dname, 256))
+ mp->dname[0] = '\0';
+ }
+ else if(dname[0] == '.' && dname[1] == '.' && dname[2] == '\0'){
+ if(!getcwd(mp->dname, 256))
+ mp->dname[0] = '\0';
+ else{
+ if((np = (char *)strrchr(mp->dname, C_FILESEP)) != NULL)
+ if(np != mp->dname)
+ *np = '\0';
+ }
+ }
+ else{
+ strncpy(mp->dname, dname, sizeof(mp->dname));
+ mp->dname[sizeof(mp->dname)-1] = '\0';
+ }
+
+ mp->head = mp->top = NULL;
+ mp->cpf = mp->fpl = 0;
+ mp->longest = 5; /* .. must be labeled! */
+ mp->flags = fb_flags;
+
+ eml.s = mp->dname;
+ emlwrite("Building file list of %s...", &eml);
+
+ if((mp->names = getfnames(mp->dname, NULL, &nentries, errbuf, sizeof(errbuf))) == NULL){
+ free((char *) mp);
+ if(*errbuf)
+ emlwrite(errbuf, NULL);
+
+ return(NULL);
+ }
+
+ /*
+ * this is the fun part. build an array of pointers to the fnames we're
+ * interested in (i.e., do any filtering), then pass that off to be
+ * sorted before building list of cells...
+ *
+ * right now default filtering on ".*" except "..", but this could
+ * easily be made a user option later on...
+ */
+ if((filtnames=(char **)malloc((nentries+1) * sizeof(char *))) == NULL){
+ emlwrite("\007Can't malloc space for name array", NULL);
+ zotmaster(&mp);
+ return(NULL);
+ }
+
+ i = 0; /* index of filt'd array */
+ np = mp->names;
+ while(nentries--){
+ int ii;
+ int width;
+
+ /*
+ * Filter dot files? Always filter ".", never filter "..",
+ * and sometimes fitler ".*"...
+ */
+ if(*np == '.' && (!(*(np+1) == '.' && *(np+2) == '\0')
+ && !(*(np+1) == '\0' && (fb_flags&FB_SAVE)))
+ && (*(np+1) == '\0' || !(gmode & MDDOTSOK))){
+ np += strlen(np) + 1;
+ continue;
+ }
+
+ filtnames[i++] = np;
+
+ ii = (int) strlen(np);
+ if((width = (int) utf8_width(np)) > mp->longest)
+ mp->longest = width; /* remember longest */
+
+ np += ii + 1; /* advance name pointer */
+ }
+
+ nentries = i; /* new # of entries */
+
+ /*
+ * sort files case independently
+ */
+ qsort((qsort_t *)filtnames, (size_t)nentries, sizeof(char *), sstrcasecmp);
+
+ /*
+ * this is so we use absolute path names for stats.
+ * remember: be careful using dname as directory name, and fix mp->dname
+ * when we're done
+ */
+ dcp = (char *) strchr(mp->dname, '\0');
+ if(dcp == mp->dname || dcp[-1] != C_FILESEP){
+ dcp[0] = C_FILESEP;
+ dcp[1] = '\0';
+ }
+ else
+ dcp--;
+
+ i = 0;
+ while(nentries--){ /* stat filtered files */
+ /* get a new cell */
+ if((ncp=(struct fcell *)malloc(sizeof(struct fcell))) == NULL){
+ emlwrite("\007Can't malloc cells for browser!", NULL);
+ zotfcells(mp->head); /* clean up cells */
+ free((char *) filtnames);
+ free((char *) mp);
+ return(NULL); /* bummer. */
+ }
+
+ ncp->next = ncp->prev = NULL;
+
+ if(mp->head == NULL){ /* tie it onto the list */
+ mp->head = mp->top = mp->current = ncp;
+ }
+ else{
+ tcp->next = ncp;
+ ncp->prev = tcp;
+ }
+
+ tcp = ncp;
+
+ /* fill in the new cell */
+ ncp->fname = filtnames[i++];
+
+ /* fill in file's mode */
+ if ((flength = strlen(ncp->fname) + 1 + strlen(dname)) < sizeof(mp->dname)){
+ strncpy(&dcp[1], ncp->fname, sizeof(mp->dname)-(dcp+1-mp->dname)); /* use absolute path! */
+ mp->dname[sizeof(mp->dname)-1] = '\0';
+ tmpstr = mp->dname;
+ }
+ else{
+ if((tmpstr = (char *)malloc((flength+1)*sizeof(char))) == NULL){
+ emlwrite("\007Can't malloc cells for temp buffer!", NULL);
+ zotfcells(mp->head); /* clean up cells */
+ free((char *) filtnames);
+ free((char *) mp);
+ return(NULL); /* bummer. */
+ }
+
+ strncpy(tmpstr, dname, flength);
+ tmpstr[flength] = '\0';
+ tmpstr = strncat(tmpstr, S_FILESEP, flength+1-1-strlen(tmpstr));
+ tmpstr[flength] = '\0';
+ tmpstr = strncat(tmpstr, ncp->fname, flength+1-1-strlen(tmpstr));
+ tmpstr[flength] = '\0';
+ }
+
+ switch(fexist(tmpstr, "t", &attsz)){
+ case FIODIR :
+ ncp->mode = FIODIR;
+ snprintf(ncp->size, sizeof(ncp->size), "(%s%s%s)",
+ (ncp->fname[0] == '.' && ncp->fname[1] == '.'
+ && ncp->fname[2] == '\0') ? PARENTDIR :
+ ((fb_flags&FB_SAVE) && ncp->fname[0] == '.' && ncp->fname[1] == '\0'
+ ? SELECTWORD : ""),
+ (ncp->fname[0] == '.' && ncp->fname[1] == '.'
+ && ncp->fname[2] == '\0') ? " " :
+ ((fb_flags&FB_SAVE) && ncp->fname[0] == '.' && ncp->fname[1] == '\0'
+ ? " " : ""),
+ DIRWORD);
+ break;
+
+ case FIOSYM :
+ ncp->mode = FIOSYM;
+ strncpy(ncp->size, "--", sizeof(ncp->size));
+ ncp->size[sizeof(ncp->size)-1] = '\0';
+ break;
+
+ default :
+ ncp->mode = FIOSUC; /* regular file */
+ strncpy(ncp->size, prettysz(attsz), sizeof(ncp->size));
+ ncp->size[sizeof(ncp->size)-1] = '\0';
+ break;
+ }
+
+ if (flength >= NLINE)
+ free((char *) tmpstr);
+ }
+
+ dcp[(dcp == mp->dname) ? 1 : 0] = '\0'; /* remember to cap dname */
+ free((char *) filtnames); /* 'n blast filt'd array*/
+
+ percdircells(mp);
+ layoutcells(mp);
+ if(strlen(mp->dname) < sizeof(browse_dir)){
+ strncpy(browse_dir, mp->dname, sizeof(browse_dir));
+ browse_dir[sizeof(browse_dir)-1] = '\0';
+ }
+ else
+ browse_dir[0] = '\0';
+
+ return(mp);
+}
+
+
+int
+fcell_is_selected(struct fcell *cell, struct bmaster *mp)
+{
+ LMLIST *lm;
+
+ if(cell && cell->fname){
+ for(lm = mp ? mp->lm : NULL; lm; lm = lm->next){
+ /* directory has to match */
+ if(!((mp->dname[0] == '\0' && (!lm->dir || lm->dir[0] =='\0'))
+ || (mp->dname[0] != '\0' && lm->dir && lm->dir[0] !='\0'
+ && !strcmp(mp->dname, lm->dir))))
+ continue;
+
+ if(lm->fname && !strcmp(cell->fname, lm->fname))
+ return(1);
+ }
+ }
+
+ return(0);
+}
+
+
+/*
+ * Adds a new name to the head of the lmlist
+ */
+void
+add_cell_to_lmlist(struct fcell *cell, struct bmaster *mp)
+{
+ LMLIST *new;
+ size_t flen, dlen;
+
+ if(mp && cell && cell->fname && cell->fname[0]){
+ if((new=(LMLIST *)malloc(sizeof(*new))) == NULL ||
+ (new->fname=malloc(sizeof(char)*((flen=strlen(cell->fname))+1))) == NULL ||
+ (new->dir=malloc(sizeof(char)*((dlen=strlen(mp->dname))+1))) == NULL){
+ emlwrite("\007Can't malloc space for filename", NULL);
+ return;
+ }
+
+ strncpy(new->fname, cell->fname, flen);
+ new->fname[flen] = '\0';
+ strncpy(new->dir, mp->dname, dlen);
+ new->dir[dlen] = '\0';
+ new->size[0] = '\0';
+ if(cell->size[0]){
+ strncpy(new->size, cell->size, sizeof(new->size));
+ new->size[sizeof(new->size)-1] = '\0';
+ }
+
+ new->next = mp->lm;
+ mp->lm = new;
+ }
+}
+
+
+/*
+ * Deletes a name from the lmlist
+ */
+void
+del_cell_from_lmlist(struct fcell *cell, struct bmaster *mp)
+{
+ LMLIST *lm, *lmprev = NULL;
+
+ if(mp && cell && cell->fname && cell->fname[0])
+ for(lm = mp ? mp->lm : NULL; lm; lm = lm->next){
+ if(lm->fname && strcmp(cell->fname, lm->fname) == 0){
+ free((char *) lm->fname);
+ if(lm->dir)
+ free((char *) lm->dir);
+
+ if(lmprev)
+ lmprev->next = lm->next;
+ else
+ mp->lm = lm->next;
+
+ free((char *) lm);
+
+ break;
+ }
+
+ lmprev = lm;
+ }
+}
+
+
+/*
+ * PaintCell - print the given cell at the given location on the display
+ * the format of a printed cell is:
+ *
+ * "<fname> <size> "
+ */
+void
+PaintCell(int row, int col,
+ int sc, /* screen columns available for this cell */
+ struct fcell *cell, int inverted)
+{
+ char buf1[NLINE], buf2[NLINE];
+ char lbuf[5];
+ int need, l_wid, f_wid, f_to_s_wid, s_wid;
+ UCS *ucs;
+
+ if(cell == NULL)
+ return;
+
+ l_wid = (gmp && ((gmp->flags & FB_LMODEPOS) > 4)) ? 4 : 0;
+ f_wid = utf8_width(cell->fname ? cell->fname : "");
+ f_to_s_wid = 1;
+ s_wid = utf8_width(cell->size ? cell->size : "");
+
+ /* the two is the space between cell columns */
+ sc = MIN(sc-2, term.t_ncol-col);
+
+ if(l_wid){
+ if(gmp && gmp->flags & FB_LMODE && cell->mode != FIODIR){
+ /*
+ * We have to figure out here if it is selected or not
+ * and use that to write the X or space.
+ */
+ lbuf[0] = '[';
+ if(fcell_is_selected(cell, gmp))
+ lbuf[1] = 'X';
+ else
+ lbuf[1] = ' ';
+
+ lbuf[2] = ']';
+ lbuf[3] = ' ';
+ }
+ else{
+ lbuf[0] = lbuf[1] = lbuf[2] = lbuf[3] = ' ';
+ }
+
+ lbuf[4] = '\0';
+ }
+
+ sc -= l_wid;
+
+ need = f_wid+f_to_s_wid+s_wid;
+
+ /* space between name and size */
+ if(need < sc)
+ f_to_s_wid += (sc-need);
+
+ /*
+ * If the width isn't enough to handle everything we're just putting a single
+ * space between fname and size and truncating on the right. That means that
+ * the sizes in the right hand column won't line up correctly when there is
+ * a lack of space. Instead, we opt for displaying as much info as possible
+ * in a slightly discordant way.
+ */
+
+ movecursor(row, col);
+ if(l_wid){
+ ucs = utf8_to_ucs4_cpystr(lbuf);
+ if(ucs){
+ pputs(ucs, 0);
+ fs_give((void **) &ucs);
+ }
+ }
+
+ utf8_snprintf(buf1, sizeof(buf1), "%*.*w%*.*w%*.*w",
+ f_wid, f_wid, cell->fname ? cell->fname : "",
+ f_to_s_wid, f_to_s_wid, "",
+ s_wid, s_wid, cell->size ? cell->size : "");
+
+ utf8_snprintf(buf2, sizeof(buf2), "%*.*w", sc, sc, buf1);
+
+ if(inverted)
+ (*term.t_rev)(1);
+
+ ucs = utf8_to_ucs4_cpystr(buf2);
+ if(ucs){
+ pputs(ucs, 0);
+ fs_give((void **) &ucs);
+ }
+
+ if(inverted)
+ (*term.t_rev)(0);
+}
+
+
+/*
+ * PaintBrowse - with the current data, display the browser. if level == 0
+ * paint the whole thing, if level == 1 just paint the cells
+ * themselves
+ */
+void
+PaintBrowser(struct bmaster *mp, int level, int *row, int *col)
+{
+ int i, cl;
+ struct fcell *tp;
+
+ if(!level){
+ ClearBrowserScreen();
+ BrowserAnchor(mp->dname);
+ }
+
+ i = 0;
+ tp = mp->top;
+ cl = COMPOSER_TOP_LINE; /* current display line */
+ while(tp){
+
+ PaintCell(cl, mp->cpf * i, mp->cpf, tp, tp == mp->current);
+
+ if(tp == mp->current){
+ if(row)
+ *row = cl;
+
+ if(col)
+ *col = mp->cpf * i;
+ }
+
+ if(++i >= mp->fpl){
+ i = 0;
+ if(++cl > term.t_nrow-(term.t_mrow+1))
+ break;
+ }
+
+ tp = tp->next;
+ }
+
+ if(level){
+ while(cl <= term.t_nrow - (term.t_mrow+1)){
+ if(!i)
+ movecursor(cl, 0);
+ peeol();
+ movecursor(++cl, 0);
+ }
+ }
+ else{
+ BrowserKeys();
+ }
+}
+
+
+/*
+ * BrowserKeys - just paints the keyhelp at the bottom of the display
+ */
+void
+BrowserKeys(void)
+{
+ menu_browse[QUIT_KEY].name = (gmode&MDBRONLY) ? "Q" : "E";
+ /* TRANSLATORS: Brwsr is an abbreviation for Browser, which is
+ a command used to look through something */
+ menu_browse[QUIT_KEY].label = (gmode&MDBRONLY) ? N_("Quit") : N_("Exit Brwsr");
+ menu_browse[GOTO_KEY].name = (gmode&MDGOTO) ? "G" : NULL;
+ menu_browse[GOTO_KEY].label = (gmode&MDGOTO) ? N_("Goto") : NULL;
+ if(gmode & MDBRONLY){
+ menu_browse[EXEC_KEY].name = "L";
+ menu_browse[EXEC_KEY].label = N_("Launch");
+ menu_browse[SELECT_KEY].name = "V";
+ menu_browse[SELECT_KEY].label = "[" N_("View") "]";
+ menu_browse[PICO_KEY].name = "E";
+ menu_browse[PICO_KEY].label = N_("Edit");
+ }
+ else{
+ menu_browse[SELECT_KEY].name = "S";
+ menu_browse[SELECT_KEY].label = "[" N_("Select") "]";
+ menu_browse[PICO_KEY].name = "A";
+ menu_browse[PICO_KEY].label = N_("Add");
+
+ if(gmp && gmp->flags & FB_LMODEPOS){
+ if(gmp && gmp->flags & FB_LMODE){ /* ListMode is already on */
+ menu_browse[EXEC_KEY].name = "X";
+ menu_browse[EXEC_KEY].label = N_("Set/Unset");
+ }
+ else{ /* ListMode is possible */
+ menu_browse[EXEC_KEY].name = "L";
+ menu_browse[EXEC_KEY].label = N_("ListMode");
+ }
+ }
+ else{ /* No ListMode possible */
+ menu_browse[EXEC_KEY].name = NULL;
+ menu_browse[EXEC_KEY].label = NULL;
+ }
+ }
+
+ wkeyhelp(menu_browse);
+}
+
+
+/*
+ * layoutcells - figure out max length of cell and how many cells can
+ * go on a line of the display
+ */
+void
+layoutcells(struct bmaster *mp)
+{
+ static int wid = -1;
+ /*
+ * Max chars/file. Actually this is max screen cells/file
+ * Longest name + "(parent dir)"
+ */
+ if(wid < 0)
+ wid = MAX(utf8_width(PARENTDIR),utf8_width(SELECTWORD)) + utf8_width(DIRWORD);
+
+ mp->cpf = mp->longest + wid + 3;
+
+ if(mp->flags & FB_LMODEPOS) /* "[X] " */
+ mp->cpf += 4;
+
+ if(gmode & MDONECOL){
+ mp->fpl = 1;
+ }
+ else{
+ int i = 1;
+
+ while(i*mp->cpf - 2 <= term.t_ncol) /* no force... */
+ i++; /* like brute force! */
+
+ mp->fpl = i - 1; /* files per line */
+ }
+
+ if(mp->fpl == 0)
+ mp->fpl = 1;
+}
+
+
+/*
+ * percdircells - bubble all the directory cells to the top of the
+ * list.
+ */
+void
+percdircells(struct bmaster *mp)
+{
+ struct fcell *dirlp, /* dir cell list pointer */
+ *lp, *nlp; /* cell list ptr and next */
+
+ dirlp = NULL;
+ for(lp = mp->head; lp; lp = nlp){
+ nlp = lp->next;
+ if(lp->mode == FIODIR){
+ if(lp->prev) /* clip from list */
+ lp->prev->next = lp->next;
+
+ if(lp->next)
+ lp->next->prev = lp->prev;
+
+ if((lp->prev = dirlp) != NULL){ /* tie it into dir portion */
+ if((lp->next = dirlp->next) != NULL)
+ lp->next->prev = lp;
+
+ dirlp->next = lp;
+ dirlp = lp;
+ }
+ else{
+ if((dirlp = lp) != mp->head)
+ dirlp->next = mp->head;
+
+ if(dirlp->next)
+ dirlp->next->prev = dirlp;
+
+ mp->head = mp->top = mp->current = dirlp;
+ }
+ }
+ }
+}
+
+
+/*
+ * PlaceCell - given a browser master and a cell, return row and col of the display that
+ * it should go on.
+ *
+ * return 1 if mp->top has changed, x,y relative to new page
+ * return 0 if otherwise (same page)
+ * return -1 on error
+ */
+int
+PlaceCell(struct bmaster *mp, struct fcell *cp, int *x, int *y)
+{
+ int cl = COMPOSER_TOP_LINE; /* current line */
+ int ci = 0; /* current index on line */
+ int rv = 0;
+ int secondtry = 0;
+ struct fcell *tp;
+
+ /* will cp fit on screen? */
+ tp = mp->top;
+ while(1){
+ if(tp == cp){ /* bingo! */
+ *x = cl;
+ *y = ci * mp->cpf;
+ break;
+ }
+
+ if((tp = tp->next) == NULL){ /* above top? */
+ if(secondtry++){
+ emlwrite("\007Internal error: can't find fname cell", NULL);
+ return(-1);
+ }
+ else{
+ tp = mp->top = mp->head; /* try from the top! */
+ cl = COMPOSER_TOP_LINE;
+ ci = 0;
+ rv = 1;
+ continue; /* start over! */
+ }
+ }
+
+ if(++ci >= mp->fpl){ /* next line? */
+ ci = 0;
+ if(++cl > term.t_nrow-(term.t_mrow+1)){ /* next page? */
+ ci = mp->fpl; /* tp is at bottom right */
+ while(ci--) /* find new top */
+ tp = tp->prev;
+ mp->top = tp;
+ ci = 0;
+ cl = COMPOSER_TOP_LINE; /* keep checking */
+ rv = 1;
+ }
+ }
+
+ }
+
+ /* not on display! */
+ return(rv);
+}
+
+
+/*
+ * zotfcells - clean up malloc'd cells of file names
+ */
+void
+zotfcells(struct fcell *hp)
+{
+ struct fcell *tp;
+
+ while(hp){
+ tp = hp;
+ hp = hp->next;
+ tp->next = NULL;
+ free((char *) tp);
+ }
+}
+
+
+/*
+ * zotmaster - blast the browser master struct
+ */
+void
+zotmaster(struct bmaster **mp)
+{
+ if(mp && *mp){
+ zotfcells((*mp)->head); /* free cells */
+ zotlmlist((*mp)->lm); /* free lmlist */
+ if((*mp)->names)
+ free((char *)(*mp)->names); /* free file names */
+
+ free((char *)*mp); /* free master */
+ *mp = NULL; /* make double sure */
+ }
+}
+
+
+/*
+ * FindCell - starting from the current cell find the first occurance of
+ * the given string wrapping around if necessary
+ */
+struct fcell *
+FindCell(struct bmaster *mp, char *utf8string)
+{
+ struct fcell *tp, *fp;
+
+ if(*utf8string == '\0')
+ return(NULL);
+
+ fp = NULL;
+ tp = mp->current->next;
+
+ while(tp && !fp){
+ if(sisin(tp->fname, utf8string))
+ fp = tp;
+ else
+ tp = tp->next;
+ }
+
+ tp = mp->head;
+ while(tp != mp->current && !fp){
+ if(sisin(tp->fname, utf8string))
+ fp = tp;
+ else
+ tp = tp->next;
+ }
+
+ return(fp);
+}
+
+
+/*
+ * sisin - case insensitive substring matching function
+ *
+ * We can't really do case-insensitive for non-ascii, so restrict
+ * that to ascii characters. The strings will be utf8.
+ */
+int
+sisin(char *bigstr, char *utf8substr)
+{
+ register int j;
+
+ while(*bigstr){
+ j = 0;
+ while(bigstr[j] == utf8substr[j]
+ || ((unsigned char)bigstr[j] < 0x80 && (unsigned char)utf8substr[j] < 0x80
+ && toupper((unsigned char)bigstr[j]) == toupper((unsigned char)utf8substr[j])))
+ if(utf8substr[++j] == '\0') /* bingo! */
+ return(1);
+
+ bigstr++;
+ }
+
+ return(0);
+}
+
+
+/*
+ * set_browser_title -
+ */
+void
+set_browser_title(char *s)
+{
+ browser_title = s;
+}
+
+
+/*
+ * BrowserAnchor - draw the browser's anchor line.
+ */
+void
+BrowserAnchor(char *utf8dir)
+{
+ char *pdir;
+ char titlebuf[NLINE];
+ char buf[NLINE];
+ char dirbuf[NLINE];
+ char *dots = "...";
+ char *br = "BROWSER";
+ char *dir = "Dir: ";
+ UCS *ucs;
+ int need, extra, avail;
+ int title_wid, b_wid, t_to_b_wid, b_to_d_wid, d_wid, dot_wid, dir_wid, after_dir_wid;
+ COLOR_PAIR *lastc = NULL;
+
+ if(!utf8dir)
+ utf8dir = "";
+
+ pdir = utf8dir;
+
+ if(browser_title)
+ snprintf(titlebuf, sizeof(buf), " %s", browser_title);
+ else if(Pmaster)
+ snprintf(titlebuf, sizeof(buf), " ALPINE %s", Pmaster->pine_version);
+ else
+ snprintf(titlebuf, sizeof(buf), " UW PICO %s", (gmode&MDBRONLY) ? "BROWSER" : version);
+
+ title_wid = utf8_width(titlebuf);
+ t_to_b_wid = 15;
+ b_wid = utf8_width(br);
+ b_to_d_wid = 4;
+ d_wid = utf8_width(dir);
+ dot_wid = 0;
+ dir_wid = utf8_width(pdir);
+
+ need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
+
+ if(need > term.t_ncol){
+ extra = need - term.t_ncol;
+ t_to_b_wid -= MIN(extra, 10);
+ need -= MIN(extra, 10);
+
+ if(need > term.t_ncol){
+ extra = need - term.t_ncol;
+ b_to_d_wid -= MIN(extra, 2);
+ need -= MIN(extra, 2);
+ }
+
+ if(need > term.t_ncol){
+ titlebuf[0] = titlebuf[1] = ' ';
+ titlebuf[2] = '\0';
+ title_wid = utf8_width(titlebuf);
+ t_to_b_wid = 0;
+ b_to_d_wid = 4;
+ need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
+ if(need > term.t_ncol){
+ extra = need - term.t_ncol;
+ b_to_d_wid -= MIN(extra, 2);
+ need -= MIN(extra, 2);
+ }
+
+ need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
+ if(need > term.t_ncol){
+ t_to_b_wid = 0;
+ b_wid = 0;
+ b_to_d_wid = 0;
+ }
+
+ need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
+ if(need > term.t_ncol)
+ d_wid = 0;
+
+ need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
+
+ if(need > term.t_ncol && dir_wid > 0){
+ dot_wid = utf8_width(dots);
+ need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
+ while(need > term.t_ncol && (pdir = strchr(pdir+1, C_FILESEP))){
+ dir_wid = utf8_width(pdir);
+ need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
+ }
+
+ if(!pdir){ /* adjust other widths to fill up space */
+ avail = term.t_ncol - (title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid);
+ if(avail > 2)
+ utf8_to_width_rhs(dirbuf, utf8dir, sizeof(dirbuf), avail);
+ else
+ dirbuf[0] = '\0';
+
+ pdir = dirbuf;
+ }
+
+ dir_wid = utf8_width(pdir);
+ }
+ }
+ }
+
+ extra = term.t_ncol - (title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid);
+
+ if(extra >= 0)
+ after_dir_wid = extra;
+ else
+ after_dir_wid = 0;
+
+ utf8_snprintf(buf, sizeof(buf), "%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w",
+ title_wid, title_wid, titlebuf,
+ t_to_b_wid, t_to_b_wid, "",
+ b_wid, b_wid, br,
+ b_to_d_wid, b_to_d_wid, "",
+ d_wid, d_wid, dir,
+ dot_wid, dot_wid, dots,
+ dir_wid, dir_wid, pdir,
+ after_dir_wid, after_dir_wid, "");
+
+ /* just making sure */
+ utf8_snprintf(titlebuf, sizeof(titlebuf), "%-*.*w", term.t_ncol, term.t_ncol, buf);
+
+ movecursor(0, 0);
+ if(Pmaster && Pmaster->colors && Pmaster->colors->tbcp
+ && pico_is_good_colorpair(Pmaster->colors->tbcp)){
+ lastc = pico_get_cur_color();
+ (void) pico_set_colorp(Pmaster->colors->tbcp, PSC_NONE);
+ }
+ else
+ (*term.t_rev)(1);
+
+ ucs = utf8_to_ucs4_cpystr(titlebuf);
+ if(ucs){
+ pputs(ucs, 0);
+ fs_give((void **) &ucs);
+ }
+
+ if(lastc){
+ (void) pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+ else
+ (*term.t_rev)(0);
+}
+
+
+/*
+ * ResizeBrowser - handle a resize event
+ */
+int
+ResizeBrowser(void)
+{
+ if(gmp){
+ layoutcells(gmp);
+ PaintBrowser(gmp, 0, NULL, NULL);
+ return(1);
+ }
+ else
+ return(0);
+}
+
+
+void
+ClearBrowserScreen(void)
+{
+ int i;
+
+ for(i = 0; i <= term.t_nrow; i++){ /* clear screen */
+ movecursor(i, 0);
+ peeol();
+ }
+}
+
+
+void
+BrowserRunChild(char *child, char *dir)
+{
+ int status;
+ char tmp[NLINE];
+ time_t t_in, t_out;
+
+ ClearBrowserScreen();
+ movecursor(0, 0);
+ (*term.t_close)();
+ if(!isdir(dir, NULL, &t_in))
+ t_in = 0;
+
+ fflush(stdout);
+ status = system(child);
+ (*term.t_open)();
+ if(t_in && isdir(dir, NULL, &t_out) && t_in < t_out){
+ struct bmaster *mp;
+
+ if((mp = getfcells(dir, 0)) != NULL){
+ zotmaster(&gmp);
+ gmp = mp;
+ }
+ /* else getfcells should explain what happened */
+ }
+
+ /* complain about non-zero exit status */
+ if((status >> 8) & 0xff){
+
+ movecursor(term.t_nrow - 1, 0);
+ snprintf(tmp, sizeof(tmp), "[ \"%.30s\" exit with error value: %d ]",
+ child, (status >> 8) & 0xff);
+ pputs_utf8(tmp, 1);
+ movecursor(term.t_nrow, 0);
+ pputs_utf8("[ Hit RETURN to continue ]", 1);
+ fflush(stdout);
+
+ while(GetKey() != (CTRL|'M')){
+ (*term.t_beep)();
+ fflush(stdout);
+ }
+ }
+}
+
+
+/*
+ * imitate pc-pine memory for where we last called the file browser.
+ */
+void
+p_chdir(struct bmaster *mp)
+{
+ if(mp && mp->dname)
+ chdir(mp->dname);
+}
+#else /* _WINDOWS */
+
+
+/*
+ * pico_file_browse - Exported version of FileBrowse below.
+ */
+int
+pico_file_browse(PICO *pdata, char *dir, size_t dirlen, char *fn, size_t fnlen,
+ char *sz, size_t szlen, int flags)
+{
+ return(FileBrowse(dir, dirlen, fn, fnlen, sz, szlen, flags, NULL));
+}
+
+int
+ResizeBrowser (void)
+{
+ return (0);
+}
+
+/*
+ * FileBrowse - Windows version of above function
+ */
+int
+FileBrowse(char *dir, size_t dirlen, char *fn, size_t fnlen,
+ char *sz, size_t szlen, int fb_flags, LMLIST **lmreturn)
+{
+ struct stat sbuf;
+ int rc;
+ char lfn[NLINE];
+
+ if (fb_flags & FB_SAVE){
+ rc = mswin_savefile(dir, dirlen, fn, MIN(dirlen,fnlen));
+ }
+ else{
+ *fn = '\0'; /* No initial file names for
+ * open. */
+ if(fb_flags & FB_LMODEPOS){
+ /*
+ * We're going to allow multiple filenames to be returned so
+ * we don't want to use the passed in fn for that. Instead, make
+ * a bigger space and use that.
+ */
+ char f[20000];
+ size_t flen, dlen;
+
+ memset(f, 0, sizeof(f));
+
+ rc = mswin_multopenfile(dir, dirlen, f, sizeof(f),
+ (fb_flags & FB_ATTACH) ? NULL : "Text Files (*.txt)#*.txt");
+
+ if(rc == 1){
+ LMLIST *lmhead = NULL, *new;
+ char *p;
+
+ /*
+ * Build an LMLIST to return to the caller.
+ */
+ for(p = f; *p; p += strlen(p)+1){
+ flen = strlen(p);
+ dlen = strlen(dir ? dir : "");
+ new = (LMLIST *) fs_get(sizeof(*new));
+ new->fname = (char *) fs_get((flen+1) * sizeof(char));
+ new->dir = (char *) fs_get((dlen+1) * sizeof(char));
+
+ strncpy(new->fname, p, flen);
+ new->fname[flen] = '\0';
+ strncpy(new->dir, dir ? dir : "", dlen);
+ new->dir[dlen] = '\0';
+
+ /* build full path to stat file. */
+ if((strlen(new->dir) + strlen(S_FILESEP) +
+ strlen(new->fname) + 1) < sizeof(lfn)){
+ strncpy(lfn, new->dir, sizeof(lfn));
+ lfn[sizeof(lfn)-1] = '\0';
+ strncat(lfn, S_FILESEP, sizeof(lfn)-strlen(lfn)-1);
+ strncat(lfn, new->fname, sizeof(lfn)-strlen(lfn)-1);
+ lfn[sizeof(lfn)-1] = '\0';
+ if(our_stat(lfn, &sbuf) < 0)
+ strncpy(new->size, "0", 32);
+ else
+ strncpy(new->size, prettysz((off_t)sbuf.st_size), 32);
+
+ new->size[32-1] = '\0';
+ }
+
+ new->next = lmhead;
+ lmhead = new;
+ }
+
+ *lmreturn = lmhead;
+ return(1);
+ }
+ }
+ else
+ rc = mswin_openfile (dir, dirlen, fn, fnlen,
+ (fb_flags & FB_ATTACH) ? NULL : "Text Files (*.txt)#*.txt");
+ }
+
+ if(rc == 1){
+ if(sz != NULL){
+ /* build full path to stat file. */
+ if((strlen(dir) + strlen(S_FILESEP) + strlen(fn) + 1) > NLINE)
+ return -1;
+
+ strncpy(lfn, dir, sizeof(lfn));
+ lfn[sizeof(lfn)-1] = '\0';
+ strncat(lfn, S_FILESEP, sizeof(lfn)-strlen(lfn)-1);
+ lfn[sizeof(lfn)-1] = '\0';
+ strncat(lfn, fn, sizeof(lfn)-strlen(lfn)-1);
+ lfn[sizeof(lfn)-1] = '\0';
+ if(our_stat(lfn, &sbuf) < 0){
+ strncpy(sz, "0", szlen);
+ sz[szlen-1] = '\0';
+ }
+ else{
+ strncpy(sz, prettysz ((off_t)sbuf.st_size), szlen);
+ sz[szlen-1] = '\0';
+ }
+ }
+
+ return(1);
+ }
+
+ return(rc ? -1 : 0);
+}
+
+#endif /* _WINDOWS */
+
+
+/*
+ * LikelyASCII - make a rough guess as to the displayability of the
+ * given file.
+ */
+int
+LikelyASCII(char *file)
+{
+#define LA_TEST_BUF 1024
+#define LA_LINE_LIMIT 300
+ int n, i, line, rv = FALSE;
+ unsigned char buf[LA_TEST_BUF];
+ FILE *fp;
+ EML eml;
+
+ if((fp = our_fopen(file, "rb")) != NULL){
+ clearerr(fp);
+ if((n = fread(buf, sizeof(char), LA_TEST_BUF * sizeof(char), fp)) > 0
+ || !ferror(fp)){
+ /*
+ * If we don't hit any newlines in a reasonable number,
+ * LA_LINE_LIMIT, of characters or the file contains NULLs,
+ * bag out...
+ */
+ rv = TRUE;
+ for(i = line = 0; i < n; i++)
+ if((line = (buf[i] == '\n') ? 0 : line + 1) >= LA_LINE_LIMIT
+ || !buf[i]){
+ rv = FALSE;
+ emlwrite(_("Can't display non-text file. Try \"Launch\"."),
+ NULL);
+ break;
+ }
+ }
+ else{
+ eml.s = file;
+ emlwrite(_("Can't read file: %s"), &eml);
+ }
+
+ fclose(fp);
+ }
+ else{
+ eml.s = file;
+ emlwrite(_("Can't open file: %s"), &eml);
+ }
+
+ return(rv);
+}
+
+
+void
+zotlmlist(LMLIST *lm)
+{
+ LMLIST *tp;
+
+ while(lm){
+ if(lm->fname)
+ free(lm->fname);
+
+ if(lm->dir)
+ free(lm->dir);
+
+ tp = lm;
+ lm = lm->next;
+ tp->next = NULL;
+ free((char *) tp);
+ }
+}
+
+
+/*
+ * time_to_check - checks the current time against the last time called
+ * and returns true if the elapsed time is > below.
+ * Newmail won't necessarily check, but we want to give it
+ * a chance to check or do a keepalive.
+ */
+int
+time_to_check(void)
+{
+ static time_t lasttime = 0L;
+
+ if(!get_input_timeout())
+ return(FALSE);
+
+ if(time((time_t *) 0) - lasttime > (Pmaster ? (time_t)(FUDGE-10) : get_input_timeout())){
+ lasttime = time((time_t *) 0);
+ return(TRUE);
+ }
+ else
+ return(FALSE);
+}