home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume3 / mg2a / part03 < prev    next >
Encoding:
Text File  |  1989-02-03  |  58.6 KB  |  2,388 lines

  1. Path: xanth!mcnc!gatech!mandrill!hal!ncoast!allbery
  2. From: BLARSON@ECLA.USC.EDU (Bob Larson)
  3. Newsgroups: comp.sources.misc
  4. Subject: v03i027: mg 2a part 3 of 15
  5. Message-ID: <12401299340.47.BLARSON@ECLA.USC.EDU>
  6. Date: 26 May 88 04:51:52 GMT
  7. Sender: allbery@ncoast.UUCP
  8. Reply-To: BLARSON@ECLA.USC.EDU (Bob Larson)
  9. Lines: 2376
  10. Approved: allbery@ncoast.UUCP
  11.  
  12. comp.sources.misc: Volume 3, Issue 27
  13. Submitted-By: "Bob Larson" <BLARSON@ECLA.USC.EDU>
  14. Archive-Name: mg2a/Part3
  15.  
  16. #    This is a shell archive.
  17. #    Remove everything above and including the cut line.
  18. #    Then run the rest of the file through sh.
  19. #----cut here-----cut here-----cut here-----cut here----#
  20. #!/bin/sh
  21. # shar:    Shell Archiver
  22. #    Run the following text with /bin/sh to create:
  23. #    dired.c
  24. #    display.c
  25. #    extend.c
  26. #    file.c
  27. # This archive created: Sun May 22 02:23:34 1988
  28. # By:    blarson
  29. cat << \SHAR_EOF > dired.c
  30. /* dired module for mg 2a    */
  31. /* by Robert A. Larson        */
  32.  
  33. #include "def.h"
  34.  
  35. #ifndef NO_DIRED
  36.  
  37. BUFFER *dired_();
  38.  
  39. /*ARGSUSED*/
  40. dired(f, n)
  41. int f, n;
  42. {
  43.     char dirname[NFILEN];
  44.     BUFFER *bp;
  45.  
  46.     dirname[0] = '\0';
  47.     if(eread("Dired: ", dirname, NFILEN, EFNEW | EFCR) == ABORT)
  48.     return ABORT;
  49.     if((bp = dired_(dirname)) == NULL) return FALSE;
  50.     curbp = bp;
  51.     return showbuffer(bp, curwp, WFHARD | WFMODE);
  52. }
  53.  
  54. /*ARGSUSED*/
  55. d_otherwindow(f, n)
  56. int f, n;
  57. {
  58.     char dirname[NFILEN];
  59.     BUFFER *bp;
  60.     WINDOW *wp;
  61.  
  62.     dirname[0] = '\0';
  63.     if(eread("Dired other window: ", dirname, NFILEN, EFNEW | EFCR) == ABORT)
  64.     return ABORT;
  65.     if((bp = dired_(dirname)) == NULL) return FALSE;
  66.     if((wp = popbuf(bp)) == NULL) return FALSE;
  67.     curbp = bp;
  68.     curwp = wp;
  69.     return TRUE;
  70. }
  71.  
  72. /*ARGSUSED*/
  73. d_del(f, n)
  74. int f, n;
  75. {
  76.     if(n < 0) return FALSE;
  77.     while(n--) {
  78.     if(llength(curwp->w_dotp) > 0)
  79.         lputc(curwp->w_dotp, 0, 'D');
  80.     if(lforw(curwp->w_dotp) != curbp->b_linep)
  81.         curwp->w_dotp = lforw(curwp->w_dotp);
  82.     }
  83.     curwp->w_flag |= WFEDIT | WFMOVE;
  84.     curwp->w_doto = 0;
  85.     return TRUE;
  86. }
  87.  
  88. /*ARGSUSED*/
  89. d_undel(f, n)
  90. int f, n;
  91. {
  92.     if(n < 0) return d_undelbak(f, -n);
  93.     while(n--) {
  94.     if(llength(curwp->w_dotp) > 0)
  95.         lputc(curwp->w_dotp, 0, ' ');
  96.     if(lforw(curwp->w_dotp) != curbp->b_linep)
  97.         curwp->w_dotp = lforw(curwp->w_dotp);
  98.     }
  99.     curwp->w_flag |= WFEDIT | WFMOVE;
  100.     curwp->w_doto = 0;
  101.     return TRUE;
  102. }
  103.  
  104. /*ARGSUSED*/
  105. d_undelbak(f, n)
  106. int f, n;
  107. {
  108.     if(n < 0) return d_undel(f, -n);
  109.     while(n--) {
  110.     if(llength(curwp->w_dotp) > 0)
  111.         lputc(curwp->w_dotp, 0, ' ');
  112.     if(lback(curwp->w_dotp) != curbp->b_linep)
  113.         curwp->w_dotp = lback(curwp->w_dotp);
  114.     }
  115.     curwp->w_doto = 0;
  116.     curwp->w_flag |= WFEDIT | WFMOVE;
  117.     return TRUE;
  118. }
  119.  
  120. /*ARGSUSED*/
  121. d_findfile(f, n)
  122. int f, n;
  123. {
  124.     char fname[NFILEN];
  125.     register BUFFER *bp;
  126.     register int s;
  127.     BUFFER *findbuffer();
  128.  
  129.     if((s = d_makename(curwp->w_dotp, fname)) == ABORT) return FALSE;
  130.     if ((bp = (s ? dired_(fname) : findbuffer(fname))) == NULL) return FALSE;
  131.     curbp = bp;
  132.     if (showbuffer(bp, curwp, WFHARD) != TRUE) return FALSE;
  133.     if (bp->b_fname[0] != 0) return TRUE;
  134.     return readin(fname);
  135. }
  136.  
  137. /*ARGSUSED*/
  138. d_ffotherwindow(f, n)
  139. int f, n;
  140. {
  141.     char fname[NFILEN];
  142.     register BUFFER *bp;
  143.     register int s;
  144.     register WINDOW *wp;
  145.     BUFFER *findbuffer();
  146.  
  147.     if((s = d_makename(curwp->w_dotp, fname)) == ABORT) return FALSE;
  148.     if ((bp = (s ? dired_(fname) : findbuffer(fname))) == NULL) return FALSE;
  149.     if ((wp = popbuf(bp)) == NULL) return FALSE;
  150.     curbp = bp;
  151.     curwp = wp;
  152.     if (bp->b_fname[0] != 0) return TRUE;  /* never true for dired buffers */
  153.     return readin(fname);
  154. }
  155.  
  156. /*ARGSUSED*/
  157. d_expunge(f, n)
  158. int f, n;
  159. {
  160.     register LINE *lp, *nlp;
  161.     char fname[NFILEN];
  162.     VOID lfree();
  163.  
  164.     for(lp = lforw(curbp->b_linep); lp != curbp->b_linep; lp = nlp) {
  165.     nlp = lforw(lp);
  166.     if(llength(lp) && lgetc(lp, 0) == 'D') {
  167.         switch(d_makename(lp, fname)) {
  168.         case ABORT:
  169.             ewprintf("Bad line in dired buffer");
  170.             return FALSE;
  171.         case FALSE:
  172.             if(unlink(fname) < 0) {
  173.             ewprintf("Could not delete '%s'", fname);
  174.             return FALSE;
  175.             }
  176.             break;
  177.         case TRUE:
  178.             if(unlinkdir(fname) < 0) {
  179.             ewprintf("Could not delete directory '%s'", fname);
  180.             return FALSE;
  181.             }
  182.             break;
  183.         }
  184.         lfree(lp);
  185.         curwp->w_flag |= WFHARD;
  186.     }
  187.     }
  188.     return TRUE;
  189. }
  190.  
  191. /*ARGSUSED*/
  192. d_copy(f, n)
  193. int f, n;
  194. {
  195.     char frname[NFILEN], toname[NFILEN];
  196.     int stat;
  197.  
  198.     if(d_makename(curwp->w_dotp, frname) != FALSE) {
  199.     ewprintf("Not a file");
  200.     return FALSE;
  201.     }
  202.     if((stat = eread("Copy %s to: ", toname, NFILEN, EFNEW | EFCR, frname))
  203.     != TRUE) return stat;
  204.     return copy(frname, toname) >= 0;
  205. }
  206.  
  207. /*ARGSUSED*/
  208. d_rename(f, n)
  209. int f, n;
  210. {
  211.     char frname[NFILEN], toname[NFILEN];
  212.     int stat;
  213.  
  214.     if(d_makename(curwp->w_dotp, frname) != FALSE) {
  215.     ewprintf("Not a file");
  216.     return FALSE;
  217.     }
  218.     if((stat = eread("Rename %s to: ", toname, NFILEN, EFNEW | EFCR, frname))
  219.     != TRUE) return stat;
  220.     return rename(frname, toname) >= 0;
  221. }
  222. #endif
  223.  
  224.  
  225. SHAR_EOF
  226. cat << \SHAR_EOF > display.c
  227. /*
  228.  * The functions in this file handle redisplay. The
  229.  * redisplay system knows almost nothing about the editing
  230.  * process; the editing functions do, however, set some
  231.  * hints to eliminate a lot of the grinding. There is more
  232.  * that can be done; the "vtputc" interface is a real
  233.  * pig. Two conditional compilation flags; the GOSLING
  234.  * flag enables dynamic programming redisplay, using the
  235.  * algorithm published by Jim Gosling in SIGOA. The MEMMAP
  236.  * changes things around for memory mapped video. With
  237.  * both off, the terminal is a VT52.
  238.  */
  239. #include    "def.h"
  240. #include    "kbd.h"
  241.  
  242. /*
  243.  * You can change these back to the types
  244.  * implied by the name if you get tight for space. If you
  245.  * make both of them "int" you get better code on the VAX.
  246.  * They do nothing if this is not Gosling redisplay, except
  247.  * for change the size of a structure that isn't used.
  248.  * A bit of a cheat.
  249.  */
  250. /* These defines really belong in sysdef.h */
  251. #ifndef XCHAR
  252. #  define    XCHAR    int
  253. #  define    XSHORT    int
  254. #endif
  255.  
  256. #ifdef    STANDOUT_GLITCH
  257. extern int SG;                /* number of standout glitches    */
  258. #endif
  259.  
  260. /*
  261.  * A video structure always holds
  262.  * an array of characters whose length is equal to
  263.  * the longest line possible. Only some of this is
  264.  * used if "ncol" isn't the same as "NCOL".
  265.  */
  266. typedef struct    {
  267.     short    v_hash;            /* Hash code, for compares.    */
  268.     short    v_flag;            /* Flag word.            */
  269.     short    v_color;        /* Color of the line.        */
  270.     XSHORT    v_cost;            /* Cost of display.        */
  271.     char    v_text[NCOL];        /* The actual characters.    */
  272. }    VIDEO;
  273.  
  274. #define VFCHG    0x0001            /* Changed.            */
  275. #define VFHBAD    0x0002            /* Hash and cost are bad.    */
  276. #define VFEXT    0x0004            /* extended line (beond ncol)    */
  277.  
  278. /*
  279.  * SCORE structures hold the optimal
  280.  * trace trajectory, and the cost of redisplay, when
  281.  * the dynamic programming redisplay code is used.
  282.  * If no fancy redisplay, this isn't used. The trace index
  283.  * fields can be "char", and the score a "short", but
  284.  * this makes the code worse on the VAX.
  285.  */
  286. typedef struct    {
  287.     XCHAR    s_itrace;        /* "i" index for track back.    */
  288.     XCHAR    s_jtrace;        /* "j" index for trace back.    */
  289.     XSHORT    s_cost;            /* Display cost.        */
  290. }    SCORE;
  291.  
  292. int    sgarbf    = TRUE;            /* TRUE if screen is garbage.    */
  293. int    vtrow    = 0;            /* Virtual cursor row.        */
  294. int    vtcol    = 0;            /* Virtual cursor column.    */
  295. int    tthue    = CNONE;        /* Current color.        */
  296. int    ttrow    = HUGE;            /* Physical cursor row.        */
  297. int    ttcol    = HUGE;            /* Physical cursor column.    */
  298. int    tttop    = HUGE;            /* Top of scroll region.    */
  299. int    ttbot    = HUGE;            /* Bottom of scroll region.    */
  300. int    lbound    = 0;            /* leftmost bound of the current line */
  301.                     /* being displayed        */
  302.  
  303. VIDEO    *vscreen[NROW-1];        /* Edge vector, virtual.    */
  304. VIDEO    *pscreen[NROW-1];        /* Edge vector, physical.    */
  305. VIDEO    video[2*(NROW-1)];        /* Actual screen data.        */
  306. VIDEO    blanks;                /* Blank line image.        */
  307.  
  308. /*
  309.  * Some predeclerations to make ANSI compilers happy
  310.  */
  311. VOID    vtinit();
  312. VOID    vttidy();
  313. VOID    vtmove();
  314. VOID    vtputc();
  315. VOID    vtpute();
  316. VOID    vteeol();
  317. VOID    update();
  318. VOID    updext();
  319. VOID    ucopy();
  320. VOID    uline();
  321. VOID    modeline();
  322. VOID    hash();
  323. VOID    setscores();
  324. VOID    traceback();
  325.  
  326. #ifdef    GOSLING
  327. /*
  328.  * This matrix is written as an array because
  329.  * we do funny things in the "setscores" routine, which
  330.  * is very compute intensive, to make the subscripts go away.
  331.  * It would be "SCORE    score[NROW][NROW]" in old speak.
  332.  * Look at "setscores" to understand what is up.
  333.  */
  334. SCORE    score[NROW*NROW];
  335. #endif
  336.  
  337. /*
  338.  * Initialize the data structures used
  339.  * by the display code. The edge vectors used
  340.  * to access the screens are set up. The operating
  341.  * system's terminal I/O channel is set up. Fill the
  342.  * "blanks" array with ASCII blanks. The rest is done
  343.  * at compile time. The original window is marked
  344.  * as needing full update, and the physical screen
  345.  * is marked as garbage, so all the right stuff happens
  346.  * on the first call to redisplay.
  347.  */
  348. VOID
  349. vtinit() {
  350.     register VIDEO    *vp;
  351.     register int    i;
  352.  
  353.     ttopen();
  354.     ttinit();
  355.     vp = &video[0];
  356.     for (i=0; i<NROW-1; ++i) {
  357.         vscreen[i] = vp;
  358.         ++vp;
  359.         pscreen[i] = vp;
  360.         ++vp;
  361.     }
  362.     blanks.v_color = CTEXT;
  363.     for (i=0; i<NCOL; ++i)
  364.         blanks.v_text[i] = ' ';
  365. }
  366.  
  367. /*
  368.  * Tidy up the virtual display system
  369.  * in anticipation of a return back to the host
  370.  * operating system. Right now all we do is position
  371.  * the cursor to the last line, erase the line, and
  372.  * close the terminal channel.
  373.  */
  374. VOID
  375. vttidy() {
  376.     ttcolor(CTEXT);
  377.     ttnowindow();                /* No scroll window.    */
  378.     ttmove(nrow-1, 0);            /* Echo line.        */
  379.     tteeol();
  380.     tttidy();
  381.     ttflush();
  382.     ttclose();
  383. }
  384.  
  385. /*
  386.  * Move the virtual cursor to an origin
  387.  * 0 spot on the virtual display screen. I could
  388.  * store the column as a character pointer to the spot
  389.  * on the line, which would make "vtputc" a little bit
  390.  * more efficient. No checking for errors.
  391.  */
  392. VOID
  393. vtmove(row, col) {
  394.     vtrow = row;
  395.     vtcol = col;
  396. }
  397.  
  398. /*
  399.  * Write a character to the virtual display,
  400.  * dealing with long lines and the display of unprintable
  401.  * things like control characters. Also expand tabs every 8
  402.  * columns. This code only puts printing characters into
  403.  * the virtual display image. Special care must be taken when
  404.  * expanding tabs. On a screen whose width is not a multiple
  405.  * of 8, it is possible for the virtual cursor to hit the
  406.  * right margin before the next tab stop is reached. This
  407.  * makes the tab code loop if you are not careful.
  408.  * Three guesses how we found this.
  409.  */
  410. VOID
  411. vtputc(c) register int c; {
  412.     register VIDEO    *vp;
  413.  
  414.     vp = vscreen[vtrow];
  415.     if (vtcol >= ncol)
  416.         vp->v_text[ncol-1] = '$';
  417.     else if (c == '\t'
  418. #ifdef    NOTAB
  419.         && !(curbp->b_flag & BFNOTAB)
  420. #endif
  421.         ) {
  422.         do {
  423.             vtputc(' ');
  424.         } while (vtcol<ncol && (vtcol&0x07)!=0);
  425.     } else if (ISCTRL(c)) {
  426.         vtputc('^');
  427.         vtputc(CCHR(c));
  428.     } else
  429.         vp->v_text[vtcol++] = c;
  430. }
  431.  
  432. /* Put a character to the virtual screen in an extended line.  If we are
  433.  * not yet on left edge, don't print it yet.  Check for overflow on
  434.  * the right margin.
  435.  */
  436. VOID
  437. vtpute(c)
  438. int c;
  439. {
  440.     register VIDEO    *vp;
  441.  
  442.     vp = vscreen[vtrow];
  443.  
  444.     if (vtcol >= ncol) vp->v_text[ncol - 1] = '$';
  445.     else if (c == '\t'
  446. #ifdef    NOTAB
  447.                && !(curbp->b_flag & BFNOTAB)
  448. #endif
  449.                       ) {
  450.     do {
  451.         vtpute(' ');
  452.     }
  453.     while (((vtcol + lbound)&0x07) != 0 && vtcol < ncol);
  454.     } else if (ISCTRL(c) != FALSE) {
  455.     vtpute('^');
  456.     vtpute(CCHR(c));
  457.     } else {
  458.     if (vtcol >= 0) vp->v_text[vtcol] = c;
  459.     ++vtcol;
  460.     }
  461. }
  462.  
  463. /* Erase from the end of the
  464.  * software cursor to the end of the
  465.  * line on which the software cursor is
  466.  * located. The display routines will decide
  467.  * if a hardware erase to end of line command
  468.  * should be used to display this.
  469.  */
  470. VOID
  471. vteeol() {
  472.     register VIDEO    *vp;
  473.  
  474.     vp = vscreen[vtrow];
  475.     while (vtcol < ncol)
  476.         vp->v_text[vtcol++] = ' ';
  477. }
  478.  
  479. /*
  480.  * Make sure that the display is
  481.  * right. This is a three part process. First,
  482.  * scan through all of the windows looking for dirty
  483.  * ones. Check the framing, and refresh the screen.
  484.  * Second, make sure that "currow" and "curcol" are
  485.  * correct for the current window. Third, make the
  486.  * virtual and physical screens the same.
  487.  */
  488. VOID
  489. update() {
  490.     register LINE    *lp;
  491.     register WINDOW *wp;
  492.     register VIDEO    *vp1;
  493.     VIDEO        *vp2;
  494.     register int    i;
  495.     register int    j;
  496.     register int    c;
  497.     register int    hflag;
  498.     register int    currow;
  499.     register int    curcol;
  500.     register int    offs;
  501.     register int    size;
  502.     VOID traceback ();
  503.     VOID uline ();
  504.  
  505.     if (typeahead()) return;
  506.     if (sgarbf) {                /* must update everything */
  507.         wp = wheadp;
  508.         while(wp != NULL) {
  509.             wp->w_flag |= WFMODE | WFHARD;
  510.             wp = wp->w_wndp;
  511.         }
  512.     }
  513.     hflag = FALSE;                /* Not hard.        */
  514.     wp = wheadp;
  515.     while (wp != NULL) {
  516.         if (wp->w_flag != 0) {        /* Need update.        */
  517.             if ((wp->w_flag&WFFORCE) == 0) {
  518.                 lp = wp->w_linep;
  519.                 for (i=0; i<wp->w_ntrows; ++i) {
  520.                     if (lp == wp->w_dotp)
  521.                         goto out;
  522.                     if (lp == wp->w_bufp->b_linep)
  523.                         break;
  524.                     lp = lforw(lp);
  525.                 }
  526.             }
  527.             i = wp->w_force;    /* Reframe this one.    */
  528.             if (i > 0) {
  529.                 --i;
  530.                 if (i >= wp->w_ntrows)
  531.                     i = wp->w_ntrows-1;
  532.             } else if (i < 0) {
  533.                 i += wp->w_ntrows;
  534.                 if (i < 0)
  535.                     i = 0;
  536.             } else
  537.                 i = wp->w_ntrows/2;
  538.             lp = wp->w_dotp;
  539.             while (i!=0 && lback(lp)!=wp->w_bufp->b_linep) {
  540.                 --i;
  541.                 lp = lback(lp);
  542.             }
  543.             wp->w_linep = lp;
  544.             wp->w_flag |= WFHARD;    /* Force full.        */
  545.         out:
  546.             lp = wp->w_linep;    /* Try reduced update.    */
  547.             i  = wp->w_toprow;
  548.             if ((wp->w_flag&~WFMODE) == WFEDIT) {
  549.                 while (lp != wp->w_dotp) {
  550.                     ++i;
  551.                     lp = lforw(lp);
  552.                 }
  553.                 vscreen[i]->v_color = CTEXT;
  554.                 vscreen[i]->v_flag |= (VFCHG|VFHBAD);
  555.                 vtmove(i, 0);
  556.                 for (j=0; j<llength(lp); ++j)
  557.                     vtputc(lgetc(lp, j));
  558.                 vteeol();
  559.             } else if ((wp->w_flag&(WFEDIT|WFHARD)) != 0) {
  560.                 hflag = TRUE;
  561.                 while (i < wp->w_toprow+wp->w_ntrows) {
  562.                     vscreen[i]->v_color = CTEXT;
  563.                     vscreen[i]->v_flag |= (VFCHG|VFHBAD);
  564.                     vtmove(i, 0);
  565.                     if (lp != wp->w_bufp->b_linep) {
  566.                         for (j=0; j<llength(lp); ++j)
  567.                             vtputc(lgetc(lp, j));
  568.                         lp = lforw(lp);
  569.                     }
  570.                     vteeol();
  571.                     ++i;
  572.                 }
  573.             }
  574.             if ((wp->w_flag&WFMODE) != 0)
  575.                 modeline(wp);
  576.             wp->w_flag  = 0;
  577.             wp->w_force = 0;
  578.         }
  579.         wp = wp->w_wndp;
  580.     }
  581.     lp = curwp->w_linep;            /* Cursor location.    */
  582.     currow = curwp->w_toprow;
  583.     while (lp != curwp->w_dotp) {
  584.         ++currow;
  585.         lp = lforw(lp);
  586.     }
  587.     curcol = 0;
  588.     i = 0;
  589.     while (i < curwp->w_doto) {
  590.         c = lgetc(lp, i++);
  591.         if (c == '\t'
  592. #ifdef    NOTAB
  593.             && !(curbp->b_flag & BFNOTAB)
  594. #endif
  595.             ) curcol |= 0x07;
  596.         else if (ISCTRL(c) != FALSE)
  597.             ++curcol;
  598.         ++curcol;
  599.     }
  600.     if (curcol >= ncol - 1) {        /* extended line. */
  601.          /* flag we are extended and changed */
  602.         vscreen[currow]->v_flag |= VFEXT | VFCHG;
  603.         updext(currow, curcol);        /* and output extended line */
  604.     } else lbound = 0;            /* not extended line */
  605.  
  606.     /* make sure no lines need to be de-extended because the cursor is
  607.     no longer on them */
  608.  
  609.     wp = wheadp;
  610.  
  611.     while (wp != NULL) {
  612.         lp = wp->w_linep;
  613.         i = wp->w_toprow;
  614.         while (i < wp->w_toprow + wp->w_ntrows) {
  615.         if (vscreen[i]->v_flag & VFEXT) {
  616.             /* always flag extended lines as changed */
  617.             vscreen[i]->v_flag |= VFCHG;
  618.             if ((wp != curwp) || (lp != wp->w_dotp) ||
  619.                 (curcol < ncol - 1)) {
  620.             vtmove(i, 0);
  621.             for (j = 0; j < llength(lp); ++j)
  622.                 vtputc(lgetc(lp, j));
  623.             vteeol();
  624.             /* this line no longer is extended */
  625.             vscreen[i]->v_flag &= ~VFEXT;
  626.             }
  627.         }
  628.         lp = lforw(lp);
  629.         ++i;
  630.         }
  631.         /* if garbaged then fix up mode lines */
  632.         if (sgarbf != FALSE) vscreen[i]->v_flag |= VFCHG;
  633.         /* and onward to the next window */
  634.         wp = wp->w_wndp;
  635.     }
  636.  
  637.     if (sgarbf != FALSE) {            /* Screen is garbage.    */
  638.         sgarbf = FALSE;            /* Erase-page clears    */
  639.         epresf = FALSE;            /* the message area.    */
  640.         tttop  = HUGE;            /* Forget where you set */
  641.         ttbot  = HUGE;            /* scroll region.    */
  642.         tthue  = CNONE;            /* Color unknown.    */
  643.         ttmove(0, 0);
  644.         tteeop();
  645.         for (i=0; i<nrow-1; ++i) {
  646.             uline(i, vscreen[i], &blanks);
  647.             ucopy(vscreen[i], pscreen[i]);
  648.         }
  649.         ttmove(currow, curcol - lbound);
  650.         ttflush();
  651.         return;
  652.     }
  653. #ifdef    GOSLING
  654.     if (hflag != FALSE) {            /* Hard update?        */
  655.         for (i=0; i<nrow-1; ++i) {    /* Compute hash data.    */
  656.             hash(vscreen[i]);
  657.             hash(pscreen[i]);
  658.         }
  659.         offs = 0;            /* Get top match.    */
  660.         while (offs != nrow-1) {
  661.             vp1 = vscreen[offs];
  662.             vp2 = pscreen[offs];
  663.             if (vp1->v_color != vp2->v_color
  664.             ||  vp1->v_hash     != vp2->v_hash)
  665.                 break;
  666.             uline(offs, vp1, vp2);
  667.             ucopy(vp1, vp2);
  668.             ++offs;
  669.         }
  670.         if (offs == nrow-1) {        /* Might get it all.    */
  671.             ttmove(currow, curcol - lbound);
  672.             ttflush();
  673.             return;
  674.         }
  675.         size = nrow-1;            /* Get bottom match.    */
  676.         while (size != offs) {
  677.             vp1 = vscreen[size-1];
  678.             vp2 = pscreen[size-1];
  679.             if (vp1->v_color != vp2->v_color
  680.             ||  vp1->v_hash     != vp2->v_hash)
  681.                 break;
  682.             uline(size-1, vp1, vp2);
  683.             ucopy(vp1, vp2);
  684.             --size;
  685.         }
  686.         if ((size -= offs) == 0)    /* Get screen size.    */
  687.             panic("Illegal screen size in update");
  688.         setscores(offs, size);        /* Do hard update.    */
  689.         traceback(offs, size, size, size);
  690.         for (i=0; i<size; ++i)
  691.             ucopy(vscreen[offs+i], pscreen[offs+i]);
  692.         ttmove(currow, curcol - lbound);
  693.         ttflush();
  694.         return;
  695.     }
  696. #endif
  697.     for (i=0; i<nrow-1; ++i) {        /* Easy update.        */
  698.         vp1 = vscreen[i];
  699.         vp2 = pscreen[i];
  700.         if ((vp1->v_flag&VFCHG) != 0) {
  701.             uline(i, vp1, vp2);
  702.             ucopy(vp1, vp2);
  703.         }
  704.     }
  705.     ttmove(currow, curcol - lbound);
  706.     ttflush();
  707. }
  708.  
  709. /*
  710.  * Update a saved copy of a line,
  711.  * kept in a VIDEO structure. The "vvp" is
  712.  * the one in the "vscreen". The "pvp" is the one
  713.  * in the "pscreen". This is called to make the
  714.  * virtual and physical screens the same when
  715.  * display has done an update.
  716.  */
  717. VOID
  718. ucopy(vvp, pvp) register VIDEO *vvp; register VIDEO *pvp; {
  719.  
  720.     vvp->v_flag &= ~VFCHG;            /* Changes done.    */
  721.     pvp->v_flag  = vvp->v_flag;        /* Update model.    */
  722.     pvp->v_hash  = vvp->v_hash;
  723.     pvp->v_cost  = vvp->v_cost;
  724.     pvp->v_color = vvp->v_color;
  725.     bcopy(vvp->v_text, pvp->v_text, ncol);
  726. }
  727.  
  728. /* updext: update the extended line which the cursor is currently
  729.  * on at a column greater than the terminal width. The line
  730.  * will be scrolled right or left to let the user see where
  731.  * the cursor is
  732.  */
  733. VOID
  734. updext(currow, curcol)
  735. int currow, curcol;
  736. {
  737.     register LINE *lp;            /* pointer to current line */
  738.     register int j;            /* index into line */
  739.  
  740.     /* calculate what column the left bound should be */
  741.     /* (force cursor into middle half of screen) */
  742.     lbound = curcol - (curcol % (ncol>>1)) - (ncol>>2);
  743.     /* scan through the line outputing characters to the virtual screen */
  744.     /* once we reach the left edge */
  745.     vtmove(currow, -lbound);            /* start scanning offscreen */
  746.     lp = curwp->w_dotp;                /* line to output */
  747.     for (j=0; j<llength(lp); ++j)        /* until the end-of-line */
  748.     vtpute(lgetc(lp, j));
  749.     vteeol();                    /* truncate the virtual line */
  750.     vscreen[currow]->v_text[0] = '$';        /* and put a '$' in column 1 */
  751. }
  752.  
  753. /*
  754.  * Update a single line. This routine only
  755.  * uses basic functionality (no insert and delete character,
  756.  * but erase to end of line). The "vvp" points at the VIDEO
  757.  * structure for the line on the virtual screen, and the "pvp"
  758.  * is the same for the physical screen. Avoid erase to end of
  759.  * line when updating CMODE color lines, because of the way that
  760.  * reverse video works on most terminals.
  761.  */
  762. VOID uline(row, vvp, pvp) VIDEO *vvp; VIDEO *pvp; {
  763. #ifdef    MEMMAP
  764.     putline(row+1, 1, &vvp->v_text[0]);
  765. #else
  766.     register char    *cp1;
  767.     register char    *cp2;
  768.     register char    *cp3;
  769.     char        *cp4;
  770.     char        *cp5;
  771.     register int    nbflag;
  772.  
  773.     if (vvp->v_color != pvp->v_color) {    /* Wrong color, do a    */
  774.         ttmove(row, 0);            /* full redraw.        */
  775. #ifdef    STANDOUT_GLITCH
  776.         if (pvp->v_color != CTEXT && SG >= 0) tteeol();
  777. #endif
  778.         ttcolor(vvp->v_color);
  779. #ifdef    STANDOUT_GLITCH
  780.         cp1 = &vvp->v_text[SG > 0 ? SG : 0];
  781.         /* the odd code for SG==0 is to avoid putting the invisable
  782.          * glitch character on the next line.
  783.          * (Hazeltine executive 80 model 30)
  784.          */
  785.         cp2 = &vvp->v_text[ncol - (SG >= 0 ? (SG!=0 ? SG : 1) : 0)];
  786. #else
  787.         cp1 = &vvp->v_text[0];
  788.         cp2 = &vvp->v_text[ncol];
  789. #endif
  790.         while (cp1 != cp2) {
  791.             ttputc(*cp1++);
  792.             ++ttcol;
  793.         }
  794. #ifndef MOVE_STANDOUT
  795.         ttcolor(CTEXT);
  796. #endif
  797.         return;
  798.     }
  799.     cp1 = &vvp->v_text[0];            /* Compute left match.    */
  800.     cp2 = &pvp->v_text[0];
  801.     while (cp1!=&vvp->v_text[ncol] && cp1[0]==cp2[0]) {
  802.         ++cp1;
  803.         ++cp2;
  804.     }
  805.     if (cp1 == &vvp->v_text[ncol])        /* All equal.        */
  806.         return;
  807.     nbflag = FALSE;
  808.     cp3 = &vvp->v_text[ncol];        /* Compute right match. */
  809.     cp4 = &pvp->v_text[ncol];
  810.     while (cp3[-1] == cp4[-1]) {
  811.         --cp3;
  812.         --cp4;
  813.         if (cp3[0] != ' ')        /* Note non-blanks in    */
  814.             nbflag = TRUE;        /* the right match.    */
  815.     }
  816.     cp5 = cp3;                /* Is erase good?    */
  817.     if (nbflag==FALSE && vvp->v_color==CTEXT) {
  818.         while (cp5!=cp1 && cp5[-1]==' ')
  819.             --cp5;
  820.         /* Alcyon hack */
  821.         if ((int)(cp3-cp5) <= tceeol)
  822.             cp5 = cp3;
  823.     }
  824.     /* Alcyon hack */
  825.     ttmove(row, (int)(cp1-&vvp->v_text[0]));
  826. #ifdef    STANDOUT_GLITCH
  827.     if (vvp->v_color != CTEXT && SG > 0) {
  828.         if(cp1 < &vvp->v_text[SG]) cp1 = &vvp->v_text[SG];
  829.         if(cp5 > &vvp->v_text[ncol-SG]) cp5 = &vvp->v_text[ncol-SG];
  830.     } else if (SG < 0)
  831. #endif
  832.         ttcolor(vvp->v_color);
  833.     while (cp1 != cp5) {
  834.         ttputc(*cp1++);
  835.         ++ttcol;
  836.     }
  837.     if (cp5 != cp3)                /* Do erase.        */
  838.         tteeol();
  839. #endif
  840. }
  841.  
  842. /*
  843.  * Redisplay the mode line for
  844.  * the window pointed to by the "wp".
  845.  * This is the only routine that has any idea
  846.  * of how the modeline is formatted. You can
  847.  * change the modeline format by hacking at
  848.  * this routine. Called by "update" any time
  849.  * there is a dirty window.
  850.  * Note that if STANDOUT_GLITCH is defined, first and last SG characters
  851.  * may never be seen.
  852.  */
  853. VOID
  854. modeline(wp) register WINDOW *wp; {
  855.     register int    n;
  856.     register BUFFER *bp;
  857.     int    mode;
  858.  
  859.     n = wp->w_toprow+wp->w_ntrows;        /* Location.        */
  860.     vscreen[n]->v_color = CMODE;        /* Mode line color.    */
  861.     vscreen[n]->v_flag |= (VFCHG|VFHBAD);    /* Recompute, display.    */
  862.     vtmove(n, 0);                /* Seek to right line.    */
  863.     bp = wp->w_bufp;
  864.     vtputc('-'); vtputc('-');
  865.     if ((bp->b_flag&BFCHG) != 0) {        /* "*" if changed.    */
  866.         vtputc('*'); vtputc('*');
  867.     } else {
  868.         vtputc('-'); vtputc('-');
  869.     }
  870.     vtputc('-');
  871.     n  = 5;
  872.     n += vtputs("Mg: ");
  873.     if (bp->b_bname[0] != '\0')
  874.         n += vtputs(&(bp->b_bname[0]));
  875.     while (n < 42) {            /* Pad out with blanks    */
  876.         vtputc(' ');
  877.         ++n;
  878.     }
  879.     vtputc('(');
  880.     ++n;
  881.     for(mode=0;;) {
  882.         n += vtputs(bp->b_modes[mode]->p_name);
  883.         if(++mode > bp->b_nmodes) break;
  884.         vtputc('-');
  885.         ++n;
  886.     }
  887.     vtputc(')');
  888.     ++n;
  889.     while (n < ncol) {            /* Pad out.        */
  890.         vtputc('-');
  891.         ++n;
  892.     }
  893. }
  894. /*
  895.  * output a string to the mode line, report how long it was.
  896.  */
  897. vtputs(s) register char *s; {
  898.     register int n = 0;
  899.  
  900.     while (*s != '\0') {
  901.         vtputc(*s++);
  902.         ++n;
  903.     }
  904.     return n;
  905. }
  906. #ifdef    GOSLING
  907. /*
  908.  * Compute the hash code for
  909.  * the line pointed to by the "vp". Recompute
  910.  * it if necessary. Also set the approximate redisplay
  911.  * cost. The validity of the hash code is marked by
  912.  * a flag bit. The cost understand the advantages
  913.  * of erase to end of line. Tuned for the VAX
  914.  * by Bob McNamara; better than it used to be on
  915.  * just about any machine.
  916.  */
  917. VOID
  918. hash(vp) register VIDEO *vp; {
  919.     register int    i;
  920.     register int    n;
  921.     register char    *s;
  922.  
  923.     if ((vp->v_flag&VFHBAD) != 0) {        /* Hash bad.        */
  924.         s = &vp->v_text[ncol-1];
  925.         for (i=ncol; i!=0; --i, --s)
  926.             if (*s != ' ')
  927.                 break;
  928.         n = ncol-i;            /* Erase cheaper?    */
  929.         if (n > tceeol)
  930.             n = tceeol;
  931.         vp->v_cost = i+n;        /* Bytes + blanks.    */
  932.         for (n=0; i!=0; --i, --s)
  933.             n = (n<<5) + n + *s;
  934.         vp->v_hash = n;            /* Hash code.        */
  935.         vp->v_flag &= ~VFHBAD;        /* Flag as all done.    */
  936.     }
  937. }
  938.  
  939. /*
  940.  * Compute the Insert-Delete
  941.  * cost matrix. The dynamic programming algorithm
  942.  * described by James Gosling is used. This code assumes
  943.  * that the line above the echo line is the last line involved
  944.  * in the scroll region. This is easy to arrange on the VT100
  945.  * because of the scrolling region. The "offs" is the origin 0
  946.  * offset of the first row in the virtual/physical screen that
  947.  * is being updated; the "size" is the length of the chunk of
  948.  * screen being updated. For a full screen update, use offs=0
  949.  * and size=nrow-1.
  950.  *
  951.  * Older versions of this code implemented the score matrix by
  952.  * a two dimensional array of SCORE nodes. This put all kinds of
  953.  * multiply instructions in the code! This version is written to
  954.  * use a linear array and pointers, and contains no multiplication
  955.  * at all. The code has been carefully looked at on the VAX, with
  956.  * only marginal checking on other machines for efficiency. In
  957.  * fact, this has been tuned twice! Bob McNamara tuned it even
  958.  * more for the VAX, which is a big issue for him because of
  959.  * the 66 line X displays.
  960.  *
  961.  * On some machines, replacing the "for (i=1; i<=size; ++i)" with
  962.  * i = 1; do { } while (++i <=size)" will make the code quite a
  963.  * bit better; but it looks ugly.
  964.  */
  965. VOID
  966. setscores(offs, size) {
  967.     register SCORE    *sp;
  968.     SCORE        *sp1;
  969.     register int    tempcost;
  970.     register int    bestcost;
  971.     register int    j;
  972.     register int    i;
  973.     register VIDEO    **vp;
  974.     VIDEO        **pp, **vbase, **pbase;
  975.  
  976.     vbase = &vscreen[offs-1];        /* By hand CSE's.    */
  977.     pbase = &pscreen[offs-1];
  978.     score[0].s_itrace = 0;            /* [0, 0]        */
  979.     score[0].s_jtrace = 0;
  980.     score[0].s_cost      = 0;
  981.     sp = &score[1];                /* Row 0, inserts.    */
  982.     tempcost = 0;
  983.     vp = &vbase[1];
  984.     for (j=1; j<=size; ++j) {
  985.         sp->s_itrace = 0;
  986.         sp->s_jtrace = j-1;
  987.         tempcost += tcinsl;
  988.         tempcost += (*vp)->v_cost;
  989.         sp->s_cost = tempcost;
  990.         ++vp;
  991.         ++sp;
  992.     }
  993.     sp = &score[NROW];            /* Column 0, deletes.    */
  994.     tempcost = 0;
  995.     for (i=1; i<=size; ++i) {
  996.         sp->s_itrace = i-1;
  997.         sp->s_jtrace = 0;
  998.         tempcost  += tcdell;
  999.         sp->s_cost = tempcost;
  1000.         sp += NROW;
  1001.     }
  1002.     sp1 = &score[NROW+1];            /* [1, 1].        */
  1003.     pp = &pbase[1];
  1004.     for (i=1; i<=size; ++i) {
  1005.         sp = sp1;
  1006.         vp = &vbase[1];
  1007.         for (j=1; j<=size; ++j) {
  1008.             sp->s_itrace = i-1;
  1009.             sp->s_jtrace = j;
  1010.             bestcost = (sp-NROW)->s_cost;
  1011.             if (j != size)        /* Cd(A[i])=0 @ Dis.    */
  1012.                 bestcost += tcdell;
  1013.             tempcost = (sp-1)->s_cost;
  1014.             tempcost += (*vp)->v_cost;
  1015.             if (i != size)        /* Ci(B[j])=0 @ Dsj.    */
  1016.                 tempcost += tcinsl;
  1017.             if (tempcost < bestcost) {
  1018.                 sp->s_itrace = i;
  1019.                 sp->s_jtrace = j-1;
  1020.                 bestcost = tempcost;
  1021.             }
  1022.             tempcost = (sp-NROW-1)->s_cost;
  1023.             if ((*pp)->v_color != (*vp)->v_color
  1024.             ||  (*pp)->v_hash  != (*vp)->v_hash)
  1025.                 tempcost += (*vp)->v_cost;
  1026.             if (tempcost < bestcost) {
  1027.                 sp->s_itrace = i-1;
  1028.                 sp->s_jtrace = j-1;
  1029.                 bestcost = tempcost;
  1030.             }
  1031.             sp->s_cost = bestcost;
  1032.             ++sp;            /* Next column.        */
  1033.             ++vp;
  1034.         }
  1035.         ++pp;
  1036.         sp1 += NROW;            /* Next row.        */
  1037.     }
  1038. }
  1039.  
  1040. /*
  1041.  * Trace back through the dynamic programming cost
  1042.  * matrix, and update the screen using an optimal sequence
  1043.  * of redraws, insert lines, and delete lines. The "offs" is
  1044.  * the origin 0 offset of the chunk of the screen we are about to
  1045.  * update. The "i" and "j" are always started in the lower right
  1046.  * corner of the matrix, and imply the size of the screen.
  1047.  * A full screen traceback is called with offs=0 and i=j=nrow-1.
  1048.  * There is some do-it-yourself double subscripting here,
  1049.  * which is acceptable because this routine is much less compute
  1050.  * intensive then the code that builds the score matrix!
  1051.  */
  1052. VOID traceback(offs, size, i, j) {
  1053.     register int    itrace;
  1054.     register int    jtrace;
  1055.     register int    k;
  1056.     register int    ninsl;
  1057.     register int    ndraw;
  1058.     register int    ndell;
  1059.  
  1060.     if (i==0 && j==0)            /* End of update.    */
  1061.         return;
  1062.     itrace = score[(NROW*i) + j].s_itrace;
  1063.     jtrace = score[(NROW*i) + j].s_jtrace;
  1064.     if (itrace == i) {            /* [i, j-1]        */
  1065.         ninsl = 0;            /* Collect inserts.    */
  1066.         if (i != size)
  1067.             ninsl = 1;
  1068.         ndraw = 1;
  1069.         while (itrace!=0 || jtrace!=0) {
  1070.             if (score[(NROW*itrace) + jtrace].s_itrace != itrace)
  1071.                 break;
  1072.             jtrace = score[(NROW*itrace) + jtrace].s_jtrace;
  1073.             if (i != size)
  1074.                 ++ninsl;
  1075.             ++ndraw;
  1076.         }
  1077.         traceback(offs, size, itrace, jtrace);
  1078.         if (ninsl != 0) {
  1079.             ttcolor(CTEXT);
  1080.             ttinsl(offs+j-ninsl, offs+size-1, ninsl);
  1081.         }
  1082.         do {                /* B[j], A[j] blank.    */
  1083.             k = offs+j-ndraw;
  1084.             uline(k, vscreen[k], &blanks);
  1085.         } while (--ndraw);
  1086.         return;
  1087.     }
  1088.     if (jtrace == j) {            /* [i-1, j]        */
  1089.         ndell = 0;            /* Collect deletes.    */
  1090.         if (j != size)
  1091.             ndell = 1;
  1092.         while (itrace!=0 || jtrace!=0) {
  1093.             if (score[(NROW*itrace) + jtrace].s_jtrace != jtrace)
  1094.                 break;
  1095.             itrace = score[(NROW*itrace) + jtrace].s_itrace;
  1096.             if (j != size)
  1097.                 ++ndell;
  1098.         }
  1099.         if (ndell != 0) {
  1100.             ttcolor(CTEXT);
  1101.             ttdell(offs+i-ndell, offs+size-1, ndell);
  1102.         }
  1103.         traceback(offs, size, itrace, jtrace);
  1104.         return;
  1105.     }
  1106.     traceback(offs, size, itrace, jtrace);
  1107.     k = offs+j-1;
  1108.     uline(k, vscreen[k], pscreen[offs+i-1]);
  1109. }
  1110. #endif
  1111. SHAR_EOF
  1112. cat << \SHAR_EOF > extend.c
  1113. /*
  1114.  *    Extended (M-X) commands, rebinding, and 
  1115.  *    startup file processing.
  1116.  */
  1117. #include    "def.h"
  1118. #include    "kbd.h"
  1119.  
  1120. #ifndef NO_MACRO
  1121. #include    "macro.h"
  1122. #endif
  1123.  
  1124. #ifdef    FKEYS
  1125. #include    "key.h"
  1126. #ifndef    NO_STARTUP
  1127. #ifndef    BINDKEY
  1128. #define    BINDKEY        /* bindkey is used by FKEYS startup code */
  1129. #endif
  1130. #endif
  1131. #endif
  1132.  
  1133. extern    char    *strncpy();
  1134. extern    int rescan();
  1135.  
  1136. /* insert a string, mainly for use from macros (created by selfinsert) */
  1137. /*ARGSUSED*/
  1138. insert(f, n)
  1139. int f, n;
  1140. {
  1141.     register char *cp;
  1142.     char buf[128];
  1143. #ifndef NO_MACRO
  1144.     register int count;
  1145.     int c;
  1146.  
  1147.     if(inmacro) {
  1148.     while(--n >= 0) {
  1149.         for(count = 0; count < maclcur->l_used; count++) {
  1150.         if((((c=maclcur->l_text[count]) == '\n') ? lnewline()
  1151.             : linsert(1, c)) != TRUE) return FALSE;
  1152.         }
  1153.     }
  1154.     maclcur = maclcur->l_fp;
  1155.     return TRUE;
  1156.     }
  1157.     if(n==1) thisflag |= CFINS; /* CFINS means selfinsert can tack on end */
  1158. #endif
  1159.     if(eread("Insert: ", buf, sizeof(buf), EFNEW) == FALSE) return FALSE;
  1160.     while(--n >= 0) {
  1161.     cp = buf;
  1162.     while(*cp) {
  1163.         if(((*cp == '\n') ? lnewline() : linsert(1, *cp)) != TRUE)
  1164.         return FALSE;
  1165.         cp++;
  1166.     }
  1167.     }
  1168.     return TRUE;
  1169. }
  1170.  
  1171. /*
  1172.  * Bind a key to a function.  Cases range from the trivial (replacing an
  1173.  * existing binding) to the extremly complex (creating a new prefix in a
  1174.  * map_element that already has one, so the map_element must be split,
  1175.  * but the keymap doesn't have enough room for another map_element, so
  1176.  * the keymap is reallocated).    No attempt is made to reclaim space no
  1177.  * longer used, if this is a problem flags must be added to indicate
  1178.  * malloced verses static storage in both keymaps and map_elements.
  1179.  * Structure assignments would come in real handy, but K&R based compilers
  1180.  * don't have them.  Care is taken so running out of memory will leave
  1181.  * the keymap in a usable state.
  1182.  */
  1183. static int remap(curmap, c, funct, pref_map)
  1184. register KEYMAP    *curmap;/* pointer to the map being changed */
  1185. int    c;        /* character being changed */
  1186. PF    funct;        /* function being changed to */
  1187. KEYMAP    *pref_map;    /* if funct==prefix, map to bind to or NULL for new */
  1188. /* extern MAP_ELEMENT *ele;    must be set before calling */
  1189. {
  1190.     register int i;
  1191.     int    n1, n2, nold;
  1192.     KEYMAP    *mp;
  1193.     PF    *pfp;
  1194.     MAP_ELEMENT *mep;
  1195.     static    KEYMAP *realocmap();
  1196.  
  1197.     if(ele >= &curmap->map_element[curmap->map_num] || c < ele->k_base) {
  1198.         if(ele > &curmap->map_element[0] && (funct!=prefix ||
  1199.             (ele-1)->k_prefmap==NULL)) {
  1200.         n1 = c - (ele-1)->k_num;
  1201.         } else n1 = HUGE;
  1202.         if(ele < &curmap->map_element[curmap->map_num] && (funct!=prefix ||
  1203.             ele->k_prefmap==NULL)) {
  1204.         n2 = ele->k_base - c;
  1205.         } else n2 = HUGE;
  1206.         if(n1 <= MAPELEDEF && n1 <= n2) {
  1207.         ele--;
  1208.         if((pfp = (PF *)malloc((unsigned)(c - ele->k_base+1) 
  1209.             * sizeof(PF))) == NULL) {
  1210.             ewprintf("Out of memory");
  1211.             return FALSE;
  1212.         }
  1213.         nold = ele->k_num - ele->k_base + 1;
  1214.         for(i=0; i < nold; i++)
  1215.             pfp[i] = ele->k_funcp[i];
  1216.         while(--n1) pfp[i++] = curmap->map_default;
  1217.         pfp[i] = funct;
  1218.         ele->k_num = c;
  1219.         ele->k_funcp = pfp;
  1220.         } else if(n2 <= MAPELEDEF) {
  1221.         if((pfp = (PF *)malloc((unsigned)(ele->k_num - c + 1) 
  1222.             * sizeof(PF))) == NULL) {
  1223.             ewprintf("Out of memory");
  1224.             return FALSE;
  1225.         }
  1226.         nold = ele->k_num - ele->k_base + 1;
  1227.         for(i=0; i < nold; i++)
  1228.             pfp[i+n2] = ele->k_funcp[i];
  1229.         while(--n2) pfp[n2] = curmap->map_default;
  1230.         pfp[0] = funct;
  1231.         ele->k_base = c;
  1232.         ele->k_funcp = pfp;
  1233.         } else {
  1234.         if(curmap->map_num >= curmap->map_max &&
  1235.             (curmap = realocmap(curmap)) == NULL) return FALSE;
  1236.         if((pfp = (PF *)malloc(sizeof(PF))) == NULL) {
  1237.             ewprintf("Out of memory");
  1238.             return FALSE;
  1239.         }
  1240.         pfp[0] = funct;
  1241.         for(mep = &curmap->map_element[curmap->map_num]; mep > ele; mep--) {
  1242.             mep->k_base    = (mep-1)->k_base;
  1243.             mep->k_num     = (mep-1)->k_num;
  1244.             mep->k_funcp   = (mep-1)->k_funcp;
  1245.             mep->k_prefmap = (mep-1)->k_prefmap;
  1246.         }
  1247.         ele->k_base = c;
  1248.         ele->k_num = c;
  1249.         ele->k_funcp = pfp;
  1250.         ele->k_prefmap = NULL;
  1251.         curmap->map_num++;
  1252.         }
  1253.         if(funct == prefix) {
  1254.         if(pref_map != NULL) {
  1255.             ele->k_prefmap = pref_map;
  1256.         } else {
  1257.             if((mp = (KEYMAP *)malloc(sizeof(KEYMAP) +
  1258.                 (MAPINIT-1)*sizeof(MAP_ELEMENT))) == NULL) {
  1259.             ewprintf("Out of memory");
  1260.             ele->k_funcp[c - ele->k_base] = curmap->map_default;
  1261.             return FALSE;
  1262.             }
  1263.             mp->map_num = 0;
  1264.             mp->map_max = MAPINIT;
  1265.             mp->map_default = rescan;
  1266.             ele->k_prefmap = mp;
  1267.         }
  1268.         }
  1269.     } else {
  1270.         n1 = c - ele->k_base;
  1271.         if(ele->k_funcp[n1] == funct && (funct!=prefix || pref_map==NULL ||
  1272.             pref_map==ele->k_prefmap))
  1273.         return TRUE;    /* no change */
  1274.         if(funct!=prefix || ele->k_prefmap==NULL) {
  1275.         if(ele->k_funcp[n1] == prefix)
  1276.             ele->k_prefmap = (KEYMAP *)NULL;
  1277.         ele->k_funcp[n1] = funct;    /* easy case */
  1278.         if(funct==prefix) {
  1279.             if(pref_map!=NULL)
  1280.             ele->k_prefmap = pref_map;
  1281.             else {
  1282.             if((mp = (KEYMAP *)malloc(sizeof(KEYMAP) +
  1283.                 (MAPINIT-1)*sizeof(MAP_ELEMENT))) == NULL) {
  1284.                 ewprintf("Out of memory");
  1285.                 ele->k_funcp[c - ele->k_base] = curmap->map_default;
  1286.                 return FALSE;
  1287.             }
  1288.             mp->map_num = 0;
  1289.             mp->map_max = MAPINIT;
  1290.             mp->map_default = rescan;
  1291.             ele->k_prefmap = mp;
  1292.             }
  1293.         }
  1294.         } else {
  1295.         /* this case is the splits */
  1296.         /* determine which side of the break c goes on */
  1297.         /* 0 = after break; 1 = before break */
  1298.         n2 = 1;
  1299.         for(i=0; n2 && i < n1; i++)
  1300.             n2 &= ele->k_funcp[i] != prefix;
  1301.         if(curmap->map_num >= curmap->map_max &&
  1302.             (curmap = realocmap(curmap)) == NULL) return FALSE;
  1303.         if((pfp = (PF *)malloc((unsigned)(ele->k_num - c + !n2) 
  1304.             * sizeof(PF))) == NULL) {
  1305.             ewprintf("Out of memory");
  1306.             return FALSE;
  1307.         }
  1308.         ele->k_funcp[n1] = prefix;
  1309.         for(i=n1+n2; i <= ele->k_num - ele->k_base; i++)
  1310.             pfp[i-n1-n2] = ele->k_funcp[i];
  1311.         for(mep = &curmap->map_element[curmap->map_num]; mep > ele; mep--) {
  1312.             mep->k_base    = (mep-1)->k_base;
  1313.             mep->k_num     = (mep-1)->k_num;
  1314.             mep->k_funcp   = (mep-1)->k_funcp;
  1315.             mep->k_prefmap = (mep-1)->k_prefmap;
  1316.         }
  1317.         ele->k_num = c - !n2;
  1318.         (ele+1)->k_base = c + n2;
  1319.         (ele+1)->k_funcp = pfp;
  1320.         ele += !n2;
  1321.         ele->k_prefmap = NULL;
  1322.         curmap->map_num++;
  1323.         if(pref_map == NULL) {
  1324.             if((mp = (KEYMAP *)malloc(sizeof(KEYMAP) +
  1325.                 (MAPINIT-1)*sizeof(MAP_ELEMENT))) == NULL) {
  1326.             ewprintf("Out of memory");
  1327.             ele->k_funcp[c - ele->k_base] = curmap->map_default;
  1328.             return FALSE;
  1329.             }
  1330.             mp->map_num = 0;
  1331.             mp->map_max = MAPINIT;
  1332.             mp->map_default = rescan;
  1333.             ele->k_prefmap = mp;
  1334.         } else ele->k_prefmap = pref_map;
  1335.         }
  1336.     }
  1337.     return TRUE;
  1338. }
  1339.  
  1340. /* reallocate a keymap, used above */
  1341. static KEYMAP *realocmap(curmap)
  1342. register KEYMAP *curmap;
  1343. {
  1344.     register KEYMAP *mp;
  1345.     register int i;
  1346.     static VOID fixmap();
  1347.     extern int nmaps;
  1348.  
  1349.     if((mp = (KEYMAP *)malloc((unsigned)(sizeof(KEYMAP)+
  1350.         (curmap->map_max+(MAPGROW-1))*sizeof(MAP_ELEMENT)))) == NULL) {
  1351.     ewprintf("Out of memory");
  1352.     return NULL;
  1353.     }
  1354.     mp->map_num = curmap->map_num;
  1355.     mp->map_max = curmap->map_max + MAPGROW;
  1356.     mp->map_default = curmap->map_default;
  1357.     for(i=curmap->map_num; i--; ) {
  1358.     mp->map_element[i].k_base    = curmap->map_element[i].k_base;
  1359.     mp->map_element[i].k_num    = curmap->map_element[i].k_num;
  1360.     mp->map_element[i].k_funcp    = curmap->map_element[i].k_funcp;
  1361.     mp->map_element[i].k_prefmap    = curmap->map_element[i].k_prefmap;
  1362.     }
  1363.     for(i=nmaps; i--; ) {
  1364.     if(map_table[i].p_map == curmap) map_table[i].p_map = mp;
  1365.     else fixmap(curmap, mp, map_table[i].p_map);
  1366.     }
  1367.     ele = &mp->map_element[ele - &curmap->map_element[0]];
  1368.     return mp;
  1369. }
  1370.  
  1371. /* fix references to a reallocated keymap (recursive) */
  1372. static VOID fixmap(curmap, mp, mt)
  1373. register KEYMAP *mt;
  1374. register KEYMAP *curmap;
  1375. KEYMAP *mp;
  1376. {
  1377.     register int i;
  1378.  
  1379.     for(i = mt->map_num; i--; ) {
  1380.     if(mt->map_element[i].k_prefmap != NULL) {
  1381.         if(mt->map_element[i].k_prefmap == curmap)
  1382.             mt->map_element[i].k_prefmap = mp;
  1383.         else fixmap(curmap, mp, mt->map_element[i].k_prefmap);
  1384.     }
  1385.     }
  1386. }
  1387.  
  1388. /*
  1389.  * do the input for local-set-key, global-set-key  and define-key
  1390.  * then call remap to do the work.
  1391.  */
  1392.  
  1393. static int dobind(curmap, p, unbind)
  1394. register KEYMAP *curmap;
  1395. char *p;
  1396. int unbind;
  1397. {
  1398.     PF    funct;
  1399.     char    prompt[80];
  1400.     char    *pep;
  1401.     int    c;
  1402.     int    s;
  1403.     KEYMAP    *pref_map = NULL;
  1404.  
  1405. #ifndef NO_MACRO
  1406.     if(macrodef) {
  1407.     /* keystrokes arn't collected.    Not hard, but pretty useless */
  1408.     /* would not work for function keys in any case */
  1409.         ewprintf("Can't rebind key in macro");
  1410.         return FALSE;
  1411.     }
  1412. #ifndef NO_STARTUP
  1413.     if(inmacro) {
  1414.         for(s=0; s < maclcur->l_used - 1; s++) {
  1415.         if(doscan(curmap, c=CHARMASK(maclcur->l_text[s])) != prefix) {
  1416.             if(remap(curmap, c, prefix, (KEYMAP *)NULL) != TRUE) {
  1417.             return FALSE;
  1418.             }
  1419.         }
  1420.         curmap = ele->k_prefmap;
  1421.         }
  1422.         (VOID) doscan(curmap, c=maclcur->l_text[s]);
  1423.         maclcur = maclcur->l_fp;
  1424.     } else {
  1425. #endif
  1426. #endif
  1427.         (VOID) strcpy(prompt, p);
  1428.         pep = prompt + strlen(prompt);
  1429.         for(;;) {
  1430.         ewprintf("%s", prompt);
  1431.         pep[-1] = ' ';
  1432.         pep = keyname(pep, c = getkey(FALSE));
  1433.         if(doscan(curmap,c) != prefix) break;
  1434.         *pep++ = '-';
  1435.         *pep = '\0';
  1436.         curmap = ele->k_prefmap;
  1437.         }
  1438. #ifndef NO_STARTUP
  1439.     }
  1440. #endif
  1441.     if(unbind) funct = rescan;
  1442.     else {
  1443.         if ((s=eread("%s to command: ", prompt, 80, EFFUNC|EFNEW, prompt))
  1444.             != TRUE) return s;
  1445.         if (((funct = name_function(prompt)) == prefix) ?
  1446.                 (pref_map = name_map(prompt)) == NULL : funct==NULL) {
  1447.         ewprintf("[No match]");
  1448.         return FALSE;
  1449.         }
  1450.     }
  1451.     return remap(curmap, c, funct, pref_map);
  1452. }
  1453.  
  1454. /*
  1455.  * bindkey: bind key sequence to a function in
  1456.  * the specified map.  Used by excline so it can bind function keys.
  1457.  * To close to release to change calling sequence, should just pass
  1458.  * KEYMAP *curmap rather than KEYMAP **mapp.
  1459. */
  1460. #ifdef    BINDKEY
  1461. bindkey(mapp, fname, keys, kcount)
  1462. KEYMAP **mapp;
  1463. char *fname;
  1464. KCHAR *keys;
  1465. int kcount;
  1466. {
  1467.     KEYMAP    *curmap = *mapp;
  1468.     PF    funct;
  1469.     int    c;
  1470.     KEYMAP    *pref_map = NULL;
  1471.  
  1472.     if(fname == NULL) funct = rescan;
  1473.     else if (((funct = name_function(fname)) == prefix) ?
  1474.         (pref_map = name_map(fname)) == NULL : funct==NULL) {
  1475.         ewprintf("[No match: %s]", fname);
  1476.         return FALSE;
  1477.     }
  1478.     while(--kcount) {
  1479.         if(doscan(curmap, c = *keys++) != prefix) {
  1480.         if(remap(curmap, c, prefix, (KEYMAP *)NULL) != TRUE)
  1481.             return FALSE;
  1482.         }
  1483.         curmap = ele->k_prefmap;
  1484.     }
  1485.     (VOID) doscan(curmap, c = *keys);
  1486.     return remap(curmap, c, funct, pref_map);
  1487. }
  1488. #endif
  1489.  
  1490. /*
  1491.  * This function modifies the fundamental keyboard map.
  1492.  */
  1493. /*ARGSUSED*/
  1494. bindtokey(f, n)
  1495. {
  1496.     return dobind(map_table[0].p_map, "Global set key: ", FALSE);
  1497. }
  1498.  
  1499. /*
  1500.  * This function modifies the current mode's keyboard map.
  1501.  */
  1502. /*ARGSUSED*/
  1503. localbind(f, n)
  1504. {
  1505.     return dobind(curbp->b_modes[curbp->b_nmodes]->p_map, "Local set key: ",
  1506.     FALSE);
  1507. }
  1508.  
  1509. /*
  1510.  * This function redefines a key in any keymap.
  1511.  */
  1512. /*ARGSUSED*/
  1513. define_key(f, n)
  1514. {
  1515.     static char buf[48] = "Define key map: ";
  1516.     MAPS *mp;
  1517.     char *strncat();
  1518.  
  1519.     buf[16] = '\0';
  1520.     if(eread(buf, &buf[16], 48 - 16, EFNEW) != TRUE) return FALSE;
  1521.     if((mp = name_mode(&buf[16])) == NULL) {
  1522.     ewprintf("Unknown map %s", &buf[16]);
  1523.     return FALSE;
  1524.     }
  1525.     (VOID) strncat(&buf[16], " key: ", 48-16-1);
  1526.     return dobind(mp->p_map, buf, FALSE);
  1527. }
  1528.  
  1529. unbindtokey(f, n)
  1530. int f, n;
  1531. {
  1532.     return dobind(map_table[0].p_map, "Global unset key: ", TRUE);
  1533. }
  1534.  
  1535. localunbind(f, n)
  1536. int f, n;
  1537. {
  1538.     return dobind(curbp->b_modes[curbp->b_nmodes]->p_map, "Local unset key: ",
  1539.     TRUE);
  1540. }
  1541.  
  1542. /*
  1543.  * Extended command. Call the message line
  1544.  * routine to read in the command name and apply autocompletion
  1545.  * to it. When it comes back, look the name up in the symbol table
  1546.  * and run the command if it is found.
  1547.  * Print an error if there is anything wrong.
  1548.  */
  1549. extend(f, n)
  1550. {
  1551.     PF    funct;
  1552.     int    s;
  1553.     char    xname[NXNAME];
  1554.  
  1555.     if(!(f & FFARG)) s = eread("M-x ", xname, NXNAME, EFNEW|EFFUNC);
  1556.     else         s = eread("%d M-x ", xname, NXNAME, EFNEW|EFFUNC, n);
  1557.     if(s != TRUE) return s;
  1558.     if((funct = name_function(xname)) != NULL) {
  1559. #ifndef NO_MACRO
  1560.         if(macrodef) {
  1561.         LINE *lp = maclcur;
  1562.         macro[macrocount-1].m_funct = funct;
  1563.         maclcur = lp->l_bp;
  1564.         maclcur->l_fp = lp->l_fp;
  1565.         free((char *)lp);
  1566.         }
  1567. #endif
  1568.         return (*funct)(f, n);
  1569.     }
  1570.     ewprintf("[No match]");
  1571.     return FALSE;
  1572. }
  1573.  
  1574. #ifndef NO_STARTUP
  1575. /*
  1576.  * Define the commands needed to do startup-file processing.
  1577.  * This code is mostly a kludge just so we can get startup-file processing.
  1578.  *
  1579.  * If you're serious about having this code, you should rewrite it.
  1580.  * To wit:
  1581.  *    It has lots of funny things in it to make the startup-file look
  1582.  *    like a GNU startup file; mostly dealing with parens and semicolons.
  1583.  *    This should all vanish.
  1584.  *
  1585.  * We define eval-expression because it's easy.     It can make
  1586.  * *-set-key or define-key set an arbitrary key sequence, so it isn't
  1587.  * useless.
  1588.  */
  1589.  
  1590. /*
  1591.  * evalexpr - get one line from the user, and run it.
  1592.  */
  1593. /*ARGSUSED*/
  1594. evalexpr(f, n)
  1595. {
  1596.     int    s;
  1597.     char    exbuf[128];
  1598.  
  1599.     if ((s = ereply("Eval: ", exbuf, 128)) != TRUE)
  1600.         return s;
  1601.     return excline(exbuf);
  1602. }
  1603. /*
  1604.  * evalbuffer - evaluate the current buffer as line commands. Useful
  1605.  *    for testing startup files.
  1606.  */
  1607. /*ARGSUSED*/
  1608. evalbuffer(f, n)
  1609. {
  1610.     register LINE    *lp;
  1611.     register BUFFER *bp = curbp;
  1612.     register int    s;
  1613.     static char    excbuf[128];
  1614.  
  1615.     for (lp = lforw(bp->b_linep); lp != bp->b_linep; lp = lforw(lp)) {
  1616.         if (llength(lp) >= 128) return FALSE;
  1617.         (VOID) strncpy(excbuf, ltext(lp), llength(lp));
  1618.         excbuf[llength(lp)] = '\0';    /* make sure it's terminated */
  1619.         if ((s = excline(excbuf)) != TRUE) return s;
  1620.     }
  1621.     return TRUE;
  1622. }
  1623. /*
  1624.  * evalfile - go get a file and evaluate it as line commands. You can
  1625.  *    go get your own startup file if need be.
  1626.  */
  1627. /*ARGSUSED*/
  1628. evalfile(f, n)
  1629. {
  1630.     register int    s;
  1631.     char        fname[NFILEN];
  1632.  
  1633.     if ((s = ereply("Load file: ", fname, NFILEN)) != TRUE)
  1634.         return s;
  1635.     return load(fname);
  1636. }
  1637.  
  1638. /*
  1639.  * load - go load the file name we got passed.
  1640.  */
  1641. load(fname) char *fname; {
  1642.     int    s = TRUE;
  1643.     int    nbytes;
  1644.     char    excbuf[128];
  1645.  
  1646.     if ((fname = adjustname(fname)) == NULL)
  1647.         return FALSE;    /* just to be careful */
  1648.  
  1649.     if (ffropen(fname) != FIOSUC) return FALSE;
  1650.     while ((s = ffgetline(excbuf, sizeof(excbuf)-1, &nbytes)) == FIOSUC) {
  1651.         excbuf[nbytes] = '\0';
  1652.         if (excline(excbuf) != TRUE) {
  1653.             s = FIOERR;
  1654.             ewprintf("Error loading file %s", fname);
  1655.             break;
  1656.         }
  1657.     }
  1658.     (VOID) ffclose();
  1659.     excbuf[nbytes] = '\0';
  1660.     if(s!=FIOEOF || (nbytes && excline(excbuf)!=TRUE))
  1661.         return FALSE;
  1662.     return TRUE;
  1663. }
  1664.  
  1665. /*
  1666.  * excline - run a line from a load file or eval-expression.
  1667.  * if FKEYS is defined, duplicate functionallity of dobind so function
  1668.  * key values don't have to fit in type char.
  1669.  */
  1670. excline(line)
  1671. register char *line;
  1672. {
  1673.     register char    *funcp, *argp = NULL;
  1674.     register int    c;
  1675.     int        status;
  1676.     int    f, n;
  1677.     LINE    *lp, *np;
  1678.     PF    fp;
  1679. #ifdef    FKEYS
  1680.     int    bind;
  1681.     KEYMAP    *curmap;
  1682.     MAPS    *mp;
  1683. #define BINDARG        0    /* this arg is key to bind (local/global set key) */
  1684. #define    BINDNO        1    /* not binding or non-quoted BINDARG */
  1685. #define BINDNEXT    2    /* next arg " (define-key) */
  1686. #define BINDDO        3    /* already found key to bind */
  1687. #define BINDEXT 1        /* space for trailing \0 */
  1688. #else
  1689. #define BINDEXT 0
  1690. #endif
  1691.     PF    name_function();
  1692.     LINE    *lalloc();
  1693.     static    char    *skipwhite(), *parsetoken();
  1694.  
  1695.     if(macrodef || inmacro) {
  1696.         ewprintf("Not now!");
  1697.         return FALSE;
  1698.     }
  1699.  
  1700.     f = 0;
  1701.     n = 1;
  1702.     funcp = skipwhite(line);
  1703.     if (*funcp == '\0') return TRUE;    /* No error on blank lines */
  1704.     line = parsetoken(funcp);
  1705.     if (*line != '\0') {
  1706.         *line++ = '\0';
  1707.         line = skipwhite(line);
  1708.         if ((*line >= '0' && *line <= '9') || *line == '-') {
  1709.             argp = line;
  1710.             line = parsetoken(line);
  1711.         }
  1712.     }
  1713.  
  1714.     if (argp != NULL) {
  1715.         f = FFARG;
  1716.         n = atoi(argp);
  1717.     }
  1718.     if((fp = name_function(funcp)) == NULL) {
  1719.         ewprintf("Unknown function: %s", funcp);
  1720.         return FALSE;
  1721.     }
  1722. #ifdef    FKEYS
  1723.     if(fp == bindtokey || fp == unbindtokey) {
  1724.         bind = BINDARG;
  1725.         curmap = map_table[0].p_map;
  1726.     } else if(fp == localbind || fp == localunbind) {
  1727.         bind = BINDARG;
  1728.         curmap = curbp->b_modes[curbp->b_nmodes]->p_map;
  1729.     } else if(fp == define_key) bind = BINDNEXT;
  1730.     else bind = BINDNO;
  1731. #endif
  1732.     /* Pack away all the args now...    */
  1733.     if((np = lalloc(0))==FALSE) return FALSE;
  1734.     np->l_fp = np->l_bp = maclcur = np;
  1735.     while (*line != '\0') {
  1736.         argp = skipwhite(line);
  1737.         if (*argp == '\0') break;
  1738.         line = parsetoken(argp);
  1739.         if (*argp != '"') {
  1740.             if (*argp == '\'') ++argp;
  1741.             if((lp = lalloc((int)(line-argp)+BINDEXT))==NULL) {
  1742.             status = FALSE;
  1743.             goto cleanup;
  1744.             }
  1745.             bcopy(argp, ltext(lp), (int)(line-argp));
  1746. #ifdef    FKEYS
  1747.             lp->l_used--;    /* don't count BINDEXT! */
  1748.             if(bind == BINDARG) bind = BINDNO;
  1749. #endif
  1750.         } else {    /* Quoted strings special */
  1751.             ++argp;
  1752. #ifdef    FKEYS
  1753.             if(bind != BINDARG) {
  1754. #endif
  1755.             if((lp = lalloc((int)(line-argp)+BINDEXT))==NULL) {
  1756.                 status = FALSE;
  1757.                 goto cleanup;
  1758.             }
  1759.             lp->l_used = 0;
  1760. #ifdef    FKEYS
  1761.             } else {
  1762.             key.k_count = 0;
  1763.             }
  1764. #endif
  1765.             while (*argp != '"' && *argp != '\0') {
  1766.             if (*argp != '\\') c = *argp++;
  1767.             else {
  1768.                 switch(*++argp) {
  1769.                 case 't': case 'T':
  1770.                     c = CCHR('I');
  1771.                     break;
  1772.                 case 'n': case 'N':
  1773.                     c = CCHR('J');
  1774.                     break;
  1775.                 case 'r': case 'R':
  1776.                     c = CCHR('M');
  1777.                     break;
  1778.                 case 'e': case 'E':
  1779.                     c = CCHR('[');
  1780.                     break;
  1781.                 case '^':
  1782. /* split into two statements due to bug in OSK cpp */
  1783.                     c = CHARMASK(*++argp);
  1784.                     c = ISLOWER(c) ?
  1785.                     CCHR(TOUPPER(c)) : CCHR(c);
  1786.                     break;
  1787.                 case '0': case '1': case '2': case '3':
  1788.                 case '4': case '5': case '6': case '7':
  1789.                     c = *argp - '0';
  1790.                     if(argp[1] <= '7' && argp[1] >= '0') {
  1791.                         c <<= 3;
  1792.                     c += *++argp - '0';
  1793.                     if(argp[1] <= '7' && argp[1] >= '0') {
  1794.                         c <<= 3;
  1795.                         c += *++argp - '0';
  1796.                     }
  1797.                     }
  1798.                     break;
  1799. #ifdef    FKEYS
  1800.                 case 'f': case 'F':
  1801.                     c = *++argp - '0';
  1802.                     if(ISDIGIT(argp[1])) {
  1803.                     c *= 10;
  1804.                     c += *++argp - '0';
  1805.                     }
  1806.                     c += KFIRST;
  1807.                     break;
  1808. #endif
  1809.                 default:
  1810.                     c = CHARMASK(*argp);
  1811.                     break;
  1812.                 }
  1813.                 argp++;
  1814.             }
  1815. #ifdef    FKEYS
  1816.             if(bind == BINDARG)
  1817.                 key.k_chars[key.k_count++] = c;
  1818.             else
  1819. #endif
  1820.                 lp->l_text[lp->l_used++] = c;
  1821.             }
  1822.             if(*line) line++;
  1823.         }
  1824. #ifdef    FKEYS
  1825.         switch(bind) {
  1826.             case BINDARG:
  1827.             bind = BINDDO;
  1828.             break;
  1829.             case BINDNEXT:
  1830.             lp->l_text[lp->l_used] = '\0';
  1831.             if((mp = name_mode(lp->l_text)) == NULL) {
  1832.                 ewprintf("No such mode: %s", lp->l_text);
  1833.                 status = FALSE;
  1834.                 free((char *)lp);
  1835.                 goto cleanup;
  1836.             }
  1837.             curmap = mp->p_map;
  1838.             free((char *)lp);
  1839.             bind = BINDARG;
  1840.             break;
  1841.             default:
  1842. #endif
  1843.             lp->l_fp = np->l_fp;
  1844.             lp->l_bp = np;
  1845.             np->l_fp = lp;
  1846.             np = lp;
  1847. #ifdef    FKEYS
  1848.         }
  1849. #endif
  1850.     }
  1851. #ifdef    FKEYS
  1852.     switch(bind) {
  1853.         default:
  1854.         ewprintf("Bad args to set key");
  1855.         status = FALSE;
  1856.         break;
  1857.         case BINDDO:
  1858.             if(fp != unbindtokey && fp != localunbind) {
  1859.             lp->l_text[lp->l_used] = '\0';
  1860.             status = bindkey(&curmap, lp->l_text, key.k_chars, key.k_count);
  1861.         } else status = bindkey(&curmap, (char *)NULL, key.k_chars, key.k_count);
  1862.         break;
  1863.         case BINDNO:
  1864. #endif
  1865.         inmacro = TRUE;
  1866.         maclcur = maclcur->l_fp;
  1867.         status = (*fp)(f, n);
  1868.         inmacro = FALSE;
  1869. #ifdef    FKEYS
  1870.     }
  1871. #endif
  1872. cleanup:
  1873.     lp = maclcur->l_fp;
  1874.     while(lp!=maclcur) {
  1875.         np = lp->l_fp;
  1876.         free((char *)lp);
  1877.         lp = np;
  1878.     }
  1879.     free((char *)lp);
  1880.     return status;
  1881. }
  1882.  
  1883. /*
  1884.  * a pair of utility functions for the above
  1885.  */
  1886. static char *
  1887. skipwhite(s)
  1888. register char *s;
  1889. {
  1890.     while(*s == ' ' || *s == '\t' || *s == ')' || *s == '(') s++;
  1891.     if (*s == ';') *s = '\0' ;
  1892.     return s;
  1893. }
  1894.  
  1895. static char *
  1896. parsetoken(s)
  1897. register char *s;
  1898. {
  1899.     if (*s != '"') {
  1900.         while(*s && *s!=' ' && *s!='\t' && *s!=')' && *s!='(') s++;
  1901.         if(*s==';') *s='\0';
  1902.     } else
  1903.         do {    /* Strings get special treatment */
  1904.             /* Beware: You can \ out the end of the string! */
  1905.         if (*s == '\\') ++s;
  1906.         } while (*++s != '"' && *s != '\0');
  1907.     return s;
  1908. }
  1909. #endif
  1910. SHAR_EOF
  1911. cat << \SHAR_EOF > file.c
  1912. /*
  1913.  *        File commands.
  1914.  */
  1915. #include    "def.h"
  1916.  
  1917. BUFFER    *findbuffer();
  1918. VOID    makename();
  1919. VOID    upmodes();
  1920. static    char *itos();
  1921.  
  1922. /*
  1923.  * insert a file into the current buffer. Real easy - just call the
  1924.  * insertfile routine with the file name.
  1925.  */
  1926. /*ARGSUSED*/
  1927. fileinsert(f, n)
  1928. {
  1929.     register int    s;
  1930.     char        fname[NFILEN];
  1931.  
  1932.     if ((s=ereply("Insert file: ", fname, NFILEN)) != TRUE)
  1933.         return (s);
  1934.     return insertfile(adjustname(fname), (char *) NULL);
  1935.                         /* don't set buffer name */
  1936. }
  1937.  
  1938. /*
  1939.  * Select a file for editing.
  1940.  * Look around to see if you can find the
  1941.  * fine in another buffer; if you can find it
  1942.  * just switch to the buffer. If you cannot find
  1943.  * the file, create a new buffer, read in the
  1944.  * text, and switch to the new buffer.
  1945.  */
  1946. /*ARGSUSED*/
  1947. filevisit(f, n)
  1948. {
  1949.     register BUFFER *bp;
  1950.     int    s;
  1951.     char    fname[NFILEN];
  1952.     char    *adjf;
  1953.  
  1954.     if ((s=ereply("Find file: ", fname, NFILEN)) != TRUE)
  1955.         return s;
  1956.     adjf = adjustname(fname);
  1957.     if ((bp = findbuffer(adjf)) == NULL) return FALSE;
  1958.     curbp = bp;
  1959.     if (showbuffer(bp, curwp, WFHARD) != TRUE) return FALSE;
  1960.     if (bp->b_fname[0] == 0)
  1961.         return readin(adjf);        /* Read it in.        */
  1962.     return TRUE;
  1963. }
  1964.  
  1965. /*
  1966.  * Pop to a file in the other window. Same as last function, just
  1967.  * popbuf instead of showbuffer.
  1968.  */
  1969. /*ARGSUSED*/
  1970. poptofile(f, n)
  1971. {
  1972.     register BUFFER *bp;
  1973.     register WINDOW *wp;
  1974.     int    s;
  1975.     char    fname[NFILEN];
  1976.     char    *adjf;
  1977.  
  1978.     if ((s=ereply("Find file in other window: ", fname, NFILEN)) != TRUE)
  1979.         return s;
  1980.     adjf = adjustname(fname);
  1981.     if ((bp = findbuffer(adjf)) == NULL) return FALSE;
  1982.     if ((wp = popbuf(bp)) == NULL) return FALSE;
  1983.     curbp = bp;
  1984.     curwp = wp;
  1985.     if (bp->b_fname[0] == 0)
  1986.         return readin(adjf);        /* Read it in.        */
  1987.     return TRUE;
  1988. }
  1989.  
  1990. /*
  1991.  * given a file name, either find the buffer it uses, or create a new
  1992.  * empty buffer to put it in.
  1993.  */
  1994. BUFFER *
  1995. findbuffer(fname)
  1996. char *fname;
  1997. {
  1998.     register BUFFER *bp;
  1999.     char    bname[NBUFN], *cp;
  2000.     unsigned count = 1;
  2001.  
  2002.     for (bp=bheadp; bp!=NULL; bp=bp->b_bufp) {
  2003.         if (fncmp(bp->b_fname, fname) == 0)
  2004.             return bp;
  2005.     }
  2006.     makename(bname, fname);            /* New buffer name.    */
  2007.     cp = bname + strlen(bname);
  2008.     while(bfind(bname, FALSE) != NULL) {
  2009.         *cp = '<';        /* add "<count>" to then name    */
  2010.         (VOID) strcpy(itos(cp, ++count)+1, ">");
  2011.     }
  2012.     return bfind(bname, TRUE);
  2013. }
  2014.  
  2015. /*
  2016.  * Put the decimal representation of num into a buffer.  Hacked to be
  2017.  * faster, smaller, and less general.
  2018.  */
  2019. static char *itos(bufp, num)
  2020. char *bufp;
  2021. unsigned num;
  2022. {
  2023.     if (num >= 10) {
  2024.         bufp = itos(bufp, num/10);
  2025.         num %= 10;
  2026.     }
  2027.     *++bufp = '0' + num;
  2028.     return bufp;
  2029. }
  2030.  
  2031. /*
  2032.  * Read the file "fname" into the current buffer.
  2033.  * Make all of the text in the buffer go away, after checking
  2034.  * for unsaved changes. This is called by the "read" command, the
  2035.  * "visit" command, and the mainline (for "uemacs file").
  2036.  */
  2037. readin(fname) char *fname; {
  2038.     register int        status;
  2039.     register WINDOW        *wp;
  2040.  
  2041.     if (bclear(curbp) != TRUE)        /* Might be old.    */
  2042.         return TRUE;
  2043.     status = insertfile(fname, fname) ;
  2044.     curbp->b_flag &= ~BFCHG;        /* No change.        */
  2045.     for (wp=wheadp; wp!=NULL; wp=wp->w_wndp) {
  2046.         if (wp->w_bufp == curbp) {
  2047.             wp->w_dotp  = wp->w_linep = lforw(curbp->b_linep);
  2048.             wp->w_doto  = 0;
  2049.             wp->w_markp = NULL;
  2050.             wp->w_marko = 0;
  2051.         }
  2052.     }
  2053.     return status;
  2054. }
  2055. /*
  2056.  * insert a file in the current buffer, after dot. Set mark
  2057.  * at the end of the text inserted, point at the beginning.
  2058.  * Return a standard status. Print a summary (lines read,
  2059.  * error message) out as well. If the
  2060.  * BACKUP conditional is set, then this routine also does the read
  2061.  * end of backup processing. The BFBAK flag, if set in a buffer,
  2062.  * says that a backup should be taken. It is set when a file is
  2063.  * read in, but not on a new file (you don't need to make a backup
  2064.  * copy of nothing).
  2065.  */
  2066. insertfile(fname, newname) char fname[], newname[]; {
  2067.     register LINE    *lp1;
  2068.     register LINE    *lp2;
  2069.     register WINDOW *wp;
  2070.     int        nbytes;
  2071.     LINE        *olp;            /* Line we started at */
  2072.     int        opos;            /* and offset into it */
  2073.     int        s, nline;
  2074.     BUFFER        *bp;
  2075.     char        line[NLINE];
  2076.  
  2077.     bp = curbp;                /* Cheap.        */
  2078.     if (newname != (char *) NULL)
  2079.         (VOID) strcpy(bp->b_fname, newname);
  2080.     if ((s=ffropen(fname)) == FIOERR)    /* Hard file open.    */
  2081.         goto out;
  2082.     if (s == FIOFNF) {            /* File not found.    */
  2083.         if (newname != NULL)
  2084.             ewprintf("(New file)");
  2085.         else    ewprintf("(File not found)");
  2086.         goto out;
  2087.     }
  2088.     opos = curwp->w_doto;
  2089.     /* Open a new line, at point, and start inserting after it */
  2090.     (VOID) lnewline();
  2091.     olp = lback(curwp->w_dotp);
  2092.     if(olp == curbp->b_linep) {
  2093.         /* if at end of buffer, create a line to insert before */
  2094.         (VOID) lnewline();
  2095.         curwp->w_dotp = lback(curwp->w_dotp);
  2096.     }
  2097.     nline = 0;            /* Don't count fake line at end */
  2098.     while ((s=ffgetline(line, NLINE, &nbytes)) != FIOERR) {
  2099.         switch(s) {
  2100.         case FIOSUC:
  2101.         ++nline;
  2102.         /* and continue */
  2103.         case FIOEOF:    /* the last line of the file        */
  2104.         if ((lp1=lalloc(nbytes)) == NULL) {
  2105.             s = FIOERR;        /* Keep message on the    */
  2106.             goto endoffile;        /* display.        */
  2107.         }
  2108.         bcopy(line, <ext(lp1)[0], nbytes);
  2109. lineread:    lp2 = lback(curwp->w_dotp);
  2110.         lp2->l_fp = lp1;
  2111.         lp1->l_fp = curwp->w_dotp;
  2112.         lp1->l_bp = lp2;
  2113.         curwp->w_dotp->l_bp = lp1;
  2114.         if(s==FIOEOF) goto endoffile;
  2115.         break;
  2116.         case FIOLONG: {    /* a line to long to fit in our buffer    */
  2117.             char *cp;
  2118.             char *cp2;
  2119.             int     i;
  2120.  
  2121.             nbytes = 0;
  2122.             for(;;) {
  2123.             if((cp = malloc((unsigned)(nbytes + NLINE))) == NULL) {
  2124.                 ewprintf("Could not allocate %d bytes",
  2125.                     nbytes + NLINE);
  2126.                 s = FIOERR;
  2127.                 if(nbytes) free(cp2);
  2128.                 goto endoffile;
  2129.             }
  2130.             if(nbytes) {
  2131.                 bcopy(cp2, cp, nbytes);
  2132.                 free(cp2);
  2133.             }
  2134.             bcopy(line, cp+nbytes, NLINE);
  2135.             nbytes += NLINE;
  2136.             switch(s = ffgetline(line, NLINE, &i)) {
  2137.                 case FIOERR:
  2138.                 free(cp);
  2139.                 goto endoffile;
  2140.                 case FIOLONG:
  2141.                 cp2 = cp;
  2142.                 break;
  2143.                 case FIOEOF:
  2144.                 case FIOSUC:
  2145.                 if((lp1=lalloc(nbytes+i)) == NULL) {
  2146.                     s = FIOERR;
  2147.                     free(cp);
  2148.                     goto endoffile;
  2149.                 }
  2150.                 bcopy(cp, <ext(lp1)[0], nbytes);
  2151.                 bcopy(line, <ext(lp1)[nbytes], i);
  2152.                 goto lineread;
  2153.             }
  2154.             }
  2155.         }
  2156.         default:
  2157.         ewprintf("Unknown code %d reading file", s);
  2158.         s = FIOERR;
  2159.         break;
  2160.         }
  2161.     }
  2162. endoffile:
  2163.     (VOID) ffclose();            /* Ignore errors.    */
  2164.     if (s==FIOEOF) {            /* Don't zap an error.    */
  2165.         if (nline == 1) ewprintf("(Read 1 line)");
  2166.         else        ewprintf("(Read %d lines)", nline);
  2167.     }
  2168.     /* Set mark at the end of the text */
  2169.     curwp->w_dotp = curwp->w_markp = lback(curwp->w_dotp);
  2170.     curwp->w_marko = llength(curwp->w_markp);
  2171.     (VOID) ldelnewline();
  2172.     curwp->w_dotp = olp;
  2173.     curwp->w_doto = opos;
  2174.     if(olp == curbp->b_linep) curwp->w_dotp = lforw(olp);
  2175. #ifndef NO_BACKUP
  2176.     if (newname != NULL)
  2177.         bp->b_flag |= BFCHG | BFBAK;    /* Need a backup.    */
  2178.     else    bp->b_flag |= BFCHG;
  2179. #else
  2180.     bp->b_flag |= BFCHG;
  2181. #endif
  2182.     /* if the insert was at the end of buffer, set lp1 to the end of
  2183.      * buffer line, and lp2 to the beginning of the newly inserted
  2184.      * text.  (Otherwise lp2 is set to NULL.)  This is
  2185.      * used below to set pointers in other windows correctly if they
  2186.      * are also at the end of buffer.
  2187.      */
  2188.     lp1 = bp->b_linep;
  2189.     if (curwp->w_markp == lp1) {
  2190.         lp2 = curwp->w_dotp;
  2191.     } else {
  2192.         (VOID) ldelnewline();        /* delete extranious newline */
  2193. out:        lp2 = NULL;
  2194.     }
  2195.     for (wp=wheadp; wp!=NULL; wp=wp->w_wndp) {
  2196.         if (wp->w_bufp == curbp) {
  2197.             wp->w_flag |= WFMODE|WFEDIT;
  2198.             if (wp != curwp && lp2 != NULL) {
  2199.                 if (wp->w_dotp == lp1)    wp->w_dotp  = lp2;
  2200.                 if (wp->w_markp == lp1) wp->w_markp = lp2;
  2201.                 if (wp->w_linep == lp1) wp->w_linep = lp2;
  2202.             }
  2203.         }
  2204.     }
  2205.     return s != FIOERR;            /* False if error.    */
  2206. }
  2207.  
  2208. /*
  2209.  * Take a file name, and from it
  2210.  * fabricate a buffer name. This routine knows
  2211.  * about the syntax of file names on the target system.
  2212.  * BDC1        left scan delimiter.
  2213.  * BDC2        optional second left scan delimiter.
  2214.  * BDC3        optional right scan delimiter.
  2215.  */
  2216. VOID
  2217. makename(bname, fname) char bname[]; char fname[]; {
  2218.     register char    *cp1;
  2219.     register char    *cp2;
  2220.  
  2221.     cp1 = &fname[0];
  2222.     while (*cp1 != 0)
  2223.         ++cp1;
  2224.     --cp1;            /* insure at least 1 character ! */
  2225. #ifdef    BDC2
  2226.     while (cp1!=&fname[0] && cp1[-1]!=BDC1 && cp1[-1]!=BDC2)
  2227.         --cp1;
  2228. #else
  2229.     while (cp1!=&fname[0] && cp1[-1]!=BDC1)
  2230.         --cp1;
  2231. #endif
  2232.     cp2 = &bname[0];
  2233. #ifdef    BDC3
  2234.     while (cp2!=&bname[NBUFN-1] && *cp1!=0 && *cp1!=BDC3)
  2235.         *cp2++ = *cp1++;
  2236. #else
  2237.     while (cp2!=&bname[NBUFN-1] && *cp1!=0)
  2238.         *cp2++ = *cp1++;
  2239. #endif
  2240.     *cp2 = 0;
  2241. }
  2242.  
  2243. /*
  2244.  * Ask for a file name, and write the
  2245.  * contents of the current buffer to that file.
  2246.  * Update the remembered file name and clear the
  2247.  * buffer changed flag. This handling of file names
  2248.  * is different from the earlier versions, and
  2249.  * is more compatable with Gosling EMACS than
  2250.  * with ITS EMACS.
  2251.  */
  2252. /*ARGSUSED*/
  2253. filewrite(f, n)
  2254. {
  2255.     register int    s;
  2256.     char        fname[NFILEN];
  2257.     char        *adjfname;
  2258.  
  2259.     if ((s=ereply("Write file: ", fname, NFILEN)) != TRUE)
  2260.         return (s);
  2261.     adjfname = adjustname(fname);
  2262.     if ((s=writeout(curbp, adjfname)) == TRUE) {
  2263.         (VOID) strcpy(curbp->b_fname, adjfname);
  2264. #ifndef NO_BACKUP
  2265.         curbp->b_flag &= ~(BFBAK | BFCHG);
  2266. #else
  2267.         curbp->b_flag &= ~BFCHG;
  2268. #endif
  2269.         upmodes(curbp);
  2270.     }
  2271.     return s;
  2272. }
  2273.  
  2274. /*
  2275.  * Save the contents of the current buffer back into
  2276.  * its associated file.
  2277.  */
  2278. #ifndef NO_BACKUP
  2279. #ifndef    MAKEBACKUP
  2280. #define    MAKEBACKUP TRUE
  2281. #endif
  2282. static int    makebackup = MAKEBACKUP;
  2283. #endif
  2284.  
  2285. /*ARGSUSED*/
  2286. filesave(f, n)
  2287. {
  2288.     return buffsave(curbp);
  2289. }
  2290.  
  2291. /*
  2292.  * Save the contents of the buffer argument into its associated file.
  2293.  * Do nothing if there have been no changes
  2294.  * (is this a bug, or a feature). Error if there is no remembered
  2295.  * file name. If this is the first write since the read or visit,
  2296.  * then a backup copy of the file is made.
  2297.  * Allow user to select whether or not to make backup files
  2298.  * by looking at the value of makebackup.
  2299.  */
  2300. buffsave(bp) BUFFER *bp; {
  2301.     register int    s;
  2302.  
  2303.     if ((bp->b_flag&BFCHG) == 0)    {    /* Return, no changes.    */
  2304.         ewprintf("(No changes need to be saved)");
  2305.         return TRUE;
  2306.     }
  2307.     if (bp->b_fname[0] == '\0') {        /* Must have a name.    */
  2308.         ewprintf("No file name");
  2309.         return (FALSE);
  2310.     }
  2311. #ifndef NO_BACKUP
  2312.     if (makebackup && (bp->b_flag&BFBAK)) {
  2313.         s = fbackupfile(bp->b_fname);
  2314.         if (s == ABORT)            /* Hard error.        */
  2315.             return FALSE;
  2316.         if (s == FALSE            /* Softer error.    */
  2317.         && (s=eyesno("Backup error, save anyway")) != TRUE)
  2318.             return s;
  2319.     }
  2320. #endif
  2321.     if ((s=writeout(bp, bp->b_fname)) == TRUE) {
  2322. #ifndef NO_BACKUP
  2323.         bp->b_flag &= ~(BFCHG | BFBAK);
  2324. #else
  2325.         bp->b_flag &= ~BFCHG;
  2326. #endif
  2327.         upmodes(bp);
  2328.     }
  2329.     return s;
  2330. }
  2331.  
  2332. #ifndef NO_BACKUP
  2333. /* Since we don't have variables (we probably should)
  2334.  * this is a command processor for changing the value of
  2335.  * the make backup flag.  If no argument is given,
  2336.  * sets makebackup to true, so backups are made.  If
  2337.  * an argument is given, no backup files are made when
  2338.  * saving a new version of a file. Only used when BACKUP
  2339.  * is #defined.
  2340.  */
  2341. /*ARGSUSED*/
  2342. makebkfile(f, n)
  2343. {
  2344.     if(f & FFARG) makebackup = n > 0;
  2345.     else makebackup = !makebackup;
  2346.     ewprintf("Backup files %sabled", makebackup ? "en" : "dis");
  2347.     return TRUE;
  2348. }
  2349. #endif
  2350.  
  2351. /*
  2352.  * This function performs the details of file
  2353.  * writing; writing the file in buffer bp to
  2354.  * file fn. Uses the file management routines
  2355.  * in the "fileio.c" package. Most of the grief
  2356.  * is checking of some sort.
  2357.  */
  2358. writeout(bp, fn) register BUFFER *bp; char *fn; {
  2359.     register int    s;
  2360.  
  2361.     if ((s=ffwopen(fn)) != FIOSUC)        /* Open writes message. */
  2362.         return (FALSE);
  2363.     s = ffputbuf(bp);
  2364.     if (s == FIOSUC) {            /* No write error.    */
  2365.         s = ffclose();
  2366.         if (s==FIOSUC)
  2367.             ewprintf("Wrote %s", fn);
  2368.     } else                    /* Ignore close error    */
  2369.         (VOID) ffclose();        /* if a write error.    */
  2370.     return s == FIOSUC;
  2371. }
  2372.  
  2373. /*
  2374.  * Tag all windows for bp (all windows if bp NULL) as needing their
  2375.  * mode line updated.
  2376.  */
  2377. VOID
  2378. upmodes(bp) register BUFFER *bp; {
  2379.     register WINDOW *wp;
  2380.  
  2381.     for (wp = wheadp; wp != NULL; wp = wp->w_wndp)
  2382.         if (bp == NULL || curwp->w_bufp == bp) wp->w_flag |= WFMODE;
  2383. }
  2384. SHAR_EOF
  2385. #    End of shell archive
  2386. exit 0
  2387. -------
  2388.