home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / unix_c / editors / uemacsmg.src < prev    next >
C/C++ Source or Header  |  1989-02-26  |  180KB  |  5,649 lines

  1. 15-Dec-85 20:52:35-MST,185524;000000000001
  2. Return-Path: <unix-sources-request@BRL.ARPA>
  3. Received: from BRL-TGR.ARPA by SIMTEL20.ARPA with TCP; Sun 15 Dec 85 20:48:13-MST
  4. Received: from usenet by TGR.BRL.ARPA id a004961; 15 Dec 85 19:46 EST
  5. From: George Jones <george@osu-eddie.uucp>
  6. Newsgroups: net.sources
  7. Subject: MicroEmacs for the Amiga
  8. Message-ID: <1012@osu-eddie.UUCP>
  9. Date: 14 Dec 85 21:31:29 GMT
  10. To:       unix-sources@BRL-TGR.ARPA
  11.  
  12. This is the source to MicroEmacs for the Amiga.  It is a fairly small Public
  13. Domain Emacs that it beats ED hands down.  It supports multiple windows and 
  14. one keyboard macro.  It is ifdefed to work on lots of different machines.
  15. It is a straight port from IBMland.  I have someone working on using the
  16. blitter to increase the speed of the display, and I have lots of ideas for
  17. things that could be done to ehnance this emacs.  I am willing to play
  18. "keeper-of-the-source" and co-ordinate efforts to enhance it.
  19.  
  20. So here it is, use it and enjoy.  Contact me if you want to help out
  21. on improving it.
  22.  
  23. Cheers,
  24. George M. Jones
  25.         
  26. cbosgd!osu-eddie!george (uucpnet)
  27. george@ohio-state.csnet (csnet)
  28. 70003,2443 (CompuServe)
  29. +1 (614) 457-8600 (work)
  30. +1 (614) 885-5102 (home)
  31.  
  32. ==============================CUT HERE==============================
  33.  
  34.  
  35.  
  36. ==================================================
  37. ansi.c
  38. ==================================================
  39. /*
  40.  * The routines in this file provide support for ANSI style terminals
  41.  * over a serial line. The serial I/O services are provided by routines in
  42.  * "termio.c". It compiles into nothing if not an ANSI device.
  43.  */
  44.  
  45. #include        <stdio.h>
  46. #include        "ed.h"
  47.  
  48. #if     ANSI
  49.  
  50. #define NROW    23                      /* Screen size.                 */
  51. #define NCOL    77                      /* Edit if you want to.         */
  52. #define BEL     0x07                    /* BEL character.               */
  53. #define ESC     0x1B                    /* ESC character.               */
  54.  
  55. extern  int     ttopen();               /* Forward references.          */
  56. extern  int     ttgetc();
  57. extern  int     ttputc();
  58. extern  int     ttflush();
  59. extern  int     ttclose();
  60. extern  int     ansimove();
  61. extern  int     ansieeol();
  62. extern  int     ansieeop();
  63. extern  int     ansibeep();
  64. extern  int     ansiopen();
  65.  
  66. /*
  67.  * Standard terminal interface dispatch table. Most of the fields point into
  68.  * "termio" code.
  69.  */
  70. TERM    term    = {
  71.         NROW-1,
  72.         NCOL,
  73.         &ansiopen,
  74.         &ttclose,
  75.         &ttgetc,
  76.         &ttputc,
  77.         &ttflush,
  78.         &ansimove,
  79.         &ansieeol,
  80.         &ansieeop,
  81.         &ansibeep
  82. };
  83.  
  84. ansimove(row, col)
  85. {
  86.         ttputc(ESC);
  87.         ttputc('[');
  88.         ansiparm(row+1);
  89.         ttputc(';');
  90.         ansiparm(col+1);
  91.         ttputc('H');
  92. }
  93.  
  94. ansieeol()
  95. {
  96.         ttputc(ESC);
  97.         ttputc('[');
  98.         ttputc('K');
  99. }
  100.  
  101. ansieeop()
  102. {
  103.         ttputc(ESC);
  104.         ttputc('[');
  105.         ttputc('J');
  106. }
  107.  
  108. ansibeep()
  109. {
  110.         ttputc(BEL);
  111.         ttflush();
  112. }
  113.  
  114. ansiparm(n)
  115. register int    n;
  116. {
  117.         register int    q;
  118.  
  119.         q = n/10;
  120.         if (q != 0)
  121.                 ansiparm(q);
  122.         ttputc((n%10) + '0');
  123. }
  124.  
  125. #endif
  126.  
  127. ansiopen()
  128. {
  129. #if     V7
  130.         register char *cp;
  131.         char *getenv();
  132.  
  133.         if ((cp = getenv("TERM")) == NULL) {
  134.                 puts("Shell variable TERM not defined!");
  135.                 exit(1);
  136.         }
  137.         if (strcmp(cp, "vt100") != 0) {
  138.                 puts("Terminal type not 'vt100'!");
  139.                 exit(1);
  140.         }
  141. #endif
  142.         ttopen();
  143. }
  144.  
  145. ==================================================
  146. basic.c
  147. ==================================================
  148. /*
  149.  * The routines in this file move the cursor around on the screen. They
  150.  * compute a new value for the cursor, then adjust ".". The display code
  151.  * always updates the cursor location, so only moves between lines, or
  152.  * functions that adjust the top line in the window and invalidate the
  153.  * framing, are hard.
  154.  */
  155. #include        <stdio.h>
  156. #include        "ed.h"
  157.  
  158. /*
  159.  * Move the cursor to the
  160.  * beginning of the current line.
  161.  * Trivial.
  162.  */
  163. gotobol(f, n)
  164. {
  165.         curwp->w_doto  = 0;
  166.         return (TRUE);
  167. }
  168.  
  169. /*
  170.  * Move the cursor backwards by "n" characters. If "n" is less than zero call
  171.  * "forwchar" to actually do the move. Otherwise compute the new cursor
  172.  * location. Error if you try and move out of the buffer. Set the flag if the
  173.  * line pointer for dot changes.
  174.  */
  175. backchar(f, n)
  176. register int    n;
  177. {
  178.         register LINE   *lp;
  179.  
  180.         if (n < 0)
  181.                 return (forwchar(f, -n));
  182.         while (n--) {
  183.                 if (curwp->w_doto == 0) {
  184.                         if ((lp=lback(curwp->w_dotp)) == curbp->b_linep)
  185.                                 return (FALSE);
  186.                         curwp->w_dotp  = lp;
  187.                         curwp->w_doto  = llength(lp);
  188.                         curwp->w_flag |= WFMOVE;
  189.                 } else
  190.                         curwp->w_doto--;
  191.         }
  192.         return (TRUE);
  193. }
  194.  
  195. /*
  196.  * Move the cursor to the end of the current line. Trivial. No errors.
  197.  */
  198. gotoeol(f, n)
  199. {
  200.         curwp->w_doto  = llength(curwp->w_dotp);
  201.         return (TRUE);
  202. }
  203.  
  204. /*
  205.  * Move the cursor forwwards by "n" characters. If "n" is less than zero call
  206.  * "backchar" to actually do the move. Otherwise compute the new cursor
  207.  * location, and move ".". Error if you try and move off the end of the
  208.  * buffer. Set the flag if the line pointer for dot changes.
  209.  */
  210. forwchar(f, n)
  211. register int    n;
  212. {
  213.         if (n < 0)
  214.                 return (backchar(f, -n));
  215.         while (n--) {
  216.                 if (curwp->w_doto == llength(curwp->w_dotp)) {
  217.                         if (curwp->w_dotp == curbp->b_linep)
  218.                                 return (FALSE);
  219.                         curwp->w_dotp  = lforw(curwp->w_dotp);
  220.                         curwp->w_doto  = 0;
  221.                         curwp->w_flag |= WFMOVE;
  222.                 } else
  223.                         curwp->w_doto++;
  224.         }
  225.         return (TRUE);
  226. }
  227.  
  228. /*
  229.  * Goto the beginning of the buffer. Massive adjustment of dot. This is
  230.  * considered to be hard motion; it really isn't if the original value of dot
  231.  * is the same as the new value of dot. Normally bound to "M-<".
  232.  */
  233. gotobob(f, n)
  234. {
  235.         curwp->w_dotp  = lforw(curbp->b_linep);
  236.         curwp->w_doto  = 0;
  237.         curwp->w_flag |= WFHARD;
  238.         return (TRUE);
  239. }
  240.  
  241. /*
  242.  * Move to the end of the buffer. Dot is always put at the end of the file
  243.  * (ZJ). The standard screen code does most of the hard parts of update.
  244.  * Bound to "M->".
  245.  */
  246. gotoeob(f, n)
  247. {
  248.         curwp->w_dotp  = curbp->b_linep;
  249.         curwp->w_doto  = 0;
  250.         curwp->w_flag |= WFHARD;
  251.         return (TRUE);
  252. }
  253.  
  254. /*
  255.  * Move forward by full lines. If the number of lines to move is less than
  256.  * zero, call the backward line function to actually do it. The last command
  257.  * controls how the goal column is set. Bound to "C-N". No errors are
  258.  * possible.
  259.  */
  260. forwline(f, n)
  261. {
  262.         register LINE   *dlp;
  263.  
  264.         if (n < 0)
  265.                 return (backline(f, -n));
  266.         if ((lastflag&CFCPCN) == 0)             /* Reset goal if last   */
  267.                 curgoal = curcol;               /* not C-P or C-N       */
  268.         thisflag |= CFCPCN;
  269.         dlp = curwp->w_dotp;
  270.         while (n-- && dlp!=curbp->b_linep)
  271.                 dlp = lforw(dlp);
  272.         curwp->w_dotp  = dlp;
  273.         curwp->w_doto  = getgoal(elp);
  274.         curwp->w_flag |= WFMOVE;
  275.         return (TRUE);
  276. }
  277.  
  278. /*
  279.  * This function is like "forwline", but goes backwards. The scheme is exactly
  280.  * the same. Check for arguments that are less than zero and call your
  281.  * alternate. Figure out the new line and call "movedot" to perform the
  282.  * motion. No errors are possible. Bound to "C-P".
  283.  */
  284. backline(f, n)
  285. {
  286.         register LINE   *dlp;
  287.  
  288.         if (n < 0)
  289.                 return (forwline(f, -n));
  290.         if ((lastflag&CFCPCN) == 0)             /* Reset goal if the    */
  291.                 curgoal = curcol;               /* last isn't C-P, C-N  */
  292.         thisflag |= CFCPCN;
  293.         dlp = curwp->w_dotp;
  294.         while (n-- && lback(dlp)!=curbp->b_linep)
  295.                 dlp = lback(dlp);
  296.         curwp->w_dotp  = dlp;
  297.         curwp->w_doto  = getgoal(dlp);
  298.         curwp->w_flag |= WFMOVE;
  299.         return (TRUE);
  300. }
  301.  
  302. /*
  303.  * This routine, given a pointer to a LINE, and the current cursor goal
  304.  * column, return the best choice for the offset. The offset is returned.
  305.  * Used by "C-N" and "C-P".
  306.  */
  307. getgoal(dlp)
  308. register LINE   *dlp;
  309. {
  310.         register int    c;
  311.         register int    col;
  312.         register int    newcol;
  313.         register int    dbo;
  314.  
  315.         col = 0;
  316.         dbo = 0;
  317.         while (dbo != llength(dlp)) {
  318.                 c = lgetc(dlp, dbo);
  319.                 newcol = col;
  320.                 if (c == '\t')
  321.                         newcol |= 0x07;
  322.                 else if (c<0x20 || c==0x7F)
  323.                         ++newcol;
  324.                 ++newcol;
  325.                 if (newcol > curgoal)
  326.                         break;
  327.                 col = newcol;
  328.                 ++dbo;
  329.         }
  330.         return (dbo);
  331. }
  332.  
  333. /*
  334.  * Scroll forward by a specified number of lines, or by a full page if no
  335.  * argument. Bound to "C-V". The "2" in the arithmetic on the window size is
  336.  * the overlap; this value is the default overlap value in ITS EMACS. Because
  337.  * this zaps the top line in the display window, we have to do a hard update.
  338.  */
  339. forwpage(f, n)
  340. register int    n;
  341. {
  342.         register LINE   *lp;
  343.  
  344.         if (f == FALSE) {
  345.                 n = curwp->w_ntrows - 2;        /* Default scroll.      */
  346.                 if (n <= 0)                     /* Forget the overlap   */
  347.                         n = 1;                  /* if tiny window.      */
  348.         } else if (n < 0)
  349.                 return (backpage(f, -n));
  350. #if     CVMVAS
  351.         else                                    /* Convert from pages   */
  352.                 n *= curwp->w_ntrows;           /* to lines.            */
  353. #endif
  354.         lp = curwp->w_linep;
  355.         while (n-- && lp!=curbp->b_linep)
  356.                 lp = lforw(lp);
  357.         curwp->w_linep = lp;
  358.         curwp->w_dotp  = lp;
  359.         curwp->w_doto  = 0;
  360.         curwp->w_flag |= WFHARD;
  361.         return (TRUE);
  362. }
  363.  
  364. /*
  365.  * This command is like "forwpage", but it goes backwards. The "2", like
  366.  * above, is the overlap between the two windows. The value is from the ITS
  367.  * EMACS manual. Bound to "M-V". We do a hard update for exactly the same
  368.  * reason.
  369.  */
  370. backpage(f, n)
  371. register int    n;
  372. {
  373.         register LINE   *lp;
  374.  
  375.         if (f == FALSE) {
  376.                 n = curwp->w_ntrows - 2;        /* Default scroll.      */
  377.                 if (n <= 0)                     /* Don't blow up if the */
  378.                         n = 1;                  /* window is tiny.      */
  379.         } else if (n < 0)
  380.                 return (forwpage(f, -n));
  381. #if     CVMVAS
  382.         else                                    /* Convert from pages   */
  383.                 n *= curwp->w_ntrows;           /* to lines.            */
  384. #endif
  385.         lp = curwp->w_linep;
  386.         while (n-- && lback(lp)!=curbp->b_linep)
  387.                 lp = lback(lp);
  388.         curwp->w_linep = lp;
  389.         curwp->w_dotp  = lp;
  390.         curwp->w_doto  = 0;
  391.         curwp->w_flag |= WFHARD;
  392.         return (TRUE);
  393. }
  394.  
  395. /*
  396.  * Set the mark in the current window to the value of "." in the window. No
  397.  * errors are possible. Bound to "M-.".
  398.  */
  399. setmark(f, n)
  400. {
  401.         curwp->w_markp = curwp->w_dotp;
  402.         curwp->w_marko = curwp->w_doto;
  403.         mlwrite("[Mark set]");
  404.         return (TRUE);
  405. }
  406.  
  407. /*
  408.  * Swap the values of "." and "mark" in the current window. This is pretty
  409.  * easy, bacause all of the hard work gets done by the standard routine
  410.  * that moves the mark about. The only possible error is "no mark". Bound to
  411.  * "C-X C-X".
  412.  */
  413. swapmark(f, n)
  414. {
  415.         register LINE   *odotp;
  416.         register int    odoto;
  417.  
  418.         if (curwp->w_markp == NULL) {
  419.                 mlwrite("No mark in this window");
  420.                 return (FALSE);
  421.         }
  422.         odotp = curwp->w_dotp;
  423.         odoto = curwp->w_doto;
  424.         curwp->w_dotp  = curwp->w_markp;
  425.         curwp->w_doto  = curwp->w_marko;
  426.         curwp->w_markp = odotp;
  427.         curwp->w_marko = odoto;
  428.         curwp->w_flag |= WFMOVE;
  429.         return (TRUE);
  430. }
  431.  
  432. ==================================================
  433. buffer.c
  434. ==================================================
  435. /*
  436.  * Buffer management.
  437.  * Some of the functions are internal,
  438.  * and some are actually attached to user
  439.  * keys. Like everyone else, they set hints
  440.  * for the display system.
  441.  */
  442. #include        <stdio.h>
  443. #include        "ed.h"
  444.  
  445. /*
  446.  * Attach a buffer to a window. The
  447.  * values of dot and mark come from the buffer
  448.  * if the use count is 0. Otherwise, they come
  449.  * from some other window.
  450.  */
  451. usebuffer(f, n)
  452. {
  453.         register BUFFER *bp;
  454.         register WINDOW *wp;
  455.         register int    s;
  456.         char            bufn[NBUFN];
  457.  
  458.         if ((s=mlreply("Use buffer: ", bufn, NBUFN)) != TRUE)
  459.                 return (s);
  460.         if ((bp=bfind(bufn, TRUE, 0)) == NULL)
  461.                 return (FALSE);
  462.         if (--curbp->b_nwnd == 0) {             /* Last use.            */
  463.                 curbp->b_dotp  = curwp->w_dotp;
  464.                 curbp->b_doto  = curwp->w_doto;
  465.                 curbp->b_markp = curwp->w_markp;
  466.                 curbp->b_marko = curwp->w_marko;
  467.         }
  468.         curbp = bp;                             /* Switch.              */
  469.         curwp->w_bufp  = bp;
  470.         curwp->w_linep = bp->b_linep;           /* For macros, ignored. */
  471.         curwp->w_flag |= WFMODE|WFFORCE|WFHARD; /* Quite nasty.         */
  472.         if (bp->b_nwnd++ == 0) {                /* First use.           */
  473.                 curwp->w_dotp  = bp->b_dotp;
  474.                 curwp->w_doto  = bp->b_doto;
  475.                 curwp->w_markp = bp->b_markp;
  476.                 curwp->w_marko = bp->b_marko;
  477.                 return (TRUE);
  478.         }
  479.         wp = wheadp;                            /* Look for old.        */
  480.         while (wp != NULL) {
  481.                 if (wp!=curwp && wp->w_bufp==bp) {
  482.                         curwp->w_dotp  = wp->w_dotp;
  483.                         curwp->w_doto  = wp->w_doto;
  484.                         curwp->w_markp = wp->w_markp;
  485.                         curwp->w_marko = wp->w_marko;
  486.                         break;
  487.                 }
  488.                 wp = wp->w_wndp;
  489.         }
  490.         return (TRUE);
  491. }
  492.  
  493. /*
  494.  * Dispose of a buffer, by name.
  495.  * Ask for the name. Look it up (don't get too
  496.  * upset if it isn't there at all!). Get quite upset
  497.  * if the buffer is being displayed. Clear the buffer (ask
  498.  * if the buffer has been changed). Then free the header
  499.  * line and the buffer header. Bound to "C-X K".
  500.  */
  501. killbuffer(f, n)
  502. {
  503.         register BUFFER *bp;
  504.         register BUFFER *bp1;
  505.         register BUFFER *bp2;
  506.         register int    s;
  507.         char            bufn[NBUFN];
  508.  
  509.         if ((s=mlreply("Kill buffer: ", bufn, NBUFN)) != TRUE)
  510.                 return (s);
  511.         if ((bp=bfind(bufn, FALSE, 0)) == NULL) /* Easy if unknown.     */
  512.                 return (TRUE);
  513.         if (bp->b_nwnd != 0) {                  /* Error if on screen.  */
  514.                 mlwrite("Buffer is being displayed");
  515.                 return (FALSE);
  516.         }
  517.         if ((s=bclear(bp)) != TRUE)             /* Blow text away.      */
  518.                 return (s);
  519.         free((char *) bp->b_linep);             /* Release header line. */
  520.         bp1 = NULL;                             /* Find the header.     */
  521.         bp2 = bheadp;
  522.         while (bp2 != bp) {
  523.                 bp1 = bp2;
  524.                 bp2 = bp2->b_bufp;
  525.         }
  526.         bp2 = bp2->b_bufp;                      /* Next one in chain.   */
  527.         if (bp1 == NULL)                        /* Unlink it.           */
  528.                 bheadp = bp2;
  529.         else
  530.                 bp1->b_bufp = bp2;
  531.         free((char *) bp);                      /* Release buffer block */
  532.         return (TRUE);
  533. }
  534.  
  535. /*
  536.  * List all of the active
  537.  * buffers. First update the special
  538.  * buffer that holds the list. Next make
  539.  * sure at least 1 window is displaying the
  540.  * buffer list, splitting the screen if this
  541.  * is what it takes. Lastly, repaint all of
  542.  * the windows that are displaying the
  543.  * list. Bound to "C-X C-B".
  544.  */
  545. listbuffers(f, n)
  546. {
  547.         register WINDOW *wp;
  548.         register BUFFER *bp;
  549.         register int    s;
  550.  
  551.         if ((s=makelist()) != TRUE)
  552.                 return (s);
  553.         if (blistp->b_nwnd == 0) {              /* Not on screen yet.   */
  554.                 if ((wp=wpopup()) == NULL)
  555.                         return (FALSE);
  556.                 bp = wp->w_bufp;
  557.                 if (--bp->b_nwnd == 0) {
  558.                         bp->b_dotp  = wp->w_dotp;
  559.                         bp->b_doto  = wp->w_doto;
  560.                         bp->b_markp = wp->w_markp;
  561.                         bp->b_marko = wp->w_marko;
  562.                 }
  563.                 wp->w_bufp  = blistp;
  564.                 ++blistp->b_nwnd;
  565.         }
  566.         wp = wheadp;
  567.         while (wp != NULL) {
  568.                 if (wp->w_bufp == blistp) {
  569.                         wp->w_linep = lforw(blistp->b_linep);
  570.                         wp->w_dotp  = lforw(blistp->b_linep);
  571.                         wp->w_doto  = 0;
  572.                         wp->w_markp = NULL;
  573.                         wp->w_marko = 0;
  574.                         wp->w_flag |= WFMODE|WFHARD;
  575.                 }
  576.                 wp = wp->w_wndp;
  577.         }
  578.         return (TRUE);
  579. }
  580.  
  581. /*
  582.  * This routine rebuilds the
  583.  * text in the special secret buffer
  584.  * that holds the buffer list. It is called
  585.  * by the list buffers command. Return TRUE
  586.  * if everything works. Return FALSE if there
  587.  * is an error (if there is no memory).
  588.  */
  589. makelist()
  590. {
  591.         register char   *cp1;
  592.         register char   *cp2;
  593.         register int    c;
  594.         register BUFFER *bp;
  595.         register LINE   *lp;
  596.         register int    nbytes;
  597.         register int    s;
  598.         register int    type;
  599.         char            b[6+1];
  600.         char            line[128];
  601.  
  602.         blistp->b_flag &= ~BFCHG;               /* Don't complain!      */
  603.         if ((s=bclear(blistp)) != TRUE)         /* Blow old text away   */
  604.                 return (s);
  605.         strcpy(blistp->b_fname, "");
  606.         if (addline("C   Size Buffer           File") == FALSE
  607.         ||  addline("-   ---- ------           ----") == FALSE)
  608.                 return (FALSE);
  609.         bp = bheadp;                            /* For all buffers      */
  610.         while (bp != NULL) {
  611.                 if ((bp->b_flag&BFTEMP) != 0) { /* Skip magic ones.     */
  612.                         bp = bp->b_bufp;
  613.                         continue;
  614.                 }
  615.                 cp1 = &line[0];                 /* Start at left edge   */
  616.                 if ((bp->b_flag&BFCHG) != 0)    /* "*" if changed       */
  617.                         *cp1++ = '*';
  618.                 else
  619.                         *cp1++ = ' ';
  620.                 *cp1++ = ' ';                   /* Gap.                 */
  621.                 nbytes = 0;                     /* Count bytes in buf.  */
  622.                 lp = lforw(bp->b_linep);
  623.                 while (lp != bp->b_linep) {
  624.                         nbytes += llength(lp)+1;
  625.                         lp = lforw(lp);
  626.                 }
  627.                 itoa(b, 6, nbytes);             /* 6 digit buffer size. */
  628.                 cp2 = &b[0];
  629.                 while ((c = *cp2++) != 0)
  630.                         *cp1++ = c;
  631.                 *cp1++ = ' ';                   /* Gap.                 */
  632.                 cp2 = &bp->b_bname[0];          /* Buffer name          */
  633.                 while ((c = *cp2++) != 0)
  634.                         *cp1++ = c;
  635.                 cp2 = &bp->b_fname[0];          /* File name            */
  636.                 if (*cp2 != 0) {
  637.                         while (cp1 < &line[1+1+6+1+NBUFN+1])
  638.                                 *cp1++ = ' ';           
  639.                         while ((c = *cp2++) != 0) {
  640.                                 if (cp1 < &line[128-1])
  641.                                         *cp1++ = c;
  642.                         }
  643.                 }
  644.                 *cp1 = 0;                       /* Add to the buffer.   */
  645.                 if (addline(line) == FALSE)
  646.                         return (FALSE);
  647.                 bp = bp->b_bufp;
  648.         }
  649.         return (TRUE);                          /* All done             */
  650. }
  651.  
  652. itoa(buf, width, num)
  653. register char   buf[];
  654. register int    width;
  655. register int    num;
  656. {
  657.         buf[width] = 0;                         /* End of string.       */
  658.         while (num >= 10) {                     /* Conditional digits.  */
  659.                 buf[--width] = (num%10) + '0';
  660.                 num /= 10;
  661.         }
  662.         buf[--width] = num + '0';               /* Always 1 digit.      */
  663.         while (width != 0)                      /* Pad with blanks.     */
  664.                 buf[--width] = ' ';
  665. }
  666.  
  667. /*
  668.  * The argument "text" points to
  669.  * a string. Append this line to the
  670.  * buffer list buffer. Handcraft the EOL
  671.  * on the end. Return TRUE if it worked and
  672.  * FALSE if you ran out of room.
  673.  */
  674. addline(text)
  675. char    *text;
  676. {
  677.         register LINE   *lp;
  678.         register int    i;
  679.         register int    ntext;
  680.  
  681.         ntext = strlen(text);
  682.         if ((lp=lalloc(ntext)) == NULL)
  683.                 return (FALSE);
  684.         for (i=0; i<ntext; ++i)
  685.                 lputc(lp, i, text[i]);
  686.         blistp->b_linep->l_bp->l_fp = lp;       /* Hook onto the end    */
  687.         lp->l_bp = blistp->b_linep->l_bp;
  688.         blistp->b_linep->l_bp = lp;
  689.         lp->l_fp = blistp->b_linep;
  690.         if (blistp->b_dotp == blistp->b_linep)  /* If "." is at the end */
  691.                 blistp->b_dotp = lp;            /* move it to new line  */
  692.         return (TRUE);
  693. }
  694.  
  695. /*
  696.  * Look through the list of
  697.  * buffers. Return TRUE if there
  698.  * are any changed buffers. Buffers
  699.  * that hold magic internal stuff are
  700.  * not considered; who cares if the
  701.  * list of buffer names is hacked.
  702.  * Return FALSE if no buffers
  703.  * have been changed.
  704.  */
  705. anycb()
  706. {
  707.         register BUFFER *bp;
  708.  
  709.         bp = bheadp;
  710.         while (bp != NULL) {
  711.                 if ((bp->b_flag&BFTEMP)==0 && (bp->b_flag&BFCHG)!=0)
  712.                         return (TRUE);
  713.                 bp = bp->b_bufp;
  714.         }
  715.         return (FALSE);
  716. }
  717.  
  718. /*
  719.  * Find a buffer, by name. Return a pointer
  720.  * to the BUFFER structure associated with it. If
  721.  * the named buffer is found, but is a TEMP buffer (like
  722.  * the buffer list) conplain. If the buffer is not found
  723.  * and the "cflag" is TRUE, create it. The "bflag" is
  724.  * the settings for the flags in in buffer.
  725.  */
  726. BUFFER  *
  727. bfind(bname, cflag, bflag)
  728. register char   *bname;
  729. {
  730.         register BUFFER *bp;
  731.         register LINE   *lp;
  732.  
  733.         bp = bheadp;
  734.         while (bp != NULL) {
  735.                 if (strcmp(bname, bp->b_bname) == 0) {
  736.                         if ((bp->b_flag&BFTEMP) != 0) {
  737.                                 mlwrite("Cannot select builtin buffer");
  738.                                 return (NULL);
  739.                         }
  740.                         return (bp);
  741.                 }
  742.                 bp = bp->b_bufp;
  743.         }
  744.         if (cflag != FALSE) {
  745.                 if ((bp=(BUFFER *)malloc(sizeof(BUFFER))) == NULL)
  746.                         return (NULL);
  747.                 if ((lp=lalloc(0)) == NULL) {
  748.                         free((char *) bp);
  749.                         return (NULL);
  750.                 }
  751.                 bp->b_bufp  = bheadp;
  752.                 bheadp = bp;
  753.                 bp->b_dotp  = lp;
  754.                 bp->b_doto  = 0;
  755.                 bp->b_markp = NULL;
  756.                 bp->b_marko = 0;
  757.                 bp->b_flag  = bflag;
  758.                 bp->b_nwnd  = 0;
  759.                 bp->b_linep = lp;
  760.                 strcpy(bp->b_fname, "");
  761.                 strcpy(bp->b_bname, bname);
  762.                 lp->l_fp = lp;
  763.                 lp->l_bp = lp;
  764.         }
  765.         return (bp);
  766. }
  767.  
  768. /*
  769.  * This routine blows away all of the text
  770.  * in a buffer. If the buffer is marked as changed
  771.  * then we ask if it is ok to blow it away; this is
  772.  * to save the user the grief of losing text. The
  773.  * window chain is nearly always wrong if this gets
  774.  * called; the caller must arrange for the updates
  775.  * that are required. Return TRUE if everything
  776.  * looks good.
  777.  */
  778. bclear(bp)
  779. register BUFFER *bp;
  780. {
  781.         register LINE   *lp;
  782.         register int    s;
  783.         
  784.         if ((bp->b_flag&BFTEMP) == 0            /* Not scratch buffer.  */
  785.         && (bp->b_flag&BFCHG) != 0              /* Something changed    */
  786.         && (s=mlyesno("Discard changes")) != TRUE)
  787.                 return (s);
  788.         bp->b_flag  &= ~BFCHG;                  /* Not changed          */
  789.         while ((lp=lforw(bp->b_linep)) != bp->b_linep)
  790.                 lfree(lp);
  791.         bp->b_dotp  = bp->b_linep;              /* Fix "."              */
  792.         bp->b_doto  = 0;
  793.         bp->b_markp = NULL;                     /* Invalidate "mark"    */
  794.         bp->b_marko = 0;
  795.         return (TRUE);
  796. }
  797.  
  798. ==================================================
  799. display.c
  800. ==================================================
  801. /*
  802.  * The functions in this file handle redisplay. There are two halves, the
  803.  * ones that update the virtual display screen, and the ones that make the
  804.  * physical display screen the same as the virtual display screen. These
  805.  * functions use hints that are left in the windows by the commands.
  806.  *
  807.  * REVISION HISTORY:
  808.  *
  809.  * ?    Steve Wilhite, 1-Dec-85
  810.  *      - massive cleanup on code.
  811.  */
  812.  
  813. #include        <stdio.h>
  814. #include        "ed.h"
  815.  
  816. #define WFDEBUG 0                       /* Window flag debug. */
  817.  
  818. typedef struct  VIDEO {
  819.         short   v_flag;                 /* Flags */
  820.         char    v_text[1];              /* Screen data. */
  821. }       VIDEO;
  822.  
  823. #define VFCHG   0x0001                  /* Changed. */
  824.  
  825. int     sgarbf  = TRUE;                 /* TRUE if screen is garbage */
  826. int     mpresf  = FALSE;                /* TRUE if message in last line */
  827. int     vtrow   = 0;                    /* Row location of SW cursor */
  828. int     vtcol   = 0;                    /* Column location of SW cursor */
  829. int     ttrow   = HUGE;                 /* Row location of HW cursor */
  830. int     ttcol   = HUGE;                 /* Column location of HW cursor */
  831.  
  832. VIDEO   **vscreen;                      /* Virtual screen. */
  833. VIDEO   **pscreen;                      /* Physical screen. */
  834.  
  835. /*
  836.  * Initialize the data structures used by the display code. The edge vectors
  837.  * used to access the screens are set up. The operating system's terminal I/O
  838.  * channel is set up. All the other things get initialized at compile time.
  839.  * The original window has "WFCHG" set, so that it will get completely
  840.  * redrawn on the first call to "update".
  841.  */
  842. vtinit()
  843. {
  844.     register int i;
  845.     register VIDEO *vp;
  846.  
  847.     (*term.t_open)();
  848.     vscreen = (VIDEO **) malloc(term.t_nrow*sizeof(VIDEO *));
  849.  
  850.     if (vscreen == NULL)
  851.         exit(1);
  852.  
  853.     pscreen = (VIDEO **) malloc(term.t_nrow*sizeof(VIDEO *));
  854.  
  855.     if (pscreen == NULL)
  856.         exit(1);
  857.  
  858.     for (i = 0; i < term.t_nrow; ++i)
  859.         {
  860.         vp = (VIDEO *) malloc(sizeof(VIDEO)+term.t_ncol);
  861.  
  862.         if (vp == NULL)
  863.             exit(1);
  864.  
  865.         vscreen[i] = vp;
  866.         vp = (VIDEO *) malloc(sizeof(VIDEO)+term.t_ncol);
  867.  
  868.         if (vp == NULL)
  869.             exit(1);
  870.  
  871.         pscreen[i] = vp;
  872.         }
  873. }
  874.  
  875. /*
  876.  * Clean up the virtual terminal system, in anticipation for a return to the
  877.  * operating system. Move down to the last line and clear it out (the next
  878.  * system prompt will be written in the line). Shut down the channel to the
  879.  * terminal.
  880.  */
  881. vttidy()
  882. {
  883.     movecursor(term.t_nrow, 0);
  884.     (*term.t_eeol)();
  885.     (*term.t_close)();
  886. }
  887.  
  888. /*
  889.  * Set the virtual cursor to the specified row and column on the virtual
  890.  * screen. There is no checking for nonsense values; this might be a good
  891.  * idea during the early stages.
  892.  */
  893. vtmove(row, col)
  894. {
  895.     vtrow = row;
  896.     vtcol = col;
  897. }
  898.  
  899. /*
  900.  * Write a character to the virtual screen. The virtual row and column are
  901.  * updated. If the line is too long put a "$" in the last column. This routine
  902.  * only puts printing characters into the virtual terminal buffers. Only
  903.  * column overflow is checked.
  904.  */
  905. vtputc(c)
  906.     int c;
  907. {
  908.     register VIDEO      *vp;
  909.  
  910.     vp = vscreen[vtrow];
  911.  
  912.     if (vtcol >= term.t_ncol)
  913.         vp->v_text[term.t_ncol - 1] = '$';
  914.     else if (c == '\t')
  915.         {
  916.         do
  917.             {
  918.             vtputc(' ');
  919.             }
  920.         while ((vtcol&0x07) != 0);
  921.         }
  922.     else if (c < 0x20 || c == 0x7F)
  923.         {
  924.         vtputc('^');
  925.         vtputc(c ^ 0x40);
  926.         }
  927.     else
  928.         vp->v_text[vtcol++] = c;                
  929. }
  930.  
  931. /*
  932.  * Erase from the end of the software cursor to the end of the line on which
  933.  * the software cursor is located.
  934.  */
  935. vteeol()
  936. {
  937.     register VIDEO      *vp;
  938.  
  939.     vp = vscreen[vtrow];
  940.     while (vtcol < term.t_ncol)
  941.         vp->v_text[vtcol++] = ' ';
  942. }
  943.  
  944. /*
  945.  * Make sure that the display is right. This is a three part process. First,
  946.  * scan through all of the windows looking for dirty ones. Check the framing,
  947.  * and refresh the screen. Second, make sure that "currow" and "curcol" are
  948.  * correct for the current window. Third, make the virtual and physical
  949.  * screens the same.
  950.  */
  951. update()
  952. {
  953.     register LINE *lp;
  954.     register WINDOW *wp;
  955.     register VIDEO *vp1;
  956.     register VIDEO *vp2;
  957.     register int i;
  958.     register int j;
  959.     register int c;
  960.  
  961.     wp = wheadp;
  962.  
  963.     while (wp != NULL)
  964.         {
  965.         /* Look at any window with update flags set on. */
  966.  
  967.         if (wp->w_flag != 0)
  968.             {
  969.             /* If not force reframe, check the framing. */
  970.  
  971.             if ((wp->w_flag & WFFORCE) == 0)
  972.                 {
  973.                 lp = wp->w_linep;
  974.  
  975.                 for (i = 0; i < wp->w_ntrows; ++i)
  976.                     {
  977.                     if (lp == wp->w_dotp)
  978.                         goto out;
  979.  
  980.                     if (lp == wp->w_bufp->b_linep)
  981.                         break;
  982.  
  983.                     lp = lforw(lp);
  984.                     }
  985.                 }
  986.  
  987.             /* Not acceptable, better compute a new value for the line at the
  988.              * top of the window. Then set the "WFHARD" flag to force full
  989.              * redraw.
  990.              */
  991.             i = wp->w_force;
  992.  
  993.             if (i > 0)
  994.                 {
  995.                 --i;
  996.  
  997.                 if (i >= wp->w_ntrows)
  998.                   i = wp->w_ntrows-1;
  999.                 }
  1000.             else if (i < 0)
  1001.                 {
  1002.                 i += wp->w_ntrows;
  1003.  
  1004.                 if (i < 0)
  1005.                     i = 0;
  1006.                 }
  1007.             else
  1008.                 i = wp->w_ntrows/2;
  1009.  
  1010.             lp = wp->w_dotp;
  1011.  
  1012.             while (i != 0 && lback(lp) != wp->w_bufp->b_linep)
  1013.                 {
  1014.                 --i;
  1015.                 lp = lback(lp);
  1016.                 }
  1017.  
  1018.             wp->w_linep = lp;
  1019.             wp->w_flag |= WFHARD;       /* Force full. */
  1020.  
  1021. out:
  1022.             /* Try to use reduced update. Mode line update has its own special
  1023.              * flag. The fast update is used if the only thing to do is within
  1024.              * the line editing.
  1025.              */
  1026.             lp = wp->w_linep;
  1027.             i = wp->w_toprow;
  1028.  
  1029.             if ((wp->w_flag & ~WFMODE) == WFEDIT)
  1030.                 {
  1031.                 while (lp != wp->w_dotp)
  1032.                     {
  1033.                     ++i;
  1034.                     lp = lforw(lp);
  1035.                     }
  1036.  
  1037.                 vscreen[i]->v_flag |= VFCHG;
  1038.                 vtmove(i, 0);
  1039.  
  1040.                 for (j = 0; j < llength(lp); ++j)
  1041.                     vtputc(lgetc(lp, j));
  1042.  
  1043.                 vteeol();
  1044.                 }
  1045.              else if ((wp->w_flag & (WFEDIT | WFHARD)) != 0)
  1046.                 {
  1047.                 while (i < wp->w_toprow+wp->w_ntrows)
  1048.                     {
  1049.                     vscreen[i]->v_flag |= VFCHG;
  1050.                     vtmove(i, 0);
  1051.  
  1052.                     if (lp != wp->w_bufp->b_linep)
  1053.                         {
  1054.                         for (j = 0; j < llength(lp); ++j)
  1055.                             vtputc(lgetc(lp, j));
  1056.  
  1057.                         lp = lforw(lp);
  1058.                         }
  1059.  
  1060.                     vteeol();
  1061.                     ++i;
  1062.                     }
  1063.                 }
  1064. #if ~WFDEBUG
  1065.             if ((wp->w_flag&WFMODE) != 0)
  1066.                 modeline(wp);
  1067.  
  1068.             wp->w_flag  = 0;
  1069.             wp->w_force = 0;
  1070. #endif
  1071.             }           
  1072. #if WFDEBUG
  1073.         modeline(wp);
  1074.         wp->w_flag =  0;
  1075.         wp->w_force = 0;
  1076. #endif
  1077.         wp = wp->w_wndp;
  1078.         }
  1079.  
  1080.     /* Always recompute the row and column number of the hardware cursor. This
  1081.      * is the only update for simple moves.
  1082.      */
  1083.     lp = curwp->w_linep;
  1084.     currow = curwp->w_toprow;
  1085.  
  1086.     while (lp != curwp->w_dotp)
  1087.         {
  1088.         ++currow;
  1089.         lp = lforw(lp);
  1090.         }
  1091.  
  1092.     curcol = 0;
  1093.     i = 0;
  1094.  
  1095.     while (i < curwp->w_doto)
  1096.         {
  1097.         c = lgetc(lp, i++);
  1098.  
  1099.         if (c == '\t')
  1100.             curcol |= 0x07;
  1101.         else if (c < 0x20 || c == 0x7F)
  1102.             ++curcol;
  1103.  
  1104.         ++curcol;
  1105.         }
  1106.  
  1107.     if (curcol >= term.t_ncol)          /* Long line. */
  1108.         curcol = term.t_ncol-1;
  1109.  
  1110.     /* Special hacking if the screen is garbage. Clear the hardware screen,
  1111.      * and update your copy to agree with it. Set all the virtual screen
  1112.      * change bits, to force a full update.
  1113.      */
  1114.     if (sgarbf != FALSE)
  1115.         {
  1116.         for (i = 0; i < term.t_nrow; ++i)
  1117.             {
  1118.             vscreen[i]->v_flag |= VFCHG;
  1119.             vp1 = pscreen[i];
  1120.             for (j = 0; j < term.t_ncol; ++j)
  1121.                 vp1->v_text[j] = ' ';
  1122.             }
  1123.  
  1124.         movecursor(0, 0);               /* Erase the screen. */
  1125.         (*term.t_eeop)();
  1126.         sgarbf = FALSE;                 /* Erase-page clears */
  1127.         mpresf = FALSE;                 /* the message area. */
  1128.         }
  1129.  
  1130.     /* Make sure that the physical and virtual displays agree. Unlike before,
  1131.      * the "updateline" code is only called with a line that has been updated
  1132.      * for sure.
  1133.      */
  1134.     for (i = 0; i < term.t_nrow; ++i)
  1135.         {
  1136.         vp1 = vscreen[i];
  1137.  
  1138.         if ((vp1->v_flag&VFCHG) != 0)
  1139.             {
  1140.             vp1->v_flag &= ~VFCHG;
  1141.             vp2 = pscreen[i];
  1142.             updateline(i, &vp1->v_text[0], &vp2->v_text[0]);
  1143.             }
  1144.         }
  1145.  
  1146.     /* Finally, update the hardware cursor and flush out buffers. */
  1147.  
  1148.     movecursor(currow, curcol);
  1149.     (*term.t_flush)();
  1150. }
  1151.  
  1152. /*
  1153.  * Update a single line. This does not know how to use insert or delete
  1154.  * character sequences; we are using VT52 functionality. Update the physical
  1155.  * row and column variables. It does try an exploit erase to end of line. The
  1156.  * RAINBOW version of this routine uses fast video.
  1157.  */
  1158. updateline(row, vline, pline)
  1159.     char vline[];
  1160.     char pline[];
  1161. {
  1162. #if RAINBOW
  1163.     register char *cp1;
  1164.     register char *cp2;
  1165.     register int nch;
  1166.  
  1167.     cp1 = &vline[0];                    /* Use fast video. */
  1168.     cp2 = &pline[0];
  1169.     putline(row+1, 1, cp1);
  1170.     nch = term.t_ncol;
  1171.  
  1172.     do
  1173.         {
  1174.         *cp2 = *cp1;
  1175.         ++cp2;
  1176.         ++cp1;
  1177.         }
  1178.     while (--nch);
  1179. #else
  1180.     register char *cp1;
  1181.     register char *cp2;
  1182.     register char *cp3;
  1183.     register char *cp4;
  1184.     register char *cp5;
  1185.     register int nbflag;
  1186.  
  1187.     cp1 = &vline[0];                    /* Compute left match.  */
  1188.     cp2 = &pline[0];
  1189.  
  1190.     while (cp1!=&vline[term.t_ncol] && cp1[0]==cp2[0])
  1191.         {
  1192.         ++cp1;
  1193.         ++cp2;
  1194.         }
  1195.  
  1196.     /* This can still happen, even though we only call this routine on changed
  1197.      * lines. A hard update is always done when a line splits, a massive
  1198.      * change is done, or a buffer is displayed twice. This optimizes out most
  1199.      * of the excess updating. A lot of computes are used, but these tend to
  1200.      * be hard operations that do a lot of update, so I don't really care.
  1201.      */
  1202.     if (cp1 == &vline[term.t_ncol])             /* All equal. */
  1203.         return;
  1204.  
  1205.     nbflag = FALSE;
  1206.     cp3 = &vline[term.t_ncol];          /* Compute right match. */
  1207.     cp4 = &pline[term.t_ncol];
  1208.  
  1209.     while (cp3[-1] == cp4[-1])
  1210.         {
  1211.         --cp3;
  1212.         --cp4;
  1213.         if (cp3[0] != ' ')              /* Note if any nonblank */
  1214.             nbflag = TRUE;              /* in right match. */
  1215.         }
  1216.  
  1217.     cp5 = cp3;
  1218.  
  1219.     if (nbflag == FALSE)                /* Erase to EOL ? */
  1220.         {
  1221.         while (cp5!=cp1 && cp5[-1]==' ')
  1222.             --cp5;
  1223.  
  1224.         if (cp3-cp5 <= 3)               /* Use only if erase is */
  1225.             cp5 = cp3;                  /* fewer characters. */
  1226.         }
  1227.  
  1228.     movecursor(row, cp1-&vline[0]);     /* Go to start of line. */
  1229.  
  1230.     while (cp1 != cp5)                  /* Ordinary. */
  1231.         {
  1232.         (*term.t_putchar)(*cp1);
  1233.         ++ttcol;
  1234.         *cp2++ = *cp1++;
  1235.         }
  1236.  
  1237.     if (cp5 != cp3)                     /* Erase. */
  1238.         {
  1239.         (*term.t_eeol)();
  1240.         while (cp1 != cp3)
  1241.             *cp2++ = *cp1++;
  1242.         }
  1243. #endif
  1244. }
  1245.  
  1246. /*
  1247.  * Redisplay the mode line for the window pointed to by the "wp". This is the
  1248.  * only routine that has any idea of how the modeline is formatted. You can
  1249.  * change the modeline format by hacking at this routine. Called by "update"
  1250.  * any time there is a dirty window.
  1251.  */
  1252. modeline(wp)
  1253.     WINDOW *wp;
  1254. {
  1255.     register char *cp;
  1256.     register int c;
  1257.     register int n;
  1258.     register BUFFER *bp;
  1259.  
  1260.     n = wp->w_toprow+wp->w_ntrows;              /* Location. */
  1261.     vscreen[n]->v_flag |= VFCHG;                /* Redraw next time. */
  1262.     vtmove(n, 0);                               /* Seek to right line. */
  1263.     vtputc('-');
  1264.     bp = wp->w_bufp;
  1265.  
  1266.     if ((bp->b_flag&BFCHG) != 0)                /* "*" if changed. */
  1267.         vtputc('*');
  1268.     else
  1269.         vtputc('-');
  1270.  
  1271.     n  = 2;
  1272.     cp = " MicroEMACS -- ";                     /* Buffer name. */
  1273.  
  1274.     while ((c = *cp++) != 0)
  1275.         {
  1276.         vtputc(c);
  1277.         ++n;
  1278.         }
  1279.  
  1280.     cp = &bp->b_bname[0];
  1281.  
  1282.     while ((c = *cp++) != 0)
  1283.         {
  1284.         vtputc(c);
  1285.         ++n;
  1286.         }
  1287.  
  1288.     vtputc(' ');
  1289.     ++n;
  1290.  
  1291.     if (bp->b_fname[0] != 0)            /* File name. */
  1292.         {
  1293.         cp = "-- File: ";
  1294.  
  1295.         while ((c = *cp++) != 0)
  1296.             {
  1297.             vtputc(c);
  1298.             ++n;
  1299.             }
  1300.  
  1301.         cp = &bp->b_fname[0];
  1302.  
  1303.         while ((c = *cp++) != 0)
  1304.             {
  1305.             vtputc(c);
  1306.             ++n;
  1307.             }
  1308.  
  1309.         vtputc(' ');
  1310.         ++n;
  1311.         }
  1312.  
  1313. #if WFDEBUG
  1314.     vtputc('-');
  1315.     vtputc((wp->w_flag&WFMODE)!=0  ? 'M' : '-');
  1316.     vtputc((wp->w_flag&WFHARD)!=0  ? 'H' : '-');
  1317.     vtputc((wp->w_flag&WFEDIT)!=0  ? 'E' : '-');
  1318.     vtputc((wp->w_flag&WFMOVE)!=0  ? 'V' : '-');
  1319.     vtputc((wp->w_flag&WFFORCE)!=0 ? 'F' : '-');
  1320.     n += 6;
  1321. #endif
  1322.  
  1323.     while (n < term.t_ncol)             /* Pad to full width. */
  1324.         {
  1325.         vtputc('-');
  1326.         ++n;
  1327.         }
  1328. }
  1329.  
  1330. /*
  1331.  * Send a command to the terminal to move the hardware cursor to row "row"
  1332.  * and column "col". The row and column arguments are origin 0. Optimize out
  1333.  * random calls. Update "ttrow" and "ttcol".
  1334.  */
  1335. movecursor(row, col)
  1336.     {
  1337.     if (row!=ttrow || col!=ttcol)
  1338.         {
  1339.         ttrow = row;
  1340.         ttcol = col;
  1341.         (*term.t_move)(row, col);
  1342.         }
  1343.     }
  1344.  
  1345. /*
  1346.  * Erase the message line. This is a special routine because the message line
  1347.  * is not considered to be part of the virtual screen. It always works
  1348.  * immediately; the terminal buffer is flushed via a call to the flusher.
  1349.  */
  1350. mlerase()
  1351.     {
  1352.     movecursor(term.t_nrow, 0);
  1353.     (*term.t_eeol)();
  1354.     (*term.t_flush)();
  1355.     mpresf = FALSE;
  1356.     }
  1357.  
  1358. /*
  1359.  * Ask a yes or no question in the message line. Return either TRUE, FALSE, or
  1360.  * ABORT. The ABORT status is returned if the user bumps out of the question
  1361.  * with a ^G. Used any time a confirmation is required.
  1362.  */
  1363. mlyesno(prompt)
  1364.     char *prompt;
  1365.     {
  1366.     register int s;
  1367.     char buf[64];
  1368.  
  1369.     for (;;)
  1370.         {
  1371.         strcpy(buf, prompt);
  1372.         strcat(buf, " [y/n]? ");
  1373.         s = mlreply(buf, buf, sizeof(buf));
  1374.  
  1375.         if (s == ABORT)
  1376.             return (ABORT);
  1377.  
  1378.         if (s != FALSE)
  1379.             {
  1380.             if (buf[0]=='y' || buf[0]=='Y')
  1381.                 return (TRUE);
  1382.  
  1383.             if (buf[0]=='n' || buf[0]=='N')
  1384.                 return (FALSE);
  1385.             }
  1386.         }
  1387.     }
  1388.  
  1389. /*
  1390.  * Write a prompt into the message line, then read back a response. Keep
  1391.  * track of the physical position of the cursor. If we are in a keyboard
  1392.  * macro throw the prompt away, and return the remembered response. This
  1393.  * lets macros run at full speed. The reply is always terminated by a carriage
  1394.  * return. Handle erase, kill, and abort keys.
  1395.  */
  1396. mlreply(prompt, buf, nbuf)
  1397.     char *prompt;
  1398.     char *buf;
  1399.     {
  1400.     register int cpos;
  1401.     register int i;
  1402.     register int c;
  1403.  
  1404.     cpos = 0;
  1405.  
  1406.     if (kbdmop != NULL)
  1407.         {
  1408.         while ((c = *kbdmop++) != '\0')
  1409.             buf[cpos++] = c;
  1410.  
  1411.         buf[cpos] = 0;
  1412.  
  1413.         if (buf[0] == 0)
  1414.             return (FALSE);
  1415.  
  1416.         return (TRUE);
  1417.         }
  1418.  
  1419.     mlwrite(prompt);
  1420.  
  1421.     for (;;)
  1422.         {
  1423.         c = (*term.t_getchar)();
  1424.  
  1425.         switch (c)
  1426.             {
  1427.             case 0x0D:                  /* Return, end of line */
  1428.                 buf[cpos++] = 0;
  1429.  
  1430.                 if (kbdmip != NULL)
  1431.                     {
  1432.                     if (kbdmip+cpos > &kbdm[NKBDM-3])
  1433.                         {
  1434.                         ctrlg(FALSE, 0);
  1435.                         (*term.t_flush)();
  1436.                         return (ABORT);
  1437.                         }
  1438.  
  1439.                     for (i=0; i<cpos; ++i)
  1440.                         *kbdmip++ = buf[i];
  1441.                     }
  1442.  
  1443.                 (*term.t_putchar)('\r');
  1444.                 ttcol = 0;
  1445.                 (*term.t_flush)();
  1446.  
  1447.                 if (buf[0] == 0)
  1448.                     return (FALSE);
  1449.  
  1450.                 return (TRUE);
  1451.  
  1452.             case 0x07:                  /* Bell, abort */
  1453.                 (*term.t_putchar)('^');
  1454.                 (*term.t_putchar)('G');
  1455.                 ttcol += 2;
  1456.                 ctrlg(FALSE, 0);
  1457.                 (*term.t_flush)();
  1458.                 return (ABORT);
  1459.  
  1460.             case 0x7F:                  /* Rubout, erase */
  1461.             case 0x08:                  /* Backspace, erase */
  1462.                 if (cpos != 0)
  1463.                     {
  1464.                     (*term.t_putchar)('\b');
  1465.                     (*term.t_putchar)(' ');
  1466.                     (*term.t_putchar)('\b');
  1467.                     --ttcol;
  1468.  
  1469.                     if (buf[--cpos] < 0x20)
  1470.                         {
  1471.                         (*term.t_putchar)('\b');
  1472.                         (*term.t_putchar)(' ');
  1473.                         (*term.t_putchar)('\b');
  1474.                         --ttcol;
  1475.                         }
  1476.  
  1477.                     (*term.t_flush)();
  1478.                     }
  1479.  
  1480.                 break;
  1481.  
  1482.             case 0x15:                  /* C-U, kill */
  1483.                 while (cpos != 0)
  1484.                     {
  1485.                     (*term.t_putchar)('\b');
  1486.                     (*term.t_putchar)(' ');
  1487.                     (*term.t_putchar)('\b');
  1488.                     --ttcol;
  1489.  
  1490.                     if (buf[--cpos] < 0x20)
  1491.                         {
  1492.                         (*term.t_putchar)('\b');
  1493.                         (*term.t_putchar)(' ');
  1494.                         (*term.t_putchar)('\b');
  1495.                         --ttcol;
  1496.                         }
  1497.                     }
  1498.  
  1499.                 (*term.t_flush)();
  1500.                 break;
  1501.  
  1502.             default:
  1503.                 if (cpos < nbuf-1)
  1504.                     {
  1505.                     buf[cpos++] = c;
  1506.  
  1507.                     if (c < ' ')
  1508.                         {
  1509.                         (*term.t_putchar)('^');
  1510.                         ++ttcol;
  1511.                         c ^= 0x40;
  1512.                         }
  1513.  
  1514.                     (*term.t_putchar)(c);
  1515.                     ++ttcol;
  1516.                     (*term.t_flush)();
  1517.                     }
  1518.             }
  1519.         }
  1520.     }
  1521.  
  1522. /*
  1523.  * Write a message into the message line. Keep track of the physical cursor
  1524.  * position. A small class of printf like format items is handled. Assumes the
  1525.  * stack grows down; this assumption is made by the "++" in the argument scan
  1526.  * loop. Set the "message line" flag TRUE.
  1527.  */
  1528. mlwrite(fmt, arg)
  1529.     char *fmt;
  1530.     {
  1531.     register int c;
  1532.     register char *ap;
  1533.  
  1534.     movecursor(term.t_nrow, 0);
  1535.     ap = (char *) &arg;
  1536.     while ((c = *fmt++) != 0) {
  1537.         if (c != '%') {
  1538.             (*term.t_putchar)(c);
  1539.             ++ttcol;
  1540.             }
  1541.         else
  1542.             {
  1543.             c = *fmt++;
  1544.             switch (c) {
  1545.                 case 'd':
  1546.                     mlputi(*(int *)ap, 10);
  1547.                     ap += sizeof(int);
  1548.                     break;
  1549.  
  1550.                 case 'o':
  1551.                     mlputi(*(int *)ap,  8);
  1552.                     ap += sizeof(int);
  1553.                     break;
  1554.  
  1555.                 case 'x':
  1556.                     mlputi(*(int *)ap, 16);
  1557.                     ap += sizeof(int);
  1558.                     break;
  1559.  
  1560.                 case 'D':
  1561.                     mlputli(*(long *)ap, 10);
  1562.                     ap += sizeof(long);
  1563.                     break;
  1564.  
  1565.                 case 's':
  1566.                     mlputs(*(char **)ap);
  1567.                     ap += sizeof(char *);
  1568.                     break;
  1569.  
  1570.                 default:
  1571.                     (*term.t_putchar)(c);
  1572.                     ++ttcol;
  1573.                 }
  1574.             }
  1575.         }
  1576.     (*term.t_eeol)();
  1577.     (*term.t_flush)();
  1578.     mpresf = TRUE;
  1579.     }
  1580.  
  1581. /*
  1582.  * Write out a string. Update the physical cursor position. This assumes that
  1583.  * the characters in the string all have width "1"; if this is not the case
  1584.  * things will get screwed up a little.
  1585.  */
  1586. mlputs(s)
  1587.     char *s;
  1588.     {
  1589.     register int c;
  1590.  
  1591.     while ((c = *s++) != 0)
  1592.         {
  1593.         (*term.t_putchar)(c);
  1594.         ++ttcol;
  1595.         }
  1596.     }
  1597.  
  1598. /*
  1599.  * Write out an integer, in the specified radix. Update the physical cursor
  1600.  * position. This will not handle any negative numbers; maybe it should.
  1601.  */
  1602. mlputi(i, r)
  1603.     {
  1604.     register int q;
  1605.     static char hexdigits[] = "0123456789ABCDEF";
  1606.  
  1607.     if (i < 0)
  1608.         {
  1609.         i = -i;
  1610.         (*term.t_putchar)('-');
  1611.         }
  1612.  
  1613.     q = i/r;
  1614.  
  1615.     if (q != 0)
  1616.         mlputi(q, r);
  1617.  
  1618.     (*term.t_putchar)(hexdigits[i%r]);
  1619.     ++ttcol;
  1620.     }
  1621.  
  1622. /*
  1623.  * do the same except as a long integer.
  1624.  */
  1625. mlputli(l, r)
  1626.     long l;
  1627.     {
  1628.     register long q;
  1629.  
  1630.     if (l < 0)
  1631.         {
  1632.         l = -l;
  1633.         (*term.t_putchar)('-');
  1634.         }
  1635.  
  1636.     q = l/r;
  1637.  
  1638.     if (q != 0)
  1639.         mlputli(q, r);
  1640.  
  1641.     (*term.t_putchar)((int)(l%r)+'0');
  1642.     ++ttcol;
  1643.     }
  1644.  
  1645. #if RAINBOW
  1646.  
  1647. putline(row, col, buf)
  1648.     int row, col;
  1649.     char buf[];
  1650.     {
  1651.     int n;
  1652.  
  1653.     n = strlen(buf);
  1654.     if (col + n - 1 > term.t_ncol)
  1655.         n = term.t_ncol - col + 1;
  1656.     Put_Data(row, col, n, buf);
  1657.     }
  1658. #endif
  1659.  
  1660. ==================================================
  1661. file.c
  1662. ==================================================
  1663. /*
  1664.  * The routines in this file
  1665.  * handle the reading and writing of
  1666.  * disk files. All of details about the
  1667.  * reading and writing of the disk are
  1668.  * in "fileio.c".
  1669.  */
  1670. #include        <stdio.h>
  1671. #include        "ed.h"
  1672.  
  1673. /*
  1674.  * Read a file into the current
  1675.  * buffer. This is really easy; all you do it
  1676.  * find the name of the file, and call the standard
  1677.  * "read a file into the current buffer" code.
  1678.  * Bound to "C-X C-R".
  1679.  */
  1680. fileread(f, n)
  1681. {
  1682.         register int    s;
  1683.         char            fname[NFILEN];
  1684.  
  1685.         if ((s=mlreply("Read file: ", fname, NFILEN)) != TRUE)
  1686.                 return (s);
  1687.         return (readin(fname));
  1688. }
  1689.  
  1690. /*
  1691.  * Select a file for editing.
  1692.  * Look around to see if you can find the
  1693.  * fine in another buffer; if you can find it
  1694.  * just switch to the buffer. If you cannot find
  1695.  * the file, create a new buffer, read in the
  1696.  * text, and switch to the new buffer.
  1697.  * Bound to C-X C-V.
  1698.  */
  1699. filevisit(f, n)
  1700. {
  1701.         register BUFFER *bp;
  1702.         register WINDOW *wp;
  1703.         register LINE   *lp;
  1704.         register int    i;
  1705.         register int    s;
  1706.         char            bname[NBUFN];
  1707.         char            fname[NFILEN];
  1708.  
  1709.         if ((s=mlreply("Visit file: ", fname, NFILEN)) != TRUE)
  1710.                 return (s);
  1711.         for (bp=bheadp; bp!=NULL; bp=bp->b_bufp) {
  1712.                 if ((bp->b_flag&BFTEMP)==0 && strcmp(bp->b_fname, fname)==0) {
  1713.                         if (--curbp->b_nwnd == 0) {
  1714.                                 curbp->b_dotp  = curwp->w_dotp;
  1715.                                 curbp->b_doto  = curwp->w_doto;
  1716.                                 curbp->b_markp = curwp->w_markp;
  1717.                                 curbp->b_marko = curwp->w_marko;
  1718.                         }
  1719.                         curbp = bp;
  1720.                         curwp->w_bufp  = bp;
  1721.                         if (bp->b_nwnd++ == 0) {
  1722.                                 curwp->w_dotp  = bp->b_dotp;
  1723.                                 curwp->w_doto  = bp->b_doto;
  1724.                                 curwp->w_markp = bp->b_markp;
  1725.                                 curwp->w_marko = bp->b_marko;
  1726.                         } else {
  1727.                                 wp = wheadp;
  1728.                                 while (wp != NULL) {
  1729.                                         if (wp!=curwp && wp->w_bufp==bp) {
  1730.                                                 curwp->w_dotp  = wp->w_dotp;
  1731.                                                 curwp->w_doto  = wp->w_doto;
  1732.                                                 curwp->w_markp = wp->w_markp;
  1733.                                                 curwp->w_marko = wp->w_marko;
  1734.                                                 break;
  1735.                                         }
  1736.                                         wp = wp->w_wndp;
  1737.                                 }
  1738.                         }
  1739.                         lp = curwp->w_dotp;
  1740.                         i = curwp->w_ntrows/2;
  1741.                         while (i-- && lback(lp)!=curbp->b_linep)
  1742.                                 lp = lback(lp);
  1743.                         curwp->w_linep = lp;
  1744.                         curwp->w_flag |= WFMODE|WFHARD;
  1745.                         mlwrite("[Old buffer]");
  1746.                         return (TRUE);
  1747.                 }
  1748.         }
  1749.         makename(bname, fname);                 /* New buffer name.     */
  1750.         while ((bp=bfind(bname, FALSE, 0)) != NULL) {
  1751.                 s = mlreply("Buffer name: ", bname, NBUFN);
  1752.                 if (s == ABORT)                 /* ^G to just quit      */
  1753.                         return (s);
  1754.                 if (s == FALSE) {               /* CR to clobber it     */
  1755.                         makename(bname, fname);
  1756.                         break;
  1757.                 }
  1758.         }
  1759.         if (bp==NULL && (bp=bfind(bname, TRUE, 0))==NULL) {
  1760.                 mlwrite("Cannot create buffer");
  1761.                 return (FALSE);
  1762.         }
  1763.         if (--curbp->b_nwnd == 0) {             /* Undisplay.           */
  1764.                 curbp->b_dotp = curwp->w_dotp;
  1765.                 curbp->b_doto = curwp->w_doto;
  1766.                 curbp->b_markp = curwp->w_markp;
  1767.                 curbp->b_marko = curwp->w_marko;
  1768.         }
  1769.         curbp = bp;                             /* Switch to it.        */
  1770.         curwp->w_bufp = bp;
  1771.         curbp->b_nwnd++;
  1772.         return (readin(fname));                 /* Read it in.          */
  1773. }
  1774.  
  1775. /*
  1776.  * Read file "fname" into the current
  1777.  * buffer, blowing away any text found there. Called
  1778.  * by both the read and visit commands. Return the final
  1779.  * status of the read. Also called by the mainline,
  1780.  * to read in a file specified on the command line as
  1781.  * an argument.
  1782.  */
  1783. readin(fname)
  1784. char    fname[];
  1785. {
  1786.         register LINE   *lp1;
  1787.         register LINE   *lp2;
  1788.         register int    i;
  1789.         register WINDOW *wp;
  1790.         register BUFFER *bp;
  1791.         register int    s;
  1792.         register int    nbytes;
  1793.         register int    nline;
  1794.         char            line[NLINE];
  1795.  
  1796.         bp = curbp;                             /* Cheap.               */
  1797.         if ((s=bclear(bp)) != TRUE)             /* Might be old.        */
  1798.                 return (s);
  1799.         bp->b_flag &= ~(BFTEMP|BFCHG);
  1800.         strcpy(bp->b_fname, fname);
  1801.         if ((s=ffropen(fname)) == FIOERR)       /* Hard file open.      */
  1802.                 goto out;
  1803.         if (s == FIOFNF) {                      /* File not found.      */
  1804.                 mlwrite("[New file]");
  1805.                 goto out;
  1806.         }
  1807.         mlwrite("[Reading file]");
  1808.         nline = 0;
  1809.         while ((s=ffgetline(line, NLINE)) == FIOSUC) {
  1810.                 nbytes = strlen(line);
  1811.                 if ((lp1=lalloc(nbytes)) == NULL) {
  1812.                         s = FIOERR;             /* Keep message on the  */
  1813.                         break;                  /* display.             */
  1814.                 }
  1815.                 lp2 = lback(curbp->b_linep);
  1816.                 lp2->l_fp = lp1;
  1817.                 lp1->l_fp = curbp->b_linep;
  1818.                 lp1->l_bp = lp2;
  1819.                 curbp->b_linep->l_bp = lp1;
  1820.                 for (i=0; i<nbytes; ++i)
  1821.                         lputc(lp1, i, line[i]);
  1822.                 ++nline;
  1823.         }
  1824.         ffclose();                              /* Ignore errors.       */
  1825.         if (s == FIOEOF) {                      /* Don't zap message!   */
  1826.                 if (nline == 1)
  1827.                         mlwrite("[Read 1 line]");
  1828.                 else
  1829.                         mlwrite("[Read %d lines]", nline);
  1830.         }
  1831. out:
  1832.         for (wp=wheadp; wp!=NULL; wp=wp->w_wndp) {
  1833.                 if (wp->w_bufp == curbp) {
  1834.                         wp->w_linep = lforw(curbp->b_linep);
  1835.                         wp->w_dotp  = lforw(curbp->b_linep);
  1836.                         wp->w_doto  = 0;
  1837.                         wp->w_markp = NULL;
  1838.                         wp->w_marko = 0;
  1839.                         wp->w_flag |= WFMODE|WFHARD;
  1840.                 }
  1841.         }
  1842.         if (s == FIOERR)                        /* False if error.      */
  1843.                 return (FALSE);
  1844.         return (TRUE);
  1845. }
  1846.  
  1847. /*
  1848.  * Take a file name, and from it
  1849.  * fabricate a buffer name. This routine knows
  1850.  * about the syntax of file names on the target system.
  1851.  * I suppose that this information could be put in
  1852.  * a better place than a line of code.
  1853.  */
  1854. makename(bname, fname)
  1855. char    bname[];
  1856. char    fname[];
  1857. {
  1858.         register char   *cp1;
  1859.         register char   *cp2;
  1860.  
  1861.         cp1 = &fname[0];
  1862.         while (*cp1 != 0)
  1863.                 ++cp1;
  1864.  
  1865. #if     AMIGA
  1866.         while (cp1!=&fname[0] && cp1[-1]!=':' && cp1[-1]!='/')
  1867.                 --cp1;
  1868. #endif
  1869. #if     VMS
  1870.         while (cp1!=&fname[0] && cp1[-1]!=':' && cp1[-1]!=']')
  1871.                 --cp1;
  1872. #endif
  1873. #if     CPM
  1874.         while (cp1!=&fname[0] && cp1[-1]!=':')
  1875.                 --cp1;
  1876. #endif
  1877. #if     MSDOS
  1878.         while (cp1!=&fname[0] && cp1[-1]!=':' && cp1[-1]!='\\')
  1879.                 --cp1;
  1880. #endif
  1881. #if     V7
  1882.         while (cp1!=&fname[0] && cp1[-1]!='/')
  1883.                 --cp1;
  1884. #endif
  1885.         cp2 = &bname[0];
  1886.         while (cp2!=&bname[NBUFN-1] && *cp1!=0 && *cp1!=';')
  1887.                 *cp2++ = *cp1++;
  1888.         *cp2 = 0;
  1889. }
  1890.  
  1891. /*
  1892.  * Ask for a file name, and write the
  1893.  * contents of the current buffer to that file.
  1894.  * Update the remembered file name and clear the
  1895.  * buffer changed flag. This handling of file names
  1896.  * is different from the earlier versions, and
  1897.  * is more compatable with Gosling EMACS than
  1898.  * with ITS EMACS. Bound to "C-X C-W".
  1899.  */
  1900. filewrite(f, n)
  1901. {
  1902.         register WINDOW *wp;
  1903.         register int    s;
  1904.         char            fname[NFILEN];
  1905.  
  1906.         if ((s=mlreply("Write file: ", fname, NFILEN)) != TRUE)
  1907.                 return (s);
  1908.         if ((s=writeout(fname)) == TRUE) {
  1909.                 strcpy(curbp->b_fname, fname);
  1910.                 curbp->b_flag &= ~BFCHG;
  1911.                 wp = wheadp;                    /* Update mode lines.   */
  1912.                 while (wp != NULL) {
  1913.                         if (wp->w_bufp == curbp)
  1914.                                 wp->w_flag |= WFMODE;
  1915.                         wp = wp->w_wndp;
  1916.                 }
  1917.         }
  1918.         return (s);
  1919. }
  1920.  
  1921. /*
  1922.  * Save the contents of the current
  1923.  * buffer in its associatd file. No nothing
  1924.  * if nothing has changed (this may be a bug, not a
  1925.  * feature). Error if there is no remembered file
  1926.  * name for the buffer. Bound to "C-X C-S". May
  1927.  * get called by "C-Z".
  1928.  */
  1929. filesave(f, n)
  1930. {
  1931.         register WINDOW *wp;
  1932.         register int    s;
  1933.  
  1934.         if ((curbp->b_flag&BFCHG) == 0)         /* Return, no changes.  */
  1935.                 return (TRUE);
  1936.         if (curbp->b_fname[0] == 0) {           /* Must have a name.    */
  1937.                 mlwrite("No file name");
  1938.                 return (FALSE);
  1939.         }
  1940.         if ((s=writeout(curbp->b_fname)) == TRUE) {
  1941.                 curbp->b_flag &= ~BFCHG;
  1942.                 wp = wheadp;                    /* Update mode lines.   */
  1943.                 while (wp != NULL) {
  1944.                         if (wp->w_bufp == curbp)
  1945.                                 wp->w_flag |= WFMODE;
  1946.                         wp = wp->w_wndp;
  1947.                 }
  1948.         }
  1949.         return (s);
  1950. }
  1951.  
  1952. /*
  1953.  * This function performs the details of file
  1954.  * writing. Uses the file management routines in the
  1955.  * "fileio.c" package. The number of lines written is
  1956.  * displayed. Sadly, it looks inside a LINE; provide
  1957.  * a macro for this. Most of the grief is error
  1958.  * checking of some sort.
  1959.  */
  1960. writeout(fn)
  1961. char    *fn;
  1962. {
  1963.         register int    s;
  1964.         register LINE   *lp;
  1965.         register int    nline;
  1966.  
  1967.         if ((s=ffwopen(fn)) != FIOSUC)          /* Open writes message. */
  1968.                 return (FALSE);
  1969.         lp = lforw(curbp->b_linep);             /* First line.          */
  1970.         nline = 0;                              /* Number of lines.     */
  1971.         while (lp != curbp->b_linep) {
  1972.                 if ((s=ffputline(&lp->l_text[0], llength(lp))) != FIOSUC)
  1973.                         break;
  1974.                 ++nline;
  1975.                 lp = lforw(lp);
  1976.         }
  1977.         if (s == FIOSUC) {                      /* No write error.      */
  1978.                 s = ffclose();
  1979.                 if (s == FIOSUC) {              /* No close error.      */
  1980.                         if (nline == 1)
  1981.                                 mlwrite("[Wrote 1 line]");
  1982.                         else
  1983.                                 mlwrite("[Wrote %d lines]", nline);
  1984.                 }
  1985.         } else                                  /* Ignore close error   */
  1986.                 ffclose();                      /* if a write error.    */
  1987.         if (s != FIOSUC)                        /* Some sort of error.  */
  1988.                 return (FALSE);
  1989.         return (TRUE);
  1990. }
  1991.  
  1992. /*
  1993.  * The command allows the user
  1994.  * to modify the file name associated with
  1995.  * the current buffer. It is like the "f" command
  1996.  * in UNIX "ed". The operation is simple; just zap
  1997.  * the name in the BUFFER structure, and mark the windows
  1998.  * as needing an update. You can type a blank line at the
  1999.  * prompt if you wish.
  2000.  */
  2001. filename(f, n)
  2002. {
  2003.         register WINDOW *wp;
  2004.         register int    s;
  2005.         char            fname[NFILEN];
  2006.  
  2007.         if ((s=mlreply("Name: ", fname, NFILEN)) == ABORT)
  2008.                 return (s);
  2009.         if (s == FALSE)
  2010.                 strcpy(curbp->b_fname, "");
  2011.         else
  2012.                 strcpy(curbp->b_fname, fname);
  2013.         wp = wheadp;                            /* Update mode lines.   */
  2014.         while (wp != NULL) {
  2015.                 if (wp->w_bufp == curbp)
  2016.                         wp->w_flag |= WFMODE;
  2017.                 wp = wp->w_wndp;
  2018.         }
  2019.         return (TRUE);
  2020. }
  2021.  
  2022. ==================================================
  2023. fileio.c
  2024. ==================================================
  2025.  
  2026. /*
  2027.  * The routines in this file read and write ASCII files from the disk. All of
  2028.  * the knowledge about files are here. A better message writing scheme should
  2029.  * be used.
  2030.  */
  2031. #include        <stdio.h>
  2032. #include        "ed.h"
  2033.  
  2034. FILE    *ffp;                           /* File pointer, all functions. */
  2035.  
  2036. /*
  2037.  * Open a file for reading.
  2038.  */
  2039. ffropen(fn)
  2040. char    *fn;
  2041. {
  2042.         if ((ffp=fopen(fn, "r")) == NULL)
  2043.                 return (FIOFNF);
  2044.         return (FIOSUC);
  2045. }
  2046.  
  2047. /*
  2048.  * Open a file for writing. Return TRUE if all is well, and FALSE on error
  2049.  * (cannot create).
  2050.  */
  2051. ffwopen(fn)
  2052. char    *fn;
  2053. {
  2054. #if     VMS
  2055.         register int    fd;
  2056.  
  2057.         if ((fd=creat(fn, 0666, "rfm=var", "rat=cr")) < 0
  2058.         || (ffp=fdopen(fd, "w")) == NULL) {
  2059. #else
  2060.         if ((ffp=fopen(fn, "w")) == NULL) {
  2061. #endif
  2062.                 mlwrite("Cannot open file for writing");
  2063.                 return (FIOERR);
  2064.         }
  2065.         return (FIOSUC);
  2066. }
  2067.  
  2068. /*
  2069.  * Close a file. Should look at the status in all systems.
  2070.  */
  2071. ffclose()
  2072. {
  2073. #if     V7
  2074.         if (fclose(ffp) != FALSE) {
  2075.                 mlwrite("Error closing file");
  2076.                 return(FIOERR);
  2077.         }
  2078.         return(FIOSUC);
  2079. #endif
  2080.         fclose(ffp);
  2081.         return (FIOSUC);
  2082. }
  2083.  
  2084. /*
  2085.  * Write a line to the already opened file. The "buf" points to the buffer,
  2086.  * and the "nbuf" is its length, less the free newline. Return the status.
  2087.  * Check only at the newline.
  2088.  */
  2089. ffputline(buf, nbuf)
  2090. char    buf[];
  2091. {
  2092.         register int    i;
  2093.  
  2094.         for (i = 0; i < nbuf; ++i)
  2095.                 fputc(buf[i]&0xFF, ffp);
  2096.  
  2097.         fputc('\n', ffp);
  2098.  
  2099.         if (ferror(ffp)) {
  2100.                 mlwrite("Write I/O error");
  2101.                 return (FIOERR);
  2102.         }
  2103.  
  2104.         return (FIOSUC);
  2105. }
  2106.  
  2107. /*
  2108.  * Read a line from a file, and store the bytes in the supplied buffer. The
  2109.  * "nbuf" is the length of the buffer. Complain about long lines and lines
  2110.  * at the end of the file that don't have a newline present. Check for I/O
  2111.  * errors too. Return status.
  2112.  */
  2113. ffgetline(buf, nbuf)
  2114. register char   buf[];
  2115. {
  2116.         register int    c;
  2117.         register int    i;
  2118.  
  2119.         i = 0;
  2120.  
  2121.         while ((c = fgetc(ffp)) != EOF && c != '\n') {
  2122.                 if (i >= nbuf-1) {
  2123.                         mlwrite("File has long line");
  2124.                         return (FIOERR);
  2125.                 }
  2126.                 buf[i++] = c;
  2127.         }
  2128.  
  2129.         if (c == EOF) {
  2130.                 if (ferror(ffp)) {
  2131.                         mlwrite("File read error");
  2132.                         return (FIOERR);
  2133.                 }
  2134.  
  2135.                 if (i != 0) {
  2136.                         mlwrite("File has funny line at EOF");
  2137.                         return (FIOERR);
  2138.                 }
  2139.                 return (FIOEOF);
  2140.         }
  2141.  
  2142.         buf[i] = 0;
  2143.         return (FIOSUC);
  2144. }
  2145.  
  2146. ==================================================
  2147. line.c
  2148. ==================================================
  2149. /*
  2150.  * The functions in this file are a general set of line management utilities.
  2151.  * They are the only routines that touch the text. They also touch the buffer
  2152.  * and window structures, to make sure that the necessary updating gets done.
  2153.  * There are routines in this file that handle the kill buffer too. It isn't
  2154.  * here for any good reason.
  2155.  *
  2156.  * Note that this code only updates the dot and mark values in the window list.
  2157.  * Since all the code acts on the current window, the buffer that we are
  2158.  * editing must be being displayed, which means that "b_nwnd" is non zero,
  2159.  * which means that the dot and mark values in the buffer headers are nonsense.
  2160.  */
  2161.  
  2162. #include        <stdio.h>
  2163. #include        "ed.h"
  2164.  
  2165. #define NBLOCK  16                      /* Line block chunk size        */
  2166. #define KBLOCK  256                     /* Kill buffer block size       */
  2167.  
  2168. char    *kbufp  = NULL;                 /* Kill buffer data             */
  2169. int     kused   = 0;                    /* # of bytes used in KB        */
  2170. int     ksize   = 0;                    /* # of bytes allocated in KB   */
  2171.  
  2172. /*
  2173.  * This routine allocates a block of memory large enough to hold a LINE
  2174.  * containing "used" characters. The block is always rounded up a bit. Return
  2175.  * a pointer to the new block, or NULL if there isn't any memory left. Print a
  2176.  * message in the message line if no space.
  2177.  */
  2178. LINE    *
  2179. lalloc(used)
  2180. register int    used;
  2181. {
  2182.         register LINE   *lp;
  2183.         register int    size;
  2184.  
  2185.         size = (used+NBLOCK-1) & ~(NBLOCK-1);
  2186.         if (size == 0)                          /* Assume that an empty */
  2187.                 size = NBLOCK;                  /* line is for type-in. */
  2188.         if ((lp = (LINE *) malloc(sizeof(LINE)+size)) == NULL) {
  2189.                 mlwrite("Cannot allocate %d bytes", size);
  2190.                 return (NULL);
  2191.         }
  2192.         lp->l_size = size;
  2193.         lp->l_used = used;
  2194.         return (lp);
  2195. }
  2196.  
  2197. /*
  2198.  * Delete line "lp". Fix all of the links that might point at it (they are
  2199.  * moved to offset 0 of the next line. Unlink the line from whatever buffer it
  2200.  * might be in. Release the memory. The buffers are updated too; the magic
  2201.  * conditions described in the above comments don't hold here.
  2202.  */
  2203. lfree(lp)
  2204. register LINE   *lp;
  2205. {
  2206.         register BUFFER *bp;
  2207.         register WINDOW *wp;
  2208.  
  2209.         wp = wheadp;
  2210.         while (wp != NULL) {
  2211.                 if (wp->w_linep == lp)
  2212.                         wp->w_linep = lp->l_fp;
  2213.                 if (wp->w_dotp  == lp) {
  2214.                         wp->w_dotp  = lp->l_fp;
  2215.                         wp->w_doto  = 0;
  2216.                 }
  2217.                 if (wp->w_markp == lp) {
  2218.                         wp->w_markp = lp->l_fp;
  2219.                         wp->w_marko = 0;
  2220.                 }
  2221.                 wp = wp->w_wndp;
  2222.         }
  2223.         bp = bheadp;
  2224.         while (bp != NULL) {
  2225.                 if (bp->b_nwnd == 0) {
  2226.                         if (bp->b_dotp  == lp) {
  2227.                                 bp->b_dotp = lp->l_fp;
  2228.                                 bp->b_doto = 0;
  2229.                         }
  2230.                         if (bp->b_markp == lp) {
  2231.                                 bp->b_markp = lp->l_fp;
  2232.                                 bp->b_marko = 0;
  2233.                         }
  2234.                 }
  2235.                 bp = bp->b_bufp;
  2236.         }
  2237.         lp->l_bp->l_fp = lp->l_fp;
  2238.         lp->l_fp->l_bp = lp->l_bp;
  2239.         free((char *) lp);
  2240. }
  2241.  
  2242. /*
  2243.  * This routine gets called when a character is changed in place in the current
  2244.  * buffer. It updates all of the required flags in the buffer and window
  2245.  * system. The flag used is passed as an argument; if the buffer is being
  2246.  * displayed in more than 1 window we change EDIT t HARD. Set MODE if the
  2247.  * mode line needs to be updated (the "*" has to be set).
  2248.  */
  2249. lchange(flag)
  2250. register int    flag;
  2251. {
  2252.         register WINDOW *wp;
  2253.  
  2254.         if (curbp->b_nwnd != 1)                 /* Ensure hard.         */
  2255.                 flag = WFHARD;
  2256.         if ((curbp->b_flag&BFCHG) == 0) {       /* First change, so     */
  2257.                 flag |= WFMODE;                 /* update mode lines.   */
  2258.                 curbp->b_flag |= BFCHG;
  2259.         }
  2260.         wp = wheadp;
  2261.         while (wp != NULL) {
  2262.                 if (wp->w_bufp == curbp)
  2263.                         wp->w_flag |= flag;
  2264.                 wp = wp->w_wndp;
  2265.         }
  2266. }
  2267.  
  2268. /*
  2269.  * Insert "n" copies of the character "c" at the current location of dot. In
  2270.  * the easy case all that happens is the text is stored in the line. In the
  2271.  * hard case, the line has to be reallocated. When the window list is updated,
  2272.  * take special care; I screwed it up once. You always update dot in the
  2273.  * current window. You update mark, and a dot in another window, if it is
  2274.  * greater than the place where you did the insert. Return TRUE if all is
  2275.  * well, and FALSE on errors.
  2276.  */
  2277. linsert(n, c)
  2278. {
  2279.         register char   *cp1;
  2280.         register char   *cp2;
  2281.         register LINE   *lp1;
  2282.         register LINE   *lp2;
  2283.         register LINE   *lp3;
  2284.         register int    doto;
  2285.         register int    i;
  2286.         register WINDOW *wp;
  2287.  
  2288.         lchange(WFEDIT);
  2289.         lp1 = curwp->w_dotp;                    /* Current line         */
  2290.         if (lp1 == curbp->b_linep) {            /* At the end: special  */
  2291.                 if (curwp->w_doto != 0) {
  2292.                         mlwrite("bug: linsert");
  2293.                         return (FALSE);
  2294.                 }
  2295.                 if ((lp2=lalloc(n)) == NULL)    /* Allocate new line    */
  2296.                         return (FALSE);
  2297.                 lp3 = lp1->l_bp;                /* Previous line        */
  2298.                 lp3->l_fp = lp2;                /* Link in              */
  2299.                 lp2->l_fp = lp1;
  2300.                 lp1->l_bp = lp2;
  2301.                 lp2->l_bp = lp3;
  2302.                 for (i=0; i<n; ++i)
  2303.                         lp2->l_text[i] = c;
  2304.                 curwp->w_dotp = lp2;
  2305.                 curwp->w_doto = n;
  2306.                 return (TRUE);
  2307.         }
  2308.         doto = curwp->w_doto;                   /* Save for later.      */
  2309.         if (lp1->l_used+n > lp1->l_size) {      /* Hard: reallocate     */
  2310.                 if ((lp2=lalloc(lp1->l_used+n)) == NULL)
  2311.                         return (FALSE);
  2312.                 cp1 = &lp1->l_text[0];
  2313.                 cp2 = &lp2->l_text[0];
  2314.                 while (cp1 != &lp1->l_text[doto])
  2315.                         *cp2++ = *cp1++;
  2316.                 cp2 += n;
  2317.                 while (cp1 != &lp1->l_text[lp1->l_used])
  2318.                         *cp2++ = *cp1++;
  2319.                 lp1->l_bp->l_fp = lp2;
  2320.                 lp2->l_fp = lp1->l_fp;
  2321.                 lp1->l_fp->l_bp = lp2;
  2322.                 lp2->l_bp = lp1->l_bp;
  2323.                 free((char *) lp1);
  2324.         } else {                                /* Easy: in place       */
  2325.                 lp2 = lp1;                      /* Pretend new line     */
  2326.                 lp2->l_used += n;
  2327.                 cp2 = &lp1->l_text[lp1->l_used];
  2328.                 cp1 = cp2-n;
  2329.                 while (cp1 != &lp1->l_text[doto])
  2330.                         *--cp2 = *--cp1;
  2331.         }
  2332.         for (i=0; i<n; ++i)                     /* Add the characters   */
  2333.                 lp2->l_text[doto+i] = c;
  2334.         wp = wheadp;                            /* Update windows       */
  2335.         while (wp != NULL) {
  2336.                 if (wp->w_linep == lp1)
  2337.                         wp->w_linep = lp2;
  2338.                 if (wp->w_dotp == lp1) {
  2339.                         wp->w_dotp = lp2;
  2340.                         if (wp==curwp || wp->w_doto>doto)
  2341.                                 wp->w_doto += n;
  2342.                 }
  2343.                 if (wp->w_markp == lp1) {
  2344.                         wp->w_markp = lp2;
  2345.                         if (wp->w_marko > doto)
  2346.                                 wp->w_marko += n;
  2347.                 }
  2348.                 wp = wp->w_wndp;
  2349.         }
  2350.         return (TRUE);
  2351. }
  2352.  
  2353. /*
  2354.  * Insert a newline into the buffer at the current location of dot in the
  2355.  * current window. The funny ass-backwards way it does things is not a botch;
  2356.  * it just makes the last line in the file not a special case. Return TRUE if
  2357.  * everything works out and FALSE on error (memory allocation failure). The
  2358.  * update of dot and mark is a bit easier then in the above case, because the
  2359.  * split forces more updating.
  2360.  */
  2361. lnewline()
  2362. {
  2363.         register char   *cp1;
  2364.         register char   *cp2;
  2365.         register LINE   *lp1;
  2366.         register LINE   *lp2;
  2367.         register int    doto;
  2368.         register WINDOW *wp;
  2369.  
  2370.         lchange(WFHARD);
  2371.         lp1  = curwp->w_dotp;                   /* Get the address and  */
  2372.         doto = curwp->w_doto;                   /* offset of "."        */
  2373.         if ((lp2=lalloc(doto)) == NULL)         /* New first half line  */
  2374.                 return (FALSE);
  2375.         cp1 = &lp1->l_text[0];                  /* Shuffle text around  */
  2376.         cp2 = &lp2->l_text[0];
  2377.         while (cp1 != &lp1->l_text[doto])
  2378.                 *cp2++ = *cp1++;
  2379.         cp2 = &lp1->l_text[0];
  2380.         while (cp1 != &lp1->l_text[lp1->l_used])
  2381.                 *cp2++ = *cp1++;
  2382.         lp1->l_used -= doto;
  2383.         lp2->l_bp = lp1->l_bp;
  2384.         lp1->l_bp = lp2;
  2385.         lp2->l_bp->l_fp = lp2;
  2386.         lp2->l_fp = lp1;
  2387.         wp = wheadp;                            /* Windows              */
  2388.         while (wp != NULL) {
  2389.                 if (wp->w_linep == lp1)
  2390.                         wp->w_linep = lp2;
  2391.                 if (wp->w_dotp == lp1) {
  2392.                         if (wp->w_doto < doto)
  2393.                                 wp->w_dotp = lp2;
  2394.                         else
  2395.                                 wp->w_doto -= doto;
  2396.                 }
  2397.                 if (wp->w_markp == lp1) {
  2398.                         if (wp->w_marko < doto)
  2399.                                 wp->w_markp = lp2;
  2400.                         else
  2401.                                 wp->w_marko -= doto;
  2402.                 }
  2403.                 wp = wp->w_wndp;
  2404.         }       
  2405.         return (TRUE);
  2406. }
  2407.  
  2408. /*
  2409.  * This function deletes "n" bytes, starting at dot. It understands how do deal
  2410.  * with end of lines, etc. It returns TRUE if all of the characters were
  2411.  * deleted, and FALSE if they were not (because dot ran into the end of the
  2412.  * buffer. The "kflag" is TRUE if the text should be put in the kill buffer.
  2413.  */
  2414. ldelete(n, kflag)
  2415. {
  2416.         register char   *cp1;
  2417.         register char   *cp2;
  2418.         register LINE   *dotp;
  2419.         register int    doto;
  2420.         register int    chunk;
  2421.         register WINDOW *wp;
  2422.  
  2423.         while (n != 0) {
  2424.                 dotp = curwp->w_dotp;
  2425.                 doto = curwp->w_doto;
  2426.                 if (dotp == curbp->b_linep)     /* Hit end of buffer.   */
  2427.                         return (FALSE);
  2428.                 chunk = dotp->l_used-doto;      /* Size of chunk.       */
  2429.                 if (chunk > n)
  2430.                         chunk = n;
  2431.                 if (chunk == 0) {               /* End of line, merge.  */
  2432.                         lchange(WFHARD);
  2433.                         if (ldelnewline() == FALSE
  2434.                         || (kflag!=FALSE && kinsert('\n')==FALSE))
  2435.                                 return (FALSE);
  2436.                         --n;
  2437.                         continue;
  2438.                 }
  2439.                 lchange(WFEDIT);
  2440.                 cp1 = &dotp->l_text[doto];      /* Scrunch text.        */
  2441.                 cp2 = cp1 + chunk;
  2442.                 if (kflag != FALSE) {           /* Kill?                */
  2443.                         while (cp1 != cp2) {
  2444.                                 if (kinsert(*cp1) == FALSE)
  2445.                                         return (FALSE);
  2446.                                 ++cp1;
  2447.                         }
  2448.                         cp1 = &dotp->l_text[doto];
  2449.                 }
  2450.                 while (cp2 != &dotp->l_text[dotp->l_used])
  2451.                         *cp1++ = *cp2++;
  2452.                 dotp->l_used -= chunk;
  2453.                 wp = wheadp;                    /* Fix windows          */
  2454.                 while (wp != NULL) {
  2455.                         if (wp->w_dotp==dotp && wp->w_doto>=doto) {
  2456.                                 wp->w_doto -= chunk;
  2457.                                 if (wp->w_doto < doto)
  2458.                                         wp->w_doto = doto;
  2459.                         }       
  2460.                         if (wp->w_markp==dotp && wp->w_marko>=doto) {
  2461.                                 wp->w_marko -= chunk;
  2462.                                 if (wp->w_marko < doto)
  2463.                                         wp->w_marko = doto;
  2464.                         }
  2465.                         wp = wp->w_wndp;
  2466.                 }
  2467.                 n -= chunk;
  2468.         }
  2469.         return (TRUE);
  2470. }
  2471.  
  2472. /*
  2473.  * Delete a newline. Join the current line with the next line. If the next line
  2474.  * is the magic header line always return TRUE; merging the last line with the
  2475.  * header line can be thought of as always being a successful operation, even
  2476.  * if nothing is done, and this makes the kill buffer work "right". Easy cases
  2477.  * can be done by shuffling data around. Hard cases require that lines be moved
  2478.  * about in memory. Return FALSE on error and TRUE if all looks ok. Called by
  2479.  * "ldelete" only.
  2480.  */
  2481. ldelnewline()
  2482. {
  2483.         register char   *cp1;
  2484.         register char   *cp2;
  2485.         register LINE   *lp1;
  2486.         register LINE   *lp2;
  2487.         register LINE   *lp3;
  2488.         register WINDOW *wp;
  2489.  
  2490.         lp1 = curwp->w_dotp;
  2491.         lp2 = lp1->l_fp;
  2492.         if (lp2 == curbp->b_linep) {            /* At the buffer end.   */
  2493.                 if (lp1->l_used == 0)           /* Blank line.          */
  2494.                         lfree(lp1);
  2495.                 return (TRUE);
  2496.         }
  2497.         if (lp2->l_used <= lp1->l_size-lp1->l_used) {
  2498.                 cp1 = &lp1->l_text[lp1->l_used];
  2499.                 cp2 = &lp2->l_text[0];
  2500.                 while (cp2 != &lp2->l_text[lp2->l_used])
  2501.                         *cp1++ = *cp2++;
  2502.                 wp = wheadp;
  2503.                 while (wp != NULL) {
  2504.                         if (wp->w_linep == lp2)
  2505.                                 wp->w_linep = lp1;
  2506.                         if (wp->w_dotp == lp2) {
  2507.                                 wp->w_dotp  = lp1;
  2508.                                 wp->w_doto += lp1->l_used;
  2509.                         }
  2510.                         if (wp->w_markp == lp2) {
  2511.                                 wp->w_markp  = lp1;
  2512.                                 wp->w_marko += lp1->l_used;
  2513.                         }
  2514.                         wp = wp->w_wndp;
  2515.                 }               
  2516.                 lp1->l_used += lp2->l_used;
  2517.                 lp1->l_fp = lp2->l_fp;
  2518.                 lp2->l_fp->l_bp = lp1;
  2519.                 free((char *) lp2);
  2520.                 return (TRUE);
  2521.         }
  2522.         if ((lp3=lalloc(lp1->l_used+lp2->l_used)) == NULL)
  2523.                 return (FALSE);
  2524.         cp1 = &lp1->l_text[0];
  2525.         cp2 = &lp3->l_text[0];
  2526.         while (cp1 != &lp1->l_text[lp1->l_used])
  2527.                 *cp2++ = *cp1++;
  2528.         cp1 = &lp2->l_text[0];
  2529.         while (cp1 != &lp2->l_text[lp2->l_used])
  2530.                 *cp2++ = *cp1++;
  2531.         lp1->l_bp->l_fp = lp3;
  2532.         lp3->l_fp = lp2->l_fp;
  2533.         lp2->l_fp->l_bp = lp3;
  2534.         lp3->l_bp = lp1->l_bp;
  2535.         wp = wheadp;
  2536.         while (wp != NULL) {
  2537.                 if (wp->w_linep==lp1 || wp->w_linep==lp2)
  2538.                         wp->w_linep = lp3;
  2539.                 if (wp->w_dotp == lp1)
  2540.                         wp->w_dotp  = lp3;
  2541.                 else if (wp->w_dotp == lp2) {
  2542.                         wp->w_dotp  = lp3;
  2543.                         wp->w_doto += lp1->l_used;
  2544.                 }
  2545.                 if (wp->w_markp == lp1)
  2546.                         wp->w_markp  = lp3;
  2547.                 else if (wp->w_markp == lp2) {
  2548.                         wp->w_markp  = lp3;
  2549.                         wp->w_marko += lp1->l_used;
  2550.                 }
  2551.                 wp = wp->w_wndp;
  2552.         }
  2553.         free((char *) lp1);
  2554.         free((char *) lp2);
  2555.         return (TRUE);
  2556. }
  2557.  
  2558. /*
  2559.  * Delete all of the text saved in the kill buffer. Called by commands when a
  2560.  * new kill context is being created. The kill buffer array is released, just
  2561.  * in case the buffer has grown to immense size. No errors.
  2562.  */
  2563. kdelete()
  2564. {
  2565.         if (kbufp != NULL) {
  2566.                 free((char *) kbufp);
  2567.                 kbufp = NULL;
  2568.                 kused = 0;
  2569.                 ksize = 0;
  2570.         }
  2571. }
  2572.  
  2573. /*
  2574.  * Insert a character to the kill buffer, enlarging the buffer if there isn't
  2575.  * any room. Always grow the buffer in chunks, on the assumption that if you
  2576.  * put something in the kill buffer you are going to put more stuff there too
  2577.  * later. Return TRUE if all is well, and FALSE on errors.
  2578.  */
  2579. kinsert(c)
  2580. {
  2581.         register char   *nbufp;
  2582.         register int    i;
  2583.  
  2584.         if (kused == ksize) {
  2585.                 if ((nbufp=malloc(ksize+KBLOCK)) == NULL)
  2586.                         return (FALSE);
  2587.                 for (i=0; i<ksize; ++i)
  2588.                         nbufp[i] = kbufp[i];
  2589.                 if (kbufp != NULL)
  2590.                         free((char *) kbufp);
  2591.                 kbufp  = nbufp;
  2592.                 ksize += KBLOCK;
  2593.         }
  2594.         kbufp[kused++] = c;
  2595.         return (TRUE);
  2596. }
  2597.  
  2598. /*
  2599.  * This function gets characters from the kill buffer. If the character index
  2600.  * "n" is off the end, it returns "-1". This lets the caller just scan along
  2601.  * until it gets a "-1" back.
  2602.  */
  2603. kremove(n)
  2604. {
  2605.         if (n >= kused)
  2606.                 return (-1);
  2607.         else
  2608.                 return (kbufp[n] & 0xFF);
  2609. }
  2610.  
  2611.  
  2612. ==================================================
  2613. main.c
  2614. ==================================================
  2615. /*
  2616.  * This program is in public domain; written by Dave G. Conroy.
  2617.  * This file contains the main driving routine, and some keyboard processing
  2618.  * code, for the MicroEMACS screen editor.
  2619.  *
  2620.  * REVISION HISTORY:
  2621.  *
  2622.  * 1.0  Steve Wilhite, 30-Nov-85
  2623.  *      - Removed the old LK201 and VT100 logic. Added code to support the
  2624.  *        DEC Rainbow keyboard (which is a LK201 layout) using the the Level
  2625.  *        1 Console In ROM INT. See "rainbow.h" for the function key definitions.
  2626.  *
  2627.  * 2.0  George Jones, 12-Dec-85
  2628.  *      - Ported to Amiga.
  2629.  */
  2630. #include        <stdio.h>
  2631. #include        "ed.h"
  2632.  
  2633. #if     VMS
  2634. #include        <ssdef.h>
  2635. #define GOOD    (SS$_NORMAL)
  2636. #endif
  2637.  
  2638. #ifndef GOOD
  2639. #define GOOD    0
  2640. #endif
  2641.  
  2642. int     currow;                         /* Working cursor row           */
  2643. int     curcol;                         /* Working cursor column        */
  2644. int     fillcol;                        /* Current fill column          */
  2645. int     thisflag;                       /* Flags, this command          */
  2646. int     lastflag;                       /* Flags, last command          */
  2647. int     curgoal;                        /* Goal column                  */
  2648. BUFFER  *curbp;                         /* Current buffer               */
  2649. WINDOW  *curwp;                         /* Current window               */
  2650. BUFFER  *bheadp;                        /* BUFFER listhead              */
  2651. WINDOW  *wheadp;                        /* WINDOW listhead              */
  2652. BUFFER  *blistp;                        /* Buffer list BUFFER           */
  2653. short   kbdm[NKBDM] = {CTLX|')'};       /* Macro                        */
  2654. short   *kbdmip;                        /* Input  for above             */
  2655. short   *kbdmop;                        /* Output for above             */
  2656. char    pat[NPAT];                      /* Pattern                      */
  2657.  
  2658. typedef struct  {
  2659.         short   k_code;                 /* Key code                     */
  2660.         int     (*k_fp)();              /* Routine to handle it         */
  2661. }       KEYTAB;
  2662.  
  2663. extern  int     ctrlg();                /* Abort out of things          */
  2664. extern  int     quit();                 /* Quit                         */
  2665. extern  int     ctlxlp();               /* Begin macro                  */
  2666. extern  int     ctlxrp();               /* End macro                    */
  2667. extern  int     ctlxe();                /* Execute macro                */
  2668. extern  int     fileread();             /* Get a file, read only        */
  2669. extern  int     filevisit();            /* Get a file, read write       */
  2670. extern  int     filewrite();            /* Write a file                 */
  2671. extern  int     filesave();             /* Save current file            */
  2672. extern  int     filename();             /* Adjust file name             */
  2673. extern  int     getccol();              /* Get current column           */
  2674. extern  int     gotobol();              /* Move to start of line        */
  2675. extern  int     forwchar();             /* Move forward by characters   */
  2676. extern  int     gotoeol();              /* Move to end of line          */
  2677. extern  int     backchar();             /* Move backward by characters  */
  2678. extern  int     forwline();             /* Move forward by lines        */
  2679. extern  int     backline();             /* Move backward by lines       */
  2680. extern  int     forwpage();             /* Move forward by pages        */
  2681. extern  int     backpage();             /* Move backward by pages       */
  2682. extern  int     gotobob();              /* Move to start of buffer      */
  2683. extern  int     gotoeob();              /* Move to end of buffer        */
  2684. extern  int     setfillcol();           /* Set fill column.             */
  2685. extern  int     setmark();              /* Set mark                     */
  2686. extern  int     swapmark();             /* Swap "." and mark            */
  2687. extern  int     forwsearch();           /* Search forward               */
  2688. extern  int     backsearch();           /* Search backwards             */
  2689. extern  int     showcpos();             /* Show the cursor position     */
  2690. extern  int     nextwind();             /* Move to the next window      */
  2691. extern  int     prevwind();             /* Move to the previous window  */
  2692. extern  int     onlywind();             /* Make current window only one */
  2693. extern  int     splitwind();            /* Split current window         */
  2694. extern  int     mvdnwind();             /* Move window down             */
  2695. extern  int     mvupwind();             /* Move window up               */
  2696. extern  int     enlargewind();          /* Enlarge display window.      */
  2697. extern  int     shrinkwind();           /* Shrink window.               */
  2698. extern  int     listbuffers();          /* Display list of buffers      */
  2699. extern  int     usebuffer();            /* Switch a window to a buffer  */
  2700. extern  int     killbuffer();           /* Make a buffer go away.       */
  2701. extern  int     reposition();           /* Reposition window            */
  2702. extern  int     refresh();              /* Refresh the screen           */
  2703. extern  int     twiddle();              /* Twiddle characters           */
  2704. extern  int     tab();                  /* Insert tab                   */
  2705. extern  int     newline();              /* Insert CR-LF                 */
  2706. extern  int     indent();               /* Insert CR-LF, then indent    */
  2707. extern  int     openline();             /* Open up a blank line         */
  2708. extern  int     deblank();              /* Delete blank lines           */
  2709. extern  int     quote();                /* Insert literal               */
  2710. extern  int     backword();             /* Backup by words              */
  2711. extern  int     forwword();             /* Advance by words             */
  2712. extern  int     forwdel();              /* Forward delete               */
  2713. extern  int     backdel();              /* Backward delete              */
  2714. extern  int     kill();                 /* Kill forward                 */
  2715. extern  int     yank();                 /* Yank back from killbuffer.   */
  2716. extern  int     upperword();            /* Upper case word.             */
  2717. extern  int     lowerword();            /* Lower case word.             */
  2718. extern  int     upperregion();          /* Upper case region.           */
  2719. extern  int     lowerregion();          /* Lower case region.           */
  2720. extern  int     capword();              /* Initial capitalize word.     */
  2721. extern  int     delfword();             /* Delete forward word.         */
  2722. extern  int     delbword();             /* Delete backward word.        */
  2723. extern  int     killregion();           /* Kill region.                 */
  2724. extern  int     copyregion();           /* Copy region to kill buffer.  */
  2725. extern  int     spawncli();             /* Run CLI in a subjob.         */
  2726. extern  int     spawn();                /* Run a command in a subjob.   */
  2727. extern  int     quickexit();            /* low keystroke style exit.    */
  2728.  
  2729. /*
  2730.  * Command table.
  2731.  * This table  is *roughly* in ASCII order, left to right across the
  2732.  * characters of the command. This expains the funny location of the
  2733.  * control-X commands.
  2734.  */
  2735. KEYTAB  keytab[] = {
  2736.         CTRL|'@',               &setmark,
  2737.         CTRL|'A',               &gotobol,
  2738.         CTRL|'B',               &backchar,
  2739.         CTRL|'C',               &spawncli,      /* Run CLI in subjob.   */
  2740.         CTRL|'D',               &forwdel,
  2741.         CTRL|'E',               &gotoeol,
  2742.         CTRL|'F',               &forwchar,
  2743.         CTRL|'G',               &ctrlg,
  2744.         CTRL|'H',               &backdel,
  2745.         CTRL|'I',               &tab,
  2746.         CTRL|'J',               &indent,
  2747.         CTRL|'K',               &kill,
  2748.         CTRL|'L',               &refresh,
  2749.         CTRL|'M',               &newline,
  2750.         CTRL|'N',               &forwline,
  2751.         CTRL|'O',               &openline,
  2752.         CTRL|'P',               &backline,
  2753.         CTRL|'Q',               "e,         /* Often unreachable    */
  2754.         CTRL|'R',               &backsearch,
  2755.         CTRL|'S',               &forwsearch,    /* Often unreachable    */
  2756.         CTRL|'T',               &twiddle,
  2757.         CTRL|'V',               &forwpage,
  2758.         CTRL|'W',               &killregion,
  2759.         CTRL|'Y',               &yank,
  2760.         CTRL|'Z',               &quickexit,     /* quick save and exit  */
  2761.         CTLX|CTRL|'B',          &listbuffers,
  2762.         CTLX|CTRL|'C',          &quit,          /* Hard quit.           */
  2763.         CTLX|CTRL|'F',          &filename,
  2764.         CTLX|CTRL|'L',          &lowerregion,
  2765.         CTLX|CTRL|'O',          &deblank,
  2766.         CTLX|CTRL|'N',          &mvdnwind,
  2767.         CTLX|CTRL|'P',          &mvupwind,
  2768.         CTLX|CTRL|'R',          &fileread,
  2769.         CTLX|CTRL|'S',          &filesave,      /* Often unreachable    */
  2770.         CTLX|CTRL|'U',          &upperregion,
  2771.         CTLX|CTRL|'V',          &filevisit,
  2772.         CTLX|CTRL|'W',          &filewrite,
  2773.         CTLX|CTRL|'X',          &swapmark,
  2774.         CTLX|CTRL|'Z',          &shrinkwind,
  2775.         CTLX|'!',               &spawn,         /* Run 1 command.       */
  2776.         CTLX|'=',               &showcpos,
  2777.         CTLX|'(',               &ctlxlp,
  2778.         CTLX|')',               &ctlxrp,
  2779.         CTLX|'1',               &onlywind,
  2780.         CTLX|'2',               &splitwind,
  2781.         CTLX|'B',               &usebuffer,
  2782.         CTLX|'E',               &ctlxe,
  2783.         CTLX|'F',               &setfillcol,
  2784.         CTLX|'K',               &killbuffer,
  2785.         CTLX|'N',               &nextwind,
  2786.         CTLX|'P',               &prevwind,
  2787.         CTLX|'Z',               &enlargewind,
  2788.         META|CTRL|'H',          &delbword,
  2789.         META|'!',               &reposition,
  2790.         META|'.',               &setmark,
  2791.         META|'>',               &gotoeob,
  2792.         META|'<',               &gotobob,
  2793.         META|'B',               &backword,
  2794.         META|'C',               &capword,
  2795.         META|'D',               &delfword,
  2796.         META|'F',               &forwword,
  2797.         META|'L',               &lowerword,
  2798.         META|'U',               &upperword,
  2799.         META|'V',               &backpage,
  2800.         META|'W',               ©region,
  2801.         META|0x7F,              &delbword,
  2802.         0x7F,                   &backdel
  2803. };
  2804.  
  2805. #define NKEYTAB (sizeof(keytab)/sizeof(keytab[0]))
  2806.  
  2807.  
  2808.  
  2809.  
  2810.  
  2811.  
  2812.  
  2813.  
  2814. #if RAINBOW
  2815.  
  2816. #include "rainbow.h"
  2817.  
  2818. /*
  2819.  * Mapping table from the LK201 function keys to the internal EMACS character.
  2820.  */
  2821.  
  2822. short lk_map[][2] = {
  2823.         Up_Key,                         CTRL+'P',
  2824.         Down_Key,                       CTRL+'N',
  2825.         Left_Key,                       CTRL+'B',
  2826.         Right_Key,                      CTRL+'F',
  2827.         Shift+Left_Key,                 META+'B',
  2828.         Shift+Right_Key,                META+'F',
  2829.         Control+Left_Key,               CTRL+'A',
  2830.         Control+Right_Key,              CTRL+'E',
  2831.         Prev_Scr_Key,                   META+'V',
  2832.         Next_Scr_Key,                   CTRL+'V',
  2833.         Shift+Up_Key,                   META+'<',
  2834.         Shift+Down_Key,                 META+'>',
  2835.         Cancel_Key,                     CTRL+'G',
  2836.         Find_Key,                       CTRL+'S',
  2837.         Shift+Find_Key,                 CTRL+'R',
  2838.         Insert_Key,                     CTRL+'Y',
  2839.         Options_Key,                    CTRL+'D',
  2840.         Shift+Options_Key,              META+'D',
  2841.         Remove_Key,                     CTRL+'W',
  2842.         Shift+Remove_Key,               META+'W',
  2843.         Select_Key,                     CTRL+'@',
  2844.         Shift+Select_Key,               CTLX+CTRL+'X',
  2845.         Interrupt_Key,                  CTRL+'U',
  2846.         Keypad_PF2,                     META+'L',
  2847.         Keypad_PF3,                     META+'C',
  2848.         Keypad_PF4,                     META+'U',
  2849.         Shift+Keypad_PF2,               CTLX+CTRL+'L',
  2850.         Shift+Keypad_PF4,               CTLX+CTRL+'U',
  2851.         Keypad_1,                       CTLX+'1',
  2852.         Keypad_2,                       CTLX+'2',
  2853.         Do_Key,                         CTLX+'E',
  2854.         Keypad_4,                       CTLX+CTRL+'B',
  2855.         Keypad_5,                       CTLX+'B',
  2856.         Keypad_6,                       CTLX+'K',
  2857.         Resume_Key,                     META+'!',
  2858.         Control+Next_Scr_Key,           CTLX+'N',
  2859.         Control+Prev_Scr_Key,           CTLX+'P',
  2860.         Control+Up_Key,                 CTLX+CTRL+'P',
  2861.         Control+Down_Key,               CTLX+CTRL+'N',
  2862.         Help_Key,                       CTLX+'=',
  2863.         Shift+Do_Key,                   CTLX+'(',
  2864.         Control+Do_Key,                 CTLX+')',
  2865.         Keypad_0,                       CTLX+'Z',
  2866.         Shift+Keypad_0,                 CTLX+CTRL+'Z',
  2867.         Main_Scr_Key,                   CTRL+'C',
  2868.         Keypad_Enter,                   CTLX+'!',
  2869.         Exit_Key,                       CTLX+CTRL+'C',
  2870.         Shift+Exit_Key,                 CTRL+'Z'
  2871.         };
  2872.  
  2873. #define lk_map_size     (sizeof(lk_map)/2)
  2874.  
  2875. #endif
  2876.  
  2877.  
  2878.  
  2879.  
  2880.  
  2881.  
  2882.  
  2883.  
  2884. main(argc, argv)
  2885. char    *argv[];
  2886. {
  2887.         register int    c;
  2888.         register int    f;
  2889.         register int    n;
  2890.         register int    mflag;
  2891.         char            bname[NBUFN];
  2892.  
  2893.         strcpy(bname, "main");                  /* Work out the name of */
  2894.         if (argc > 1)                           /* the default buffer.  */
  2895.                 makename(bname, argv[1]);
  2896.         edinit(bname);                          /* Buffers, windows.    */
  2897.         vtinit();                               /* Displays.            */
  2898.         if (argc > 1) {
  2899.                 update();                       /* You have to update   */
  2900.                 readin(argv[1]);                /* in case "[New file]" */
  2901.         }
  2902.         lastflag = 0;                           /* Fake last flags.     */
  2903. loop:
  2904.         update();                               /* Fix up the screen    */
  2905.         c = getkey();
  2906.         if (mpresf != FALSE) {
  2907.                 mlerase();
  2908.                 update();
  2909.                 if (c == ' ')                   /* ITS EMACS does this  */
  2910.                         goto loop;
  2911.         }
  2912.         f = FALSE;
  2913.         n = 1;
  2914.         if (c == (CTRL|'U')) {                  /* ^U, start argument   */
  2915.                 f = TRUE;
  2916.                 n = 4;                          /* with argument of 4 */
  2917.                 mflag = 0;                      /* that can be discarded. */
  2918.                 mlwrite("Arg: 4");
  2919.                 while ((c=getkey()) >='0' && c<='9' || c==(CTRL|'U') || c=='-'){
  2920.                         if (c == (CTRL|'U'))
  2921.                                 n = n*4;
  2922.                         /*
  2923.                          * If dash, and start of argument string, set arg.
  2924.                          * to -1.  Otherwise, insert it.
  2925.                          */
  2926.                         else if (c == '-') {
  2927.                                 if (mflag)
  2928.                                         break;
  2929.                                 n = 0;
  2930.                                 mflag = -1;
  2931.                         }
  2932.                         /*
  2933.                          * If first digit entered, replace previous argument
  2934.                          * with digit and set sign.  Otherwise, append to arg.
  2935.                          */
  2936.                         else {
  2937.                                 if (!mflag) {
  2938.                                         n = 0;
  2939.                                         mflag = 1;
  2940.                                 }
  2941.                                 n = 10*n + c - '0';
  2942.                         }
  2943.                         mlwrite("Arg: %d", (mflag >=0) ? n : (n ? -n : -1));
  2944.                 }
  2945.                 /*
  2946.                  * Make arguments preceded by a minus sign negative and change
  2947.                  * the special argument "^U -" to an effective "^U -1".
  2948.                  */
  2949.                 if (mflag == -1) {
  2950.                         if (n == 0)
  2951.                                 n++;
  2952.                         n = -n;
  2953.                 }
  2954.         }
  2955.         if (c == (CTRL|'X'))                    /* ^X is a prefix       */
  2956.                 c = CTLX | getctl();
  2957.         if (kbdmip != NULL) {                   /* Save macro strokes.  */
  2958.                 if (c!=(CTLX|')') && kbdmip>&kbdm[NKBDM-6]) {
  2959.                         ctrlg(FALSE, 0);
  2960.                         goto loop;
  2961.                 }
  2962.                 if (f != FALSE) {
  2963.                         *kbdmip++ = (CTRL|'U');
  2964.                         *kbdmip++ = n;
  2965.                 }
  2966.                 *kbdmip++ = c;
  2967.         }
  2968.         execute(c, f, n);                       /* Do it.               */
  2969.         goto loop;
  2970. }
  2971.  
  2972. /*
  2973.  * Initialize all of the buffers and windows. The buffer name is passed down
  2974.  * as an argument, because the main routine may have been told to read in a
  2975.  * file by default, and we want the buffer name to be right.
  2976.  */
  2977. edinit(bname)
  2978. char    bname[];
  2979. {
  2980.         register BUFFER *bp;
  2981.         register WINDOW *wp;
  2982.  
  2983.         bp = bfind(bname, TRUE, 0);             /* First buffer         */
  2984.         blistp = bfind("[List]", TRUE, BFTEMP); /* Buffer list buffer   */
  2985.         wp = (WINDOW *) malloc(sizeof(WINDOW)); /* First window         */
  2986.         if (bp==NULL || wp==NULL || blistp==NULL)
  2987.                 exit(1);
  2988.         curbp  = bp;                            /* Make this current    */
  2989.         wheadp = wp;
  2990.         curwp  = wp;
  2991.         wp->w_wndp  = NULL;                     /* Initialize window    */
  2992.         wp->w_bufp  = bp;
  2993.         bp->b_nwnd  = 1;                        /* Displayed.           */
  2994.         wp->w_linep = bp->b_linep;
  2995.         wp->w_dotp  = bp->b_linep;
  2996.         wp->w_doto  = 0;
  2997.         wp->w_markp = NULL;
  2998.         wp->w_marko = 0;
  2999.         wp->w_toprow = 0;
  3000.         wp->w_ntrows = term.t_nrow-1;           /* "-1" for mode line.  */
  3001.         wp->w_force = 0;
  3002.         wp->w_flag  = WFMODE|WFHARD;            /* Full.                */
  3003. }
  3004.         
  3005. /*
  3006.  * This is the general command execution routine. It handles the fake binding
  3007.  * of all the keys to "self-insert". It also clears out the "thisflag" word,
  3008.  * and arranges to move it to the "lastflag", so that the next command can
  3009.  * look at it. Return the status of command.
  3010.  */
  3011. execute(c, f, n)
  3012. {
  3013.         register KEYTAB *ktp;
  3014.         register int    status;
  3015.  
  3016.         ktp = &keytab[0];                       /* Look in key table.   */
  3017.         while (ktp < &keytab[NKEYTAB]) {
  3018.                 if (ktp->k_code == c) {
  3019.                         thisflag = 0;
  3020.                         status   = (*ktp->k_fp)(f, n);
  3021.                         lastflag = thisflag;
  3022.                         return (status);
  3023.                 }
  3024.                 ++ktp;
  3025.         }
  3026.  
  3027.         /*
  3028.          * If a space was typed, fill column is defined, the argument is non-
  3029.          * negative, and we are now past fill column, perform word wrap.
  3030.          */
  3031.         if (c == ' ' && fillcol > 0 && n>=0 && getccol(FALSE) > fillcol)
  3032.                 wrapword();
  3033.  
  3034.         if ((c>=0x20 && c<=0x7E)                /* Self inserting.      */
  3035.         ||  (c>=0xA0 && c<=0xFE)) {
  3036.                 if (n <= 0) {                   /* Fenceposts.          */
  3037.                         lastflag = 0;
  3038.                         return (n<0 ? FALSE : TRUE);
  3039.                 }
  3040.                 thisflag = 0;                   /* For the future.      */
  3041.                 status   = linsert(n, c);
  3042.                 lastflag = thisflag;
  3043.                 return (status);
  3044.         }
  3045.         lastflag = 0;                           /* Fake last flags.     */
  3046.         return (FALSE);
  3047. }
  3048.  
  3049. /*
  3050.  * Read in a key.
  3051.  * Do the standard keyboard preprocessing. Convert the keys to the internal
  3052.  * character set.
  3053.  */
  3054. getkey()
  3055. {
  3056.         register int    c;
  3057.  
  3058.         c = (*term.t_getchar)();
  3059.  
  3060. #if RAINBOW
  3061.  
  3062.         if (c & Function_Key)
  3063.                 {
  3064.                 int i;
  3065.  
  3066.                 for (i = 0; i < lk_map_size; i++)
  3067.                         if (c == lk_map[i][0])
  3068.                                 return lk_map[i][1];
  3069.                 }
  3070.         else if (c == Shift + 015) return CTRL | 'J';
  3071.         else if (c == Shift + 0x7F) return META | 0x7F;
  3072. #endif
  3073.  
  3074.         if (c == METACH) {                      /* Apply M- prefix      */
  3075.                 c = getctl();
  3076.                 return (META | c);
  3077.         }
  3078.  
  3079.         if (c>=0x00 && c<=0x1F)                 /* C0 control -> C-     */
  3080.                 c = CTRL | (c+'@');
  3081.         return (c);
  3082. }
  3083.  
  3084. /*
  3085.  * Get a key.
  3086.  * Apply control modifications to the read key.
  3087.  */
  3088. getctl()
  3089. {
  3090.         register int    c;
  3091.  
  3092.         c = (*term.t_getchar)();
  3093.         if (c>='a' && c<='z')                   /* Force to upper       */
  3094.                 c -= 0x20;
  3095.         if (c>=0x00 && c<=0x1F)                 /* C0 control -> C-     */
  3096.                 c = CTRL | (c+'@');
  3097.         return (c);
  3098. }
  3099.  
  3100. /*
  3101.  * Fancy quit command, as implemented by Norm. If the current buffer has
  3102.  * changed do a write current buffer and exit emacs, otherwise simply exit.
  3103.  */
  3104. quickexit(f, n)
  3105. {
  3106.         if ((curbp->b_flag&BFCHG) != 0          /* Changed.             */
  3107.         && (curbp->b_flag&BFTEMP) == 0)         /* Real.                */
  3108.                 filesave(f, n);
  3109.         quit(f, n);                             /* conditionally quit   */
  3110. }
  3111.  
  3112. /*
  3113.  * Quit command. If an argument, always quit. Otherwise confirm if a buffer
  3114.  * has been changed and not written out. Normally bound to "C-X C-C".
  3115.  */
  3116. quit(f, n)
  3117. {
  3118.         register int    s;
  3119.  
  3120.         if (f != FALSE                          /* Argument forces it.  */
  3121.         || anycb() == FALSE                     /* All buffers clean.   */
  3122.         || (s=mlyesno("Quit")) == TRUE) {       /* User says it's OK.   */
  3123.                 vttidy();
  3124.                 exit(GOOD);
  3125.         }
  3126.         return (s);
  3127. }
  3128.  
  3129. /*
  3130.  * Begin a keyboard macro.
  3131.  * Error if not at the top level in keyboard processing. Set up variables and
  3132.  * return.
  3133.  */
  3134. ctlxlp(f, n)
  3135. {
  3136.         if (kbdmip!=NULL || kbdmop!=NULL) {
  3137.                 mlwrite("Not now");
  3138.                 return (FALSE);
  3139.         }
  3140.         mlwrite("[Start macro]");
  3141.         kbdmip = &kbdm[0];
  3142.         return (TRUE);
  3143. }
  3144.  
  3145. /*
  3146.  * End keyboard macro. Check for the same limit conditions as the above
  3147.  * routine. Set up the variables and return to the caller.
  3148.  */
  3149. ctlxrp(f, n)
  3150. {
  3151.         if (kbdmip == NULL) {
  3152.                 mlwrite("Not now");
  3153.                 return (FALSE);
  3154.         }
  3155.         mlwrite("[End macro]");
  3156.         kbdmip = NULL;
  3157.         return (TRUE);
  3158. }
  3159.  
  3160. /*
  3161.  * Execute a macro.
  3162.  * The command argument is the number of times to loop. Quit as soon as a
  3163.  * command gets an error. Return TRUE if all ok, else FALSE.
  3164.  */
  3165. ctlxe(f, n)
  3166. {
  3167.         register int    c;
  3168.         register int    af;
  3169.         register int    an;
  3170.         register int    s;
  3171.  
  3172.         if (kbdmip!=NULL || kbdmop!=NULL) {
  3173.                 mlwrite("Not now");
  3174.                 return (FALSE);
  3175.         }
  3176.         if (n <= 0) 
  3177.                 return (TRUE);
  3178.         do {
  3179.                 kbdmop = &kbdm[0];
  3180.                 do {
  3181.                         af = FALSE;
  3182.                         an = 1;
  3183.                         if ((c = *kbdmop++) == (CTRL|'U')) {
  3184.                                 af = TRUE;
  3185.                                 an = *kbdmop++;
  3186.                                 c  = *kbdmop++;
  3187.                         }
  3188.                         s = TRUE;
  3189.                 } while (c!=(CTLX|')') && (s=execute(c, af, an))==TRUE);
  3190.                 kbdmop = NULL;
  3191.         } while (s==TRUE && --n);
  3192.         return (s);
  3193. }
  3194.  
  3195. /*
  3196.  * Abort.
  3197.  * Beep the beeper. Kill off any keyboard macro, etc., that is in progress.
  3198.  * Sometimes called as a routine, to do general aborting of stuff.
  3199.  */
  3200. ctrlg(f, n)
  3201. {
  3202.         (*term.t_beep)();
  3203.         if (kbdmip != NULL) {
  3204.                 kbdm[0] = (CTLX|')');
  3205.                 kbdmip  = NULL;
  3206.         }
  3207.         return (ABORT);
  3208. }
  3209.  
  3210.  
  3211. ==================================================
  3212. random.c
  3213. ==================================================
  3214. /*
  3215.  * This file contains the command processing functions for a number of random
  3216.  * commands. There is no functional grouping here, for sure.
  3217.  */
  3218.  
  3219. #include        <stdio.h>
  3220. #include        "ed.h"
  3221.  
  3222. int     tabsize;                        /* Tab size (0: use real tabs)  */
  3223.  
  3224. /*
  3225.  * Set fill column to n. 
  3226.  */
  3227. setfillcol(f, n)
  3228. {
  3229.         fillcol = n;
  3230.         return(TRUE);
  3231. }
  3232.  
  3233. /*
  3234.  * Display the current position of the cursor, in origin 1 X-Y coordinates,
  3235.  * the character that is under the cursor (in octal), and the fraction of the
  3236.  * text that is before the cursor. The displayed column is not the current
  3237.  * column, but the column that would be used on an infinite width display.
  3238.  * Normally this is bound to "C-X =".
  3239.  */
  3240. showcpos(f, n)
  3241. {
  3242.         register LINE   *clp;
  3243.         register long   nch;
  3244.         register int    cbo;
  3245.         register long   nbc;
  3246.         register int    cac;
  3247.         register int    ratio;
  3248.         register int    col;
  3249.         register int    i;
  3250.         register int    c;
  3251.  
  3252.         clp = lforw(curbp->b_linep);            /* Grovel the data.     */
  3253.         cbo = 0;
  3254.         nch = 0;
  3255.         for (;;) {
  3256.                 if (clp==curwp->w_dotp && cbo==curwp->w_doto) {
  3257.                         nbc = nch;
  3258.                         if (cbo == llength(clp))
  3259.                                 cac = '\n';
  3260.                         else
  3261.                                 cac = lgetc(clp, cbo);
  3262.                 }
  3263.                 if (cbo == llength(clp)) {
  3264.                         if (clp == curbp->b_linep)
  3265.                                 break;
  3266.                         clp = lforw(clp);
  3267.                         cbo = 0;
  3268.                 } else
  3269.                         ++cbo;
  3270.                 ++nch;
  3271.         }
  3272.         col = getccol(FALSE);                   /* Get real column.     */
  3273.         ratio = 0;                              /* Ratio before dot.    */
  3274.         if (nch != 0)
  3275.                 ratio = (100L*nbc) / nch;
  3276.         mlwrite("X=%d Y=%d CH=0x%x .=%D (%d%% of %D)",
  3277.                 col+1, currow+1, cac, nbc, ratio, nch);
  3278.         return (TRUE);
  3279. }
  3280.  
  3281. /*
  3282.  * Return current column.  Stop at first non-blank given TRUE argument.
  3283.  */
  3284. getccol(bflg)
  3285. int bflg;
  3286. {
  3287.         register int c, i, col;
  3288.         col = 0;
  3289.         for (i=0; i<curwp->w_doto; ++i) {
  3290.                 c = lgetc(curwp->w_dotp, i);
  3291.                 if (c!=' ' && c!='\t' && bflg)
  3292.                         break;
  3293.                 if (c == '\t')
  3294.                         col |= 0x07;
  3295.                 else if (c<0x20 || c==0x7F)
  3296.                         ++col;
  3297.                 ++col;
  3298.         }
  3299.         return(col);
  3300. }
  3301.  
  3302. /*
  3303.  * Twiddle the two characters on either side of dot. If dot is at the end of
  3304.  * the line twiddle the two characters before it. Return with an error if dot
  3305.  * is at the beginning of line; it seems to be a bit pointless to make this
  3306.  * work. This fixes up a very common typo with a single stroke. Normally bound
  3307.  * to "C-T". This always works within a line, so "WFEDIT" is good enough.
  3308.  */
  3309. twiddle(f, n)
  3310. {
  3311.         register LINE   *dotp;
  3312.         register int    doto;
  3313.         register int    cl;
  3314.         register int    cr;
  3315.  
  3316.         dotp = curwp->w_dotp;
  3317.         doto = curwp->w_doto;
  3318.         if (doto==llength(dotp) && --doto<0)
  3319.                 return (FALSE);
  3320.         cr = lgetc(dotp, doto);
  3321.         if (--doto < 0)
  3322.                 return (FALSE);
  3323.         cl = lgetc(dotp, doto);
  3324.         lputc(dotp, doto+0, cr);
  3325.         lputc(dotp, doto+1, cl);
  3326.         lchange(WFEDIT);
  3327.         return (TRUE);
  3328. }
  3329.  
  3330. /*
  3331.  * Quote the next character, and insert it into the buffer. All the characters
  3332.  * are taken literally, with the exception of the newline, which always has
  3333.  * its line splitting meaning. The character is always read, even if it is
  3334.  * inserted 0 times, for regularity. Bound to "M-Q" (for me) and "C-Q" (for
  3335.  * Rich, and only on terminals that don't need XON-XOFF).
  3336.  */
  3337. quote(f, n)
  3338. {
  3339.         register int    s;
  3340.         register int    c;
  3341.  
  3342.         c = (*term.t_getchar)();
  3343.         if (n < 0)
  3344.                 return (FALSE);
  3345.         if (n == 0)
  3346.                 return (TRUE);
  3347.         if (c == '\n') {
  3348.                 do {
  3349.                         s = lnewline();
  3350.                 } while (s==TRUE && --n);
  3351.                 return (s);
  3352.         }
  3353.         return (linsert(n, c));
  3354. }
  3355.  
  3356. /*
  3357.  * Set tab size if given non-default argument (n <> 1).  Otherwise, insert a
  3358.  * tab into file.  If given argument, n, of zero, change to true tabs.
  3359.  * If n > 1, simulate tab stop every n-characters using spaces. This has to be
  3360.  * done in this slightly funny way because the tab (in ASCII) has been turned
  3361.  * into "C-I" (in 10 bit code) already. Bound to "C-I".
  3362.  */
  3363. tab(f, n)
  3364. {
  3365.         if (n < 0)
  3366.                 return (FALSE);
  3367.         if (n == 0 || n > 1) {
  3368.                 tabsize = n;
  3369.                 return(TRUE);
  3370.         }
  3371.         if (! tabsize)
  3372.                 return(linsert(1, '\t'));
  3373.         return(linsert(tabsize - (getccol(FALSE) % tabsize), ' '));
  3374. }
  3375.  
  3376. /*
  3377.  * Open up some blank space. The basic plan is to insert a bunch of newlines,
  3378.  * and then back up over them. Everything is done by the subcommand
  3379.  * procerssors. They even handle the looping. Normally this is bound to "C-O".
  3380.  */
  3381. openline(f, n)
  3382. {
  3383.         register int    i;
  3384.         register int    s;
  3385.  
  3386.         if (n < 0)
  3387.                 return (FALSE);
  3388.         if (n == 0)
  3389.                 return (TRUE);
  3390.         i = n;                                  /* Insert newlines.     */
  3391.         do {
  3392.                 s = lnewline();
  3393.         } while (s==TRUE && --i);
  3394.         if (s == TRUE)                          /* Then back up overtop */
  3395.                 s = backchar(f, n);             /* of them all.         */
  3396.         return (s);
  3397. }
  3398.  
  3399. /*
  3400.  * Insert a newline. Bound to "C-M". If you are at the end of the line and the
  3401.  * next line is a blank line, just move into the blank line. This makes "C-O"
  3402.  * and "C-X C-O" work nicely, and reduces the ammount of screen update that
  3403.  * has to be done. This would not be as critical if screen update were a lot
  3404.  * more efficient.
  3405.  */
  3406. newline(f, n)
  3407. {
  3408.         int nicol;
  3409.         register LINE   *lp;
  3410.         register int    s;
  3411.  
  3412.         if (n < 0)
  3413.                 return (FALSE);
  3414.         while (n--) {
  3415.                 lp = curwp->w_dotp;
  3416.                 if (llength(lp) == curwp->w_doto
  3417.                 && lp != curbp->b_linep
  3418.                 && llength(lforw(lp)) == 0) {
  3419.                         if ((s=forwchar(FALSE, 1)) != TRUE)
  3420.                                 return (s);
  3421.                 } else if ((s=lnewline()) != TRUE)
  3422.                         return (s);
  3423.         }
  3424.         return (TRUE);
  3425. }
  3426.  
  3427. /*
  3428.  * Delete blank lines around dot. What this command does depends if dot is
  3429.  * sitting on a blank line. If dot is sitting on a blank line, this command
  3430.  * deletes all the blank lines above and below the current line. If it is
  3431.  * sitting on a non blank line then it deletes all of the blank lines after
  3432.  * the line. Normally this command is bound to "C-X C-O". Any argument is
  3433.  * ignored.
  3434.  */
  3435. deblank(f, n)
  3436. {
  3437.         register LINE   *lp1;
  3438.         register LINE   *lp2;
  3439.         register int    nld;
  3440.  
  3441.         lp1 = curwp->w_dotp;
  3442.         while (llength(lp1)==0 && (lp2=lback(lp1))!=curbp->b_linep)
  3443.                 lp1 = lp2;
  3444.         lp2 = lp1;
  3445.         nld = 0;
  3446.         while ((lp2=lforw(lp2))!=curbp->b_linep && llength(lp2)==0)
  3447.                 ++nld;
  3448.         if (nld == 0)
  3449.                 return (TRUE);
  3450.         curwp->w_dotp = lforw(lp1);
  3451.         curwp->w_doto = 0;
  3452.         return (ldelete(nld));
  3453. }
  3454.  
  3455. /*
  3456.  * Insert a newline, then enough tabs and spaces to duplicate the indentation
  3457.  * of the previous line. Assumes tabs are every eight characters. Quite simple.
  3458.  * Figure out the indentation of the current line. Insert a newline by calling
  3459.  * the standard routine. Insert the indentation by inserting the right number
  3460.  * of tabs and spaces. Return TRUE if all ok. Return FALSE if one of the
  3461.  * subcomands failed. Normally bound to "C-J".
  3462.  */
  3463. indent(f, n)
  3464. {
  3465.         register int    nicol;
  3466.         register int    c;
  3467.         register int    i;
  3468.  
  3469.         if (n < 0)
  3470.                 return (FALSE);
  3471.         while (n--) {
  3472.                 nicol = 0;
  3473.                 for (i=0; i<llength(curwp->w_dotp); ++i) {
  3474.                         c = lgetc(curwp->w_dotp, i);
  3475.                         if (c!=' ' && c!='\t')
  3476.                                 break;
  3477.                         if (c == '\t')
  3478.                                 nicol |= 0x07;
  3479.                         ++nicol;
  3480.                 }
  3481.                 if (lnewline() == FALSE
  3482.                 || ((i=nicol/8)!=0 && linsert(i, '\t')==FALSE)
  3483.                 || ((i=nicol%8)!=0 && linsert(i,  ' ')==FALSE))
  3484.                         return (FALSE);
  3485.         }
  3486.         return (TRUE);
  3487. }
  3488.  
  3489. /*
  3490.  * Delete forward. This is real easy, because the basic delete routine does
  3491.  * all of the work. Watches for negative arguments, and does the right thing.
  3492.  * If any argument is present, it kills rather than deletes, to prevent loss
  3493.  * of text if typed with a big argument. Normally bound to "C-D".
  3494.  */
  3495. forwdel(f, n)
  3496. {
  3497.         if (n < 0)
  3498.                 return (backdel(f, -n));
  3499.         if (f != FALSE) {                       /* Really a kill.       */
  3500.                 if ((lastflag&CFKILL) == 0)
  3501.                         kdelete();
  3502.                 thisflag |= CFKILL;
  3503.         }
  3504.         return (ldelete(n, f));
  3505. }
  3506.  
  3507. /*
  3508.  * Delete backwards. This is quite easy too, because it's all done with other
  3509.  * functions. Just move the cursor back, and delete forwards. Like delete
  3510.  * forward, this actually does a kill if presented with an argument. Bound to
  3511.  * both "RUBOUT" and "C-H".
  3512.  */
  3513. backdel(f, n)
  3514. {
  3515.         register int    s;
  3516.  
  3517.         if (n < 0)
  3518.                 return (forwdel(f, -n));
  3519.         if (f != FALSE) {                       /* Really a kill.       */
  3520.                 if ((lastflag&CFKILL) == 0)
  3521.                         kdelete();
  3522.                 thisflag |= CFKILL;
  3523.         }
  3524.         if ((s=backchar(f, n)) == TRUE)
  3525.                 s = ldelete(n, f);
  3526.         return (s);
  3527. }
  3528.  
  3529. /*
  3530.  * Kill text. If called without an argument, it kills from dot to the end of
  3531.  * the line, unless it is at the end of the line, when it kills the newline.
  3532.  * If called with an argument of 0, it kills from the start of the line to dot.
  3533.  * If called with a positive argument, it kills from dot forward over that
  3534.  * number of newlines. If called with a negative argument it kills backwards
  3535.  * that number of newlines. Normally bound to "C-K".
  3536.  */
  3537. kill(f, n)
  3538. {
  3539.         register int    chunk;
  3540.         register LINE   *nextp;
  3541.  
  3542.         if ((lastflag&CFKILL) == 0)             /* Clear kill buffer if */
  3543.                 kdelete();                      /* last wasn't a kill.  */
  3544.         thisflag |= CFKILL;
  3545.         if (f == FALSE) {
  3546.                 chunk = llength(curwp->w_dotp)-curwp->w_doto;
  3547.                 if (chunk == 0)
  3548.                         chunk = 1;
  3549.         } else if (n == 0) {
  3550.                 chunk = curwp->w_doto;
  3551.                 curwp->w_doto = 0;
  3552.         } else if (n > 0) {
  3553.                 chunk = llength(curwp->w_dotp)-curwp->w_doto+1;
  3554.                 nextp = lforw(curwp->w_dotp);
  3555.                 while (--n) {
  3556.                         if (nextp == curbp->b_linep)
  3557.                                 return (FALSE);
  3558.                         chunk += llength(nextp)+1;
  3559.                         nextp = lforw(nextp);
  3560.                 }
  3561.         } else {
  3562.                 mlwrite("neg kill");
  3563.                 return (FALSE);
  3564.         }
  3565.         return (ldelete(chunk, TRUE));
  3566. }
  3567.  
  3568. /*
  3569.  * Yank text back from the kill buffer. This is really easy. All of the work
  3570.  * is done by the standard insert routines. All you do is run the loop, and
  3571.  * check for errors. Bound to "C-Y". The blank lines are inserted with a call
  3572.  * to "newline" instead of a call to "lnewline" so that the magic stuff that
  3573.  * happens when you type a carriage return also happens when a carriage return
  3574.  * is yanked back from the kill buffer.
  3575.  */
  3576. yank(f, n)
  3577. {
  3578.         register int    c;
  3579.         register int    i;
  3580.         extern   int    kused;
  3581.  
  3582.         if (n < 0)
  3583.                 return (FALSE);
  3584.         while (n--) {
  3585.                 i = 0;
  3586.                 while ((c=kremove(i)) >= 0) {
  3587.                         if (c == '\n') {
  3588.                                 if (newline(FALSE, 1) == FALSE)
  3589.                                         return (FALSE);
  3590.                         } else {
  3591.                                 if (linsert(1, c) == FALSE)
  3592.                                         return (FALSE);
  3593.                         }
  3594.                         ++i;
  3595.                 }
  3596.         }
  3597.         return (TRUE);
  3598. }
  3599.  
  3600. ==================================================
  3601. region.c
  3602. ==================================================
  3603. /*
  3604.  * The routines in this file
  3605.  * deal with the region, that magic space
  3606.  * between "." and mark. Some functions are
  3607.  * commands. Some functions are just for
  3608.  * internal use.
  3609.  */
  3610. #include        <stdio.h>
  3611. #include        "ed.h"
  3612.  
  3613. /*
  3614.  * Kill the region. Ask "getregion"
  3615.  * to figure out the bounds of the region.
  3616.  * Move "." to the start, and kill the characters.
  3617.  * Bound to "C-W".
  3618.  */
  3619. killregion(f, n)
  3620. {
  3621.         register int    s;
  3622.         REGION          region;
  3623.  
  3624.         if ((s=getregion(®ion)) != TRUE)
  3625.                 return (s);
  3626.         if ((lastflag&CFKILL) == 0)             /* This is a kill type  */
  3627.                 kdelete();                      /* command, so do magic */
  3628.         thisflag |= CFKILL;                     /* kill buffer stuff.   */
  3629.         curwp->w_dotp = region.r_linep;
  3630.         curwp->w_doto = region.r_offset;
  3631.         return (ldelete(region.r_size, TRUE));
  3632. }
  3633.  
  3634. /*
  3635.  * Copy all of the characters in the
  3636.  * region to the kill buffer. Don't move dot
  3637.  * at all. This is a bit like a kill region followed
  3638.  * by a yank. Bound to "M-W".
  3639.  */
  3640. copyregion(f, n)
  3641. {
  3642.         register LINE   *linep;
  3643.         register int    loffs;
  3644.         register int    s;
  3645.         REGION          region;
  3646.  
  3647.         if ((s=getregion(®ion)) != TRUE)
  3648.                 return (s);
  3649.         if ((lastflag&CFKILL) == 0)             /* Kill type command.   */
  3650.                 kdelete();
  3651.         thisflag |= CFKILL;
  3652.         linep = region.r_linep;                 /* Current line.        */
  3653.         loffs = region.r_offset;                /* Current offset.      */
  3654.         while (region.r_size--) {
  3655.                 if (loffs == llength(linep)) {  /* End of line.         */
  3656.                         if ((s=kinsert('\n')) != TRUE)
  3657.                                 return (s);
  3658.                         linep = lforw(linep);
  3659.                         loffs = 0;
  3660.                 } else {                        /* Middle of line.      */
  3661.                         if ((s=kinsert(lgetc(linep, loffs))) != TRUE)
  3662.                                 return (s);
  3663.                         ++loffs;
  3664.                 }
  3665.         }
  3666.         return (TRUE);
  3667. }
  3668.  
  3669. /*
  3670.  * Lower case region. Zap all of the upper
  3671.  * case characters in the region to lower case. Use
  3672.  * the region code to set the limits. Scan the buffer,
  3673.  * doing the changes. Call "lchange" to ensure that
  3674.  * redisplay is done in all buffers. Bound to 
  3675.  * "C-X C-L".
  3676.  */
  3677. lowerregion(f, n)
  3678. {
  3679.         register LINE   *linep;
  3680.         register int    loffs;
  3681.         register int    c;
  3682.         register int    s;
  3683.         REGION          region;
  3684.  
  3685.         if ((s=getregion(®ion)) != TRUE)
  3686.                 return (s);
  3687.         lchange(WFHARD);
  3688.         linep = region.r_linep;
  3689.         loffs = region.r_offset;
  3690.         while (region.r_size--) {
  3691.                 if (loffs == llength(linep)) {
  3692.                         linep = lforw(linep);
  3693.                         loffs = 0;
  3694.                 } else {
  3695.                         c = lgetc(linep, loffs);
  3696.                         if (c>='A' && c<='Z')
  3697.                                 lputc(linep, loffs, c+'a'-'A');
  3698.                         ++loffs;
  3699.                 }
  3700.         }
  3701.         return (TRUE);
  3702. }
  3703.  
  3704. /*
  3705.  * Upper case region. Zap all of the lower
  3706.  * case characters in the region to upper case. Use
  3707.  * the region code to set the limits. Scan the buffer,
  3708.  * doing the changes. Call "lchange" to ensure that
  3709.  * redisplay is done in all buffers. Bound to 
  3710.  * "C-X C-L".
  3711.  */
  3712. upperregion(f, n)
  3713. {
  3714.         register LINE   *linep;
  3715.         register int    loffs;
  3716.         register int    c;
  3717.         register int    s;
  3718.         REGION          region;
  3719.  
  3720.         if ((s=getregion(®ion)) != TRUE)
  3721.                 return (s);
  3722.         lchange(WFHARD);
  3723.         linep = region.r_linep;
  3724.         loffs = region.r_offset;
  3725.         while (region.r_size--) {
  3726.                 if (loffs == llength(linep)) {
  3727.                         linep = lforw(linep);
  3728.                         loffs = 0;
  3729.                 } else {
  3730.                         c = lgetc(linep, loffs);
  3731.                         if (c>='a' && c<='z')
  3732.                                 lputc(linep, loffs, c-'a'+'A');
  3733.                         ++loffs;
  3734.                 }
  3735.         }
  3736.         return (TRUE);
  3737. }
  3738.  
  3739. /*
  3740.  * This routine figures out the
  3741.  * bounds of the region in the current window, and
  3742.  * fills in the fields of the "REGION" structure pointed
  3743.  * to by "rp". Because the dot and mark are usually very
  3744.  * close together, we scan outward from dot looking for
  3745.  * mark. This should save time. Return a standard code.
  3746.  * Callers of this routine should be prepared to get
  3747.  * an "ABORT" status; we might make this have the
  3748.  * conform thing later.
  3749.  */
  3750. getregion(rp)
  3751. register REGION *rp;
  3752. {
  3753.         register LINE   *flp;
  3754.         register LINE   *blp;
  3755.         register int    fsize;
  3756.         register int    bsize;
  3757.  
  3758.         if (curwp->w_markp == NULL) {
  3759.                 mlwrite("No mark set in this window");
  3760.                 return (FALSE);
  3761.         }
  3762.         if (curwp->w_dotp == curwp->w_markp) {
  3763.                 rp->r_linep = curwp->w_dotp;
  3764.                 if (curwp->w_doto < curwp->w_marko) {
  3765.                         rp->r_offset = curwp->w_doto;
  3766.                         rp->r_size = curwp->w_marko-curwp->w_doto;
  3767.                 } else {
  3768.                         rp->r_offset = curwp->w_marko;
  3769.                         rp->r_size = curwp->w_doto-curwp->w_marko;
  3770.                 }
  3771.                 return (TRUE);
  3772.         }
  3773.         blp = curwp->w_dotp;
  3774.         bsize = curwp->w_doto;
  3775.         flp = curwp->w_dotp;
  3776.         fsize = llength(flp)-curwp->w_doto+1;
  3777.         while (flp!=curbp->b_linep || lback(blp)!=curbp->b_linep) {
  3778.                 if (flp != curbp->b_linep) {
  3779.                         flp = lforw(flp);
  3780.                         if (flp == curwp->w_markp) {
  3781.                                 rp->r_linep = curwp->w_dotp;
  3782.                                 rp->r_offset = curwp->w_doto;
  3783.                                 rp->r_size = fsize+curwp->w_marko;
  3784.                                 return (TRUE);
  3785.                         }
  3786.                         fsize += llength(flp)+1;
  3787.                 }
  3788.                 if (lback(blp) != curbp->b_linep) {
  3789.                         blp = lback(blp);
  3790.                         bsize += llength(blp)+1;
  3791.                         if (blp == curwp->w_markp) {
  3792.                                 rp->r_linep = blp;
  3793.                                 rp->r_offset = curwp->w_marko;
  3794.                                 rp->r_size = bsize - curwp->w_marko;
  3795.                                 return (TRUE);
  3796.                         }
  3797.                 }
  3798.         }
  3799.         mlwrite("Bug: lost mark");
  3800.         return (FALSE);
  3801. }
  3802.  
  3803. ==================================================
  3804. search.c
  3805. ==================================================
  3806. /*
  3807.  * The functions in this file implement commands that search in the forward
  3808.  * and backward directions. There are no special characters in the search
  3809.  * strings. Probably should have a regular expression search, or something
  3810.  * like that.
  3811.  *
  3812.  * REVISION HISTORY:
  3813.  *
  3814.  * ?    Steve Wilhite, 1-Dec-85
  3815.  *      - massive cleanup on code.
  3816.  */
  3817.  
  3818. #include        <stdio.h>
  3819. #include        "ed.h"
  3820.  
  3821. /*
  3822.  * Search forward. Get a search string from the user, and search, beginning at
  3823.  * ".", for the string. If found, reset the "." to be just after the match
  3824.  * string, and [perhaps] repaint the display. Bound to "C-S".
  3825.  */
  3826. forwsearch(f, n)
  3827.     {
  3828.     register LINE *clp;
  3829.     register int cbo;
  3830.     register LINE*tlp;
  3831.     register int tbo;
  3832.     register int c;
  3833.     register char *pp;
  3834.     register int s;
  3835.  
  3836.     if ((s = readpattern("Search")) != TRUE)
  3837.         return (s);
  3838.  
  3839.     clp = curwp->w_dotp;
  3840.     cbo = curwp->w_doto;
  3841.  
  3842.     while (clp != curbp->b_linep)
  3843.         {
  3844.         if (cbo == llength(clp))
  3845.             {
  3846.             clp = lforw(clp);
  3847.             cbo = 0;
  3848.             c = '\n';
  3849.             }
  3850.         else
  3851.             c = lgetc(clp, cbo++);
  3852.  
  3853.         if (eq(c, pat[0]) != FALSE)
  3854.             {
  3855.             tlp = clp;
  3856.             tbo = cbo;
  3857.             pp  = &pat[1];
  3858.  
  3859.             while (*pp != 0)
  3860.                 {
  3861.                 if (tlp == curbp->b_linep)
  3862.                     goto fail;
  3863.  
  3864.                 if (tbo == llength(tlp))
  3865.                     {
  3866.                     tlp = lforw(tlp);
  3867.                     tbo = 0;
  3868.                     c = '\n';
  3869.                     }
  3870.                 else
  3871.                     c = lgetc(tlp, tbo++);
  3872.  
  3873.                 if (eq(c, *pp++) == FALSE)
  3874.                     goto fail;
  3875.                 }
  3876.  
  3877.             curwp->w_dotp  = tlp;
  3878.             curwp->w_doto  = tbo;
  3879.             curwp->w_flag |= WFMOVE;
  3880.             return (TRUE);
  3881.             }
  3882. fail:;
  3883.         }
  3884.  
  3885.     mlwrite("Not found");
  3886.     return (FALSE);
  3887.     }
  3888.  
  3889. /*
  3890.  * Reverse search. Get a search string from the user, and search, starting at
  3891.  * "." and proceeding toward the front of the buffer. If found "." is left
  3892.  * pointing at the first character of the pattern [the last character that was
  3893.  j matched]. Bound to "C-R".
  3894.  */
  3895. backsearch(f, n)
  3896.     {
  3897.     register LINE *clp;
  3898.     register int cbo;
  3899.     register LINE *tlp;
  3900.     register int tbo;
  3901.     register int c;
  3902.     register char *epp;
  3903.     register char *pp;
  3904.     register int s;
  3905.  
  3906.     if ((s = readpattern("Reverse search")) != TRUE)
  3907.         return (s);
  3908.  
  3909.     for (epp = &pat[0]; epp[1] != 0; ++epp)
  3910.         ;
  3911.  
  3912.     clp = curwp->w_dotp;
  3913.     cbo = curwp->w_doto;
  3914.  
  3915.     for (;;)
  3916.         {
  3917.         if (cbo == 0)
  3918.             {
  3919.             clp = lback(clp);
  3920.  
  3921.             if (clp == curbp->b_linep)
  3922.                 {
  3923.                 mlwrite("Not found");
  3924.                 return (FALSE);
  3925.                 }
  3926.  
  3927.             cbo = llength(clp)+1;
  3928.             }
  3929.  
  3930.         if (--cbo == llength(clp))
  3931.             c = '\n';
  3932.         else
  3933.             c = lgetc(clp, cbo);
  3934.  
  3935.         if (eq(c, *epp) != FALSE)
  3936.             {
  3937.             tlp = clp;
  3938.             tbo = cbo;
  3939.             pp  = epp;
  3940.  
  3941.             while (pp != &pat[0])
  3942.                 {
  3943.                 if (tbo == 0)
  3944.                     {
  3945.                     tlp = lback(tlp);
  3946.                     if (tlp == curbp->b_linep)
  3947.                         goto fail;
  3948.  
  3949.                     tbo = llength(tlp)+1;
  3950.                     }
  3951.  
  3952.                 if (--tbo == llength(tlp))
  3953.                     c = '\n';
  3954.                 else
  3955.                     c = lgetc(tlp, tbo);
  3956.  
  3957.                 if (eq(c, *--pp) == FALSE)
  3958.                     goto fail;
  3959.                 }
  3960.  
  3961.             curwp->w_dotp  = tlp;
  3962.             curwp->w_doto  = tbo;
  3963.             curwp->w_flag |= WFMOVE;
  3964.             return (TRUE);
  3965.             }
  3966. fail:;
  3967.         }
  3968.     }
  3969.  
  3970. /*
  3971.  * Compare two characters. The "bc" comes from the buffer. It has it's case
  3972.  * folded out. The "pc" is from the pattern.
  3973.  */
  3974. eq(bc, pc)
  3975.     int bc;
  3976.     int pc;
  3977.     {
  3978.     if (bc>='a' && bc<='z')
  3979.         bc -= 0x20;
  3980.  
  3981.     if (pc>='a' && pc<='z')
  3982.         pc -= 0x20;
  3983.  
  3984.     if (bc == pc)
  3985.         return (TRUE);
  3986.  
  3987.     return (FALSE);
  3988.     }
  3989.  
  3990. /*
  3991.  * Read a pattern. Stash it in the external variable "pat". The "pat" is not
  3992.  * updated if the user types in an empty line. If the user typed an empty line,
  3993.  * and there is no old pattern, it is an error. Display the old pattern, in the
  3994.  * style of Jeff Lomicka. There is some do-it-yourself control expansion.
  3995.  */
  3996. readpattern(prompt)
  3997.     char *prompt;
  3998.     {
  3999.     register char *cp1;
  4000.     register char *cp2;
  4001.     register int c;
  4002.     register int s;
  4003.     char tpat[NPAT+20];
  4004.  
  4005.     cp1 = &tpat[0];                     /* Copy prompt */
  4006.     cp2 = prompt;
  4007.  
  4008.     while ((c = *cp2++) != '\0')
  4009.         *cp1++ = c;
  4010.  
  4011.     if (pat[0] != '\0')                 /* Old pattern */
  4012.         {
  4013.         *cp1++ = ' ';
  4014.         *cp1++ = '[';
  4015.         cp2 = &pat[0];
  4016.  
  4017.         while ((c = *cp2++) != 0)
  4018.             {
  4019.             if (cp1 < &tpat[NPAT+20-6]) /* "??]: \0" */
  4020.                 {
  4021.                 if (c<0x20 || c==0x7F) {
  4022.                     *cp1++ = '^';
  4023.                     c ^= 0x40;
  4024.                     }
  4025.                 else if (c == '%')      /* Map "%" to */
  4026.                     *cp1++ = c;         /* "%%". */
  4027.  
  4028.                 *cp1++ = c;
  4029.                 }
  4030.             }
  4031.  
  4032.         *cp1++ = ']';
  4033.         }
  4034.  
  4035.     *cp1++ = ':';                       /* Finish prompt */
  4036.     *cp1++ = ' ';
  4037.     *cp1++ = '\0';
  4038.     s = mlreply(tpat, tpat, NPAT);      /* Read pattern */
  4039.  
  4040.     if (s == TRUE)                      /* Specified */
  4041.         strcpy(pat, tpat);
  4042.     else if (s == FALSE && pat[0] != 0)         /* CR, but old one */
  4043.         s = TRUE;
  4044.  
  4045.     return (s);
  4046.     }
  4047.  
  4048. ==================================================
  4049. spawn.c
  4050. ==================================================
  4051. /*
  4052.  * The routines in this file are called to create a subjob running a command
  4053.  * interpreter. This code is a big fat nothing on CP/M-86. You lose.
  4054.  */
  4055. #include        <stdio.h>
  4056. #include        "ed.h"
  4057.  
  4058. #if     AMIGA
  4059. #define  NEW   1006
  4060. #endif
  4061.  
  4062. #if     VMS
  4063. #define EFN     0                               /* Event flag.          */
  4064.  
  4065. #include        <ssdef.h>                       /* Random headers.      */
  4066. #include        <stsdef.h>
  4067. #include        <descrip.h>
  4068. #include        <iodef.h>
  4069.  
  4070. extern  int     oldmode[];                      /* In "termio.c"        */
  4071. extern  int     newmode[];                      /* In "termio.c"        */
  4072. extern  short   iochan;                         /* In "termio.c"        */
  4073. #endif
  4074.  
  4075. #if     MSDOS
  4076. #include        <dos.h>
  4077. #endif
  4078.  
  4079. #if     V7
  4080. #include        <signal.h>
  4081. #endif
  4082.  
  4083. /*
  4084.  * Create a subjob with a copy of the command intrepreter in it. When the
  4085.  * command interpreter exits, mark the screen as garbage so that you do a full
  4086.  * repaint. Bound to "C-C". The message at the start in VMS puts out a newline.
  4087.  * Under some (unknown) condition, you don't get one free when DCL starts up.
  4088.  */
  4089. spawncli(f, n)
  4090. {
  4091. #if     AMIGA
  4092.         long newcli;
  4093.  
  4094.         newcli = Open("CON:1/1/639/199/MicroEmacs Subprocess", NEW);
  4095.         mlwrite("[Starting new CLI]");
  4096.         sgarbf = TRUE;
  4097.         Execute("", newcli, 0);
  4098.         Close(newcli);
  4099.         return(TRUE);
  4100. #endif
  4101.  
  4102. #if     V7
  4103.         register char *cp;
  4104.         char    *getenv();
  4105. #endif
  4106. #if     VMS
  4107.         movecursor(term.t_nrow, 0);             /* In last line.        */
  4108.         mlputs("[Starting DCL]\r\n");
  4109.         (*term.t_flush)();                      /* Ignore "ttcol".      */
  4110.         sgarbf = TRUE;
  4111.         return (sys(NULL));                     /* NULL => DCL.         */
  4112. #endif
  4113. #if     CPM
  4114.         mlwrite("Not in CP/M-86");
  4115. #endif
  4116. #if     MSDOS
  4117.         movecursor(term.t_nrow, 0);             /* Seek to last line.   */
  4118.         (*term.t_flush)();
  4119.         sys("\\command.com", "");               /* Run CLI.             */
  4120.         sgarbf = TRUE;
  4121.         return(TRUE);
  4122. #endif
  4123. #if     V7
  4124.         movecursor(term.t_nrow, 0);             /* Seek to last line.   */
  4125.         (*term.t_flush)();
  4126.         ttclose();                              /* stty to old settings */
  4127.         if ((cp = getenv("SHELL")) != NULL && *cp != '\0')
  4128.                 system(cp);
  4129.         else
  4130.                 system("exec /bin/sh");
  4131.         sgarbf = TRUE;
  4132.         sleep(2);
  4133.         ttopen();
  4134.         return(TRUE);
  4135. #endif
  4136. }
  4137.  
  4138. /*
  4139.  * Run a one-liner in a subjob. When the command returns, wait for a single
  4140.  * character to be typed, then mark the screen as garbage so a full repaint is
  4141.  * done. Bound to "C-X !".
  4142.  */
  4143. spawn(f, n)
  4144. {
  4145.         register int    s;
  4146.         char            line[NLINE];
  4147. #if     AMIGA
  4148.         long newcli;
  4149.  
  4150.         newcli = Open("CON:1/1/639/199/MicroEmacs Subprocess", NEW);
  4151.         if ((s=mlreply("CLI command: ", line, NLINE)) != TRUE)
  4152.                 return (s);
  4153.         Execute(line,0,newcli);
  4154.         Close(newcli);
  4155.         while ((*term.t_getchar)() != '\r')     /* Pause.               */
  4156.                 ;
  4157.         sgarbf = TRUE;
  4158.         return(TRUE);
  4159. #endif
  4160. #if     VMS
  4161.         if ((s=mlreply("DCL command: ", line, NLINE)) != TRUE)
  4162.                 return (s);
  4163.         (*term.t_putchar)('\n');                /* Already have '\r'    */
  4164.         (*term.t_flush)();
  4165.         s = sys(line);                          /* Run the command.     */
  4166.         mlputs("\r\n\n[End]");                  /* Pause.               */
  4167.         (*term.t_flush)();
  4168.         while ((*term.t_getchar)() != '\r')
  4169.                 ;
  4170.         sgarbf = TRUE;
  4171.         return (s);
  4172. #endif
  4173. #if     CPM
  4174.         mlwrite("Not in CP/M-86");
  4175.         return (FALSE);
  4176. #endif
  4177. #if     MSDOS
  4178.         if ((s=mlreply("MS-DOS command: ", line, NLINE)) != TRUE)
  4179.                 return (s);
  4180.         system(line);
  4181.         while ((*term.t_getchar)() != '\r')     /* Pause.               */
  4182.                 ;
  4183.         sgarbf = TRUE;
  4184.         return (TRUE);
  4185. #endif
  4186. #if     V7
  4187.         if ((s=mlreply("! ", line, NLINE)) != TRUE)
  4188.                 return (s);
  4189.         (*term.t_putchar)('\n');                /* Already have '\r'    */
  4190.         (*term.t_flush)();
  4191.         ttclose();                              /* stty to old modes    */
  4192.         system(line);
  4193.         sleep(2);
  4194.         ttopen();
  4195.         mlputs("[End]");                        /* Pause.               */
  4196.         (*term.t_flush)();
  4197.         while ((s = (*term.t_getchar)()) != '\r' && s != ' ')
  4198.                 ;
  4199.         sgarbf = TRUE;
  4200.         return (TRUE);
  4201. #endif
  4202. }
  4203.  
  4204. #if     VMS
  4205. /*
  4206.  * Run a command. The "cmd" is a pointer to a command string, or NULL if you
  4207.  * want to run a copy of DCL in the subjob (this is how the standard routine
  4208.  * LIB$SPAWN works. You have to do wierd stuff with the terminal on the way in
  4209.  * and the way out, because DCL does not want the channel to be in raw mode.
  4210.  */
  4211. sys(cmd)
  4212. register char   *cmd;
  4213. {
  4214.         struct  dsc$descriptor  cdsc;
  4215.         struct  dsc$descriptor  *cdscp;
  4216.         long    status;
  4217.         long    substatus;
  4218.         long    iosb[2];
  4219.  
  4220.         status = SYS$QIOW(EFN, iochan, IO$_SETMODE, iosb, 0, 0,
  4221.                           oldmode, sizeof(oldmode), 0, 0, 0, 0);
  4222.         if (status!=SS$_NORMAL || (iosb[0]&0xFFFF)!=SS$_NORMAL)
  4223.                 return (FALSE);
  4224.         cdscp = NULL;                           /* Assume DCL.          */
  4225.         if (cmd != NULL) {                      /* Build descriptor.    */
  4226.                 cdsc.dsc$a_pointer = cmd;
  4227.                 cdsc.dsc$w_length  = strlen(cmd);
  4228.                 cdsc.dsc$b_dtype   = DSC$K_DTYPE_T;
  4229.                 cdsc.dsc$b_class   = DSC$K_CLASS_S;
  4230.                 cdscp = &cdsc;
  4231.         }
  4232.         status = LIB$SPAWN(cdscp, 0, 0, 0, 0, 0, &substatus, 0, 0, 0);
  4233.         if (status != SS$_NORMAL)
  4234.                 substatus = status;
  4235.         status = SYS$QIOW(EFN, iochan, IO$_SETMODE, iosb, 0, 0,
  4236.                           newmode, sizeof(newmode), 0, 0, 0, 0);
  4237.         if (status!=SS$_NORMAL || (iosb[0]&0xFFFF)!=SS$_NORMAL)
  4238.                 return (FALSE);
  4239.         if ((substatus&STS$M_SUCCESS) == 0)     /* Command failed.      */
  4240.                 return (FALSE);
  4241.         return (TRUE);
  4242. }
  4243. #endif
  4244.  
  4245. #if     MSDOS
  4246. /*
  4247.  * This routine, once again by Bob McNamara, is a C translation of the "system"
  4248.  * routine in the MWC-86 run time library. It differs from the "system" routine
  4249.  * in that it does not unconditionally append the string ".exe" to the end of
  4250.  * the command name. We needed to do this because we want to be able to spawn
  4251.  * off "command.com". We really do not understand what it does, but if you don't
  4252.  * do it exactly "malloc" starts doing very very strange things.
  4253.  */
  4254. sys(cmd, tail)
  4255. char    *cmd;
  4256. char    *tail;
  4257. {
  4258. #if MWC_86
  4259.         register unsigned n;
  4260.         extern   char     *__end;
  4261.  
  4262.         n = __end + 15;
  4263.         n >>= 4;
  4264.         n = ((n + dsreg() + 16) & 0xFFF0) + 16;
  4265.         return(execall(cmd, tail, n));
  4266. #endif
  4267.  
  4268. #if LATTICE
  4269.         return forklp(cmd, tail, NULL);
  4270. #endif
  4271. }
  4272. #endif
  4273.  
  4274. ==================================================
  4275. tcap.c
  4276. ==================================================
  4277. #include <stdio.h>
  4278. #include "ed.h"
  4279.  
  4280. #if TERMCAP
  4281.  
  4282. #define NROW    24
  4283. #define NCOL    80
  4284. #define BEL     0x07
  4285. #define ESC     0x1B
  4286.  
  4287. extern int      ttopen();
  4288. extern int      ttgetc();
  4289. extern int      ttputc();
  4290. extern int      ttflush();
  4291. extern int      ttclose();
  4292. extern int      tcapmove();
  4293. extern int      tcapeeol();
  4294. extern int      tcapeeop();
  4295. extern int      tcapbeep();
  4296. extern int      tcapopen();
  4297. extern int      tput();
  4298. extern char     *tgoto();
  4299.  
  4300. #define TCAPSLEN 315
  4301.  
  4302. char tcapbuf[TCAPSLEN];
  4303. char    PC,
  4304.         *CM,
  4305.         *CL,
  4306.         *CE,
  4307.         *UP,
  4308.         *CD;
  4309.  
  4310.  
  4311. TERM term = {
  4312.         NROW-1,
  4313.         NCOL,
  4314.         &tcapopen,
  4315.         &ttclose,
  4316.         &ttgetc,
  4317.         &ttputc,
  4318.         &ttflush,
  4319.         &tcapmove,
  4320.         &tcapeeol,
  4321.         &tcapeeop,
  4322.         &tcapbeep
  4323. };
  4324.  
  4325. tcapopen()
  4326.  
  4327. {
  4328.         char *getenv();
  4329.         char *t, *p, *tgetstr();
  4330.         char tcbuf[1024];
  4331.         char *tv_stype;
  4332.         char err_str[72];
  4333.  
  4334.         if ((tv_stype = getenv("TERM")) == NULL)
  4335.         {
  4336.                 puts("Environment variable TERM not defined!");
  4337.                 exit(1);
  4338.         }
  4339.  
  4340.         if((tgetent(tcbuf, tv_stype)) != 1)
  4341.         {
  4342.                 sprintf(err_str, "Unknown terminal type %s!", tv_stype);
  4343.                 puts(err_str);
  4344.                 exit(1);
  4345.         }
  4346.  
  4347.         p = tcapbuf;
  4348.         t = tgetstr("pc", &p);
  4349.         if(t)
  4350.                 PC = *t;
  4351.  
  4352.         CD = tgetstr("cd", &p);
  4353.         CM = tgetstr("cm", &p);
  4354.         CE = tgetstr("ce", &p);
  4355.         UP = tgetstr("up", &p);
  4356.  
  4357.         if(CD == NULL || CM == NULL || CE == NULL || UP == NULL)
  4358.         {
  4359.                 puts("Incomplete termcap entry\n");
  4360.                 exit(1);
  4361.         }
  4362.  
  4363.         if (p >= &tcapbuf[TCAPSLEN])
  4364.         {
  4365.                 puts("Terminal description too big!\n");
  4366.                 exit(1);
  4367.         }
  4368.         ttopen();
  4369. }
  4370. tcapmove(row, col)
  4371. register int row, col;
  4372. {
  4373.         putpad(tgoto(CM, col, row));
  4374. }
  4375.  
  4376. tcapeeol()
  4377. {
  4378.         putpad(CE);
  4379. }
  4380.  
  4381. tcapeeop()
  4382. {
  4383.         putpad(CD);
  4384. }
  4385.  
  4386. tcapbeep()
  4387. {
  4388.         ttputc(BEL);
  4389. }
  4390.  
  4391. putpad(str)
  4392. char    *str;
  4393. {
  4394.         tputs(str, 1, ttputc);
  4395. }
  4396.  
  4397. putnpad(str, n)
  4398. char    *str;
  4399. {
  4400.         tputs(str, n, ttputc);
  4401. }
  4402. #endif TERMCAP
  4403.  
  4404. ==================================================
  4405. termio.c
  4406. ==================================================
  4407. /*
  4408.  * The functions in this file negotiate with the operating system for
  4409.  * characters, and write characters in a barely buffered fashion on the display.
  4410.  * All operating systems.
  4411.  */
  4412. #include        <stdio.h>
  4413. #include        "ed.h"
  4414.  
  4415. #if     AMIGA
  4416. #define NEW 1006
  4417. #define LEN 1
  4418.  
  4419. static long terminal;
  4420. #endif
  4421.  
  4422. #if     VMS
  4423. #include        <stsdef.h>
  4424. #include        <ssdef.h>
  4425. #include        <descrip.h>
  4426. #include        <iodef.h>
  4427. #include        <ttdef.h>
  4428.  
  4429. #define NIBUF   128                     /* Input  buffer size           */
  4430. #define NOBUF   1024                    /* MM says bug buffers win!     */
  4431. #define EFN     0                       /* Event flag                   */
  4432.  
  4433. char    obuf[NOBUF];                    /* Output buffer                */
  4434. int     nobuf;                          /* # of bytes in above          */
  4435. char    ibuf[NIBUF];                    /* Input buffer                 */
  4436. int     nibuf;                          /* # of bytes in above          */
  4437. int     ibufi;                          /* Read index                   */
  4438. int     oldmode[2];                     /* Old TTY mode bits            */
  4439. int     newmode[2];                     /* New TTY mode bits            */
  4440. short   iochan;                         /* TTY I/O channel              */
  4441. #endif
  4442.  
  4443. #if     CPM
  4444. #include        <bdos.h>
  4445. #endif
  4446.  
  4447. #if     MSDOS
  4448. #undef  LATTICE
  4449. #include        <dos.h>
  4450. #endif
  4451.  
  4452. #if RAINBOW
  4453. #include "rainbow.h"
  4454. #endif
  4455.  
  4456. #if V7
  4457. #include        <sgtty.h>               /* for stty/gtty functions */
  4458. struct  sgttyb  ostate;                 /* saved tty state */
  4459. struct  sgttyb  nstate;                 /* values for editor mode */
  4460. #endif
  4461.  
  4462. /*
  4463.  * This function is called once to set up the terminal device streams.
  4464.  * On VMS, it translates SYS$INPUT until it finds the terminal, then assigns
  4465.  * a channel to it and sets it raw. On CPM it is a no-op.
  4466.  */
  4467. ttopen()
  4468. {
  4469. #if     AMIGA
  4470.         terminal = Open("RAW:1/1/639/199/MicroEmacs", NEW);
  4471. #endif
  4472. #if     VMS
  4473.         struct  dsc$descriptor  idsc;
  4474.         struct  dsc$descriptor  odsc;
  4475.         char    oname[40];
  4476.         int     iosb[2];
  4477.         int     status;
  4478.  
  4479.         odsc.dsc$a_pointer = "SYS$INPUT";
  4480.         odsc.dsc$w_length  = strlen(odsc.dsc$a_pointer);
  4481.         odsc.dsc$b_dtype   = DSC$K_DTYPE_T;
  4482.         odsc.dsc$b_class   = DSC$K_CLASS_S;
  4483.         idsc.dsc$b_dtype   = DSC$K_DTYPE_T;
  4484.         idsc.dsc$b_class   = DSC$K_CLASS_S;
  4485.         do {
  4486.                 idsc.dsc$a_pointer = odsc.dsc$a_pointer;
  4487.                 idsc.dsc$w_length  = odsc.dsc$w_length;
  4488.                 odsc.dsc$a_pointer = &oname[0];
  4489.                 odsc.dsc$w_length  = sizeof(oname);
  4490.                 status = LIB$SYS_TRNLOG(&idsc, &odsc.dsc$w_length, &odsc);
  4491.                 if (status!=SS$_NORMAL && status!=SS$_NOTRAN)
  4492.                         exit(status);
  4493.                 if (oname[0] == 0x1B) {
  4494.                         odsc.dsc$a_pointer += 4;
  4495.                         odsc.dsc$w_length  -= 4;
  4496.                 }
  4497.         } while (status == SS$_NORMAL);
  4498.         status = SYS$ASSIGN(&odsc, &iochan, 0, 0);
  4499.         if (status != SS$_NORMAL)
  4500.                 exit(status);
  4501.         status = SYS$QIOW(EFN, iochan, IO$_SENSEMODE, iosb, 0, 0,
  4502.                           oldmode, sizeof(oldmode), 0, 0, 0, 0);
  4503.         if (status!=SS$_NORMAL || (iosb[0]&0xFFFF)!=SS$_NORMAL)
  4504.                 exit(status);
  4505.         newmode[0] = oldmode[0];
  4506.         newmode[1] = oldmode[1] | TT$M_PASSALL | TT$M_NOECHO;
  4507.         status = SYS$QIOW(EFN, iochan, IO$_SETMODE, iosb, 0, 0,
  4508.                           newmode, sizeof(newmode), 0, 0, 0, 0);
  4509.         if (status!=SS$_NORMAL || (iosb[0]&0xFFFF)!=SS$_NORMAL)
  4510.                 exit(status);
  4511. #endif
  4512. #if     CPM
  4513. #endif
  4514. #if     MSDOS
  4515. #endif
  4516. #if     V7
  4517.         gtty(1, &ostate);                       /* save old state */
  4518.         gtty(1, &nstate);                       /* get base of new state */
  4519.         nstate.sg_flags |= RAW;
  4520.         nstate.sg_flags &= ~(ECHO|CRMOD);       /* no echo for now... */
  4521.         stty(1, &nstate);                       /* set mode */
  4522. #endif
  4523. }
  4524.  
  4525. /*
  4526.  * This function gets called just before we go back home to the command
  4527.  * interpreter. On VMS it puts the terminal back in a reasonable state.
  4528.  * Another no-operation on CPM.
  4529.  */
  4530. ttclose()
  4531. {
  4532. #if     AMIGA
  4533.         Close(terminal);
  4534. #endif
  4535. #if     VMS
  4536.         int     status;
  4537.         int     iosb[1];
  4538.  
  4539.         ttflush();
  4540.         status = SYS$QIOW(EFN, iochan, IO$_SETMODE, iosb, 0, 0,
  4541.                  oldmode, sizeof(oldmode), 0, 0, 0, 0);
  4542.         if (status!=SS$_NORMAL || (iosb[0]&0xFFFF)!=SS$_NORMAL)
  4543.                 exit(status);
  4544.         status = SYS$DASSGN(iochan);
  4545.         if (status != SS$_NORMAL)
  4546.                 exit(status);
  4547. #endif
  4548. #if     CPM
  4549. #endif
  4550. #if     MSDOS
  4551. #endif
  4552. #if     V7
  4553.         stty(1, &ostate);
  4554. #endif
  4555. }
  4556.  
  4557. /*
  4558.  * Write a character to the display. On VMS, terminal output is buffered, and
  4559.  * we just put the characters in the big array, after checking for overflow.
  4560.  * On CPM terminal I/O unbuffered, so we just write the byte out. Ditto on
  4561.  * MS-DOS (use the very very raw console output routine).
  4562.  */
  4563. ttputc(c)
  4564. #if     AMIGA
  4565.         char c;
  4566. #endif
  4567. {
  4568. #if     AMIGA
  4569.         Write(terminal, &c, LEN);
  4570. #endif
  4571. #if     VMS
  4572.         if (nobuf >= NOBUF)
  4573.                 ttflush();
  4574.         obuf[nobuf++] = c;
  4575. #endif
  4576.  
  4577. #if     CPM
  4578.         bios(BCONOUT, c, 0);
  4579. #endif
  4580.  
  4581. #if     MSDOS & CWC86
  4582.         dosb(CONDIO, c, 0);
  4583. #endif
  4584.  
  4585. #if RAINBOW
  4586.         Put_Char(c);                    /* fast video */
  4587. #endif
  4588.  
  4589. #if     V7
  4590.         fputc(c, stdout);
  4591. #endif
  4592. }
  4593.  
  4594. /*
  4595.  * Flush terminal buffer. Does real work where the terminal output is buffered
  4596.  * up. A no-operation on systems where byte at a time terminal I/O is done.
  4597.  */
  4598. ttflush()
  4599. {
  4600. #if     AMIGA
  4601. #endif
  4602. #if     VMS
  4603.         int     status;
  4604.         int     iosb[2];
  4605.  
  4606.         status = SS$_NORMAL;
  4607.         if (nobuf != 0) {
  4608.                 status = SYS$QIOW(EFN, iochan, IO$_WRITELBLK|IO$M_NOFORMAT,
  4609.                          iosb, 0, 0, obuf, nobuf, 0, 0, 0, 0);
  4610.                 if (status == SS$_NORMAL)
  4611.                         status = iosb[0] & 0xFFFF;
  4612.                 nobuf = 0;
  4613.         }
  4614.         return (status);
  4615. #endif
  4616. #if     CPM
  4617. #endif
  4618. #if     MSDOS
  4619. #endif
  4620. #if     V7
  4621.         fflush(stdout);
  4622. #endif
  4623. }
  4624.  
  4625. /*
  4626.  * Read a character from the terminal, performing no editing and doing no echo
  4627.  * at all. More complex in VMS that almost anyplace else, which figures. Very
  4628.  * simple on CPM, because the system can do exactly what you want.
  4629.  */
  4630. ttgetc()
  4631. {
  4632. #if     AMIGA
  4633.         char ch;
  4634.  
  4635.         Read(terminal, &ch, LEN);
  4636.         return (int) ch;
  4637. #endif
  4638. #if     VMS
  4639.         int     status;
  4640.         int     iosb[2];
  4641.         int     term[2];
  4642.  
  4643.         while (ibufi >= nibuf) {
  4644.                 ibufi = 0;
  4645.                 term[0] = 0;
  4646.                 term[1] = 0;
  4647.                 status = SYS$QIOW(EFN, iochan, IO$_READLBLK|IO$M_TIMED,
  4648.                          iosb, 0, 0, ibuf, NIBUF, 0, term, 0, 0);
  4649.                 if (status != SS$_NORMAL)
  4650.                         exit(status);
  4651.                 status = iosb[0] & 0xFFFF;
  4652.                 if (status!=SS$_NORMAL && status!=SS$_TIMEOUT)
  4653.                         exit(status);
  4654.                 nibuf = (iosb[0]>>16) + (iosb[1]>>16);
  4655.                 if (nibuf == 0) {
  4656.                         status = SYS$QIOW(EFN, iochan, IO$_READLBLK,
  4657.                                  iosb, 0, 0, ibuf, 1, 0, term, 0, 0);
  4658.                         if (status != SS$_NORMAL
  4659.                         || (status = (iosb[0]&0xFFFF)) != SS$_NORMAL)
  4660.                                 exit(status);
  4661.                         nibuf = (iosb[0]>>16) + (iosb[1]>>16);
  4662.                 }
  4663.         }
  4664.         return (ibuf[ibufi++] & 0xFF);          /* Allow multinational  */
  4665. #endif
  4666.  
  4667. #if     CPM
  4668.         return (biosb(BCONIN, 0, 0));
  4669. #endif
  4670.  
  4671. #if RAINBOW
  4672.         int Ch;
  4673.  
  4674.         while ((Ch = Read_Keyboard()) < 0);
  4675.  
  4676.         if ((Ch & Function_Key) == 0)
  4677.                 if (!((Ch & 0xFF) == 015 || (Ch & 0xFF) == 0177))
  4678.                         Ch &= 0xFF;
  4679.  
  4680.         return Ch;
  4681. #endif
  4682.  
  4683. #if     MSDOS & MWC86
  4684.         return (dosb(CONRAW, 0, 0));
  4685. #endif
  4686.  
  4687. #if     V7
  4688.         return(fgetc(stdin));
  4689. #endif
  4690. }
  4691.  
  4692. ==================================================
  4693. vt52.c
  4694. ==================================================
  4695. /*
  4696.  * The routines in this file
  4697.  * provide support for VT52 style terminals
  4698.  * over a serial line. The serial I/O services are
  4699.  * provided by routines in "termio.c". It compiles
  4700.  * into nothing if not a VT52 style device. The
  4701.  * bell on the VT52 is terrible, so the "beep"
  4702.  * routine is conditionalized on defining BEL.
  4703.  */
  4704. #include        <stdio.h>
  4705. #include        "ed.h"
  4706.  
  4707. #if     VT52
  4708.  
  4709. #define NROW    24                      /* Screen size.                 */
  4710. #define NCOL    80                      /* Edit if you want to.         */
  4711. #define BIAS    0x20                    /* Origin 0 coordinate bias.    */
  4712. #define ESC     0x1B                    /* ESC character.               */
  4713. #define BEL     0x07                    /* ascii bell character         */
  4714.  
  4715. extern  int     ttopen();               /* Forward references.          */
  4716. extern  int     ttgetc();
  4717. extern  int     ttputc();
  4718. extern  int     ttflush();
  4719. extern  int     ttclose();
  4720. extern  int     vt52move();
  4721. extern  int     vt52eeol();
  4722. extern  int     vt52eeop();
  4723. extern  int     vt52beep();
  4724. extern  int     vt52open();
  4725.  
  4726. /*
  4727.  * Dispatch table. All the
  4728.  * hard fields just point into the
  4729.  * terminal I/O code.
  4730.  */
  4731. TERM    term    = {
  4732.         NROW-1,
  4733.         NCOL,
  4734.         &vt52open,
  4735.         &ttclose,
  4736.         &ttgetc,
  4737.         &ttputc,
  4738.         &ttflush,
  4739.         &vt52move,
  4740.         &vt52eeol,
  4741.         &vt52eeop,
  4742.         &vt52beep
  4743. };
  4744.  
  4745. vt52move(row, col)
  4746. {
  4747.         ttputc(ESC);
  4748.         ttputc('Y');
  4749.         ttputc(row+BIAS);
  4750.         ttputc(col+BIAS);
  4751. }
  4752.  
  4753. vt52eeol()
  4754. {
  4755.         ttputc(ESC);
  4756.         ttputc('K');
  4757. }
  4758.  
  4759. vt52eeop()
  4760. {
  4761.         ttputc(ESC);
  4762.         ttputc('J');
  4763. }
  4764.  
  4765. vt52beep()
  4766. {
  4767. #ifdef  BEL
  4768.         ttputc(BEL);
  4769.         ttflush();
  4770. #endif
  4771. }
  4772.  
  4773. #endif
  4774.  
  4775. vt52open()
  4776. {
  4777. #if     V7
  4778.         register char *cp;
  4779.         char *getenv();
  4780.  
  4781.         if ((cp = getenv("TERM")) == NULL) {
  4782.                 puts("Shell variable TERM not defined!");
  4783.                 exit(1);
  4784.         }
  4785.         if (strcmp(cp, "vt52") != 0 && strcmp(cp, "z19") != 0) {
  4786.                 puts("Terminal type not 'vt52'or 'z19' !");
  4787.                 exit(1);
  4788.         }
  4789. #endif
  4790.         ttopen();
  4791. }
  4792.  
  4793. ==================================================
  4794. window.c
  4795. ==================================================
  4796.  
  4797. /*
  4798.  * Window management. Some of the functions are internal, and some are
  4799.  * attached to keys that the user actually types.
  4800.  */
  4801.  
  4802. #include        <stdio.h>
  4803. #include        "ed.h"
  4804.  
  4805. /*
  4806.  * Reposition dot in the current window to line "n". If the argument is
  4807.  * positive, it is that line. If it is negative it is that line from the
  4808.  * bottom. If it is 0 the window is centered (this is what the standard
  4809.  * redisplay code does). With no argument it defaults to 1. Bound to M-!.
  4810.  * Because of the default, it works like in Gosling.
  4811.  */
  4812. reposition(f, n)
  4813.     {
  4814.     curwp->w_force = n;
  4815.     curwp->w_flag |= WFFORCE;
  4816.     return (TRUE);
  4817.     }
  4818.  
  4819. /*
  4820.  * Refresh the screen. With no argument, it just does the refresh. With an
  4821.  * argument it recenters "." in the current window. Bound to "C-L".
  4822.  */
  4823. refresh(f, n)
  4824.     {
  4825.     if (f == FALSE)
  4826.         sgarbf = TRUE;
  4827.     else
  4828.         {
  4829.         curwp->w_force = 0;             /* Center dot. */
  4830.         curwp->w_flag |= WFFORCE;
  4831.         }
  4832.  
  4833.     return (TRUE);
  4834.     }
  4835.  
  4836. /*
  4837.  * The command make the next window (next => down the screen) the current
  4838.  * window. There are no real errors, although the command does nothing if
  4839.  * there is only 1 window on the screen. Bound to "C-X C-N".
  4840.  */
  4841. nextwind(f, n)
  4842.     {
  4843.     register WINDOW *wp;
  4844.  
  4845.     if ((wp = curwp->w_wndp) == NULL)
  4846.         wp = wheadp;
  4847.  
  4848.     curwp = wp;
  4849.     curbp = wp->w_bufp;
  4850.     return (TRUE);
  4851.     }
  4852.  
  4853. /*
  4854.  * This command makes the previous window (previous => up the screen) the
  4855.  * current window. There arn't any errors, although the command does not do a
  4856.  * lot if there is 1 window.
  4857.  */
  4858. prevwind(f, n)
  4859.     {
  4860.     register WINDOW *wp1;
  4861.     register WINDOW *wp2;
  4862.  
  4863.     wp1 = wheadp;
  4864.     wp2 = curwp;
  4865.  
  4866.     if (wp1 == wp2)
  4867.         wp2 = NULL;
  4868.  
  4869.     while (wp1->w_wndp != wp2)
  4870.         wp1 = wp1->w_wndp;
  4871.  
  4872.     curwp = wp1;
  4873.     curbp = wp1->w_bufp;
  4874.     return (TRUE);
  4875.     }
  4876.  
  4877. /*
  4878.  * This command moves the current window down by "arg" lines. Recompute the
  4879.  * top line in the window. The move up and move down code is almost completely
  4880.  * the same; most of the work has to do with reframing the window, and picking
  4881.  * a new dot. We share the code by having "move down" just be an interface to
  4882.  * "move up". Magic. Bound to "C-X C-N".
  4883.  */
  4884. mvdnwind(f, n)
  4885.     int n;
  4886.     {
  4887.     return (mvupwind(f, -n));
  4888.     }
  4889.  
  4890. /*
  4891.  * Move the current window up by "arg" lines. Recompute the new top line of
  4892.  * the window. Look to see if "." is still on the screen. If it is, you win.
  4893.  * If it isn't, then move "." to center it in the new framing of the window
  4894.  * (this command does not really move "."; it moves the frame). Bound to
  4895.  * "C-X C-P".
  4896.  */
  4897. mvupwind(f, n)
  4898.     int n;
  4899.     {
  4900.     register LINE *lp;
  4901.     register int i;
  4902.  
  4903.     lp = curwp->w_linep;
  4904.  
  4905.     if (n < 0)
  4906.         {
  4907.         while (n++ && lp!=curbp->b_linep)
  4908.             lp = lforw(lp);
  4909.         }
  4910.     else
  4911.         {
  4912.         while (n-- && lback(lp)!=curbp->b_linep)
  4913.             lp = lback(lp);
  4914.         }
  4915.  
  4916.     curwp->w_linep = lp;
  4917.     curwp->w_flag |= WFHARD;            /* Mode line is OK. */
  4918.  
  4919.     for (i = 0; i < curwp->w_ntrows; ++i)
  4920.         {
  4921.         if (lp == curwp->w_dotp)
  4922.             return (TRUE);
  4923.         if (lp == curbp->b_linep)
  4924.             break;
  4925.         lp = lforw(lp);
  4926.         }
  4927.  
  4928.     lp = curwp->w_linep;
  4929.     i  = curwp->w_ntrows/2;
  4930.  
  4931.     while (i-- && lp != curbp->b_linep)
  4932.         lp = lforw(lp);
  4933.  
  4934.     curwp->w_dotp  = lp;
  4935.     curwp->w_doto  = 0;
  4936.     return (TRUE);
  4937.     }
  4938.  
  4939. /*
  4940.  * This command makes the current window the only window on the screen. Bound
  4941.  * to "C-X 1". Try to set the framing so that "." does not have to move on the
  4942.  * display. Some care has to be taken to keep the values of dot and mark in
  4943.  * the buffer structures right if the distruction of a window makes a buffer
  4944.  * become undisplayed.
  4945.  */
  4946. onlywind(f, n)
  4947. {
  4948.         register WINDOW *wp;
  4949.         register LINE   *lp;
  4950.         register int    i;
  4951.  
  4952.         while (wheadp != curwp) {
  4953.                 wp = wheadp;
  4954.                 wheadp = wp->w_wndp;
  4955.                 if (--wp->w_bufp->b_nwnd == 0) {
  4956.                         wp->w_bufp->b_dotp  = wp->w_dotp;
  4957.                         wp->w_bufp->b_doto  = wp->w_doto;
  4958.                         wp->w_bufp->b_markp = wp->w_markp;
  4959.                         wp->w_bufp->b_marko = wp->w_marko;
  4960.                 }
  4961.                 free((char *) wp);
  4962.         }
  4963.         while (curwp->w_wndp != NULL) {
  4964.                 wp = curwp->w_wndp;
  4965.                 curwp->w_wndp = wp->w_wndp;
  4966.                 if (--wp->w_bufp->b_nwnd == 0) {
  4967.                         wp->w_bufp->b_dotp  = wp->w_dotp;
  4968.                         wp->w_bufp->b_doto  = wp->w_doto;
  4969.                         wp->w_bufp->b_markp = wp->w_markp;
  4970.                         wp->w_bufp->b_marko = wp->w_marko;
  4971.                 }
  4972.                 free((char *) wp);
  4973.         }
  4974.         lp = curwp->w_linep;
  4975.         i  = curwp->w_toprow;
  4976.         while (i!=0 && lback(lp)!=curbp->b_linep) {
  4977.                 --i;
  4978.                 lp = lback(lp);
  4979.         }
  4980.         curwp->w_toprow = 0;
  4981.         curwp->w_ntrows = term.t_nrow-1;
  4982.         curwp->w_linep  = lp;
  4983.         curwp->w_flag  |= WFMODE|WFHARD;
  4984.         return (TRUE);
  4985. }
  4986.  
  4987. /*
  4988.  * Split the current window. A window smaller than 3 lines cannot be split.
  4989.  * The only other error that is possible is a "malloc" failure allocating the
  4990.  * structure for the new window. Bound to "C-X 2".
  4991.  */
  4992. splitwind(f, n)
  4993. {
  4994.         register WINDOW *wp;
  4995.         register LINE   *lp;
  4996.         register int    ntru;
  4997.         register int    ntrl;
  4998.         register int    ntrd;
  4999.         register WINDOW *wp1;
  5000.         register WINDOW *wp2;
  5001.  
  5002.         if (curwp->w_ntrows < 3) {
  5003.                 mlwrite("Cannot split a %d line window", curwp->w_ntrows);
  5004.                 return (FALSE);
  5005.         }
  5006.         if ((wp = (WINDOW *) malloc(sizeof(WINDOW))) == NULL) {
  5007.                 mlwrite("Cannot allocate WINDOW block");
  5008.                 return (FALSE);
  5009.         }
  5010.         ++curbp->b_nwnd;                        /* Displayed twice.     */
  5011.         wp->w_bufp  = curbp;
  5012.         wp->w_dotp  = curwp->w_dotp;
  5013.         wp->w_doto  = curwp->w_doto;
  5014.         wp->w_markp = curwp->w_markp;
  5015.         wp->w_marko = curwp->w_marko;
  5016.         wp->w_flag  = 0;
  5017.         wp->w_force = 0;
  5018.         ntru = (curwp->w_ntrows-1) / 2;         /* Upper size           */
  5019.         ntrl = (curwp->w_ntrows-1) - ntru;      /* Lower size           */
  5020.         lp = curwp->w_linep;
  5021.         ntrd = 0;
  5022.         while (lp != curwp->w_dotp) {
  5023.                 ++ntrd;
  5024.                 lp = lforw(lp);
  5025.         }
  5026.         lp = curwp->w_linep;
  5027.         if (ntrd <= ntru) {                     /* Old is upper window. */
  5028.                 if (ntrd == ntru)               /* Hit mode line.       */
  5029.                         lp = lforw(lp);
  5030.                 curwp->w_ntrows = ntru;
  5031.                 wp->w_wndp = curwp->w_wndp;
  5032.                 curwp->w_wndp = wp;
  5033.                 wp->w_toprow = curwp->w_toprow+ntru+1;
  5034.                 wp->w_ntrows = ntrl;
  5035.         } else {                                /* Old is lower window  */
  5036.                 wp1 = NULL;
  5037.                 wp2 = wheadp;
  5038.                 while (wp2 != curwp) {
  5039.                         wp1 = wp2;
  5040.                         wp2 = wp2->w_wndp;
  5041.                 }
  5042.                 if (wp1 == NULL)
  5043.                         wheadp = wp;
  5044.                 else
  5045.                         wp1->w_wndp = wp;
  5046.                 wp->w_wndp   = curwp;
  5047.                 wp->w_toprow = curwp->w_toprow;
  5048.                 wp->w_ntrows = ntru;
  5049.                 ++ntru;                         /* Mode line.           */
  5050.                 curwp->w_toprow += ntru;
  5051.                 curwp->w_ntrows  = ntrl;
  5052.                 while (ntru--)
  5053.                         lp = lforw(lp);
  5054.         }
  5055.         curwp->w_linep = lp;                    /* Adjust the top lines */
  5056.         wp->w_linep = lp;                       /* if necessary.        */
  5057.         curwp->w_flag |= WFMODE|WFHARD;
  5058.         wp->w_flag |= WFMODE|WFHARD;
  5059.         return (TRUE);
  5060. }
  5061.  
  5062. /*
  5063.  * Enlarge the current window. Find the window that loses space. Make sure it
  5064.  * is big enough. If so, hack the window descriptions, and ask redisplay to do
  5065.  * all the hard work. You don't just set "force reframe" because dot would
  5066.  * move. Bound to "C-X Z".
  5067.  */
  5068. enlargewind(f, n)
  5069. {
  5070.         register WINDOW *adjwp;
  5071.         register LINE   *lp;
  5072.         register int    i;
  5073.  
  5074.         if (n < 0)
  5075.                 return (shrinkwind(f, -n));
  5076.         if (wheadp->w_wndp == NULL) {
  5077.                 mlwrite("Only one window");
  5078.                 return (FALSE);
  5079.         }
  5080.         if ((adjwp=curwp->w_wndp) == NULL) {
  5081.                 adjwp = wheadp;
  5082.                 while (adjwp->w_wndp != curwp)
  5083.                         adjwp = adjwp->w_wndp;
  5084.         }
  5085.         if (adjwp->w_ntrows <= n) {
  5086.                 mlwrite("Impossible change");
  5087.                 return (FALSE);
  5088.         }
  5089.         if (curwp->w_wndp == adjwp) {           /* Shrink below.        */
  5090.                 lp = adjwp->w_linep;
  5091.                 for (i=0; i<n && lp!=adjwp->w_bufp->b_linep; ++i)
  5092.                         lp = lforw(lp);
  5093.                 adjwp->w_linep  = lp;
  5094.                 adjwp->w_toprow += n;
  5095.         } else {                                /* Shrink above.        */
  5096.                 lp = curwp->w_linep;
  5097.                 for (i=0; i<n && lback(lp)!=curbp->b_linep; ++i)
  5098.                         lp = lback(lp);
  5099.                 curwp->w_linep  = lp;
  5100.                 curwp->w_toprow -= n;
  5101.         }
  5102.         curwp->w_ntrows += n;
  5103.         adjwp->w_ntrows -= n;
  5104.         curwp->w_flag |= WFMODE|WFHARD;
  5105.         adjwp->w_flag |= WFMODE|WFHARD;
  5106.         return (TRUE);
  5107. }
  5108.  
  5109. /*
  5110.  * Shrink the current window. Find the window that gains space. Hack at the
  5111.  * window descriptions. Ask the redisplay to do all the hard work. Bound to
  5112.  * "C-X C-Z".
  5113.  */
  5114. shrinkwind(f, n)
  5115. {
  5116.         register WINDOW *adjwp;
  5117.         register LINE   *lp;
  5118.         register int    i;
  5119.  
  5120.         if (n < 0)
  5121.                 return (enlargewind(f, -n));
  5122.         if (wheadp->w_wndp == NULL) {
  5123.                 mlwrite("Only one window");
  5124.                 return (FALSE);
  5125.         }
  5126.         if ((adjwp=curwp->w_wndp) == NULL) {
  5127.                 adjwp = wheadp;
  5128.                 while (adjwp->w_wndp != curwp)
  5129.                         adjwp = adjwp->w_wndp;
  5130.         }
  5131.         if (curwp->w_ntrows <= n) {
  5132.                 mlwrite("Impossible change");
  5133.                 return (FALSE);
  5134.         }
  5135.         if (curwp->w_wndp == adjwp) {           /* Grow below.          */
  5136.                 lp = adjwp->w_linep;
  5137.                 for (i=0; i<n && lback(lp)!=adjwp->w_bufp->b_linep; ++i)
  5138.                         lp = lback(lp);
  5139.                 adjwp->w_linep  = lp;
  5140.                 adjwp->w_toprow -= n;
  5141.         } else {                                /* Grow above.          */
  5142.                 lp = curwp->w_linep;
  5143.                 for (i=0; i<n && lp!=curbp->b_linep; ++i)
  5144.                         lp = lforw(lp);
  5145.                 curwp->w_linep  = lp;
  5146.                 curwp->w_toprow += n;
  5147.         }
  5148.         curwp->w_ntrows -= n;
  5149.         adjwp->w_ntrows += n;
  5150.         curwp->w_flag |= WFMODE|WFHARD;
  5151.         adjwp->w_flag |= WFMODE|WFHARD;
  5152.         return (TRUE);
  5153. }
  5154.  
  5155. /*
  5156.  * Pick a window for a pop-up. Split the screen if there is only one window.
  5157.  * Pick the uppermost window that isn't the current window. An LRU algorithm
  5158.  * might be better. Return a pointer, or NULL on error.
  5159.  */
  5160. WINDOW  *
  5161. wpopup()
  5162. {
  5163.         register WINDOW *wp;
  5164.  
  5165.         if (wheadp->w_wndp == NULL              /* Only 1 window        */
  5166.         && splitwind(FALSE, 0) == FALSE)        /* and it won't split   */
  5167.                 return (NULL);
  5168.         wp = wheadp;                            /* Find window to use   */
  5169.         while (wp!=NULL && wp==curwp)
  5170.                 wp = wp->w_wndp;
  5171.         return (wp);
  5172. }
  5173.  
  5174.  
  5175. OK
  5176. ==================================================
  5177. word.c
  5178. ==================================================
  5179. /*
  5180.  * The routines in this file implement commands that work word at a time.
  5181.  * There are all sorts of word mode commands. If I do any sentence and/or
  5182.  * paragraph mode commands, they are likely to be put in this file.
  5183.  */
  5184.  
  5185. #include        <stdio.h>
  5186. #include        "ed.h"
  5187.  
  5188.  
  5189. /* Word wrap on n-spaces. Back-over whatever precedes the point on the current
  5190.  * line and stop on the first word-break or the beginning of the line. If we
  5191.  * reach the beginning of the line, jump back to the end of the word and start
  5192.  * a new line.  Otherwise, break the line at the word-break, eat it, and jump
  5193.  * back to the end of the word.
  5194.  *      NOTE:  This function may leaving trailing blanks.
  5195.  * Returns TRUE on success, FALSE on errors.
  5196.  */
  5197. wrapword(n)
  5198. int n;
  5199. {
  5200.         register int cnt, oldp;
  5201.         oldp = curwp->w_dotp;
  5202.         cnt = -1;
  5203.         do {                            
  5204.                 cnt++;
  5205.                 if (! backchar(NULL, 1))
  5206.                         return(FALSE);
  5207.         }
  5208.         while (! inword());
  5209.         if (! backword(NULL, 1))
  5210.                 return(FALSE);
  5211.         if (oldp == (int) (curwp->w_dotp && curwp->w_doto)) {
  5212.                 if (! backdel(NULL, 1))
  5213.                         return(FALSE);
  5214.                 if (! newline(NULL, 1))
  5215.                         return(FALSE);
  5216.         }
  5217.         return(forwword(NULL, 1) && forwchar(NULL, cnt));
  5218. }
  5219.                                 
  5220. /*
  5221.  * Move the cursor backward by "n" words. All of the details of motion are
  5222.  * performed by the "backchar" and "forwchar" routines. Error if you try to
  5223.  * move beyond the buffers.
  5224.  */
  5225. backword(f, n)
  5226. {
  5227.         if (n < 0)
  5228.                 return (forwword(f, -n));
  5229.         if (backchar(FALSE, 1) == FALSE)
  5230.                 return (FALSE);
  5231.         while (n--) {
  5232.                 while (inword() == FALSE) {
  5233.                         if (backchar(FALSE, 1) == FALSE)
  5234.                                 return (FALSE);
  5235.                 }
  5236.                 while (inword() != FALSE) {
  5237.                         if (backchar(FALSE, 1) == FALSE)
  5238.                                 return (FALSE);
  5239.                 }
  5240.         }
  5241.         return (forwchar(FALSE, 1));
  5242. }
  5243.  
  5244. /*
  5245.  * Move the cursor forward by the specified number of words. All of the motion
  5246.  * is done by "forwchar". Error if you try and move beyond the buffer's end.
  5247.  */
  5248. forwword(f, n)
  5249. {
  5250.         if (n < 0)
  5251.                 return (backword(f, -n));
  5252.         while (n--) {
  5253.                 while (inword() == FALSE) {
  5254.                         if (forwchar(FALSE, 1) == FALSE)
  5255.                                 return (FALSE);
  5256.                 }
  5257.                 while (inword() != FALSE) {
  5258.                         if (forwchar(FALSE, 1) == FALSE)
  5259.                                 return (FALSE);
  5260.                 }
  5261.         }
  5262.         return (TRUE);
  5263. }
  5264.  
  5265. /*
  5266.  * Move the cursor forward by the specified number of words. As you move,
  5267.  * convert any characters to upper case. Error if you try and move beyond the
  5268.  * end of the buffer. Bound to "M-U".
  5269.  */
  5270. upperword(f, n)
  5271. {
  5272.         register int    c;
  5273.  
  5274.         if (n < 0)
  5275.                 return (FALSE);
  5276.         while (n--) {
  5277.                 while (inword() == FALSE) {
  5278.                         if (forwchar(FALSE, 1) == FALSE)
  5279.                                 return (FALSE);
  5280.                 }
  5281.                 while (inword() != FALSE) {
  5282.                         c = lgetc(curwp->w_dotp, curwp->w_doto);
  5283.                         if (c>='a' && c<='z') {
  5284.                                 c -= 'a'-'A';
  5285.                                 lputc(curwp->w_dotp, curwp->w_doto, c);
  5286.                                 lchange(WFHARD);
  5287.                         }
  5288.                         if (forwchar(FALSE, 1) == FALSE)
  5289.                                 return (FALSE);
  5290.                 }
  5291.         }
  5292.         return (TRUE);
  5293. }
  5294.  
  5295. /*
  5296.  * Move the cursor forward by the specified number of words. As you move
  5297.  * convert characters to lower case. Error if you try and move over the end of
  5298.  * the buffer. Bound to "M-L".
  5299.  */
  5300. lowerword(f, n)
  5301. {
  5302.         register int    c;
  5303.  
  5304.         if (n < 0)
  5305.                 return (FALSE);
  5306.         while (n--) {
  5307.                 while (inword() == FALSE) {
  5308.                         if (forwchar(FALSE, 1) == FALSE)
  5309.                                 return (FALSE);
  5310.                 }
  5311.                 while (inword() != FALSE) {
  5312.                         c = lgetc(curwp->w_dotp, curwp->w_doto);
  5313.                         if (c>='A' && c<='Z') {
  5314.                                 c += 'a'-'A';
  5315.                                 lputc(curwp->w_dotp, curwp->w_doto, c);
  5316.                                 lchange(WFHARD);
  5317.                         }
  5318.                         if (forwchar(FALSE, 1) == FALSE)
  5319.                                 return (FALSE);
  5320.                 }
  5321.         }
  5322.         return (TRUE);
  5323. }
  5324.  
  5325. /*
  5326.  * Move the cursor forward by the specified number of words. As you move
  5327.  * convert the first character of the word to upper case, and subsequent
  5328.  * characters to lower case. Error if you try and move past the end of the
  5329.  * buffer. Bound to "M-C".
  5330.  */
  5331. capword(f, n)
  5332. {
  5333.         register int    c;
  5334.  
  5335.         if (n < 0)
  5336.                 return (FALSE);
  5337.         while (n--) {
  5338.                 while (inword() == FALSE) {
  5339.                         if (forwchar(FALSE, 1) == FALSE)
  5340.                                 return (FALSE);
  5341.                 }
  5342.                 if (inword() != FALSE) {
  5343.                         c = lgetc(curwp->w_dotp, curwp->w_doto);
  5344.                         if (c>='a' && c<='z') {
  5345.                                 c -= 'a'-'A';
  5346.                                 lputc(curwp->w_dotp, curwp->w_doto, c);
  5347.                                 lchange(WFHARD);
  5348.                         }
  5349.                         if (forwchar(FALSE, 1) == FALSE)
  5350.                                 return (FALSE);
  5351.                         while (inword() != FALSE) {
  5352.                                 c = lgetc(curwp->w_dotp, curwp->w_doto);
  5353.                                 if (c>='A' && c<='Z') {
  5354.                                         c += 'a'-'A';
  5355.                                         lputc(curwp->w_dotp, curwp->w_doto, c);
  5356.                                         lchange(WFHARD);
  5357.                                 }
  5358.                                 if (forwchar(FALSE, 1) == FALSE)
  5359.                                         return (FALSE);
  5360.                         }
  5361.                 }
  5362.         }
  5363.         return (TRUE);
  5364. }
  5365.  
  5366. /*
  5367.  * Kill forward by "n" words. Remember the location of dot. Move forward by
  5368.  * the right number of words. Put dot back where it was and issue the kill
  5369.  * command for the right number of characters. Bound to "M-D".
  5370.  */
  5371. delfword(f, n)
  5372. {
  5373.         register int    size;
  5374.         register LINE   *dotp;
  5375.         register int    doto;
  5376.  
  5377.         if (n < 0)
  5378.                 return (FALSE);
  5379.         dotp = curwp->w_dotp;
  5380.         doto = curwp->w_doto;
  5381.         size = 0;
  5382.         while (n--) {
  5383.                 while (inword() == FALSE) {
  5384.                         if (forwchar(FALSE, 1) == FALSE)
  5385.                                 return (FALSE);
  5386.                         ++size;
  5387.                 }
  5388.                 while (inword() != FALSE) {
  5389.                         if (forwchar(FALSE, 1) == FALSE)
  5390.                                 return (FALSE);
  5391.                         ++size;
  5392.                 }
  5393.         }
  5394.         curwp->w_dotp = dotp;
  5395.         curwp->w_doto = doto;
  5396.         return (ldelete(size, TRUE));
  5397. }
  5398.  
  5399. /*
  5400.  * Kill backwards by "n" words. Move backwards by the desired number of words,
  5401.  * counting the characters. When dot is finally moved to its resting place,
  5402.  * fire off the kill command. Bound to "M-Rubout" and to "M-Backspace".
  5403.  */
  5404. delbword(f, n)
  5405. {
  5406.         register int    size;
  5407.  
  5408.         if (n < 0)
  5409.                 return (FALSE);
  5410.         if (backchar(FALSE, 1) == FALSE)
  5411.                 return (FALSE);
  5412.         size = 0;
  5413.         while (n--) {
  5414.                 while (inword() == FALSE) {
  5415.                         if (backchar(FALSE, 1) == FALSE)
  5416.                                 return (FALSE);
  5417.                         ++size;
  5418.                 }
  5419.                 while (inword() != FALSE) {
  5420.                         if (backchar(FALSE, 1) == FALSE)
  5421.                                 return (FALSE);
  5422.                         ++size;
  5423.                 }
  5424.         }
  5425.         if (forwchar(FALSE, 1) == FALSE)
  5426.                 return (FALSE);
  5427.         return (ldelete(size, TRUE));
  5428. }
  5429.  
  5430. /*
  5431.  * Return TRUE if the character at dot is a character that is considered to be
  5432.  * part of a word. The word character list is hard coded. Should be setable.
  5433.  */
  5434. inword()
  5435. {
  5436.         register int    c;
  5437.  
  5438.         if (curwp->w_doto == llength(curwp->w_dotp))
  5439.                 return (FALSE);
  5440.         c = lgetc(curwp->w_dotp, curwp->w_doto);
  5441.         if (c>='a' && c<='z')
  5442.                 return (TRUE);
  5443.         if (c>='A' && c<='Z')
  5444.                 return (TRUE);
  5445.         if (c>='0' && c<='9')
  5446.                 return (TRUE);
  5447.         if (c=='$' || c=='_')                   /* For identifiers      */
  5448.                 return (TRUE);
  5449.         return (FALSE);
  5450. }
  5451.  
  5452. ==================================================
  5453. ed.h
  5454. ==================================================
  5455. /*
  5456.  * This file is the general header file for all parts of the MicroEMACS
  5457.  * display editor. It contains definitions used by everyone, and it contains
  5458.  * the stuff you have to edit to create a version of the editor for a specific
  5459.  * operating system and terminal.
  5460.  */
  5461.  
  5462. #define AMIGA   1                       /* AmigaDOS, Lattice            */
  5463. #define ST520   0                       /* ST520, TOS                   */
  5464. #define MWC86   0
  5465. #define V7      0                       /* V7 UN*X or Coherent          */
  5466. #define VMS     0                       /* VAX/VMS                      */
  5467. #define CPM     0                       /* CP/M-86                      */
  5468.  
  5469. #ifndef MSDOS                           /* Already defined for Lattice  */
  5470. #define MSDOS   0                       /* MS-DOS                       */
  5471. #endif
  5472.  
  5473. #define ANSI    1
  5474. #define VT52    0                       /* VT52 terminal (Zenith).      */
  5475. #define VT100   0                       /* Handle VT100 style keypad.   */
  5476. #define LK201   0                       /* Handle LK201 style keypad.   */
  5477. #define RAINBOW 0                       /* Use Rainbow fast video.      */
  5478. #define TERMCAP 0                       /* Use TERMCAP                  */
  5479.  
  5480. #define CVMVAS  1                       /* C-V, M-V arg. in screens.    */
  5481.  
  5482. #define NFILEN  80                      /* # of bytes, file name        */
  5483. #define NBUFN   16                      /* # of bytes, buffer name      */
  5484. #define NLINE   256                     /* # of bytes, line             */
  5485. #define NKBDM   256                     /* # of strokes, keyboard macro */
  5486. #define NPAT    80                      /* # of bytes, pattern          */
  5487. #define HUGE    1000                    /* Huge number                  */
  5488.  
  5489. #define AGRAVE  0x60                    /* M- prefix,   Grave (LK201)   */
  5490. #define METACH  0x1B                    /* M- prefix,   Control-[, ESC  */
  5491. #define CTMECH  0x1C                    /* C-M- prefix, Control-\       */
  5492. #define EXITCH  0x1D                    /* Exit level,  Control-]       */
  5493. #define CTRLCH  0x1E                    /* C- prefix,   Control-^       */
  5494. #define HELPCH  0x1F                    /* Help key,    Control-_       */
  5495.  
  5496. #define CTRL    0x0100                  /* Control flag, or'ed in       */
  5497. #define META    0x0200                  /* Meta flag, or'ed in          */
  5498. #define CTLX    0x0400                  /* ^X flag, or'ed in            */
  5499.  
  5500. #define FALSE   0                       /* False, no, bad, etc.         */
  5501. #define TRUE    1                       /* True, yes, good, etc.        */
  5502. #define ABORT   2                       /* Death, ^G, abort, etc.       */
  5503.  
  5504. #define FIOSUC  0                       /* File I/O, success.           */
  5505. #define FIOFNF  1                       /* File I/O, file not found.    */
  5506. #define FIOEOF  2                       /* File I/O, end of file.       */
  5507. #define FIOERR  3                       /* File I/O, error.             */
  5508.  
  5509. #define CFCPCN  0x0001                  /* Last command was C-P, C-N    */
  5510. #define CFKILL  0x0002                  /* Last command was a kill      */
  5511.  
  5512. /*
  5513.  * There is a window structure allocated for every active display window. The
  5514.  * windows are kept in a big list, in top to bottom screen order, with the
  5515.  * listhead at "wheadp". Each window contains its own values of dot and mark.
  5516.  * The flag field contains some bits that are set by commands to guide
  5517.  * redisplay; although this is a bit of a compromise in terms of decoupling,
  5518.  * the full blown redisplay is just too expensive to run for every input
  5519.  * character. 
  5520.  */
  5521. typedef struct  WINDOW {
  5522.         struct  WINDOW *w_wndp;         /* Next window                  */
  5523.         struct  BUFFER *w_bufp;         /* Buffer displayed in window   */
  5524.         struct  LINE *w_linep;          /* Top line in the window       */
  5525.         struct  LINE *w_dotp;           /* Line containing "."          */
  5526.         short   w_doto;                 /* Byte offset for "."          */
  5527.         struct  LINE *w_markp;          /* Line containing "mark"       */
  5528.         short   w_marko;                /* Byte offset for "mark"       */
  5529.         char    w_toprow;               /* Origin 0 top row of window   */
  5530.         char    w_ntrows;               /* # of rows of text in window  */
  5531.         char    w_force;                /* If NZ, forcing row.          */
  5532.         char    w_flag;                 /* Flags.                       */
  5533. }       WINDOW;
  5534.  
  5535. #define WFFORCE 0x01                    /* Window needs forced reframe  */
  5536. #define WFMOVE  0x02                    /* Movement from line to line   */
  5537. #define WFEDIT  0x04                    /* Editing within a line        */
  5538. #define WFHARD  0x08                    /* Better to a full display     */
  5539. #define WFMODE  0x10                    /* Update mode line.            */
  5540.  
  5541. /*
  5542.  * Text is kept in buffers. A buffer header, described below, exists for every
  5543.  * buffer in the system. The buffers are kept in a big list, so that commands
  5544.  * that search for a buffer by name can find the buffer header. There is a
  5545.  * safe store for the dot and mark in the header, but this is only valid if
  5546.  * the buffer is not being displayed (that is, if "b_nwnd" is 0). The text for
  5547.  * the buffer is kept in a circularly linked list of lines, with a pointer to
  5548.  * the header line in "b_linep".
  5549.  */
  5550. typedef struct  BUFFER {
  5551.         struct  BUFFER *b_bufp;         /* Link to next BUFFER          */
  5552.         struct  LINE *b_dotp;           /* Link to "." LINE structure   */
  5553.         short   b_doto;                 /* Offset of "." in above LINE  */
  5554.         struct  LINE *b_markp;          /* The same as the above two,   */
  5555.         short   b_marko;                /* but for the "mark"           */
  5556.         struct  LINE *b_linep;          /* Link to the header LINE      */
  5557.         char    b_nwnd;                 /* Count of windows on buffer   */
  5558.         char    b_flag;                 /* Flags                        */
  5559.         char    b_fname[NFILEN];        /* File name                    */
  5560.         char    b_bname[NBUFN];         /* Buffer name                  */
  5561. }       BUFFER;
  5562.  
  5563. #define BFTEMP  0x01                    /* Internal temporary buffer    */
  5564. #define BFCHG   0x02                    /* Changed since last write     */
  5565.  
  5566. /*
  5567.  * The starting position of a region, and the size of the region in
  5568.  * characters, is kept in a region structure.  Used by the region commands.
  5569.  */
  5570. typedef struct  {
  5571.         struct  LINE *r_linep;          /* Origin LINE address.         */
  5572.         short   r_offset;               /* Origin LINE offset.          */
  5573.         short   r_size;                 /* Length in characters.        */
  5574. }       REGION;
  5575.  
  5576. /*
  5577.  * All text is kept in circularly linked lists of "LINE" structures. These
  5578.  * begin at the header line (which is the blank line beyond the end of the
  5579.  * buffer). This line is pointed to by the "BUFFER". Each line contains a the
  5580.  * number of bytes in the line (the "used" size), the size of the text array,
  5581.  * and the text. The end of line is not stored as a byte; it's implied. Future
  5582.  * additions will include update hints, and a list of marks into the line.
  5583.  */
  5584. typedef struct  LINE {
  5585.         struct  LINE *l_fp;             /* Link to the next line        */
  5586.         struct  LINE *l_bp;             /* Link to the previous line    */
  5587.         short   l_size;                 /* Allocated size               */
  5588.         short   l_used;                 /* Used size                    */
  5589.         char    l_text[1];              /* A bunch of characters.       */
  5590. }       LINE;
  5591.  
  5592. #define lforw(lp)       ((lp)->l_fp)
  5593. #define lback(lp)       ((lp)->l_bp)
  5594. #define lgetc(lp, n)    ((lp)->l_text[(n)]&0xFF)
  5595. #define lputc(lp, n, c) ((lp)->l_text[(n)]=(c))
  5596. #define llength(lp)     ((lp)->l_used)
  5597.  
  5598. /*
  5599.  * The editor communicates with the display using a high level interface. A
  5600.  * "TERM" structure holds useful variables, and indirect pointers to routines
  5601.  * that do useful operations. The low level get and put routines are here too.
  5602.  * This lets a terminal, in addition to having non standard commands, have
  5603.  * funny get and put character code too. The calls might get changed to
  5604.  * "termp->t_field" style in the future, to make it possible to run more than
  5605.  * one terminal type.
  5606.  */  
  5607. typedef struct  {
  5608.         short   t_nrow;                 /* Number of rows.              */
  5609.         short   t_ncol;                 /* Number of columns.           */
  5610.         int     (*t_open)();            /* Open terminal at the start.  */
  5611.         int     (*t_close)();           /* Close terminal at end.       */
  5612.         int     (*t_getchar)();         /* Get character from keyboard. */
  5613.         int     (*t_putchar)();         /* Put character to display.    */
  5614.         int     (*t_flush)();           /* Flush output buffers.        */
  5615.         int     (*t_move)();            /* Move the cursor, origin 0.   */
  5616.         int     (*t_eeol)();            /* Erase to end of line.        */
  5617.         int     (*t_eeop)();            /* Erase to end of page.        */
  5618.         int     (*t_beep)();            /* Beep.                        */
  5619. }       TERM;
  5620.  
  5621. extern  int     fillcol;                /* Fill column                  */
  5622. extern  int     currow;                 /* Cursor row                   */
  5623. extern  int     curcol;                 /* Cursor column                */
  5624. extern  int     thisflag;               /* Flags, this command          */
  5625. extern  int     lastflag;               /* Flags, last command          */
  5626. extern  int     curgoal;                /* Goal for C-P, C-N            */
  5627. extern  int     mpresf;                 /* Stuff in message line        */
  5628. extern  int     sgarbf;                 /* State of screen unknown      */
  5629. extern  WINDOW  *curwp;                 /* Current window               */
  5630. extern  BUFFER  *curbp;                 /* Current buffer               */
  5631. extern  WINDOW  *wheadp;                /* Head of list of windows      */
  5632. extern  BUFFER  *bheadp;                /* Head of list of buffers      */
  5633. extern  BUFFER  *blistp;                /* Buffer for C-X C-B           */
  5634. extern  short   kbdm[];                 /* Holds kayboard macro data    */
  5635. extern  short   *kbdmip;                /* Input pointer for above      */
  5636. extern  short   *kbdmop;                /* Output pointer for above     */
  5637. extern  char    pat[];                  /* Search pattern               */
  5638. extern  TERM    term;                   /*(Terminal information.        */
  5639.  
  5640. extern  BUFFER  *bfind();               /* Lookup a buffer by name      */
  5641. extern  WINDOW  *wpopup();              /* Pop up window creation       */
  5642. extern  LINE    *lalloc();              /* Allocate a line              */
  5643.  
  5644. -- 
  5645. -----------
  5646. George M. Jones        {cbosgd,ihnp4}!osu-eddie!george
  5647. Work Phone:        george@ohio-state.csnet
  5648. (614) 457-8600        CompuServe: 70003,2443
  5649.