home *** CD-ROM | disk | FTP | other *** search
/ Shareware 1 2 the Maxx / sw_1.zip / sw_1 / EDITORS / TDE20.ZIP / ED.C < prev    next >
C/C++ Source or Header  |  1992-06-05  |  67KB  |  2,196 lines

  1. /*******************  start of original comments  ********************/
  2. /*
  3.  * Written by Douglas Thomson (1989/1990)
  4.  *
  5.  * This source code is released into the public domain.
  6.  */
  7.  
  8. /*
  9.  * Name:    dte - Doug's Text Editor program - main editor module
  10.  * Purpose: This file contains the main editor module, and a number of the
  11.  *           smaller miscellaneous editing commands.
  12.  *          It also contains the code for dispatching commands.
  13.  * File:    ed.c
  14.  * Author:  Douglas Thomson
  15.  * System:  this file is intended to be system-independent
  16.  * Date:    October 1, 1989
  17.  * I/O:     file being edited
  18.  *          files read or written
  19.  *          user commands and prompts
  20.  * Notes:   see the file "dte.doc" for general program documentation
  21.  */
  22. /*********************  end of original comments   ********************/
  23.  
  24. /*
  25.  * The basic editor routines have been EXTENSIVELY rewritten.  I have added
  26.  * support for lines longer than 80 columns and I have added line number
  27.  * support.  I like to know the real line number that editor functions are
  28.  * working on and I like to know the total number of lines in a file.
  29.  *
  30.  * I rewrote the big series of ifs in the dispatch subroutine.  It is now
  31.  * an array of pointers to functions.  We know what function to call as soon
  32.  * as a key is pressed.  It is also makes it easier to implement a configuration
  33.  * utility and macros.
  34.  *
  35.  * I added a few functions that I use quite often and I deleted a few that I
  36.  * rarely use.  Added are Split Line, Join Line, and Duplicate Line.   Deleted
  37.  * are Goto Marker 0-9 (others?).
  38.  *
  39.  * ************ In tde 1.3, I put Goto Marker 0-9 back in.  ***************
  40.  *
  41.  * I felt that the insert routine should be separated into two routines.  One
  42.  * for inserting the various combinations of newlines and one for inserting
  43.  * 'regular' text characters, ASCII and extended ASCII characters.
  44.  *
  45.  * One of Doug's design considerations was keeping screen updates to a minimum.
  46.  * I have expanded upon that idea and added support for updating windows
  47.  * LOCALly, GLOBALly, or NOT_LOCALly.  For example, scrolling in one window
  48.  * does not affect the text in another window - LOCAL update.  Adding, deleting,
  49.  * or modifying text in one window may affect text in other windows - GLOBAL
  50.  * update.  Sometimes, updates to the current window are handled in the task
  51.  * routines so updates to other windows are done NOT_LOCALly.
  52.  *
  53.  * Also note that using functions copy_line and un_copy_line to change a line
  54.  * automatically adjusts the g_status.end_mem pointer.  If a function bypasses
  55.  * those functions, adjusting the g_status.end_mem pointer must be done
  56.  * explicitly.
  57.  *
  58.  * New editor name:  tde, the Thomson-Davis Editor.
  59.  * Author:           Frank Davis
  60.  * Date:             June 5, 1991, version 1.0
  61.  * Date:             July 29, 1991, version 1.1
  62.  * Date:             October 5, 1991, version 1.2
  63.  * Date:             January 20, 1992, version 1.3
  64.  * Date:             February 17, 1992, version 1.4
  65.  * Date:             April 1, 1992, version 1.5
  66.  * Date:             June 5, 1992, version 2.0
  67.  *
  68.  * This modification of Douglas Thomson's code is released into the
  69.  * public domain, Frank Davis.   You may distribute it freely.
  70.  */
  71.  
  72. #include "tdestr.h"     /* typedefs for global variables */
  73. #include "define.h"     /* editor function defs */
  74. #include "tdefunc.h"    /* prototypes for all functions in tde */
  75. #include "global.h"     /* global variables */
  76. #include "prompts.h"    /* prompt assignments */
  77. #include "default.h"    /* default function key assignments */
  78.  
  79.  
  80. /*
  81.  * Name:    tab_key
  82.  * Purpose: To make the necessary changes after the user types the tab key.
  83.  * Date:    June 5, 1991
  84.  * Passed:  window:  pointer to current window
  85.  * Notes:   If in insert mode, then this function adds the required
  86.  *           number of spaces in the file.
  87.  *          If not in insert mode, then tab simply moves the cursor right
  88.  *           the required distance.
  89.  */
  90. int  tab_key( WINDOW *window )
  91. {
  92. int spaces;      /* the spaces to move to the next tab stop */
  93. char *source;    /* source for block move to make room for c */
  94. char *dest;      /* destination for block move */
  95. int pad, len;
  96. register int rcol;
  97. int old_bcol;
  98. register WINDOW *win;  /* put window pointer in a register */
  99. int rc;
  100.  
  101.    win  = window;
  102.    if (*win->cursor == CONTROL_Z)
  103.       return( OK );
  104.    rcol = win->rcol;
  105.    old_bcol = win->bcol;
  106.    show_ruler_char( win );
  107.    /*
  108.     * work out the number of spaces to the next tab stop
  109.     */
  110.    if (mode.smart_tab)
  111.       spaces = next_smart_tab( win );
  112.    else
  113.       spaces = mode.tab_size - (rcol % mode.tab_size);
  114.  
  115.    if (mode.insert && rcol + spaces < g_display.line_length) {
  116.       copy_line( win->cursor, win->bottom_line );
  117.       /*
  118.        * work out how many characters need to be inserted
  119.        */
  120.       len = linelen( g_status.line_buff );
  121.       pad = rcol > len ? rcol - len : 0;
  122.       if (g_status.line_buff[len] == CONTROL_Z)
  123.          ++pad;
  124.       if (len + pad + spaces >= g_display.line_length) {
  125.          /*
  126.           *  line too long to add
  127.           */
  128.          error( WARNING, win->bottom_line, ed1 );
  129.          rc = ERROR;
  130.       } else {
  131.          if (pad > 0  || spaces > 0) {
  132.             if (g_status.line_buff[len] == CONTROL_Z) {
  133.                g_status.line_buff[len] = '\n';
  134.                g_status.line_buff[len+1] = CONTROL_Z;
  135.                ++win->file_info->length;
  136.                show_size( window );
  137.                --pad;
  138.                ++len;
  139.             }
  140.             source = g_status.line_buff + rcol - pad;
  141.             dest = source + pad + spaces;
  142.             memmove( dest, source, len+pad-rcol+2 );
  143.  
  144.             /*
  145.              * if padding was required, then put in the required spaces
  146.              */
  147.             memset( source, ' ', pad + spaces );
  148.          }
  149.  
  150.          win->file_info->dirty = GLOBAL;
  151.          show_changed_line( win );
  152.          rcol += spaces;
  153.          win->ccol += spaces;
  154.          rc = OK;
  155.       }
  156.    } else if (rcol + spaces <= g_display.line_length) {
  157.       /*
  158.        * advance the cursor without changing the text underneath
  159.        */
  160.       rcol += spaces;
  161.       win->ccol += spaces;
  162.       rc = OK;
  163.    }
  164.    check_virtual_col( win, rcol, win->ccol );
  165.    if (old_bcol != win->bcol) {
  166.       make_ruler( win );
  167.       show_ruler( win );
  168.    }
  169.    return( rc );
  170. }
  171.  
  172.  
  173. /*
  174.  * Name:    backtab
  175.  * Purpose: To make the necessary changes after the user presses the backtab.
  176.  * Date:    November 1, 1991
  177.  * Passed:  window:  pointer to current window
  178.  * Notes:   If in insert mode, then this function subs the required
  179.  *           number of spaces in the file.
  180.  *          If not in insert mode, then tab simply moves the cursor left
  181.  *           the required distance.
  182.  */
  183. int  backtab( WINDOW *window )
  184. {
  185. int spaces;      /* the spaces to move to the next tab stop */
  186. char *source;    /* source for block move to make room for c */
  187. char *dest;      /* destination for block move */
  188. int pad, len;
  189. register int rcol;
  190. int old_bcol;
  191. register WINDOW *win;  /* put window pointer in a register */
  192.  
  193.    win  = window;
  194.    rcol = win->rcol;
  195.    if (*win->cursor == CONTROL_Z || win->rcol == 0)
  196.       return( OK );
  197.    old_bcol = win->bcol;
  198.    show_ruler_char( win );
  199.    /*
  200.     * work out the number of spaces to the previous tab stop
  201.     */
  202.    if (mode.smart_tab)
  203.       spaces = prev_smart_tab( win );
  204.    else
  205.       spaces = win->rcol % mode.tab_size;
  206.    if (spaces == 0)
  207.       spaces = mode.tab_size;
  208.    copy_line( win->cursor, win->bottom_line );
  209.    len = linelen( g_status.line_buff );
  210.    if (mode.insert && rcol - spaces < len) {
  211.       pad = rcol > len ? rcol - len : 0;
  212.       if (g_status.line_buff[len] == CONTROL_Z)
  213.          ++pad;
  214.       if (pad > 0  || spaces > 0) {
  215.          if (g_status.line_buff[len] == CONTROL_Z) {
  216.             g_status.line_buff[len] = '\n';
  217.             g_status.line_buff[len+1] = CONTROL_Z;
  218.             ++win->file_info->length;
  219.             show_size( win );
  220.             --pad;
  221.             ++len;
  222.          }
  223.          /*
  224.           * if padding was required, then put in the required spaces
  225.           */
  226.          if (pad > 0) {
  227.             source = g_status.line_buff + rcol - pad;
  228.             dest = source + pad;
  229.             memmove( dest, source, pad+2 );
  230.             memset( source, ' ', pad );
  231.          }
  232.          source = g_status.line_buff + rcol;
  233.          dest = source - spaces;
  234.          memmove( dest, source, len+pad-rcol+2 );
  235.       }
  236.  
  237.       win->file_info->dirty = GLOBAL;
  238.       show_changed_line( win );
  239.       rcol -= spaces;
  240.       win->ccol -= spaces;
  241.    } else {
  242.       /*
  243.        * move the cursor without changing the text underneath
  244.        */
  245.       rcol -= spaces;
  246.       if (rcol < 0)
  247.          rcol = 0;
  248.       win->ccol -= spaces;
  249.    }
  250.    check_virtual_col( win, rcol, win->ccol );
  251.    if (old_bcol != win->bcol) {
  252.       make_ruler( win );
  253.       show_ruler( win );
  254.    }
  255.    return( OK );
  256. }
  257.  
  258.  
  259. /*
  260.  * Name:    next_smart_tab
  261.  * Purpose: To find next smart tab
  262.  * Date:    June 5, 1992
  263.  * Passed:  window: pointer to the current window
  264.  * Notes:   To find a smart tab 1) find the first non-blank line above the
  265.  *            current line, 2) find the first non-blank character after
  266.  *            column of the cursor.
  267.  */
  268. int  next_smart_tab( WINDOW *window )
  269. {
  270. register int spaces;    /* the spaces to move to the next tab stop */
  271. text_ptr s;             /* pointer to text */
  272.  
  273.    /*
  274.     * find first previous non-blank line above the cursor.
  275.     */
  276.    s = find_prev( cpb( window->cursor ) );
  277.    while (s != NULL && is_line_blank( s ) )
  278.       s = find_prev( s );
  279.    if (s != NULL) {
  280.       /*
  281.        * if cursor is past the eol of the smart line, lets find the
  282.        *   next fixed tab.
  283.        */
  284.       if ((unsigned)window->rcol >= linelen( s ))
  285.          spaces = mode.tab_size - (window->rcol % mode.tab_size);
  286.       else {
  287.          spaces = 0;
  288.          s = cpf( s ) + window->rcol;
  289.  
  290.          /*
  291.           * if we are on a word, find the end of it.
  292.           */
  293.          while (*s != ' '  &&  *s != '\n') {
  294.             ++s;
  295.             ++spaces;
  296.          }
  297.  
  298.          /*
  299.           * now find the start of the next word.
  300.           */
  301.          if (*s != '\n')
  302.             while (*s == ' ') {
  303.                ++s;
  304.                ++spaces;
  305.             }
  306.       }
  307.    } else
  308.       spaces = mode.tab_size - (window->rcol % mode.tab_size);
  309.    return( spaces );
  310. }
  311.  
  312.  
  313. /*
  314.  * Name:    prev_smart_tab
  315.  * Purpose: To find previous smart tab
  316.  * Date:    June 5, 1992
  317.  * Passed:  window: pointer to the current window
  318.  * Notes:   To find a smart tab 1) find the first non-blank line above the
  319.  *            current line, 2) find the first non-blank character before
  320.  *            column of the cursor.
  321.  *          there are several cases to consider:  1) the cursor is past the
  322.  *            the end of the smart line, 2) the smart pointer is in the
  323.  *            middle of a word, 3) there are no more words between the
  324.  *            smart pointer and the beginning of the line.
  325.  */
  326. int  prev_smart_tab( WINDOW *window )
  327. {
  328. register int spaces;    /* the spaces to move to the next tab stop */
  329. text_ptr s;             /* pointer to text */
  330. unsigned int len;
  331. int i;
  332.  
  333.    /*
  334.     * find first previous non-blank line above the cursor, if it exists.
  335.     */
  336.    s = find_prev( cpb( window->cursor ) );
  337.    while (s != NULL && is_line_blank( s ) )
  338.       s = find_prev( s );
  339.  
  340.    if (s != NULL) {
  341.  
  342.       /*
  343.        * if there are no words between the cursor and column 1 of the
  344.        *   smart tab line, find previous fixed tab.
  345.        */
  346.       if (window->rcol < first_non_blank( s ))
  347.          spaces = window->rcol % mode.tab_size;
  348.       else {
  349.          s = cpf( s );
  350.          len = linelen( s );
  351.  
  352.          /*
  353.           * now, we need to figure the initial pointer and space.
  354.           *   if the cursor is past the eol of the smart line, then
  355.           *   set the smart pointer "s" to the end of line and "spaces" to
  356.           *   the number of characters between the cursor and the eol
  357.           *   of the smart line.  otherwise, set the smart pointer "s" to
  358.           *   the column of the cursor and "spaces" to 0.
  359.           */
  360.          if (len < (unsigned)window->rcol) {
  361.             s += len;
  362.             spaces = window->rcol - len;
  363.          } else {
  364.             s += window->rcol;
  365.             spaces = 0;
  366.          }
  367.  
  368.          s = cpb( s );
  369.  
  370.          /*
  371.           * if the cursor is past the eol of the smart line, find the start
  372.           *   of the word that is at the eol.
  373.           */
  374.          if ((unsigned)window->rcol >= len) {
  375.  
  376.             /*
  377.              * skip any space that has not been trimmed from the eol.
  378.              */
  379.             while (*(s-1) == ' ') {
  380.                --s;
  381.                ++spaces;
  382.             }
  383.  
  384.             /*
  385.              * now find the beginning of the first word at eol.
  386.              */
  387.             while (*(s-1) != ' ') {
  388.                --s;
  389.                ++spaces;
  390.             }
  391.          } else {
  392.             /*
  393.              * initially, lets keep a count of the number of non-spaces
  394.              *   that we skip.  if the pointer happens to be in the middle
  395.              *   of a word, then lets find the begining of the word.
  396.              */
  397.             i = 0;
  398.             while (*s != ' ' && *s != '\n' && *s != CONTROL_Z) {
  399.                --s;
  400.                ++spaces;
  401.                i++;
  402.             }
  403.             if (i > 1)
  404.                /*
  405.                 * in the above while loop, we went one character too far when
  406.                 *   we found the beginning of the word under the pointer.
  407.                 *   if "i" is greater than 1, then the smart pointer "s"
  408.                 *   was somewhere in the middle of a word.  so, lets return
  409.                 *   the number of spaces needed to reach the beginning of
  410.                 *   the word from the current cursor position.
  411.                 */
  412.                --spaces;
  413.             else {
  414.                /*
  415.                 * if we get this far, we have just found the first character
  416.                 *   of the first word of the smart pointer "s".  now, lets
  417.                 *   skip the spaces between the word we just found and the
  418.                 *   next previous word.
  419.                 * we may run into '\n' or ^Z if there are no previous words
  420.                 *   on the line.
  421.                 */
  422.                while (*s == ' ') {
  423.                   --s;
  424.                   ++spaces;
  425.                }
  426.  
  427.                /*
  428.                 * now, lets find the start of the word we just found.
  429.                 */
  430.                while (*s != ' '  &&  *(s-1) != ' ' && *s != '\n'  &&
  431.                       *s != CONTROL_Z) {
  432.                   --s;
  433.                   ++spaces;
  434.                }
  435.  
  436.                /*
  437.                 * if *s == '\n' or *s == CONTROL_Z, then there were no
  438.                 *   previous words.  lets find the previous fixed tab.
  439.                 */
  440.                if (*s == '\n' || *s == CONTROL_Z)
  441.                   spaces = window->rcol % mode.tab_size;
  442.             }
  443.          }
  444.          if (spaces > window->rcol)
  445.             spaces = window->rcol;
  446.       }
  447.    } else
  448.       spaces = window->rcol % mode.tab_size;
  449.  
  450.    /*
  451.     * spaces cannot be negative.
  452.     */
  453.    if (spaces < 0)
  454.       spaces = 0;
  455.    return( spaces );
  456. }
  457.  
  458.  
  459. /*
  460.  * Name:    insert_newline
  461.  * Purpose: insert a newline
  462.  * Date:    June 5, 1991
  463.  * Passed:  window:  pointer to current window
  464.  * Notes:   There a several ways to insert a line into a file:  1) pressing
  465.  *          a key, 2) word wrap, 3) any others?
  466.  *          When doing word wrap or format paragraph, don't show any changes.
  467.  *            Wait until the function finishes then show all changes at once.
  468.  */
  469. int  insert_newline( WINDOW *window )
  470. {
  471. char *source;       /* source for block move to make room for c */
  472. char *dest;         /* destination for block move */
  473. int len;            /* length of current line */
  474. int add;            /* characters to be added (usually 1 in insert mode) */
  475. int rcol;
  476. text_ptr prev;      /* previous lines scanned for autoindent */
  477. long length;
  478. int carriage_return;
  479. int split_line;
  480. int wordwrap;
  481. int dirty;
  482. int old_bcol;
  483. register WINDOW *win;  /* put window pointer in a register */
  484.  
  485.    win = window;
  486.    length = win->file_info->length;
  487.    if (win->rline > length && *win->cursor != CONTROL_Z)
  488.       return( OK );
  489.    wordwrap = mode.word_wrap;
  490.    switch (g_status.command) {
  491.       case WordWrap:
  492.          wordwrap = mode.word_wrap;
  493.          carriage_return = TRUE;
  494.          split_line = FALSE;
  495.          break;
  496.       case Rturn :
  497.          show_ruler_char( win );
  498.          carriage_return = TRUE;
  499.          split_line = FALSE;
  500.          break;
  501.       case AddLine :
  502.          split_line = carriage_return = FALSE;
  503.          break;
  504.       case SplitLine :
  505.          split_line = carriage_return = TRUE;
  506.          break;
  507.    }
  508.  
  509.    /*
  510.     * make window temporarily invisible to the un_copy_line function
  511.     */
  512.    win->visible = FALSE;
  513.    win->cursor = cpf( win->cursor );
  514.    copy_line( win->cursor, win->bottom_line );
  515.    len = linelen( g_status.line_buff );
  516.  
  517.    source = g_status.line_buff + len;
  518.    if (carriage_return || split_line) {
  519.       if (win->rcol < len)
  520.          source = g_status.line_buff + win->rcol;
  521.    }
  522.    /*
  523.     *  make room for '\n' just after source (source+1)
  524.     */
  525.    memmove( source+1, source, linelen( source )+2 );
  526.  
  527.    *source = '\n';
  528.    un_copy_line( win->cursor, win, TRUE );
  529.    adjust_windows_cursor( win, 1 );
  530.  
  531.    ++win->file_info->length;
  532.    win->file_info->dirty = NOT_LOCAL;
  533.    if (length == 0l || wordwrap || win->cline == win->bottom_line)
  534.       win->file_info->dirty = GLOBAL;
  535.    else if (!split_line)
  536.       update_line( win );
  537.  
  538.    /*
  539.     * If the cursor is to move down to the next line, then update
  540.     *  the line and column appropriately.
  541.     */
  542.    if (carriage_return || split_line) {
  543.       dirty = win->file_info->dirty;
  544.       prev = win->cursor;
  545.       win->cursor = find_next( win->cursor );
  546.       if (win->cline < win->bottom_line)
  547.          win->cline++;
  548.       win->rline++;
  549.       rcol = win->rcol;
  550.       old_bcol = win->bcol;
  551.  
  552.       /*
  553.        * indentation is only required if we are in the right mode,
  554.        *  the user typed <CR>, and if there is not space followed
  555.        *  by something after the cursor.
  556.        */
  557.       if (mode.indent || wordwrap) {
  558.          /*
  559.           * autoindentation is required. Match the indentation of
  560.           *  the first line above that is not blank.
  561.           */
  562.          add = find_left_margin( prev, wordwrap );
  563.          copy_line( win->cursor, win->bottom_line );
  564.          len = linelen( g_status.line_buff );
  565.          source = g_status.line_buff;
  566.          if (len + add > MAX_LINE_LENGTH)
  567.             add = MAX_LINE_LENGTH - len;
  568.          dest = source + add;
  569.          memmove( dest, source, len+2 );
  570.  
  571.          /*
  572.           * now put in the autoindent characters
  573.           */
  574.          memset( source, ' ', add );
  575.          win->rcol = add;
  576.          un_copy_line( win->cursor, win, TRUE );
  577.       } else
  578.          win->rcol = 0;
  579.       if (split_line) {
  580.          win->cursor = cpb( win->cursor );
  581.          win->cursor = find_prev( win->cursor );
  582.          if (win->cline > win->top_line + window->ruler)
  583.             win->cline--;
  584.          win->rline--;
  585.          win->rcol = rcol;
  586.       }
  587.       check_virtual_col( win, win->rcol, win->ccol );
  588.       if (dirty == GLOBAL || win->file_info->dirty == LOCAL || wordwrap)
  589.          win->file_info->dirty = GLOBAL;
  590.       else
  591.          win->file_info->dirty = dirty;
  592.    }
  593.  
  594.    /*
  595.     * record that file has been modified
  596.     */
  597.    if (win->file_info->dirty != GLOBAL)
  598.       my_scroll_down( win );
  599.    restore_marked_block( win, 1 );
  600.    show_size( win );
  601.    win->visible = TRUE;
  602.    if (old_bcol != win->bcol) {
  603.       make_ruler( win );
  604.       show_ruler( win );
  605.    }
  606.    return( OK );
  607. }
  608.  
  609.  
  610. /*
  611.  * Name:    insert_overwrite
  612.  * Purpose: To make the necessary changes after the user has typed a normal
  613.  *           printable character
  614.  * Date:    June 5, 1991
  615.  * Passed:  window:  pointer to current window
  616.  */
  617. int  insert_overwrite( WINDOW *window )
  618. {
  619. char *source;       /* source for block move to make room for c */
  620. char *dest;         /* destination for block move */
  621. int len;            /* length of current line */
  622. int pad;            /* padding to add if cursor beyond end of line */
  623. int add;            /* characters to be added (usually 1 in insert mode) */
  624. register int rcol;
  625. register WINDOW *win;  /* put window pointer in a register */
  626. int rc;
  627.  
  628.    win = window;
  629.    if (*win->cursor == CONTROL_Z || g_status.key_pressed >= 256)
  630.       rc = OK;
  631.    else {
  632.       rcol = win->rcol;
  633.       /*
  634.        * first check we have room - the editor can not
  635.        *  cope with lines wider than g_display.line_length
  636.        */
  637.       if (rcol >= g_display.line_length) {
  638.          /*
  639.           * cannot insert more characters
  640.           */
  641.          error( WARNING, win->bottom_line, ed2 );
  642.          rc = ERROR;
  643.       } else {
  644.          copy_line( win->cursor, win->bottom_line );
  645.  
  646.          /*
  647.           * work out how many characters need to be inserted
  648.           */
  649.          len = linelen( g_status.line_buff );
  650.          pad = rcol > len ? rcol - len : 0;
  651.  
  652.          /*
  653.           * if this is the last line in a file, the last character in the
  654.           * line buffer will be CONTROL_Z.  increment pad and insert a \n.
  655.           */
  656.          if (g_status.line_buff[len] == CONTROL_Z)
  657.             ++pad;
  658.  
  659.          if (mode.insert || rcol >= len)
  660.             /*
  661.              * inserted characters, or overwritten characters at the end of
  662.              *  the line, are inserted.
  663.              */
  664.             add = 1;
  665.          else
  666.             /*
  667.              *  and no extra space is required to overwrite existing characters
  668.              */
  669.             add = 0;
  670.  
  671.          /*
  672.           * check that current line would not get too long.
  673.           */
  674.          if (len + pad + add >= g_display.line_length) {
  675.             /*
  676.              * no more room to add
  677.              */
  678.             error( WARNING, win->bottom_line, ed3 );
  679.             rc = ERROR;
  680.          } else {
  681.  
  682.             /*
  683.              * make room for whatever needs to be inserted
  684.              */
  685.             if (pad > 0  || add > 0) {
  686.                source = g_status.line_buff + len;
  687.                if (*source == CONTROL_Z) {
  688.                   if (rcol > len)
  689.                      source = g_status.line_buff + rcol + 1;
  690.                   *source++ = '\n';
  691.                   *source   = CONTROL_Z;
  692.                   ++win->file_info->length;
  693.                   show_size( win );
  694.                   --pad;
  695.                   ++len;
  696.                }
  697.                source = g_status.line_buff + rcol - pad;
  698.                dest = source + pad + add;
  699.                memmove( dest, source, len + pad - rcol + 2 );
  700.                /*
  701.                 * put in the required padding
  702.                 */
  703.                memset( source, ' ', pad );
  704.             }
  705.             g_status.line_buff[rcol] = (char)g_status.key_pressed;
  706.  
  707.             /*
  708.              * always increment the real column (rcol) then adjust the
  709.              * logical and base column as needed.   show the changed line
  710.              * in all but the LOCAL window.  In the LOCAL window, there are
  711.              * two cases:  1) update the line, or 2) redraw the window if
  712.              * cursor goes too far right.
  713.              */
  714.             win->file_info->dirty = NOT_LOCAL;
  715.             show_changed_line( win );
  716.             if (win->ccol < win->end_col) {
  717.                show_curl_line( win );
  718.                show_ruler_char( win );
  719.                win->ccol++;
  720.             } else {
  721.                win->bcol++;
  722.                win->file_info->dirty = LOCAL;
  723.                make_ruler( win );
  724.                show_ruler( win );
  725.             }
  726.             rcol++;
  727.          }
  728.  
  729.          /*
  730.           * record that file has been modified and adjust cursors,
  731.           * file start and end pointers as needed.
  732.           */
  733.          check_virtual_col( win, rcol, win->ccol );
  734.          win->file_info->modified = TRUE;
  735.          if (mode.word_wrap) {
  736.             g_status.command = WordWrap;
  737.             word_wrap( win );
  738.          }
  739.          rc = OK;
  740.       }
  741.    }
  742.    return( rc );
  743. }
  744.  
  745.  
  746. /*
  747.  * Name:    join_line
  748.  * Purpose: To join current line and line below at cursor
  749.  * Date:    June 5, 1991
  750.  * Passed:  window:  pointer to current window
  751.  * Notes:   trunc the line then join with line below if it exists
  752.  */
  753. int  join_line( WINDOW *window )
  754. {
  755. register int len;   /* length of current line */
  756. char *source;       /* source for block move to delete word */
  757. char *dest;         /* destination for block move */
  758. text_ptr p;         /* next line in file */
  759. int pad, i;         /* padding spaces required */
  760. int cr;             /* does current line end with carriage return? */
  761. register WINDOW *win;  /* put window pointer in a register */
  762. int rc;
  763.  
  764.    win = window;
  765.    if (win->rline > win->file_info->length || *win->cursor == CONTROL_Z)
  766.       return( OK );
  767.  
  768.    win->cursor = cpf( win->cursor );
  769.    load_undo_buffer( win->cursor );
  770.    copy_line( win->cursor, win->bottom_line );
  771.    if (win->rcol < (len = linelen( g_status.line_buff ))) {
  772.       /*
  773.        * delete rest of line
  774.        */
  775.       dest = g_status.line_buff + win->rcol;
  776.  
  777.       if (g_status.line_buff[len] == '\n')
  778.          *dest++ = '\n';
  779.  
  780.       *dest = CONTROL_Z;
  781.       un_copy_line( win->cursor, win, FALSE );
  782.       win->file_info->dirty = GLOBAL;
  783.    }
  784.  
  785.    rc = OK;
  786.    /*
  787.     * we need to combine with the next line, if any
  788.     */
  789.    if ((p = find_next( win->cursor )) != NULL && *p != CONTROL_Z) {
  790.       /*
  791.        * add padding if required
  792.        */
  793.       len = linelen( g_status.line_buff );
  794.       cr  = g_status.line_buff[len] == '\n' ? 1 : 0;
  795.       pad = win->rcol > len ? win->rcol - len : 0;
  796.  
  797.       /*
  798.        * check room to combine lines
  799.        */
  800.       if (len + pad + cr + linelen( p ) >= g_display.line_length) {
  801.          /*
  802.           * cannot combine lines.
  803.           */
  804.          error( WARNING, win->bottom_line, ed4 );
  805.          rc = ERROR;
  806.       } else {
  807.  
  808.          /*
  809.           * do the move
  810.           */
  811.          source = g_status.line_buff + win->rcol - pad;
  812.          dest   = source + pad;
  813.          memmove( dest, source, len + pad - win->rcol + 1 + cr );
  814.  
  815.          /*
  816.           * insert the padding
  817.           */
  818.          while (pad--)
  819.             *source++ = ' ';
  820.  
  821.          /*
  822.           * remove the \n separating the two lines.
  823.           */
  824.          i = 0;
  825.          if (*source == '\n') {
  826.             *source = CONTROL_Z;
  827.             i = -1;
  828.          }
  829.          g_status.copied = TRUE;
  830.          un_copy_line( win->cursor, win, FALSE );
  831.          adjust_windows_cursor( win, i );
  832.          --win->file_info->length;
  833.          restore_marked_block( win, -1 );
  834.          show_size( win );
  835.          win->file_info->dirty = GLOBAL;
  836.       }
  837.    }
  838.    return( rc );
  839. }
  840.  
  841.  
  842. /*
  843.  * Name:    word_delete
  844.  * Purpose: To delete from the cursor to the start of the next word.
  845.  * Date:    September 1, 1991
  846.  * Passed:  window:  pointer to current window
  847.  * Notes:   If the cursor is at the right of the line, then combine the
  848.  *           current line with the next one, leaving the cursor where it
  849.  *           is.
  850.  *          If the cursor is on an alphanumeric character, then all
  851.  *           subsequent alphanumeric characters are deleted.
  852.  *          If the cursor is on a space, then all subsequent spaces
  853.  *           are deleted.
  854.  *          If the cursor is on a punctuation character, then all
  855.  *           subsequent punctuation characters are deleted.
  856.  */
  857. int  word_delete( WINDOW *window )
  858. {
  859. int len;            /* length of current line */
  860. register int start; /* column that next word starts in */
  861. char *source;       /* source for block move to delete word */
  862. char *dest;         /* destination for block move */
  863. int alpha;          /* is the cursor char alphanumeric? */
  864. text_ptr p;
  865. register WINDOW *win;  /* put window pointer in a register */
  866.  
  867.    win = window;
  868.    if (win->rline > win->file_info->length || *win->cursor == CONTROL_Z)
  869.       return( OK );
  870.    win->cursor = cpf( win->cursor );
  871.    copy_line( win->cursor, win->bottom_line );
  872.    if (win->rcol >= (len = linelen( g_status.line_buff ))) {
  873.       join_line( win );
  874.       p = win->cursor + win->rcol;
  875.       if (*p != CONTROL_Z)
  876.          load_undo_buffer( p );
  877.    } else {
  878.       /*
  879.        * normal word delete
  880.        *
  881.        * find the start of the next word
  882.        */
  883.       start = win->rcol;
  884.       if (g_status.line_buff[start] == ' ') {
  885.          /*
  886.           * the cursor was on a space, so eat all consecutive spaces
  887.           *  from the cursor onwards.
  888.           */
  889.          while (g_status.line_buff[start] == ' ')
  890.             ++start;
  891.       } else {
  892.          /*
  893.           * eat all consecutive characters in the same class (spaces
  894.           *  are considered to be in the same class as the cursor
  895.           *  character)
  896.           */
  897.          alpha = myisalnum( g_status.line_buff[start++] );
  898.          while (start < len) {
  899.             if (g_status.line_buff[start] == ' ')
  900.                 /*
  901.                  * the next character that is not a space will
  902.                  *  end the delete
  903.                  */
  904.                 alpha = -1;
  905.             else if (alpha != myisalnum( g_status.line_buff[start] )) {
  906.                 if (g_status.line_buff[start] != ' ')
  907.                     break;
  908.             }
  909.             ++start;
  910.          }
  911.       }
  912.  
  913.       /*
  914.        * move text to delete word
  915.        */
  916.       source = g_status.line_buff + start;
  917.       dest = g_status.line_buff + win->rcol;
  918.       memmove( dest, source, len-start+2 );
  919.       win->file_info->modified = TRUE;
  920.       win->file_info->dirty = GLOBAL;
  921.       if (g_status.command == WordDelete)
  922.          show_changed_line( win );
  923.    }
  924.    return( OK );
  925. }
  926.  
  927.  
  928. /*
  929.  * Name:    dup_line
  930.  * Purpose: Duplicate current line
  931.  * Date:    June 5, 1991
  932.  * Passed:  window:  pointer to current window
  933.  * Notes:   cursor stays on current line
  934.  */
  935. int  dup_line( WINDOW *window )
  936. {
  937. register int len;       /* length of current line */
  938. long number;
  939. text_ptr d, s;
  940. register WINDOW *win;  /* put window pointer in a register */
  941. int  rc;
  942.  
  943.    win = window;
  944.    if (win->rline > win->file_info->length)
  945.       return( ERROR );
  946.    win->cursor = cpf( win->cursor );
  947.  
  948.    un_copy_line( win->cursor, win, TRUE );
  949.    /*
  950.     * don't dup the ^Z or a NULL line
  951.     */
  952.    if (*win->cursor != CONTROL_Z && (d=find_next( win->cursor )) != NULL) {
  953.  
  954.       /*
  955.        * don't use buffers to dup the line.  use hw_move to make space and
  956.        * copy current line at same time.  d is set to beginning of next line.
  957.        */
  958.       s = win->cursor;
  959.       len = linelen( s );
  960.       if (s[len] == '\n')
  961.          ++len;
  962.       number = ptoul( g_status.end_mem ) - ptoul( s );
  963.       hw_move( d, s, number );
  964.       ++win->file_info->length;
  965.       g_status.end_mem = addltop( len, g_status.end_mem );
  966.       adjust_start_end( win->file_info, len );
  967.       addorsub_all_cursors( win, len );
  968.       adjust_windows_cursor( win, 1 );
  969.  
  970.       /*
  971.        * if current line is the bottom line, we can't see the dup line because
  972.        * cursor doesn't move and dup line is added after current line.
  973.        */
  974.       if  (win->cline != win->bottom_line)
  975.          my_scroll_down( win );
  976.       win->file_info->dirty = NOT_LOCAL;
  977.  
  978.       /*
  979.        * record that file has been modified
  980.        */
  981.       win->file_info->modified = TRUE;
  982.       restore_marked_block( win, 1 );
  983.       show_size( win );
  984.       show_avail_mem( );
  985.       rc = OK;
  986.    } else {
  987.       /*
  988.        * cannot duplicate line
  989.        */
  990.       error( WARNING, win->bottom_line, ed5 );
  991.       rc = ERROR;
  992.    }
  993.    return( rc );
  994. }
  995.  
  996.  
  997. /*
  998.  * Name:    back_space
  999.  * Purpose: To delete the character to the left of the cursor.
  1000.  * Date:    June 5, 1991
  1001.  * Passed:  window:  pointer to current window
  1002.  * Notes:   If the cursor is at the left of the line, then combine the
  1003.  *           current line with the previous one.
  1004.  *          If in indent mode, and the cursor is on the first non-blank
  1005.  *           character of the line, then match the indentation of an
  1006.  *           earlier line.
  1007.  */
  1008. int  back_space( WINDOW *window )
  1009. {
  1010. int len;            /* length of the current line */
  1011. char *source;       /* source of block move to delete character */
  1012. char *dest;         /* destination of block move */
  1013. text_ptr p;         /* previous line in file */
  1014. int plen;           /* length of previous line */
  1015. int del_count;      /* number of characters to delete */
  1016. int pos;            /* the position of the first non-blank char */
  1017. register int rcol;
  1018. int ccol;
  1019. int old_bcol;
  1020. register WINDOW *win;  /* put window pointer in a register */
  1021.  
  1022.    win = window;
  1023.    if (win->rline > win->file_info->length || *win->cursor == CONTROL_Z)
  1024.       return( OK );
  1025.    win->cursor = cpf( win->cursor );
  1026.    copy_line( win->cursor, win->bottom_line );
  1027.    len = linelen( g_status.line_buff );
  1028.    rcol = win->rcol;
  1029.    ccol = win->ccol;
  1030.    old_bcol = win->bcol;
  1031.    if (rcol == 0) {
  1032.       /*
  1033.        * combine this line with the previous, if any
  1034.        */
  1035.       win->cursor = cpb( win->cursor );
  1036.       if ((p = find_prev( win->cursor )) != NULL) {
  1037.          if (len + 2 + (plen = linelen( p )) >= g_display.line_length) {
  1038.             /*
  1039.              * cannot combine lines
  1040.              */
  1041.             error( WARNING, win->bottom_line, ed4 );
  1042.             return( ERROR );
  1043.          }
  1044.  
  1045.          un_copy_line( win->cursor, win, TRUE );
  1046.          copy_line( p, win->bottom_line );
  1047.          load_undo_buffer( p );
  1048.          g_status.line_buff[plen] = CONTROL_Z;
  1049.          win->cursor = p;
  1050.          un_copy_line( win->cursor, win, FALSE );
  1051.  
  1052.          /*
  1053.           * make sure cursor stays on the screen, at the end of the
  1054.           *  previous line
  1055.           */
  1056.          if (win->cline > win->top_line + win->ruler)
  1057.             --win->cline;
  1058.          --win->rline;
  1059.          rcol = plen;
  1060.          ccol = rcol - win->bcol;
  1061.          --win->file_info->length;
  1062.          restore_marked_block( win, -1 );
  1063.          adjust_windows_cursor( win, -1 );
  1064.          show_size( win );
  1065.          check_virtual_col( win, rcol, ccol );
  1066.          win->file_info->dirty = GLOBAL;
  1067.          make_ruler( win );
  1068.          show_ruler( win );
  1069.       }
  1070.    } else {
  1071.       /*
  1072.        * normal delete
  1073.        *
  1074.        * find out how much to delete (depends on indent mode)
  1075.        */
  1076.       del_count = 1;   /* the default */
  1077.       if (mode.indent) {
  1078.          /*
  1079.           * indent only happens if the cursor is on the first
  1080.           *  non-blank character of the line
  1081.           */
  1082.          if ((pos = first_non_blank( g_status.line_buff )) == rcol
  1083.                     || g_status.line_buff[pos] == '\n'
  1084.                     || g_status.line_buff[pos] == CONTROL_Z) {
  1085.             /*
  1086.              * now work out how much to indent
  1087.              */
  1088.             p = cpb( win->cursor );
  1089.             for (p=find_prev( p ); p != NULL; p=find_prev( p )) {
  1090.                if ((plen=first_non_blank( p )) < rcol && *(p+plen)!='\n') {
  1091.                   /*
  1092.                    * found the line to match
  1093.                    */
  1094.                   del_count = rcol - plen;
  1095.                   break;
  1096.                }
  1097.             }
  1098.          }
  1099.       }
  1100.  
  1101.       /*
  1102.        * move text to delete char(s), unless no chars actually there
  1103.        */
  1104.       if (rcol - del_count < len) {
  1105.          dest = g_status.line_buff + rcol - del_count;
  1106.          if (rcol > len) {
  1107.             source = g_status.line_buff + len;
  1108.             len = 2;
  1109.          } else {
  1110.             source = g_status.line_buff + rcol;
  1111.             len = len - rcol + 2;
  1112.          }
  1113.          memmove( dest, source, len );
  1114.       }
  1115.       rcol -= del_count;
  1116.       ccol -= del_count;
  1117.       win->file_info->dirty = NOT_LOCAL;
  1118.       show_ruler_char( win );
  1119.       show_changed_line( win );
  1120.       check_virtual_col( win, rcol, ccol );
  1121.       if (!win->file_info->dirty)
  1122.          show_curl_line( win );
  1123.       if (old_bcol != win->bcol) {
  1124.          make_ruler( win );
  1125.          show_ruler( win );
  1126.       }
  1127.    }
  1128.    win->file_info->modified = TRUE;
  1129.    return( OK );
  1130. }
  1131.  
  1132.  
  1133. /*
  1134.  * Name:    line_kill
  1135.  * Purpose: To delete the line the cursor is on.
  1136.  * Date:    June 5, 1991
  1137.  * Passed:  window:  pointer to current window
  1138.  * Notes:   If *window->cursor is pointing to CONTROL_Z then do not do a
  1139.  *          line kill (can't kill a NULL line).
  1140.  */
  1141. int  line_kill( WINDOW *window )
  1142. {
  1143. int i = 0;
  1144. text_ptr s;         /* next line in file */
  1145. register WINDOW *win;  /* put window pointer in a register */
  1146.  
  1147.    win = window;
  1148.    if (win->file_info->length > 0  && *win->cursor != CONTROL_Z) {
  1149.       s = win->cursor = cpf( win->cursor );
  1150.       load_undo_buffer( g_status.copied ? g_status.line_buff : win->cursor );
  1151.       g_status.copied = TRUE;
  1152.       g_status.line_buff[0] = CONTROL_Z;
  1153.       /*
  1154.        * if line to delete has \n at end of line then decrement file length.
  1155.        */
  1156.       if (*(s + linelen( s )) == '\n') {
  1157.          --win->file_info->length;
  1158.          --i;
  1159.       }
  1160.       un_copy_line( s, win, FALSE );
  1161.       win->file_info->dirty = NOT_LOCAL;
  1162.  
  1163.       /*
  1164.        * move all cursors one according to i, restore begin and end block
  1165.        */
  1166.       adjust_windows_cursor( win, i );
  1167.       restore_marked_block( win, i );
  1168.  
  1169.       /*
  1170.        * we are not doing a GLOBAL update, so update current window here
  1171.        */
  1172.       if (win->file_info->dirty == NOT_LOCAL)
  1173.          my_scroll_down( win );
  1174.       show_size( win );
  1175.       return( OK );
  1176.    } else
  1177.       return( ERROR );
  1178. }
  1179.  
  1180.  
  1181. /*
  1182.  * Name:    char_del_under
  1183.  * Purpose: To delete the character under the cursor.
  1184.  * Date:    June 5, 1991
  1185.  * Passed:  window:  pointer to current window
  1186.  * Notes:   If the cursor is beyond the end of the line, then this
  1187.  *           command is ignored.
  1188.  *          DeleteChar and StreamDeleteChar use this function.
  1189.  */
  1190. int  char_del_under( WINDOW *window )
  1191. {
  1192. char *source;    /* source of block move to delete character */
  1193. register int len;
  1194. register WINDOW *win;  /* put window pointer in a register */
  1195.  
  1196.    win = window;
  1197.    if (win->rline > win->file_info->length || *win->cursor == CONTROL_Z)
  1198.       return( OK );
  1199.    copy_line( win->cursor, win->bottom_line );
  1200.    if (win->rcol < (len = linelen( g_status.line_buff ))) {
  1201.       /*
  1202.        * move text to delete using buffer
  1203.        */
  1204.       source = g_status.line_buff + win->rcol + 1;
  1205.       memmove( source-1, source, len - win->rcol + 2 );
  1206.       win->file_info->dirty    = GLOBAL;
  1207.       win->file_info->modified = TRUE;
  1208.       show_changed_line( win );
  1209.    } else if (g_status.command == StreamDeleteChar)
  1210.       join_line( win );
  1211.    return( OK );
  1212. }
  1213.  
  1214.  
  1215. /*
  1216.  * Name:    eol_kill
  1217.  * Purpose: To delete everything from the cursor to the end of the line.
  1218.  * Date:    June 5, 1991
  1219.  * Passed:  window:  pointer to current window
  1220.  * Notes:   If the cursor is beyond the end of the line, then this
  1221.  *           command is ignored.
  1222.  */
  1223. int  eol_kill( WINDOW *window )
  1224. {
  1225. register char *dest;  /* the start of the delete area */
  1226. register WINDOW *win;  /* put window pointer in a register */
  1227.  
  1228.    win = window;
  1229.    if (win->rline > win->file_info->length || *win->cursor == CONTROL_Z)
  1230.       return( OK );
  1231.    copy_line( win->cursor, win->bottom_line );
  1232.    load_undo_buffer( g_status.line_buff );
  1233.    if ((unsigned)win->rcol < linelen( g_status.line_buff )) {
  1234.       /*
  1235.        * truncate to delete rest of line
  1236.        */
  1237.       dest = g_status.line_buff + win->rcol;
  1238.       *dest++ = '\n';
  1239.       *dest = CONTROL_Z;
  1240.       win->file_info->dirty = GLOBAL;
  1241.       show_changed_line( win );
  1242.    }
  1243.    return( OK );
  1244. }
  1245.  
  1246.  
  1247. /*
  1248.  * Name:    undo_line
  1249.  * Purpose: To retrieve unaltered line if possible.
  1250.  * Date:    June 5, 1991
  1251.  * Passed:  window:  pointer to current window
  1252.  * Notes:   Changes are made to the line buffer so the underlying text has
  1253.  *          not changed.  Put the unchanged line from the file into the
  1254.  *          line buffer and display it.
  1255.  */
  1256. int  undo_line( WINDOW *window )
  1257. {
  1258. register WINDOW *win;  /* put window pointer in a register */
  1259.  
  1260.    win = window;
  1261.    if (win->rline <= win->file_info->length  &&  g_status.copied) {
  1262.       g_status.copied = FALSE;
  1263.       copy_line( win->cursor, win->bottom_line );
  1264.       win->file_info->dirty = GLOBAL;
  1265.       show_changed_line( win );
  1266.    }
  1267.    return( OK );
  1268. }
  1269.  
  1270.  
  1271. /*
  1272.  * Name:    undo
  1273.  * Purpose: To retrieve (pop) a line from the undo stack
  1274.  * Date:    September 26, 1991
  1275.  * Passed:  window:  pointer to current window
  1276.  * Notes:   Insert an empty line into the file then pop the line in the undo
  1277.  *          stack.  When we pop line 0, there are no more lines on the stack.
  1278.  *          Set the stack pointer to -1 to indicate an empty stack.
  1279.  */
  1280. int  undo( WINDOW *window )
  1281. {
  1282. char *source;    /* pointer to line_buff */
  1283. unsigned int len;
  1284. unsigned int number;
  1285. register WINDOW *win;  /* put window pointer in a register */
  1286.  
  1287.    if (g_status.bot_stack != NULL) {
  1288.       win = window;
  1289.       win->cursor = cpf( win->cursor );
  1290.       un_copy_line( win->cursor, win, TRUE );
  1291.       copy_line( win->cursor, win->bottom_line );
  1292.  
  1293.       /*
  1294.        *  make room for '\n'.  then, after we un_copy the g_status.line_buff, we
  1295.        *  have added a line to the file.
  1296.        */
  1297.       source = g_status.line_buff;
  1298.       number = linelen( source ) + 2;
  1299.       len = linelen( g_status.top_stack ) + 1;
  1300.       memmove( source+len, source, number );
  1301.       _fmemcpy( source, g_status.top_stack, len );
  1302.       un_copy_line( win->cursor, win, TRUE );
  1303.  
  1304.       /*
  1305.        *  ajust cursors in other windows opened to the same file.
  1306.        */
  1307.       adjust_windows_cursor( win, 1 );
  1308.  
  1309.  
  1310.       /*
  1311.        * we have now undeleted a line.  increment the file length and display
  1312.        * it.
  1313.        */
  1314.       win->file_info->length++;
  1315.       win->file_info->dirty = GLOBAL;
  1316.       show_size( win );
  1317.  
  1318.       /*
  1319.        * now "pop" the line off the stack.
  1320.        */
  1321.       number = (unsigned)(ptoul( g_status.bot_stack ) -
  1322.                           ptoul( g_status.top_stack )) - len;
  1323.       hw_move( g_status.top_stack, g_status.top_stack+len, number );
  1324.  
  1325.       /*
  1326.        * bottom of stack now moves up.  it the bottom of the stack
  1327.        *   is equal to top of stack, then the stack is empty.
  1328.        */
  1329.       g_status.bot_stack = cpb( g_status.bot_stack ) - len;
  1330.       if (ptoul( g_status.bot_stack ) == ptoul( g_status.top_stack ))
  1331.          g_status.bot_stack = NULL;
  1332.    }
  1333.    return( OK );
  1334. }
  1335.  
  1336.  
  1337. /*
  1338.  * Name:    beg_next_line
  1339.  * Purpose: To move the cursor to the beginning of the next line.
  1340.  * Date:    October 4, 1991
  1341.  * Passed:  window:  pointer to current window
  1342.  */
  1343. int  beg_next_line( WINDOW *window )
  1344. {
  1345. int rc;
  1346.  
  1347.    window->rcol = 0;
  1348.    check_virtual_col( window, window->rcol, window->ccol );
  1349.    rc = prepare_move_down( window );
  1350.    sync( window );
  1351.    make_ruler( window );
  1352.    show_ruler( window );
  1353.    return( rc );
  1354. }
  1355.  
  1356.  
  1357. /*
  1358.  * Name:    next_line
  1359.  * Purpose: To move the cursor to the first character of the next line.
  1360.  * Date:    October 4, 1991
  1361.  * Passed:  window:  pointer to current window
  1362.  */
  1363. int  next_line( WINDOW *window )
  1364. {
  1365. register int rcol;
  1366. register WINDOW *win;  /* put window pointer in a register */
  1367. int rc;
  1368.  
  1369.    win = window;
  1370.    rc = prepare_move_down( win );
  1371.    rcol = first_non_blank( win->cursor );
  1372.    if (win->cursor[rcol] == '\n')
  1373.       rcol = 0;
  1374.    check_virtual_col( win, rcol, win->ccol );
  1375.    sync( win );
  1376.    make_ruler( win );
  1377.    show_ruler( win );
  1378.    return( rc );
  1379. }
  1380.  
  1381.  
  1382. /*
  1383.  * Name:    home
  1384.  * Purpose: To move the cursor to the left of the current line.
  1385.  * Date:    June 5, 1991
  1386.  * Passed:  window:  pointer to current window
  1387.  * Notes:   this routine is made a little more complicated with cursor sync.
  1388.  *            if the g_status.copied flag is set we need to see from what file
  1389.  *            the line_buff was copied.
  1390.  */
  1391. int  home( WINDOW *window )
  1392. {
  1393. register int rcol;
  1394. register WINDOW *win;  /* put window pointer in a register */
  1395.  
  1396.    win = window;
  1397.    if (g_status.copied && win->file_info == g_status.current_window->file_info){
  1398.       rcol = first_non_blank( g_status.line_buff );
  1399.       if (g_status.line_buff[rcol] == '\n')
  1400.          rcol = 0;
  1401.    } else {
  1402.       win->cursor = cpf( win->cursor );
  1403.       rcol = first_non_blank( win->cursor );
  1404.       if (win->cursor[rcol] == '\n')
  1405.          rcol = 0;
  1406.    }
  1407.    if (win->rcol == rcol)
  1408.       rcol = 0;
  1409.    check_virtual_col( win, rcol, win->ccol );
  1410.    sync( win );
  1411.    make_ruler( win );
  1412.    show_ruler( win );
  1413.    return( OK );
  1414. }
  1415.  
  1416.  
  1417. /*
  1418.  * Name:    goto_eol
  1419.  * Purpose: To move the cursor to the eol character of the current line.
  1420.  * Date:    June 5, 1991
  1421.  * Passed:  window:  pointer to current window
  1422.  * Notes:   this routine is made a little more complicated with cursor sync.
  1423.  *            if the g_status.copied flag is set we need to see from what file
  1424.  *            the line_buff was copied.
  1425.  */
  1426. int  goto_eol( WINDOW *window )
  1427. {
  1428. register int rcol;
  1429. register WINDOW *win;  /* put window pointer in a register */
  1430.  
  1431.    win = window;
  1432.    if (g_status.copied) {
  1433.       if (win->file_info == g_status.current_window->file_info)
  1434.          rcol = linelen( g_status.line_buff );
  1435.       else
  1436.          rcol = linelen( win->cursor );
  1437.    } else
  1438.       rcol = linelen( win->cursor );
  1439.    win->ccol = win->start_col + rcol - win->bcol;
  1440.    check_virtual_col( win, rcol, win->ccol );
  1441.    sync( win );
  1442.    make_ruler( win );
  1443.    show_ruler( win );
  1444.    return( OK );
  1445. }
  1446.  
  1447.  
  1448. /*
  1449.  * Name:    goto_top
  1450.  * Purpose: To move the cursor to the top of the current window.
  1451.  * Date:    June 5, 1991
  1452.  * Passed:  window:  pointer to current window
  1453.  * Notes:   If the start of the file occurs before the top of the window,
  1454.  *           then the start of the file is moved to the top of the window.
  1455.  */
  1456. int  goto_top( WINDOW *window )
  1457. {
  1458. text_ptr cursor;  /* anticipated cursor line */
  1459. register WINDOW *win;  /* put window pointer in a register */
  1460.  
  1461.    win = window;
  1462.    un_copy_line( win->cursor, win, TRUE );
  1463.    update_line( win );
  1464.    win->cursor = cpb( win->cursor );
  1465.    for (; win->cline > win->top_line+win->ruler; win->cline--,win->rline--) {
  1466.       if ((cursor = find_prev( win->cursor )) == NULL)
  1467.          break;
  1468.       win->cursor = cursor;
  1469.    }
  1470.    show_curl_line( win );
  1471.    sync( win );
  1472.    return( OK );
  1473. }
  1474.  
  1475.  
  1476. /*
  1477.  * Name:    goto_bottom
  1478.  * Purpose: To move the cursor to the bottom of the current window.
  1479.  * Date:    June 5, 1991
  1480.  * Passed:  window:  pointer to current window
  1481.  */
  1482. int  goto_bottom( WINDOW *window )
  1483. {
  1484. text_ptr cursor;
  1485. register WINDOW *win;  /* put window pointer in a register */
  1486.  
  1487.    win = window;
  1488.    un_copy_line( win->cursor, win, TRUE );
  1489.    update_line( win );
  1490.    win->cursor = cpf( win->cursor );
  1491.    for (; win->cline < win->bottom_line; win->cline++,win->rline++) {
  1492.       if ((cursor = find_next( win->cursor )) == NULL ||
  1493.                     find_next( cursor ) == NULL)
  1494.          break;
  1495.       win->cursor = cursor;
  1496.    }
  1497.    show_curl_line( win );
  1498.    sync( win );
  1499.    return( OK );
  1500. }
  1501.  
  1502.  
  1503. /*
  1504.  * Name:    set_tabstop
  1505.  * Purpose: To set the current interval between tab stops
  1506.  * Date:    October 1, 1989
  1507.  * Notes:   Tab interval must be reasonable, and this function will
  1508.  *           not allow tabs more than MAX_COLS / 2.
  1509.  */
  1510. int  set_tabstop( WINDOW *window )
  1511. {
  1512. char num_str[MAX_COLS];  /* tab interval as a character string */
  1513. int tab;                 /* new tab interval */
  1514. register int rc;
  1515.  
  1516.    itoa( mode.tab_size, num_str, 10 );
  1517.    /*
  1518.     * tab interval:
  1519.     */
  1520.    rc = get_name( ed7, window->bottom_line, num_str, g_display.message_color );
  1521.    if (rc == OK) {
  1522.       tab = atoi( num_str );
  1523.       if (tab < MAX_COLS/2)
  1524.          mode.tab_size = tab;
  1525.       else {
  1526.          /*
  1527.           * tab size too long
  1528.           */
  1529.          error( WARNING, window->bottom_line, ed8 );
  1530.          rc = ERROR;
  1531.       }
  1532.    }
  1533.    return( rc );
  1534. }
  1535.  
  1536.  
  1537. /*
  1538.  * Name:    show_line_col
  1539.  * Purpose: show current real line and column of current cursor position
  1540.  * Date:    June 5, 1991
  1541.  * Passed:  window:  pointer to current window
  1542.  * Notes:   Blank old position and display new position.  current line and
  1543.  *          column may take up to 11 columns, which allows the display of
  1544.  *          999 columns and 9,999,999 lines.
  1545.  */
  1546. void show_line_col( WINDOW *window )
  1547. {
  1548. int i;
  1549. register int k;
  1550. char line_col[20], num[10];
  1551.  
  1552.    /*
  1553.     * blank out current line:column position.
  1554.     */
  1555.    memset( line_col, ' ', 12 );
  1556.    line_col[12] = '\0';
  1557.  
  1558.    /*
  1559.     * convert column to ascii and store in display buffer.
  1560.     */
  1561.    itoa( window->rcol+1, num, 10 );
  1562.    i = strlen( num ) - 1;
  1563.    for (k=11; i>=0; i--, k--)
  1564.       line_col[k] = num[i];
  1565.  
  1566.    /*
  1567.     * put in colon to separate line and column
  1568.     */
  1569.    line_col[k--] = ':';
  1570.  
  1571.    /*
  1572.     * convert line to ascii and store in display buffer.
  1573.     */
  1574.    ltoa( window->rline, num, 10 );
  1575.    i = strlen( num ) - 1;
  1576.    for (; i>=0; i--, k--)
  1577.       line_col[k] = num[i];
  1578.  
  1579.    /*
  1580.     * find line to start line:column display then output
  1581.     */
  1582.    s_output( line_col, window->top_line-1, window->end_col-11,
  1583.              g_display.head_color );
  1584.    show_asterisk( window );
  1585. }
  1586.  
  1587.  
  1588. /*
  1589.  * Name:    show_asterisk
  1590.  * Purpose: give user an indication if file is dirty
  1591.  * Date:    September 16, 1991
  1592.  * Passed:  window:  pointer to current window
  1593.  */
  1594. void show_asterisk( WINDOW *window )
  1595. {
  1596.    c_output( window->file_info->modified ? '*' : ' ', window->start_col+4,
  1597.              window->top_line-1, g_display.head_color );
  1598. }
  1599.  
  1600.  
  1601. /*
  1602.  * Name:    toggle_overwrite
  1603.  * Purpose: toggle overwrite-insert mode
  1604.  * Date:    September 16, 1991
  1605.  * Passed:  arg_filler:  argument to satify function prototype
  1606.  */
  1607. int  toggle_overwrite( WINDOW *arg_filler )
  1608. {
  1609.    mode.insert = !mode.insert;
  1610.    show_insert_mode( );
  1611.    set_cursor_size( mode.insert ? g_display.insert_cursor :
  1612.                     g_display.overw_cursor );
  1613.    return( OK );
  1614. }
  1615.  
  1616.  
  1617. /*
  1618.  * Name:    toggle_smart_tabs
  1619.  * Purpose: toggle smart tab mode
  1620.  * Date:    June 5, 1992
  1621.  * Passed:  arg_filler:  argument to satify function prototype
  1622.  */
  1623. int  toggle_smart_tabs( WINDOW *arg_filler )
  1624. {
  1625.    mode.smart_tab = !mode.smart_tab;
  1626.    show_smarttab_mode( );
  1627.    return( OK );
  1628. }
  1629.  
  1630.  
  1631. /*
  1632.  * Name:    toggle_indent
  1633.  * Purpose: toggle indent mode
  1634.  * Date:    September 16, 1991
  1635.  * Passed:  arg_filler:  argument to satify function prototype
  1636.  */
  1637. int  toggle_indent( WINDOW *arg_filler )
  1638. {
  1639.    mode.indent = !mode.indent;
  1640.    show_indent_mode( );
  1641.    return( OK );
  1642. }
  1643.  
  1644.  
  1645. /*
  1646.  * Name:    set_left_margin
  1647.  * Purpose: set left margin for word wrap
  1648.  * Date:    November 27, 1991
  1649.  * Passed:  window
  1650.  */
  1651. int  set_left_margin( WINDOW *window )
  1652. {
  1653. register int rc;
  1654. char temp[80];
  1655.  
  1656.    itoa( mode.left_margin + 1, temp, 10 );
  1657.    /*
  1658.     * enter left margin
  1659.     */
  1660.    rc = get_name( ed9, window->bottom_line, temp, g_display.message_color );
  1661.    if (rc == OK) {
  1662.       rc = atoi( temp ) - 1;
  1663.       if (rc < 0 || rc >= mode.right_margin) {
  1664.          /*
  1665.           * left margin out of range
  1666.           */
  1667.          error( WARNING, window->bottom_line, ed10 );
  1668.          rc = ERROR;
  1669.       } else {
  1670.          mode.left_margin = rc;
  1671.          show_all_rulers( );
  1672.       }
  1673.    }
  1674.    return( rc );
  1675. }
  1676.  
  1677.  
  1678. /*
  1679.  * Name:    set_right_margin
  1680.  * Purpose: set right margin for word wrap
  1681.  * Date:    November 27, 1991
  1682.  * Passed:  window
  1683.  */
  1684. int  set_right_margin( WINDOW *window )
  1685. {
  1686. register int rc;
  1687. char temp[80];
  1688.  
  1689.    itoa( mode.right_margin + 1, temp, 10 );
  1690.    /*
  1691.     * enter right margin
  1692.     */
  1693.    rc = get_name( ed11, window->bottom_line, temp, g_display.message_color );
  1694.    if (rc == OK) {
  1695.       rc = atoi( temp ) - 1;
  1696.       if (rc <= mode.left_margin || rc > MAX_LINE_LENGTH) {
  1697.          /*
  1698.           * right margin out of range
  1699.           */
  1700.          error( WARNING, window->bottom_line, ed12 );
  1701.          rc = ERROR;
  1702.       } else {
  1703.          mode.right_margin = rc;
  1704.          show_all_rulers( );
  1705.       }
  1706.    }
  1707.    return( rc );
  1708. }
  1709.  
  1710.  
  1711. /*
  1712.  * Name:    set_paragraph_margin
  1713.  * Purpose: set column to begin paragraph
  1714.  * Date:    November 27, 1991
  1715.  * Passed:  window
  1716.  * Notes:   paragraph may be indented, flush, or offset.
  1717.  */
  1718. int  set_paragraph_margin( WINDOW *window )
  1719. {
  1720. register int rc;
  1721. char temp[80];
  1722.  
  1723.    itoa( mode.parg_margin + 1, temp, 10 );
  1724.    /*
  1725.     * enter paragraph margin
  1726.     */
  1727.    rc = get_name( ed13, window->bottom_line, temp, g_display.message_color );
  1728.    if (rc == OK) {
  1729.       rc = atoi( temp ) - 1;
  1730.       if (rc < 0 || rc >= mode.right_margin) {
  1731.          /*
  1732.           * paragraph margin out of range
  1733.           */
  1734.          error( WARNING, window->bottom_line, ed14 );
  1735.          rc = ERROR;
  1736.       } else {
  1737.          mode.parg_margin = rc;
  1738.          show_all_rulers( );
  1739.       }
  1740.    }
  1741.    return( rc );
  1742. }
  1743.  
  1744.  
  1745. /*
  1746.  * Name:    toggle_crlf
  1747.  * Purpose: toggle crlf mode
  1748.  * Date:    November 27, 1991
  1749.  * Passed:  arg_filler:  argument to satify function prototype
  1750.  */
  1751. int  toggle_crlf( WINDOW *arg_filler )
  1752. {
  1753.    mode.crlf = (mode.crlf == CRLF) ? LF : CRLF;
  1754.    show_crlf_mode( );
  1755.    return( OK );
  1756. }
  1757.  
  1758.  
  1759. /*
  1760.  * Name:    toggle_ww
  1761.  * Purpose: toggle word wrap mode
  1762.  * Date:    November 27, 1991
  1763.  * Passed:  arg_filler:  argument to satify function prototype
  1764.  */
  1765. int  toggle_ww( WINDOW *arg_filler )
  1766. {
  1767.    ++mode.word_wrap;
  1768.    if (mode.word_wrap > DYNAMIC_WRAP)
  1769.       mode.word_wrap = NO_WRAP;
  1770.    show_wordwrap_mode( );
  1771.    return( OK );
  1772. }
  1773.  
  1774.  
  1775. /*
  1776.  * Name:    toggle_trailing
  1777.  * Purpose: toggle eleminating trainling space at eol
  1778.  * Date:    November 25, 1991
  1779.  * Passed:  arg_filler:  argument to satify function prototype
  1780.  */
  1781. int  toggle_trailing( WINDOW *arg_filler )
  1782. {
  1783.    mode.trailing = !mode.trailing;
  1784.    show_trailing( );
  1785.    return( OK );
  1786. }
  1787.  
  1788.  
  1789. /*
  1790.  * Name:    toggle_z
  1791.  * Purpose: toggle writing control z at eof
  1792.  * Date:    November 25, 1991
  1793.  * Passed:  arg_filler:  argument to satify function prototype
  1794.  */
  1795. int  toggle_z( WINDOW *arg_filler )
  1796. {
  1797.    mode.control_z = !mode.control_z;
  1798.    show_control_z( );
  1799.    return( OK );
  1800. }
  1801.  
  1802.  
  1803. /*
  1804.  * Name:    toggle_eol
  1805.  * Purpose: toggle writing eol character at eol
  1806.  * Date:    November 25, 1991
  1807.  * Passed:  arg_filler:  argument to satify function prototype
  1808.  */
  1809. int  toggle_eol( WINDOW *arg_filler )
  1810. {
  1811. register file_infos *file;
  1812.  
  1813.    mode.show_eol = !mode.show_eol;
  1814.    for (file=g_status.file_list; file != NULL; file=file->next)
  1815.       file->dirty = GLOBAL;
  1816.    return( OK );
  1817. }
  1818.  
  1819.  
  1820. /*
  1821.  * Name:    toggle_search_case
  1822.  * Purpose: toggle search case
  1823.  * Date:    September 16, 1991
  1824.  * Passed:  arg_filler:  argument to satify function prototype
  1825.  */
  1826. int  toggle_search_case( WINDOW *arg_filler )
  1827. {
  1828.    bm.search_case = (bm.search_case == IGNORE) ? MATCH : IGNORE;
  1829.    show_search_case( );
  1830.    if (bm.search_defined == OK)
  1831.       build_boyer_array( );
  1832.    return( OK );
  1833. }
  1834.  
  1835.  
  1836. /*
  1837.  * Name:    toggle_sync
  1838.  * Purpose: toggle sync mode
  1839.  * Date:    January 15, 1992
  1840.  * Passed:  arg_filler:  argument to satify function prototype
  1841.  */
  1842. int  toggle_sync( WINDOW *arg_filler )
  1843. {
  1844.    mode.sync = !mode.sync;
  1845.    show_sync_mode( );
  1846.    return( OK );
  1847. }
  1848.  
  1849.  
  1850. /*
  1851.  * Name:    toggle_ruler
  1852.  * Purpose: toggle ruler
  1853.  * Date:    March 5, 1992
  1854.  * Passed:  arg_filler:  argument to satify function prototype
  1855.  */
  1856. int  toggle_ruler( WINDOW *arg_filler )
  1857. {
  1858. register WINDOW *wp;
  1859.  
  1860.    mode.ruler = !mode.ruler;
  1861.    wp = g_status.window_list;
  1862.    while (wp != NULL) {
  1863.       if (mode.ruler) {
  1864.          /*
  1865.           * there has to be more than one line in a window to display a ruler.
  1866.           *   even if the ruler mode is on, we need to check the num of lines.
  1867.           */
  1868.          if (wp->bottom_line - wp->top_line >0) {
  1869.             if (wp->cline == wp->top_line)
  1870.                ++wp->cline;
  1871.             if (wp->cline > wp->bottom_line)
  1872.                wp->cline = wp->bottom_line;
  1873.             wp->ruler = TRUE;
  1874.          } else
  1875.             wp->ruler = FALSE;
  1876.       } else {
  1877.  
  1878.          /*
  1879.           * if this is the first page in a file, then we may need to "pull"
  1880.           *   the file up before displaying the first page.
  1881.           */
  1882.          if (wp->rline == ((wp->cline - wp->ruler) - (wp->top_line - 1)))
  1883.             --wp->cline;
  1884.          if (wp->cline < wp->top_line)
  1885.             wp->cline = wp->top_line;
  1886.          wp->ruler = FALSE;
  1887.       }
  1888.       make_ruler( wp );
  1889.       setup_window( wp );
  1890.       if (wp->visible)
  1891.          redraw_current_window( wp );
  1892.       wp = wp->next;
  1893.    }
  1894.    return( OK );
  1895. }
  1896.  
  1897.  
  1898. /*
  1899.  * Name:    sync
  1900.  * Purpose: carry out cursor movements in all visible windows
  1901.  * Date:    January 15, 1992
  1902.  * Passed:  window
  1903.  * Notes:   switch sync semaphore when we do this so we don't get into a
  1904.  *          recursive loop.  All cursor movement commands un_copy_line before
  1905.  *          moving the cursor off the current line.   You MUST make certain
  1906.  *          that the current line is uncopied in the task routines before
  1907.  *          calling sync.
  1908.  */
  1909. void sync( WINDOW *window )
  1910. {
  1911. register WINDOW *wp;
  1912. register file_infos *fp;
  1913.  
  1914.    if (mode.sync && mode.sync_sem) {
  1915.       mode.sync_sem = FALSE;
  1916.       wp = g_status.window_list;
  1917.       while (wp != NULL) {
  1918.          if (wp->visible && wp != window) {
  1919.             (*do_it[g_status.command])( wp );
  1920.             show_line_col( wp );
  1921.             show_ruler_pointer( wp );
  1922.          }
  1923.          wp = wp->next;
  1924.       }
  1925.       mode.sync_sem = TRUE;
  1926.       fp = g_status.file_list;
  1927.       while (fp != NULL) {
  1928.          if (fp->dirty != FALSE)
  1929.             fp->dirty = GLOBAL;
  1930.          fp = fp->next;
  1931.       }
  1932.    }
  1933. }
  1934.  
  1935.  
  1936. /*
  1937.  * Name:    editor
  1938.  * Purpose: Set up the editor structures and display changes as needed.
  1939.  * Date:    June 5, 1991
  1940.  * Notes:   Master editor routine.
  1941.  */
  1942. void editor( )
  1943. {
  1944. char *name;  /* name of file to start editing */
  1945. register WINDOW *window;            /* current active window */
  1946. int c;
  1947.  
  1948.    /*
  1949.     * Check that user specified file to edit, if not offer help
  1950.     */
  1951.    if (g_status.argc > 1)
  1952.       c = edit_next_file( g_status.current_window );
  1953.    else {
  1954.       name = g_status.rw_name;
  1955.       *name = '\0';
  1956.       /*
  1957.        * file name to edit
  1958.        */
  1959.       c = get_name( ed15, g_display.nlines, name, g_display.text_color );
  1960.       if (c == ERROR || *name == '\0')
  1961.          return;
  1962.       if (c == OK)
  1963.          c = attempt_edit_display( name, GLOBAL );
  1964.    }
  1965.    g_status.stop = c == OK ? FALSE : TRUE;
  1966.    if (c == OK)
  1967.       set_cursor_size( mode.insert ? g_display.insert_cursor :
  1968.                        g_display.overw_cursor );
  1969.  
  1970.    /*
  1971.     * main loop - keep updating the display and processing any commands
  1972.     *  while user has not pressed the stop key
  1973.     */
  1974.    for (; g_status.stop != TRUE;) {
  1975.       window = g_status.current_window;
  1976.       display_dirty_windows( window );
  1977.  
  1978.       /*
  1979.        * set the critical error handler flag to a known state before we
  1980.        *   do each editor command.
  1981.        */
  1982.       ceh.flag = OK;
  1983.  
  1984.       /*
  1985.        * Get a key from the user.  Look up the function assigned to that key.
  1986.        * All regular text keys are assigned to function 0.  Text characters
  1987.        * are less than 0x100, decimal 256, which includes the ASCII and
  1988.        * extended ASCII character set.
  1989.        */
  1990.       g_status.key_pressed = getkey( );
  1991.       g_status.command = getfunc( g_status.key_pressed );
  1992.       if (g_status.wrapped) {
  1993.          g_status.wrapped = FALSE;
  1994.          show_search_message( CLR_SEARCH, g_display.mode_color );
  1995.       }
  1996.       g_status.control_break = FALSE;
  1997.       if (g_status.command >= 0 && g_status.command < NUM_FUNCS) {
  1998.          record_keys( window->bottom_line );
  1999.          (*do_it[g_status.command])( window );
  2000.       }
  2001.    }
  2002.    cls( );
  2003.    xygoto( 0, 0 );
  2004. }
  2005.  
  2006.  
  2007. /*
  2008.  * Name:    display_dirty_windows
  2009.  * Purpose: Set up the editor structures and display changes as needed.
  2010.  * Date:    June 5, 1991
  2011.  * Notes:   Display all windows with dirty files.
  2012.  */
  2013. void display_dirty_windows( WINDOW *window )
  2014. {
  2015. register WINDOW *below;         /* window below current */
  2016. register WINDOW *above;         /* window above current */
  2017. file_infos *file;               /* temporary file structure */
  2018.  
  2019.    /*
  2020.     * update all windows that point to any file that has been changed
  2021.     */
  2022.    above = below = window;
  2023.    while (above->prev || below->next) {
  2024.       if (above->prev) {
  2025.          above = above->prev;
  2026.          show_dirty_window( above );
  2027.       }
  2028.       if (below->next) {
  2029.          below = below->next;
  2030.          show_dirty_window( below );
  2031.       }
  2032.    }
  2033.    file = window->file_info;
  2034.    if (file->dirty == LOCAL || file->dirty == GLOBAL)
  2035.       display_current_window( window );
  2036.    for (file=g_status.file_list; file != NULL; file=file->next)
  2037.       file->dirty = FALSE;
  2038.  
  2039.    /*
  2040.     * Set the cursor position at window->ccol, window->cline.  Show the
  2041.     * user where in the file the cursor is positioned.
  2042.     */
  2043.    xygoto( window->ccol, window->cline );
  2044.    show_line_col( window );
  2045.    show_ruler_pointer( window );
  2046. }
  2047.  
  2048.  
  2049.  
  2050. /*
  2051.  * Name:    show_dirty_window
  2052.  * Purpose: show changes in non-current window
  2053.  * Date:    June 5, 1991
  2054.  * Passed:  window:  pointer to current window
  2055.  */
  2056. void show_dirty_window( WINDOW *window )
  2057. {
  2058. register WINDOW *win;   /* register window pointer */
  2059. int dirty;
  2060.  
  2061.   win = window;
  2062.   if (win->visible) {
  2063.      dirty = win->file_info->dirty;
  2064.      if (dirty == GLOBAL || dirty == NOT_LOCAL) {
  2065.         display_current_window( win );
  2066.         show_size( win );
  2067.      }
  2068.      show_asterisk( win );
  2069.   }
  2070. }
  2071.  
  2072.  
  2073. /*
  2074.  * Name:    play_back
  2075.  * Purpose: play back a series of keystrokes assigned to key
  2076.  * Date:    April 1, 1992
  2077.  * Notes:   go thru the macro key list playing back the recorded keystrokes.
  2078.  */
  2079. int  play_back( WINDOW *window )
  2080. {
  2081. int key;
  2082. int rc = OK;
  2083.  
  2084.    /*
  2085.     * if we are recording a macro, let's just return if we do a recursive
  2086.     *   definition.  Otherwise, we end up executing our recursive macro
  2087.     *   while we are defining it.
  2088.     */
  2089.    if (mode.record == TRUE && g_status.key_pressed == g_status.recording_key)
  2090.       rc = ERROR;
  2091.    else {
  2092.  
  2093.       /*
  2094.        * set the global macro flags, so other routines will know
  2095.        *   if a macro is executing.
  2096.        */
  2097.       g_status.macro_executing = TRUE;
  2098.       do {
  2099.  
  2100.          /*
  2101.           * find the first keystroke in the macro.
  2102.           */
  2103.          g_status.macro_next = macro.first_stroke[g_status.key_pressed-256];
  2104.          key = macro.strokes[g_status.macro_next].key;
  2105.          if (key != MAX_KEYS+1  &&  key != -1) {
  2106.             do {
  2107.  
  2108.                /*
  2109.                 * set up all editor variables as if we were entering
  2110.                 *   keys from the keyboard.
  2111.                 */
  2112.                window = g_status.current_window;
  2113.                display_dirty_windows( window );
  2114.                ceh.flag = OK;
  2115.                g_status.key_pressed = macro.strokes[g_status.macro_next].key;
  2116.                g_status.command = getfunc( g_status.key_pressed );
  2117.                if (g_status.wrapped) {
  2118.                   g_status.wrapped = FALSE;
  2119.                   show_search_message( CLR_SEARCH, g_display.mode_color );
  2120.                }
  2121.  
  2122.                /*
  2123.                 * while there are no errors or Control-Breaks, let's keep on
  2124.                 *   executing a macro.  g_status.control_break is a global
  2125.                 *   editor flag that is set in our Control-Break interrupt
  2126.                 *   handler routine.
  2127.                 */
  2128.                if (g_status.control_break == TRUE) {
  2129.                   rc = ERROR;
  2130.                   break;
  2131.                }
  2132.  
  2133.                /*
  2134.                 * we haven't called any editor function yet.  we need
  2135.                 *   to look at the editor command that is to be executed.
  2136.                 *   if the command is PlayBack, we need to break out of
  2137.                 *   this inner do loop and start executing the macro
  2138.                 *   from the beginning (the outer do loop).
  2139.                 *
  2140.                 * if we don't break out now from a recursive macro, we will
  2141.                 *   recursively call PlayBack and we might overflow
  2142.                 *   the stack.
  2143.                 */
  2144.                if (g_status.command == PlayBack)
  2145.                   break;
  2146.  
  2147.                if (g_status.command >= 0 && g_status.command < NUM_FUNCS)
  2148.                    rc = (*do_it[g_status.command])( window );
  2149.             } while (rc == OK && (g_status.macro_next =
  2150.                           macro.strokes[g_status.macro_next].next) != -1);
  2151.             if (g_status.macro_next == -1)
  2152.                rc = ERROR;
  2153.          }
  2154.       } while (rc == OK);
  2155.       g_status.macro_executing = FALSE;
  2156.    }
  2157.    return( OK );
  2158. }
  2159.  
  2160.  
  2161. /*
  2162.  * Name:    Pause
  2163.  * Purpose: Enter pause state for macros
  2164.  * Date:    June 5, 1992
  2165.  * Passed:  arg_filler:  argument to satify function prototype
  2166.  * Returns: ERROR if the ESC key was pressed, OK otherwise.
  2167.  * Notes:   this little function is quite useful in macro definitions.  if
  2168.  *          it is called near the beginning of a macro, the user may decide
  2169.  *          whether or not to stop the macro.
  2170.  */
  2171. int  pause( WINDOW *arg_filler )
  2172. {
  2173. int c;
  2174.  
  2175.    /*
  2176.     * tell user we are paused.
  2177.     */
  2178.    s_output( paused1, g_display.mode_line, 23, g_display.mode_color | 0x80 );
  2179.    s_output( paused2, g_display.mode_line, 23+strlen( paused1 ),
  2180.              g_display.mode_color );
  2181.  
  2182.    /*
  2183.     * get the user's response and restore the mode line.
  2184.     */
  2185.    c = getkey( );
  2186.    show_modes( );
  2187.    if (mode.record == TRUE) {
  2188.       /*
  2189.        * if recording a macro, show recording message
  2190.        */
  2191.       s_output( main15, g_display.mode_line, 23, g_display.mode_color | 0x80 );
  2192.       show_avail_strokes( );
  2193.    }
  2194.    return( c == ESC ? ERROR : OK );
  2195. }
  2196.