home *** CD-ROM | disk | FTP | other *** search
/ vsiftp.vmssoftware.com / VSIPUBLIC@vsiftp.vmssoftware.com.tar / FREEWARE / FREEWARE40.ZIP / pine / pico / browse.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-04-06  |  36.0 KB  |  1,609 lines

  1. #if    !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: browse.c,v 4.10 1993/10/20 23:06:57 mikes Exp $";
  3. #endif
  4. /*
  5.  * Program:    Routines to support file browser in pico and Pine composer
  6.  *
  7.  *
  8.  * Michael Seibel
  9.  * Networks and Distributed Computing
  10.  * Computing and Communications
  11.  * University of Washington
  12.  * Administration Builiding, AG-44
  13.  * Seattle, Washington, 98195, USA
  14.  * Internet: mikes@cac.washington.edu
  15.  *
  16.  * Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  17.  *
  18.  * Copyright 1991-1993  University of Washington
  19.  *
  20.  *  Permission to use, copy, modify, and distribute this software and its
  21.  * documentation for any purpose and without fee to the University of
  22.  * Washington is hereby granted, provided that the above copyright notice
  23.  * appears in all copies and that both the above copyright notice and this
  24.  * permission notice appear in supporting documentation, and that the name
  25.  * of the University of Washington not be used in advertising or publicity
  26.  * pertaining to distribution of the software without specific, written
  27.  * prior permission.  This software is made available "as is", and
  28.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  29.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  30.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  31.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  32.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  33.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  34.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  35.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  36.  *
  37.  * Pine and Pico are trademarks of the University of Washington.
  38.  * No commercial use of these trademarks may be made without prior
  39.  * written permission of the University of Washington.
  40.  *
  41.  *
  42.  * NOTES:
  43.  *
  44.  *   Misc. thoughts (mss, 5 Apr 92)
  45.  * 
  46.  *      This is supposed to be just a general purpose browser, equally
  47.  *    callable from either pico or the pine composer.  Someday, it could
  48.  *     even be used to "wrap" the unix file business for really novice 
  49.  *      users.  The stubs are here for renaming, copying, creating directories,
  50.  *      deleting, undeleting (thought is delete just moves files to 
  51.  *      ~/.pico.deleted directory or something and undelete offers the 
  52.  *      files in there for undeletion: kind of like the mac trashcan).
  53.  *
  54.  *   Nice side effects
  55.  *
  56.  *      Since the full path name is always maintained and referencing ".." 
  57.  *      stats the path stripped of its trailing name, the unpleasantness of 
  58.  *      symbolic links is hidden.  
  59.  *
  60.  *   Fleshed out the file managements stuff (mss, 24 June 92)
  61.  *
  62.  *
  63.  */
  64. #include <stdio.h>
  65. #include <ctype.h>
  66. #include <errno.h>
  67. #include "osdep.h"
  68. #include "pico.h"
  69. #include "estruct.h"
  70. #include "edef.h"
  71. #include "efunc.h"
  72. #ifdef HEBREW
  73. #include "hebrew.h"
  74. #endif
  75.  
  76. #if    defined(bsd)
  77. extern int errno;
  78. #endif
  79.  
  80. /*
  81.  * directory cell structure
  82.  */
  83. struct fcell {
  84.     char *fname;                /* file name            */
  85.     unsigned mode;                /* file's mode           */
  86.     char size[16];                /* file's size in s    */
  87.     struct fcell *next;
  88.     struct fcell *prev;
  89. };
  90.  
  91.  
  92. /*
  93.  * master browser structure
  94.  */
  95. static struct bmaster {
  96.     struct fcell *head;                /* first cell in list  */
  97.     struct fcell *top;                /* cell in top left    */
  98.     struct fcell *current;            /* currently selected  */
  99.     int    longest;                /* longest file name   */
  100.     int       fpl;                    /* file names per line */
  101.     int    cpf;                    /* chars / file / line */
  102.     char   dname[NLINE];            /* this dir's name     */
  103.     char   *names;                /* malloc'd name array */
  104. } *gmp;                        /* global master ptr   */
  105.  
  106. #ifdef    ANSI
  107.     struct bmaster *getfcells(char *);
  108.     int    PaintCell(int, int, int, struct fcell *, int);
  109.     int    PaintBrowser(struct bmaster *, int);
  110.     int    BrowserKeys(void);
  111.     int    layoutcells(struct bmaster *);
  112.     int    PlaceCell(struct bmaster *, struct fcell *, int *, int *);
  113.     int    zotfcells(struct fcell *);
  114.     int    zotmaster(struct bmaster **);
  115.     struct fcell *FindCell(struct bmaster *, char *);
  116.     int    sisin(char *, char *);
  117.     int    BrowserAnchor(char *);
  118. #else
  119.     struct bmaster *getfcells();
  120.     int    PaintCell();
  121.     int    PaintBrowser();
  122.     int    BrowserKeys();
  123.     int    layoutcells();
  124.     int    PlaceCell();
  125.     int    zotfcells();
  126.     int    zotmaster();
  127.     struct fcell *FindCell();
  128.     int    sisin();
  129.     int    BrowserAnchor();
  130. #endif
  131.  
  132.  
  133. /*
  134.  * function key mappings...
  135.  */
  136. static int  bfmappings[12][2] = { { F1,  '?'},    /* function key */
  137.                   { F2,  's'},    /* mappings... */
  138.                       { F3,  'e'},
  139.                         { F4,  'g'},
  140.                       { F5,  'r'},
  141.                        { F6,  'w'},
  142.                       { F7,  '-'},
  143.                       { F8,  ' '},
  144.                       { F9,  'd'},
  145.                       { F10, NODATA },
  146.                       { F11, 'm'},
  147.                       { F12, NODATA } };
  148.  
  149.  
  150. /*
  151.  * Browser help for pico (pine composer help handed to us by pine)
  152.  */
  153. static char *BrowseHelpText[] = {
  154. "Help for Browse Command",
  155. "  ",
  156. "\tPico's file browser is used to select a file from the",
  157. "\tfile system for inclusion in the edited text.",
  158. "  ",
  159. "~\tBoth directories and files are displayed.  Press ~S",
  160. "~\tor ~R~e~t~u~r~n to select a file or directory.  When a file",
  161. "\tis selected during the \"Read File\" command, it is",
  162. "\tinserted into edited text.  Answering \"yes\" to the",
  163. "\tverification question after a directory is selected causes",
  164. "\tthe contents of that directory to be displayed for selection.",
  165. "  ",
  166. "\tThe file named \"..\" is special, and means the \"parent\"",
  167. "\tof the directory being displayed.  Select this directory",
  168. "\tto move upward in the directory tree.",
  169. "  ",
  170. "End of Browser Help.",
  171. "  ",
  172. NULL
  173. };
  174.  
  175.  
  176.  
  177.  
  178.  
  179. /*
  180.  * FileBrowse - display contents of given directory dir
  181.  *
  182.  *         returns:
  183.  *                   dir points to currently selected directory
  184.  *                   fn  points to currently selected file
  185.  *                   sz  points to size of file if ptr passed was non-NULL
  186.  *                   1 if a file's been selected
  187.  *                   0 if no files seleted
  188.  *                  -1 if there where problems
  189.  */
  190. FileBrowse(dir, fn, sz)
  191. char *dir, *fn, *sz;            /* dir, name and optional size */
  192. {
  193.     int status, i, j, c;
  194.     int row, col;
  195.     char *p, child[NLINE];
  196.     struct bmaster *mp, *getfcells();
  197.     struct fcell *tp;
  198.  
  199.     child[0] = '\0';
  200.  
  201.     if((gmode&MDSCUR) && homeless(dir)){
  202.     emlwrite("\007Can't read %s in restricted mode", dir);
  203.     sleep(2);
  204.     return(0);
  205.     }
  206.  
  207.     /* build contents of cell structures */
  208.     if((gmp = getfcells(dir)) == NULL)
  209.       return(-1);
  210.  
  211.     /* paint screen */
  212.     PaintBrowser(gmp, 0);
  213.  
  214.     while(1){                        /* the big loop */
  215.     movecursor(term.t_nrow-2, 0);
  216.  
  217.     (*term.t_flush)();
  218. #ifdef HEBREW
  219.     message_mode=1;
  220. #endif
  221.     c = GetKey();
  222. #ifdef HEBREW
  223.     message_mode=0;
  224. #endif
  225.  
  226.     if(Pmaster){
  227.         if(c == NODATA || time_to_check()){        /* new mail ? */
  228.         if((*Pmaster->newmail)(&j, 0, c == NODATA ? 0 : 2) >= 0){
  229.             mlerase();
  230.             (*Pmaster->showmsg)(c);
  231.             mpresf = 1;
  232.         }
  233.  
  234.         if(j || mpresf){
  235.             (*term.t_move)(term.t_nrow-2, 0);
  236.             (*Pmaster->clearcur)();
  237.         }
  238.  
  239.         if(c == NODATA)            /* GetKey timed out */
  240.           continue;
  241.         }
  242.     }
  243.     else{
  244.         if(timeout && (c == NODATA || time_to_check()))
  245.           if(pico_new_mail())
  246.         emlwrite("You may possibly have new mail.", NULL);
  247.         
  248.         if(c == NODATA)
  249.           continue;
  250.     }
  251.  
  252.     if(mpresf){                /* blast old messages */
  253.         if(mpresf++ > MESSDELAY){        /* every few keystrokes */
  254.         mlerase();
  255.         }
  256.         }
  257.  
  258.     switch(normal(c, bfmappings, 2)){    /* process commands */
  259.  
  260.       case K_PAD_RIGHT:            /* move right */
  261.       case (CTRL|'@'):
  262.       case (CTRL|'F'):            /* forward  */
  263.         if(gmp->current->next == NULL){
  264.         (*term.t_beep)();
  265.         break;
  266.         }
  267.  
  268.         PlaceCell(gmp, gmp->current, &row, &col);
  269.         PaintCell(row, col, gmp->cpf, gmp->current, 0);
  270.         gmp->current = gmp->current->next;
  271.         if(PlaceCell(gmp, gmp->current, &row, &col)){
  272.         PaintBrowser(gmp, 1);
  273.         }
  274.         else
  275.           PaintCell(row, col, gmp->cpf, gmp->current, 1);
  276.         break;
  277.  
  278.       case K_PAD_LEFT:                /* move left */
  279.       case (CTRL|'B'):                /* back */
  280.         if(gmp->current->prev == NULL){
  281.         (*term.t_beep)();
  282.         break;
  283.         }
  284.  
  285.         PlaceCell(gmp, gmp->current, &row, &col);
  286.         PaintCell(row, col, gmp->cpf, gmp->current, 0);
  287.         gmp->current = gmp->current->prev;
  288.         if(PlaceCell(gmp, gmp->current, &row, &col)){
  289.         PaintBrowser(gmp, 1);
  290.         }
  291.         else
  292.           PaintCell(row, col, gmp->cpf, gmp->current, 1);
  293.         break;
  294.  
  295.       case (CTRL|'A'):                /* beginning of line */
  296.         tp = gmp->current;
  297.         i = col;
  298.         while(i > 0){
  299.         i -= gmp->cpf;
  300.         if(tp->prev != NULL)
  301.           tp = tp->prev;
  302.         }
  303.         PlaceCell(gmp, gmp->current, &row, &col);
  304.         PaintCell(row, col, gmp->cpf, gmp->current, 0);
  305.         gmp->current = tp;
  306.         if(PlaceCell(gmp, tp, &row, &col)){
  307.         PaintBrowser(gmp, 1);
  308.         }
  309.         else
  310.           PaintCell(row, col, gmp->cpf, gmp->current, 1);
  311.         break;
  312.  
  313.       case (CTRL|'E'):                /* end of line */
  314.         tp = gmp->current;
  315.         i = col + gmp->cpf;
  316.         while(i+gmp->cpf <= gmp->cpf * gmp->fpl){
  317.         i += gmp->cpf;
  318.         if(tp->next != NULL)
  319.           tp = tp->next;
  320.         }
  321.  
  322.         PlaceCell(gmp, gmp->current, &row, &col);
  323.         PaintCell(row, col, gmp->cpf, gmp->current, 0);
  324.         gmp->current = tp;
  325.         if(PlaceCell(gmp, tp, &row, &col)){
  326.         PaintBrowser(gmp, 1);
  327.         }
  328.         else
  329.           PaintCell(row, col, gmp->cpf, gmp->current, 1);
  330.         break;
  331.  
  332.       case (CTRL|'V'):                /* page forward */
  333.       case ' ':
  334.       case K_PAD_NEXTPAGE :
  335.       case K_PAD_END :
  336.         tp = gmp->top;
  337.         i = term.t_nrow - 4;
  338.  
  339.         while(i-- && tp->next != NULL){
  340.         j = 0;
  341.         while(++j <= gmp->fpl  && tp->next != NULL)
  342.           tp = tp->next;
  343.         }
  344.  
  345.         if(tp == NULL)
  346.           continue;
  347.  
  348.         PlaceCell(gmp, gmp->current, &row, &col);
  349.         PaintCell(row, col, gmp->cpf, gmp->current, 0);
  350.         gmp->current = tp;
  351.         if(PlaceCell(gmp, tp, &row, &col)){
  352.         PaintBrowser(gmp, 1);
  353.         }
  354.         else
  355.           PaintCell(row, col, gmp->cpf, gmp->current, 1);
  356.         break;
  357.  
  358.       case '-' :
  359.       case (CTRL|'Y'):                /* page backward */
  360.       case K_PAD_PREVPAGE :
  361.       case K_PAD_HOME :
  362.         tp = gmp->top;
  363.         i = term.t_nrow - 6;
  364.         while(i-- && tp != NULL){
  365.         j = gmp->fpl;
  366.         while(j-- && tp != NULL)
  367.           tp = tp->prev;
  368.         }
  369.  
  370.         if(tp || (gmp->current != gmp->top)){    /* clear old hilite */
  371.         PlaceCell(gmp, gmp->current, &row, &col);
  372.         PaintCell(row, col, gmp->cpf, gmp->current, 0);
  373.         }
  374.  
  375.         if(tp)                    /* new page ! */
  376.         gmp->current = tp;
  377.         else if(gmp->current != gmp->top)        /* goto top of page */
  378.         gmp->current = gmp->top;
  379.         else                    /* do nothing */
  380.           continue;
  381.  
  382.         if(PlaceCell(gmp, gmp->current, &row, &col)){
  383.         PaintBrowser(gmp, 1);
  384.         }
  385.         else
  386.           PaintCell(row, col, gmp->cpf, gmp->current, 1);
  387.  
  388.         break;
  389.  
  390.       case K_PAD_DOWN :
  391.       case (CTRL|'N'):                /* next */
  392.         tp = gmp->current;
  393.         i = gmp->fpl;
  394.         while(i--){
  395.         if(tp->next == NULL){
  396.             (*term.t_beep)();
  397.             break;
  398.         }
  399.         else
  400.           tp = tp->next;
  401.         }
  402.         if(i != -1)                    /* can't go down */
  403.           break;
  404.  
  405.         PlaceCell(gmp, gmp->current, &row, &col);
  406.         PaintCell(row, col, gmp->cpf, gmp->current, 0);
  407.         gmp->current = tp;
  408.         if(PlaceCell(gmp, tp, &row, &col)){
  409.         PaintBrowser(gmp, 1);
  410.         }
  411.         else
  412.           PaintCell(row, col, gmp->cpf, gmp->current, 1);
  413.         break;
  414.  
  415.       case K_PAD_UP :
  416.       case (CTRL|'P'):                /* previous */
  417.         tp = gmp->current;
  418.         i = gmp->fpl;
  419.         while(i-- && tp)
  420.           tp = tp->prev;
  421.  
  422.         if(tp == NULL)
  423.           break;
  424.  
  425.         PlaceCell(gmp, gmp->current, &row, &col);
  426.         PaintCell(row, col, gmp->cpf, gmp->current, 0);
  427.         gmp->current = tp;
  428.         if(PlaceCell(gmp, tp, &row, &col)){
  429.         PaintBrowser(gmp, 1);
  430.         }
  431.         else
  432.           PaintCell(row, col, gmp->cpf, gmp->current, 1);
  433.         break;
  434.  
  435.       case 'e':                    /* user exits */
  436.       case 'E':
  437.         zotmaster(&gmp);
  438.         return(0);
  439.  
  440.       case 'x':                        /* user exits wrong */
  441.       case 'X':
  442.         if(!(gmode&MDBRONLY)){
  443.         emlwrite("\007Unknown command '%c'", (void *)c);
  444.         break;
  445.         }
  446.  
  447.         zotmaster(&gmp);
  448.         return(0);
  449.  
  450.       case 'd':                    /* delete */
  451.       case 'D':
  452.         if(gmp->current->mode){
  453. /* BUG: if dir is empty it should be deleted */
  454.         emlwrite("\007Can't delete a directory", NULL);
  455.         break;
  456.         }
  457.  
  458.         if(gmode&MDSCUR){                /* not allowed! */
  459.         emlwrite("Delete not allowed in restricted mode",NULL);
  460.         break;
  461.         }
  462.  
  463.         sprintf(child, "%s%c%s", gmp->dname, C_FILESEP, 
  464.             gmp->current->fname);
  465.  
  466.         i = 0;
  467.         while(i++ < 2){        /* verify twice!! */
  468.         if(i == 1){
  469.             if(fexist(child, "w", (long *)NULL) != FIOSUC)
  470.               sprintf(s,"File is write protected! OVERRIDE", child);
  471.             else
  472.               sprintf(s, "Delete file \"%s\"", child);
  473.         }
  474.         else
  475.           strcpy(s, "File CANNOT be UNdeleted!  Really delete");
  476.  
  477.         if((status = mlyesno(s, FALSE)) != TRUE){
  478.             if(status ==  ABORT)
  479.               emlwrite("\007Delete Cancelled", NULL);
  480.             else
  481.               emlwrite("File Not Deleted", NULL);
  482.             break;
  483.         }
  484.         }
  485.  
  486.         if(status == TRUE){
  487.         if(unlink(child) < 0){
  488.             emlwrite("Delete Failed: %s", errstr(errno));
  489.         }
  490.         else{            /* fix up pointers and redraw */
  491.             tp = gmp->current;
  492.             if(tp->next){
  493.             gmp->current = tp->next;
  494.             if(tp->next->prev = tp->prev)
  495.               tp->prev->next = tp->next;
  496.             }
  497.             else if(tp->prev) {
  498.             gmp->current = tp->prev;
  499.             if(tp->prev->next = tp->next)
  500.               tp->next->prev = tp->prev;
  501.             }
  502.  
  503.             tp->fname = NULL;
  504.             tp->next = tp->prev = NULL;
  505.             if(tp != gmp->current)
  506.               free((char *) tp);
  507.  
  508.             if((tp = FindCell(gmp, gmp->current->fname)) != NULL){
  509.             gmp->current = tp;
  510.             PlaceCell(gmp, gmp->current, &row, &col);
  511.             }
  512.                 
  513.             PaintBrowser(gmp, 1);
  514.             mlerase();
  515.         }
  516.         }
  517.         break;
  518.  
  519.       case '?':                    /* HELP! */
  520.         if(Pmaster)
  521.           (*Pmaster->helper)(Pmaster->browse_help,
  522.                  "Help for Browsing", 1);
  523.         else
  524.           pico_help(BrowseHelpText, "Help for Browsing", 1);
  525.         /* fall thru to repaint everything */
  526.  
  527.       case (CTRL|'L'):
  528.         PaintBrowser(gmp, 0);
  529.         break;
  530.  
  531.       case 'g':                /* jump to a directory */
  532.       case 'G':
  533.         i = 0;
  534.         child[0] = '\0';
  535.  
  536.         while(!i){
  537.  
  538.         wkeyhelp("GC0000000000", "Get Help,Cancel");
  539.  
  540.         status = mlreply("Directory to go to: ", child, NLINE, QFFILE);
  541.  
  542.         switch(status){
  543.           case HELPCH:
  544.             emlwrite("\007No help yet!", NULL);
  545. /* remove break and sleep after help text is installed */
  546.             sleep(3);
  547.             break;
  548.           case (CTRL|'L'):
  549.             PaintBrowser(gmp, 0);
  550.             break;
  551.           case ABORT:
  552.             emlwrite("Cancelled", NULL);
  553.             i++;
  554.             break;
  555.           case FALSE:
  556.           case TRUE:
  557.             i++;
  558.  
  559.             if(*child == '\0')
  560.               strcpy(child, gethomedir(NULL));
  561.  
  562.             if(!compresspath(gmp->dname, child, NLINE)){
  563.             emlwrite("Invalid Directory: %s", child);
  564.             break;
  565.             }
  566.  
  567.             if((gmode&MDSCUR) && homeless(child)){
  568.             emlwrite("Restricted mode browsing limited to home directory",NULL);
  569.             break;
  570.             }
  571.  
  572.             if(isdir(child, (long *) NULL)){
  573.             if((mp = getfcells(child)) == NULL){
  574.                 /* getfcells should explain what happened */
  575.                 i++;
  576.                 break;
  577.             }
  578.  
  579.             zotmaster(&gmp);
  580.             gmp = mp;
  581.             PaintBrowser(gmp, 0);
  582.             }
  583.             else
  584.               emlwrite("\007Not a directory: \"%s\"", child);
  585.  
  586.             break;
  587.           default:
  588.             break;
  589.         }
  590.         }
  591.         BrowserKeys();
  592.         break;
  593.  
  594.       case 'm':                    /* copy */
  595.       case 'M':
  596.         if(gmp->current->mode){
  597.         emlwrite("\007Can't copy a directory", NULL);
  598.         break;
  599.         }
  600.  
  601.         if(gmode&MDSCUR){                /* not allowed! */
  602.         emlwrite("Copy not allowed in restricted mode",NULL);
  603.         break;
  604.         }
  605.  
  606.         i = 0;
  607.         child[0] = '\0';
  608.  
  609.         while(!i){
  610.  
  611.         wkeyhelp("GC0000000000", "Get Help,Cancel");
  612.  
  613.         switch(status=mlreply("Name of new copy: ", child, NLINE, QFFILE)){
  614.           case HELPCH:
  615.             emlwrite("\007No help yet!", NULL);
  616. /* remove break and sleep after help text is installed */
  617.             sleep(3);
  618.             break;
  619.           case (CTRL|'L'):
  620.             PaintBrowser(gmp, 0);
  621.             break;
  622.           case ABORT:
  623.             emlwrite("Make Copy Cancelled", NULL);
  624.             i++;
  625.             break;
  626.           case FALSE:
  627.             i++;
  628.             mlerase();
  629.             break;
  630.           case TRUE:
  631.             i++;
  632.  
  633.             if(child[0] == '\0'){
  634.             emlwrite("No destination, file not copied", NULL);
  635.             break;
  636.             }
  637.  
  638.             if(!strcmp(gmp->current->fname, child)){
  639.             emlwrite("\007Can't copy file on to itself!", NULL);
  640.             break;
  641.             }
  642.  
  643.             strcpy(s, child);         /* add full path! */
  644.             sprintf(child, "%s%c%s", gmp->dname, C_FILESEP, s);
  645.  
  646.             if((status = fexist(child, "w", (long *)NULL)) == FIOSUC){
  647.             sprintf(s,"File \"%s\" exists! OVERWRITE", child);
  648.             if((status = mlyesno(s, 0)) != TRUE){
  649.                 if(status == ABORT)
  650.                   emlwrite("\007Make Copy Cancelled", NULL);
  651.                 else
  652.                   emlwrite("File Not Renamed", NULL);
  653.                 break;
  654.             }
  655.             }
  656.             else if(status != FIOFNF){
  657.             fioperr(status, child);
  658.             break;
  659.             }
  660.  
  661.             sprintf(s, "%s%c%s", gmp->dname, C_FILESEP, 
  662.                 gmp->current->fname);
  663.  
  664.             if(copy(s, child) < 0){
  665.             /* copy()  will report any error messages */
  666.             break;
  667.             }
  668.             else{            /* highlight new file */
  669.             emlwrite("File copied to %s", child);
  670.  
  671.             if((p = strrchr(child, C_FILESEP)) == NULL){
  672.                 emlwrite("Problems refiguring browser", NULL);
  673.                 break;
  674.             }
  675.  
  676.             *p = '\0';
  677.             strcpy(s, (p == child) ? S_FILESEP: child);
  678.  
  679.             /*
  680.              * new file in same dir? if so, refigure files
  681.              * and redraw...
  682.              */
  683.             if(!strcmp(s, gmp->dname)){ 
  684.                 strcpy(child, gmp->current->fname);
  685.                 if((mp = getfcells(gmp->dname)) == NULL)
  686.                   /* getfcells should explain what happened */
  687.                   break;
  688.  
  689.                 zotmaster(&gmp);
  690.                 gmp = mp;
  691.                 if((tp = FindCell(gmp, child)) != NULL){
  692.                 gmp->current = tp;
  693.                 PlaceCell(gmp, gmp->current, &row, &col);
  694.                 }
  695.  
  696.                 PaintBrowser(gmp, 1);
  697.             }
  698.             }
  699.             break;
  700.           default:
  701.             break;
  702.         }
  703.         }
  704.         BrowserKeys();
  705.         break;
  706.  
  707.       case 'r':                    /* rename */
  708.       case 'R':
  709.         i = 0;
  710.         child[0] = '\0';
  711.  
  712.         if(!strcmp(gmp->current->fname, "..")){
  713.         emlwrite("\007Can't rename \"..\"", NULL);
  714.         break;
  715.         }
  716.  
  717.         if(gmode&MDSCUR){                /* not allowed! */
  718.         emlwrite("Rename not allowed in restricted mode",NULL);
  719.         break;
  720.         }
  721.  
  722.         while(!i){
  723.  
  724.         wkeyhelp("GC0000000000", "Get Help,Cancel");
  725.  
  726.         switch(status=mlreply("Rename file to: ", child, NLINE, QFFILE)){
  727.           case HELPCH:
  728.             emlwrite("\007No help yet!", NULL);
  729. /* remove break and sleep after help text is installed */
  730.             sleep(3);
  731.             break;
  732.           case (CTRL|'L'):
  733.             PaintBrowser(gmp, 0);
  734.             break;
  735.           case ABORT:
  736.             emlwrite("Cancelled", NULL);
  737.             i++;
  738.             break;
  739.           case FALSE:
  740.           case TRUE:
  741.             i++;
  742.  
  743.             if(child[0] == '\0' || status == FALSE){
  744.             mlerase();
  745.             break;
  746.             }
  747.  
  748.             strcpy(s, child);
  749.             sprintf(child, "%s%c%s", gmp->dname, C_FILESEP, s);
  750.  
  751.             status = fexist(child, "w", (long *)NULL);
  752.             if(status == FIOSUC || status == FIOFNF){
  753.             if(status == FIOSUC){
  754.                 sprintf(s,"File \"%s\" exists! OVERWRITE", child);
  755.  
  756.                 if((status = mlyesno(s, FALSE)) != TRUE){
  757.                 if(status ==  ABORT)
  758.                   emlwrite("\007Cancelled", NULL);
  759.                 else
  760.                   emlwrite("Not Renamed", NULL);
  761.                 break;
  762.                 }
  763.             }
  764.  
  765.             sprintf(s, "%s%c%s", gmp->dname, C_FILESEP, 
  766.                 gmp->current->fname);
  767.  
  768.             if(rename(s, child) < 0){
  769.                 emlwrite("Rename Failed: %s", errstr(errno));
  770.             }
  771.             else{
  772.                 if((p = strrchr(child, C_FILESEP)) == NULL){
  773.                 emlwrite("Problems refiguring browser", NULL);
  774.                 break;
  775.                 }
  776.                 
  777.                 *p = '\0';
  778.                 strcpy(s, (p == child) ? S_FILESEP: child);
  779.  
  780.                 if((mp = getfcells(s)) == NULL)
  781.                   /* getfcells should explain what happened */
  782.                   break;
  783.  
  784.                 zotmaster(&gmp);
  785.                 gmp = mp;
  786.  
  787.                 if((tp = FindCell(gmp, ++p)) != NULL){
  788.                 gmp->current = tp;
  789.                 PlaceCell(gmp, gmp->current, &row, &col);
  790.                 }
  791.  
  792.                 PaintBrowser(gmp, 1);
  793.                 mlerase();
  794.             }
  795.             }
  796.             else{
  797.             fioperr(status, child);
  798.             }
  799.             break;
  800.           default:
  801.             break;
  802.         }
  803.         }
  804.         BrowserKeys();
  805.         break;
  806.  
  807.       case 's':                    /* user's selected */
  808.       case 'S':
  809.       case (CTRL|'M'):
  810.  
  811.         if(gmp->current->mode){
  812.         *child = '\0';
  813.  
  814.         while(1){
  815.             i = mlyesno("A directory is selected, enter it", 1);
  816.             if(i == TRUE){
  817.             strcpy(s, gmp->dname);
  818.             p = gmp->current->fname;
  819.             if(p[0] == '.' && p[1] == '.' && p[2] == '\0'){
  820.  
  821.                 if((p=strrchr(s, C_FILESEP)) != NULL){
  822.                 *p = '\0';
  823.  
  824.                 if((gmode&MDSCUR) && homeless(s)){
  825.                     emlwrite("\007Can't visit parent in restricted mode", NULL);
  826.                     break;
  827.                 }
  828.  
  829.                 strcpy(child, &p[1]);
  830.                 if(p == s
  831. #ifdef    DOS
  832.                    || (s[1] == ':' && s[2] == '\0')
  833. #endif
  834.                          ){    /* is it root? */
  835.                     if(*child)
  836. #ifdef    DOS
  837.                       strcat(s, S_FILESEP);
  838. #else
  839.                       strcpy(s, S_FILESEP);
  840. #endif
  841.                     else{
  842.                     emlwrite("\007Can't move up a directory", NULL);
  843.                     break;
  844.                     }
  845.                 }
  846.                 }
  847.             }
  848.             else{
  849.                 if(s[1] != '\0')        /* were in root? */
  850.                   strcat(s, S_FILESEP);
  851.                 strcat(s, gmp->current->fname);
  852.             }
  853.  
  854.             if((mp = getfcells(s)) == NULL)
  855.               /* getfcells should explain what happened */
  856.               break;
  857.  
  858.             zotmaster(&gmp);
  859.             gmp = mp;
  860.  
  861.             if(*child){
  862.                 if((tp = FindCell(gmp, child)) != NULL){
  863.                 gmp->current = tp;
  864.                 PlaceCell(gmp, gmp->current, &row, &col);
  865.                 }
  866.                 else
  867.                   emlwrite("\007Problem finding dir \"%s\"",child);
  868.             }
  869.  
  870.             PaintBrowser(gmp, 0);
  871.             break;
  872.             }
  873.             else if(i == FALSE){
  874.             mlerase();
  875.             break;
  876.             }
  877.             else if(i == ABORT){
  878.             emlwrite("Cancelled", NULL);
  879.             break;
  880.             }
  881.             else if(i == (CTRL|'L')){
  882.             PaintBrowser(gmp, 0);
  883.             }
  884.             else
  885.               (*term.t_beep)();
  886.         }
  887.         }
  888.         else{                /* just return */
  889.         strcpy(dir, gmp->dname);
  890.         strcpy(fn, gmp->current->fname);
  891.         if(sz != NULL)            /* size uninteresting */
  892.           strcpy(sz, gmp->current->size);
  893.  
  894.         if(gmode&MDBRONLY){
  895.             i = 0;
  896.             sprintf(child, "%s%c%s", gmp->dname, C_FILESEP, fn);
  897.             /* stat file for executable bit */
  898.             if(fexist(child, "x", NULL) == FIOSUC){
  899.             while(!i){
  900.                 switch(mlyesno("Program selected, run it", 1)){
  901.                   case TRUE:
  902.                 strcpy(child, fn);
  903.                 while(!i){
  904.                     switch(status=mlreply("Command line : ",
  905.                                child, NLINE, QNORML)){
  906.                       case HELPCH:
  907.                     emlwrite("\007No help yet!", NULL);
  908. /* remove break and sleep after help text is installed */
  909.                     sleep(3);
  910.                     break;
  911.                       case (CTRL|'L'):
  912.                     PaintBrowser(gmp, 0);
  913.                     break;
  914.                       case ABORT:
  915.                     emlwrite("Cancelled", NULL);
  916.                     i++;
  917.                     break;
  918.                       case FALSE:
  919.                       case TRUE:
  920.                     i++;
  921.  
  922.                     if(child[0]=='\0' || status==FALSE){
  923.                         mlerase();
  924.                         break;
  925.                     }
  926.  
  927.                     vttidy();
  928.                     system(child);
  929.                     ttopen();
  930.  
  931.                     /* recompute browser */
  932.                     /* redraw browser */
  933.                     PaintBrowser(gmp, 0);
  934.                     break;
  935.                     default:
  936.                     break;
  937.                     }
  938.                 }
  939.  
  940.                 break;
  941.                   case FALSE:
  942.                 i += 2;
  943.                 mlerase();
  944.                 break;
  945.                   case ABORT:
  946.                 i++;
  947.                 emlwrite("Cancelled", NULL);
  948.                 break;
  949.                   case (CTRL|'L'):
  950.                 PaintBrowser(gmp, 0);
  951.                 break;
  952.                   default:
  953.                 (*term.t_beep)();
  954.                 break;
  955.                 }
  956.             }
  957.             }
  958.  
  959.             if(i == 2)        /* do something else to executable? */
  960.               i = 0;
  961.  
  962.             while(!i){
  963.             *child = '\0';
  964.             wkeyhelp("GC0000000000", "Get Help,Cancel");
  965.             switch(status=mlreply("Program to use on file : ",
  966.                           child, NLINE, QNORML)){
  967.               case HELPCH:
  968.                 emlwrite("\007No help yet!", NULL);
  969. /* remove break and sleep after help text is installed */
  970.                 sleep(3);
  971.                 break;
  972.               case (CTRL|'L'):
  973.                 PaintBrowser(gmp, 0);
  974.                 break;
  975.               case ABORT:
  976.                 emlwrite("Cancelled", NULL);
  977.                 i++;
  978.                 break;
  979.               case FALSE:
  980.               case TRUE:
  981.                 i++;
  982.  
  983.                 if(child[0] == '\0' || status == FALSE){
  984.                 mlerase();
  985.                 break;
  986.                 }
  987.  
  988.                 strcat(child, " ");
  989.                 strcat(child, gmp->dname);
  990.                 strcat(child, S_FILESEP);
  991.                 strcat(child, fn);
  992.  
  993.                 vttidy();
  994.                 system(child);
  995.                 ttopen();
  996.  
  997.                 /* recompute browser */
  998.                 /* redraw browser */
  999.                 PaintBrowser(gmp, 0);
  1000.                 break;
  1001.               default:
  1002.                 break;
  1003.             }
  1004.             }
  1005.  
  1006.             BrowserKeys();
  1007.         }
  1008.         else{
  1009.             zotmaster(&gmp);
  1010.             return(1);
  1011.         }
  1012.         }
  1013.         break;
  1014.  
  1015.       case 'w':                /* Where is */
  1016.       case 'W':
  1017.         i = 0;
  1018.  
  1019.         while(!i){
  1020.  
  1021.         wkeyhelp("GC0000000000", "Get Help,Cancel");
  1022.  
  1023.         switch(readpattern("File name to find")){
  1024.           case HELPCH:
  1025.             emlwrite("\007No help yet!", NULL);
  1026. /* remove break and sleep after help text is installed */
  1027.             sleep(3);
  1028.             break;
  1029.           case (CTRL|'L'):
  1030.             PaintBrowser(gmp, 0);
  1031.             break;
  1032.           case ABORT:
  1033.             emlwrite("Cancelled", NULL);
  1034.             i++;
  1035.             break;
  1036.           case FALSE:
  1037.             mlerase();
  1038.             i++;
  1039.             break;
  1040.           case TRUE:
  1041.             if((tp = FindCell(gmp, pat)) != NULL){
  1042.             PlaceCell(gmp, gmp->current, &row, &col);
  1043.             PaintCell(row, col, gmp->cpf, gmp->current, 0);
  1044.             gmp->current = tp;
  1045.  
  1046.             if(PlaceCell(gmp, tp, &row, &col)){ /* top changed */
  1047.                 PaintBrowser(gmp, 1);
  1048.             }
  1049.             else
  1050.               PaintCell(row, col, gmp->cpf, gmp->current, 1);
  1051.             mlerase();
  1052.             }
  1053.             else
  1054.               emlwrite("\"%s\" not found", pat);
  1055.  
  1056.             i++;
  1057.             break;
  1058.           default:
  1059.             break;
  1060.         }
  1061.         }
  1062.         mlerase();
  1063.         BrowserKeys();
  1064.         break;
  1065.  
  1066.       case (CTRL|'Z'):
  1067.         if(gmode&MDSSPD){
  1068.         bktoshell();
  1069.         PaintBrowser(gmp,0);
  1070.         break;
  1071.         }                    /* fall thru with error! */
  1072.  
  1073.       default:                /* what? */
  1074.         if(c < 0xff)
  1075.           emlwrite("\007Unknown command: '%c'", (void *) c);
  1076.         else if(c & CTRL)
  1077.           emlwrite("\007Unknown command: ^%c", (void *)(c&0xff));
  1078.         else
  1079.           emlwrite("\007Unknown command", NULL);
  1080.       case NODATA:                /* no op */
  1081.         break;
  1082.     }
  1083.     }
  1084. }
  1085.  
  1086.  
  1087.  
  1088. /*
  1089.  * getfcells - make a master browser struct and fill it in
  1090.  *             return NULL if there's a problem.
  1091.  */
  1092. struct bmaster *getfcells(dname)
  1093. char *dname;
  1094. {
  1095.     int  i,                     /* various return codes */
  1096.          nentries = 0;                /* number of dir ents */
  1097.     long l;
  1098.     char *np,                    /* names of files in dir */
  1099.          *dcp,                    /* to add file to path */
  1100.          **filtnames;                /* array filtered names */
  1101.     struct fcell *ncp,                /* new cell pointer */
  1102.                  *tcp;                /* trailing cell ptr */
  1103.     struct bmaster *mp;
  1104.  
  1105.     if((mp=(struct bmaster *)malloc(sizeof(struct bmaster))) == NULL){
  1106.     emlwrite("\007Can't malloc space for master filename cell", NULL);
  1107.     return(NULL);
  1108.     }
  1109.  
  1110.     if(dname[0] == '.' && dname[1] == '\0'){        /* remember this dir */
  1111.     if(!getcwd(mp->dname, 256))
  1112.       mp->dname[0] = '\0';
  1113.     }
  1114.     else if(dname[0] == '.' && dname[1] == '.' && dname[2] == '\0'){
  1115.     if(!getcwd(mp->dname, 256))
  1116.       mp->dname[0] = '\0';
  1117.     else{
  1118.         if((np = (char *)strrchr(mp->dname, C_FILESEP)) != NULL)
  1119.           if(np != mp->dname)
  1120.         *np = '\0';
  1121.     }
  1122.     }
  1123.     else
  1124.       strcpy(mp->dname, dname);
  1125.  
  1126.     mp->head = mp->top = NULL;
  1127.     mp->cpf = mp->fpl = 0;
  1128.     mp->longest = 5;                /* .. must be labeled! */
  1129.  
  1130.     if((mp->names = getfnames(mp->dname, &nentries)) == NULL){
  1131.     free((char *) mp);
  1132.     return(NULL);
  1133.     }
  1134.  
  1135.     /*
  1136.      * this is the fun part.  build an array of pointers to the fnames we're
  1137.      * interested in (i.e., do any filtering), then pass that off to be
  1138.      * sorted before building list of cells...
  1139.      *
  1140.      * right now default filtering on ".*" except "..", but this could
  1141.      * easily be made a user option later on...
  1142.      */
  1143.     if((filtnames=(char **)malloc((nentries+1) * sizeof(char *))) == NULL){
  1144.     emlwrite("\007Can't malloc space for name array", NULL);
  1145.     zotmaster(&mp);
  1146.     return(NULL);
  1147.     }
  1148.  
  1149.     i = 0;                    /* index of filt'd array */
  1150.     np = mp->names;
  1151.     while(nentries--){
  1152.     if(*np == '.'){                    /* filter .* */
  1153.         if(np[1] == '\0' || (np[1] != '.' && np[2] != '\0')){
  1154.         np += strlen(np) + 1;
  1155.         continue;
  1156.         }
  1157.     }
  1158.  
  1159.     filtnames[i++] = np;
  1160.  
  1161.     if((l = strlen(np)) > mp->longest)    /* cast l ? */
  1162.       mp->longest = l;            /* remember longest */
  1163.     np += l + 1;                /* advance name pointer */
  1164.  
  1165.     }
  1166.     nentries = i;                /* new # of entries */
  1167.  
  1168.     /* 
  1169.      * sort files case independently
  1170.      */
  1171.     qsort((QSType *)filtnames, (size_t)nentries, sizeof(char *), sstrcasecmp);
  1172.  
  1173.     /* 
  1174.      * this is so we use absolute path names for stats.
  1175.      * remember: be careful using dname as directory name, and fix mp->dname
  1176.      * when we're done
  1177.      */
  1178.     dcp = (char *)strchr(mp->dname, '\0');
  1179.     if(dcp == mp->dname || dcp[-1] != C_FILESEP){
  1180.     dcp[0] = C_FILESEP;
  1181.     dcp[1] = '\0';
  1182.     }
  1183.     else
  1184.       dcp--;
  1185.  
  1186.     i = 0;
  1187.     while(nentries--){                /* stat filtered files */
  1188.     /* get a new cell */
  1189.     if((ncp=(struct fcell *)malloc(sizeof(struct fcell))) == NULL){
  1190.         emlwrite("\007Can't malloc cells for browser!", NULL);
  1191.         zotfcells(mp->head);        /* clean up cells */
  1192.         free((char *) filtnames);
  1193.         free((char *) mp);
  1194.         return(NULL);            /* bummer. */
  1195.     }
  1196.     ncp->next = ncp->prev = NULL;
  1197.     ncp->mode = 0;
  1198.  
  1199.     if(mp->head == NULL){            /* tie it onto the list */
  1200.         mp->head = mp->top = mp->current = ncp;
  1201.     }
  1202.     else{
  1203.         tcp->next = ncp;
  1204.         ncp->prev = tcp;
  1205.     }
  1206.     tcp = ncp;
  1207.  
  1208.     /* fill in the new cell */
  1209.     ncp->fname = filtnames[i++];
  1210.  
  1211.     strcpy(&dcp[1], ncp->fname);        /* use abolute path! */
  1212.  
  1213.     if(ncp->fname[0] == '.' && ncp->fname[1] == '.' && ncp->fname[2] == '\0'){
  1214.         ncp->mode = 1;                /* parent is special */
  1215.         strcpy(ncp->size, "(parent dir)");
  1216.     }
  1217.     else if(isdir(mp->dname, &l)){
  1218.         ncp->mode = 1;
  1219.         strcpy(ncp->size, "(dir)");
  1220.     }
  1221.     else
  1222.       strcpy(ncp->size,  prettysz(l));
  1223.     }
  1224.  
  1225.     *dcp = '\0';                /* remember to cap dname */
  1226.     free((char *) filtnames);            /* 'n blast filt'd array*/
  1227.  
  1228.     layoutcells(mp);
  1229.  
  1230.     return(mp);
  1231. }
  1232.  
  1233.  
  1234.  
  1235. /*
  1236.  * PaintCell - print the given cell at the given location on the display
  1237.  *             the format of a printed cell is:
  1238.  *
  1239.  *                       "<fname>       <size>  "
  1240.  */
  1241. PaintCell(x, y, l, cell, inverted)
  1242. struct fcell *cell;
  1243. int    x, y, l, inverted;
  1244. {
  1245.     char *p;                    /* temp str pointer */
  1246.     int   i = 0,                /* current display count */
  1247.           j, sl, fl;                /* lengths */
  1248.  
  1249.     if(cell == NULL)
  1250.     return(-1);
  1251.  
  1252.     fl = strlen(cell->fname);
  1253.     sl = strlen(cell->size);
  1254.  
  1255.     movecursor(x, y);
  1256.     if(inverted)
  1257.       (*term.t_rev)(1);
  1258.     
  1259.     /* room for fname? */
  1260.     p = (fl+2 > l) ? &cell->fname[fl-(l-2)] : cell->fname;
  1261.     while(*p != '\0'){                /* write file name */
  1262.     pputc(*p++, 0);
  1263.     i++;
  1264.     }
  1265.  
  1266.     if(sl+3 <= l-i){                /* room for size? */
  1267.     j = (l-i)-(sl+2);            /* put space between */
  1268.     i += j;
  1269.     while(j--)                /* file name and size */
  1270.       pputc(' ', 0);
  1271.  
  1272.     p = cell->size;
  1273.     while(*p != '\0'){            /* write file size */
  1274.         pputc(*p++, 0);
  1275.         i++;
  1276.     }
  1277.     }
  1278.  
  1279.     if(inverted)
  1280.       (*term.t_rev)(0);
  1281.  
  1282.     while(i++ < l)                /* pad ending */
  1283.       pputc(' ', 0);
  1284.  
  1285.     return(1);
  1286. }
  1287.  
  1288.  
  1289.  
  1290. /*
  1291.  * PaintBrowse - with the current data, display the browser.  if level == 0 
  1292.  *               paint the whole thing, if level == 1 just paint the cells
  1293.  *               themselves
  1294.  */
  1295. PaintBrowser(mp, level)
  1296. struct bmaster *mp;
  1297. int level;
  1298. {
  1299.     int i, cl;
  1300.     struct fcell *tp;
  1301.  
  1302.     if(!level){
  1303.     cl = (Pmaster) ? term.t_nrow : term.t_nrow - 1;
  1304.     for(i = 0; i <= cl; i++){        /* clear screen */
  1305.         movecursor(i, 0);
  1306.         peeol();
  1307.     }
  1308.  
  1309.     BrowserAnchor(mp->dname);
  1310.     }
  1311.  
  1312.     i = 0;
  1313.     tp = mp->top;
  1314.     cl = COMPOSER_TOP_LINE;            /* current display line */
  1315.     while(tp){
  1316.  
  1317.     PaintCell(cl, mp->cpf * i, mp->cpf, tp, tp == mp->current);
  1318.  
  1319.     if(++i >= mp->fpl){
  1320.         i = 0;
  1321.         if(++cl > term.t_nrow-3)
  1322.           break;
  1323.     }
  1324.  
  1325.     tp = tp->next;
  1326.     }
  1327.  
  1328.     if(level){
  1329.     while(cl <= term.t_nrow - 3){
  1330.         if(!i)
  1331.           movecursor(cl, 0);
  1332.         peeol();
  1333.         movecursor(++cl, 0);
  1334.     }
  1335.     }
  1336.     else{
  1337.     BrowserKeys();
  1338.     }
  1339.  
  1340.     return(1);
  1341. }
  1342.  
  1343.  
  1344. /*
  1345.  * BrowserKeys - just paints the keyhelp at the bottom of the display
  1346.  */
  1347. BrowserKeys()
  1348. {
  1349.     char *oldkeys;
  1350.  
  1351.     /* reassign HelpKeyNames temporarily */
  1352.     oldkeys = HelpKeyNames;
  1353.     if(gmode&MDBRONLY)
  1354.       HelpKeyNames = "~?,~X,~R,~-,~D,~M,~S,~G,~W,~ ,~U,~K,                             "; 
  1355.     else if(!(gmode&MDFKEY))
  1356.       HelpKeyNames = "~?,~E,~R,~-,~D,~M,~S,~G,~W,~ ,~U,~K,                             "; 
  1357.  
  1358.     if(gmode&MDBRONLY)
  1359.       wkeyhelp("GXR-DMSJW 00", "Help,Exit,Rename,Back Pg,Del File,Make Copy,Select,Goto Dir,Where is,Fwd Pg");
  1360.     else
  1361.       wkeyhelp("GER-DMSJW 00", "Help,Exit Brwsr,Rename,Back Pg,Del File,Make Copy,Select,Goto Dir,Where is,Fwd Pg");
  1362.     
  1363.     if(!(gmode&MDFKEY))
  1364.       HelpKeyNames = oldkeys;            /* retore HelpKeyNames */
  1365. }
  1366.  
  1367.  
  1368. /*
  1369.  * layoutcells - figure out max length of cell and how many cells can 
  1370.  *               go on a line of the display
  1371.  */
  1372. layoutcells(mp)
  1373. struct bmaster *mp;
  1374. {
  1375.     int i = 1;
  1376.  
  1377.     mp->cpf = mp->longest + 12;            /* max chars / file */
  1378.  
  1379.     while(i*mp->cpf < term.t_ncol)        /* no force... */
  1380.       i++;                    /* like brute force! */
  1381.  
  1382.     mp->fpl = i - 1;                /* files per line */
  1383. }
  1384.  
  1385.  
  1386. /*
  1387.  * PlaceCell - given a browser master, return row and col of the display that
  1388.  *             it should go.  
  1389.  *
  1390.  *             return 1 if mp->top has changed, x,y relative to new page
  1391.  *             return 0 if otherwise (same page)
  1392.  *             return -1 on error
  1393.  */
  1394. PlaceCell(mp, cp, x, y)
  1395. struct bmaster *mp;
  1396. struct fcell *cp;
  1397. int    *x, *y;
  1398. {
  1399.     int cl = COMPOSER_TOP_LINE;            /* current line */
  1400.     int ci = 0;                    /* current index on line */
  1401.     int rv = 0;
  1402.     int secondtry = 0;
  1403.     struct fcell *tp;
  1404.  
  1405.     /* will cp fit on screen? */
  1406.     tp = mp->top;
  1407.     while(1){
  1408.     if(tp == cp){                /* bingo! */
  1409.         *x = cl;
  1410.         *y = ci * mp->cpf;
  1411.         break;
  1412.     }
  1413.  
  1414.     if((tp = tp->next) == NULL){        /* above top? */
  1415.         if(secondtry++){
  1416.         emlwrite("\007Internal error: can't find fname cell", NULL);
  1417.         return(-1);
  1418.         }
  1419.         else{
  1420.         tp = mp->top = mp->head;    /* try from the top! */
  1421.         cl = COMPOSER_TOP_LINE;
  1422.         ci = 0;
  1423.         rv = 1;
  1424.         continue;            /* start over! */
  1425.         }
  1426.     }
  1427.  
  1428.     if(++ci >= mp->fpl){            /* next line? */
  1429.         ci = 0;
  1430.         if(++cl > term.t_nrow-3){        /* next page? */
  1431.         ci = mp->fpl;            /* tp is at bottom right */
  1432.         while(ci--)            /* find new top */
  1433.           tp = tp->prev;
  1434.         mp->top = tp;
  1435.         ci = 0;
  1436.         cl = COMPOSER_TOP_LINE;        /* keep checking */
  1437.         rv = 1;
  1438.         }
  1439.     }
  1440.  
  1441.     }
  1442.  
  1443.     /* not on display! */
  1444.     return(rv);
  1445.     
  1446. }
  1447.  
  1448.  
  1449. /*
  1450.  * zotfcells - clean up malloc'd cells of file names
  1451.  */
  1452. zotfcells(hp)
  1453. struct fcell *hp;
  1454. {
  1455.     struct fcell *tp;
  1456.  
  1457.     while(hp){
  1458.     tp = hp;
  1459.     hp = hp->next;
  1460.     tp->next = NULL;
  1461.     free((char *) tp);
  1462.     }
  1463. }
  1464.  
  1465.  
  1466. /*
  1467.  * zotmaster - blast the browser master struct
  1468.  */
  1469. zotmaster(mp)
  1470. struct bmaster **mp;
  1471. {
  1472.     zotfcells((*mp)->head);            /* free cells       */
  1473.     free((char *)(*mp)->names);            /* free file names  */
  1474.     free((char *)*mp);                /* free master      */
  1475.     *mp = NULL;                    /* make double sure */
  1476. }
  1477.  
  1478.  
  1479. /*
  1480.  * FindCell - starting from the current cell find the first occurance of 
  1481.  *            the given string wrapping around if necessary
  1482.  */
  1483. struct fcell *FindCell(mp, string)
  1484. struct bmaster *mp;
  1485. char   *string;
  1486. {
  1487.     struct fcell *tp, *fp;
  1488.  
  1489.     if(*string == '\0')
  1490.       return(NULL);
  1491.  
  1492.     fp = NULL;
  1493.     tp = mp->current->next;
  1494.     
  1495.     while(tp && !fp){
  1496.     if(sisin(tp->fname, string))
  1497.       fp = tp;
  1498.     else
  1499.       tp = tp->next;
  1500.     }
  1501.  
  1502.     tp = mp->head;
  1503.     while(tp != mp->current && !fp){
  1504.     if(sisin(tp->fname, string))
  1505.       fp = tp;
  1506.     else
  1507.       tp = tp->next;
  1508.     }
  1509.  
  1510.     return(fp);
  1511. }
  1512.  
  1513.  
  1514. /*
  1515.  * sisin - case insensitive substring matching function
  1516.  */
  1517. sisin(s1, s2)
  1518. char *s1, *s2;
  1519. {
  1520.     register int j;
  1521.  
  1522.     while(*s1){
  1523.     j = 0;
  1524.     while(toupper(s1[j]) == toupper(s2[j]))
  1525.       if(s2[++j] == '\0')            /* bingo! */
  1526.         return(1);
  1527.  
  1528.     s1++;
  1529.     }
  1530.     return(0);
  1531.  
  1532.  
  1533. /*
  1534.  * BrowserAnchor - draw the browser's anchor line.
  1535.  */
  1536. BrowserAnchor(dir)
  1537. char *dir;
  1538. {
  1539.     register char *p;
  1540.     register int  i, j, l;
  1541.  
  1542.     movecursor(0, 0);
  1543.     (*term.t_rev)(1);
  1544.  
  1545.     i = 0;
  1546.     l = strlen(dir);
  1547.     j = (term.t_ncol-(l+16))/2;
  1548.  
  1549.     if(Pmaster)
  1550.       sprintf(s,"   PINE %s", Pmaster->pine_version);
  1551.     else
  1552.       sprintf(s,"   UW PICO(tm) %s", (gmode&MDBRONLY) ? "BROWSER" : version);
  1553.  
  1554.     p = s;
  1555.     while(*p){
  1556.     pputc(*p++, 0);
  1557.     i++;
  1558.     }
  1559.  
  1560.     if(l > term.t_ncol - i - 21){        /* fit dir name on line */
  1561.     p = dir;
  1562.     while((p = strchr(p, C_FILESEP)) != NULL)
  1563.       if((l - (p - dir)) <= term.t_ncol - i - 21)
  1564.         break;
  1565.       else
  1566.         p++;
  1567.  
  1568.     if(*p == '\0')                /* no suitable length! */
  1569.       p = &dir[l-(term.t_ncol-i-19)];
  1570.  
  1571.     sprintf(s,"%s Dir ...%s", (gmode&MDBRONLY) ? "" : " BROWSER  ", p);
  1572.     }
  1573.     else 
  1574.       sprintf(s,"%s  Dir: %s", (gmode&MDBRONLY) ? "" : " BROWSER  ", dir);
  1575.  
  1576.     if(i < j)                    /* keep it centered */
  1577.       j = j - i;                /* as long as we can */
  1578.     else
  1579.       j = ((term.t_ncol-i)-(strlen(p)+15))/2;
  1580.  
  1581.     while(j-- && i++)
  1582.       pputc(' ', 0);
  1583.     p = s;
  1584.  
  1585.     while(i++ < term.t_ncol && *p)        /* show directory */
  1586.       pputc(*p++, 0);
  1587.  
  1588.     while(i++<term.t_ncol)
  1589.       pputc(' ', 0);
  1590.  
  1591.     (*term.t_rev)(0);
  1592. }
  1593.  
  1594.  
  1595. /*
  1596.  * ResizeBrowser - handle a resize event
  1597.  */
  1598. ResizeBrowser()
  1599. {
  1600.     if(gmp){
  1601.     layoutcells(gmp);
  1602.     PaintBrowser(gmp, 0);
  1603.     return(1);
  1604.     }
  1605.     else
  1606.       return(0);
  1607. }
  1608.