#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-2021 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 *bottom; /* last 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 menu; /* current menu to display */ 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 *); struct bmaster *RepaintBrowser(struct bmaster *, 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); 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 *); #define HELP_MENU {"?", N_("Get Help"), KS_SCREENHELP} #define OTHER_MENU {"O", N_("OTHER CMDS"), KS_NONE} static KEYMENU menu_browse[] = { HELP_MENU, {NULL, NULL, KS_NONE}, {NULL, NULL, KS_NONE}, {"-", N_("Prev Pg"), KS_PREVPAGE}, {"D", N_("Delete"), KS_NONE}, {"C",N_("Copy"), KS_NONE}, OTHER_MENU, {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} }; static KEYMENU menu_other[] = { HELP_MENU, {NULL, NULL, KS_NONE}, {NULL, NULL, KS_NONE}, {NULL, NULL, KS_NONE}, {NULL, NULL, KS_NONE}, {"1", N_("One Col"), KS_NONE}, OTHER_MENU, {NULL, NULL, KS_NONE}, {NULL, NULL, KS_NONE}, {NULL, NULL, KS_NONE}, {NULL, NULL, KS_NONE}, {".", N_("Dot files"), KS_NONE} }; #define QUIT_KEY 1 #define EXEC_KEY 2 #define GOTO_KEY 10 #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) ? "alpine -k -F" : "alpine -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 ~a~l~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_("~ ~. Switch show dot files."), N_("~ ~1 Switch single column display."), " ", 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 * * input: * 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; int flags; int add_file; char *p, *envp, child[2*NLINE+2], tmp[2*NLINE+1]; 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; emlwwrite(_("Can'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, 0)) != 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", NLINE, gmp->dname, C_FILESEP, NLINE, 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) emlwwrite(_("Type L command to use ListMode"), NULL); else unknown_command(c); break; } if(gmp->current->mode == FIODIR){ emlwwrite(_("Can'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. */ emlwwrite(_("Already 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", NLINE, gmp->dname, C_FILESEP, NLINE, gmp->current->fname); while(!i){ static EXTRAKEYS opts[] = { {"^X", N_("Add Name"), CTRL|'X', KS_NONE}, {NULL, NULL, 0, KS_NONE}, {NULL, NULL, 0, KS_NONE}, {NULL, NULL, 0, KS_NONE}, {NULL, NULL, 0, KS_NONE}, {NULL, NULL, 0, KS_NONE}, {NULL, NULL, 0, KS_NONE}, {NULL, NULL, 0, KS_NONE}, {NULL, NULL, 0, KS_NONE}, {NULL, NULL, 0, KS_NONE} }; status = mlreply_utf8(_("Command to execute: "), tmp, NLINE, QNORML, opts); switch(status){ case HELPCH: emlwwrite(_("No 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 */ emlwwrite(_("Can'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 */ 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; if(tp == gmp->bottom) gmp->bottom = tp->prev; tp->fname = NULL; tp->next = tp->prev = NULL; if(tp != gmp->current) free((char *) tp); if((tp = FindCell(gmp, gmp->current->fname, 0)) != 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: emlwwrite(_("No 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; emlwwrite(_("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; emlwwrite(_("Not a directory: \"%s\""), &eml); } break; default: break; } } BrowserKeys(); break; case '.': /* display dot files */ gmode ^= MDDOTSOK; gmp = RepaintBrowser(gmp, fb_flags); break; case '1': /* One column mode */ gmode ^= MDONECOL; gmp = RepaintBrowser(gmp, fb_flags); break; case 'o': /* Other menu */ case 'O': if (gmp){ if(gmp->menu == 0){ gmp->menu = 1; } else { gmp->menu = 0; } BrowserKeys(); } break; case 'a': /* Add */ case 'A': if(gmode&MDSCUR){ /* not allowed! */ emlwrite(_("Add not allowed in restricted mode"),NULL); break; } add_file = 1; 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){ int repaint = 0; EXTRAKEYS opts[10]; memset((void *) &opts, 0, 10*sizeof(EXTRAKEYS)); opts[0].name = "^X"; opts[0].label = add_file ? N_("Add Dir") : N_("Add File"); opts[0].key = (CTRL|'X'); switch(status=mlreply_utf8(add_file ? _("Name of file to add: ") : _("Name of directory to add: "), child, NLINE, QFFILE, opts)){ case HELPCH: emlwwrite(_("No 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|'X'): if(add_file > 0) add_file = 0; else add_file = 1; break; case ABORT: emlwrite(add_file > 0 ? _("Add File Cancelled") : _("Add Directory 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 */ /* FALLTHROUGH */ case TRUE: i++; if(child[0] == '\0'){ emlwrite(add_file > 0 ? _("No file named. Add Cancelled.") : _("No directory 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; emlwwrite(_("Restricted mode allows Add in %s only"), &eml); break; } if((status = fexist(child, "w", (off_t *)NULL)) == FIOSUC){ snprintf(tmp, sizeof(tmp), _("%s \"%.*s\" already exists!"), add_file > 0 ? "File" : "Directory", NLINE - 20, child); emlwrite(tmp, NULL); break; } else if(status != FIOFNF){ fioperr(status, child); break; } if(add_file == 0){ if(our_mkdir(child, (0700)) < 0){ eml.s = child; emlwrite(_("Error adding Directory \"%s\""), &eml); } else /* success! Directory added! */ repaint = 1; } else if(ffwopen(child, FALSE) != FIOSUC){ /* ffwopen should've complained */ break; } else{ /* highlight new file */ ffclose(); eml.s = child; emlwrite(_("Added File \"%s\""), &eml); repaint = 1; } if(repaint > 0){ 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, 0)) != 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){ emlwwrite(_("Can'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: emlwwrite(_("No 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)){ emlwwrite(_("Can'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; emlwwrite(_("Restricted mode allows Copy in %s only"), &eml); break; } if((status = fexist(child, "w", (off_t *)NULL)) == FIOSUC){ /* TRANSLATORS: A question: File 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", NLINE, gmp->dname, C_FILESEP, NLINE, 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, 0)) != 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, "..")){ emlwwrite(_("Can'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: emlwwrite(_("No 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; emlwwrite(_("Restricted 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", NLINE, gmp->dname, C_FILESEP, NLINE, 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, 0)) != 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; emlwwrite( _("Can'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{ emlwwrite(_("Can'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)-1); 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, 0)) != NULL){ gmp->current = tp; PlaceCell(gmp, gmp->current, &row, &col); } else{ eml.s = child; emlwwrite(_("Problem 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", NLINE, gmp->dname, C_FILESEP, NLINE, 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){ emlwwrite(_("Can'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; flags = SR_ORIGMEN | SR_FORWARD | SR_NOEXACT; while(!i){ switch(readpattern(_("File name to find"), FALSE, flags)){ case HELPCH: emlwwrite(_("No 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|'P'): if(flags & SR_FORWARD){ flags &= ~SR_FORWARD; flags |= SR_BACKWRD; } else { flags &= ~SR_BACKWRD; flags |= SR_FORWARD; } 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, flags & SR_BACKWRD)) != 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){ emlwwrite(_("Can'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->bottom = mp->head = mp->top = NULL; mp->menu = 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) emlwwrite(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){ emlwwrite(_("Can'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 filter ".*"... */ 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){ emlwwrite(_("Can'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{ mp->bottom = ncp; 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){ emlwwrite(_("Can'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){ emlwwrite(_("Can'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: * * " " */ 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); else if (*term.t_eri) (*term.t_eri)(); 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(); } } /* * RepaintBrowser - factorized method to just repaint everything */ struct bmaster * RepaintBrowser(struct bmaster *gmp, int fb_flags) { struct bmaster *mp; struct fcell *tp; int row, col; char *fn; fn = gmp->current->fname; if((mp = getfcells(gmp->dname, fb_flags)) == NULL){ /* getfcells should explain what happened */ return(gmp); } zotmaster(&gmp); gmp = mp; tp = NULL; if(*fn){ if((tp = FindCell(gmp, fn, 0)) != NULL){ gmp->current = tp; PlaceCell(gmp, gmp->current, &row, &col); } } PaintBrowser(gmp, 0, NULL, NULL); return(gmp); } /* * BrowserKeys - just paints the keyhelp at the bottom of the display */ void BrowserKeys(void) { if (gmp && gmp->menu == 1){ menu_other[GOTO_KEY].name = (gmode&MDGOTO) ? "G" : NULL; menu_other[GOTO_KEY].label = (gmode&MDGOTO) ? N_("Goto") : NULL; wkeyhelp(menu_other); return; } 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"); 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++){ emlwwrite(_("Internal 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, int bsearch) { struct fcell *tp, *fp; if(*utf8string == '\0') return(NULL); fp = NULL; tp = bsearch ? mp->current->prev : mp->current->next; while(tp && !fp){ if(sisin(tp->fname, utf8string)) fp = tp; else tp = bsearch ? tp->prev : tp->next; } tp = bsearch ? mp->bottom : mp->head; while(tp != mp->current && !fp){ if(sisin(tp->fname, utf8string)) fp = tp; else tp = bsearch ? tp->prev : 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); }