home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / EDITOR / TDE120.ZIP / ED.C < prev    next >
Encoding:
C/C++ Source or Header  |  1991-10-05  |  43.0 KB  |  1,425 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 reworked.  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.
  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.  * I felt that the insert routine should be separated into two routines.  One
  40.  * for inserting the various combinations of newlines and one for inserting
  41.  * 'regular' text characters (ASCII and extended ASCII characters).
  42.  *
  43.  * One of Doug's design considerations was keeping screen updates to a minimum.
  44.  * I have expanded upon that idea and added support for updating windows
  45.  * LOCALly, GLOBALly, or NOT_LOCALly.  For example, scrolling in one window
  46.  * does not affect the text in another window - LOCAL update.  Adding, deleting,
  47.  * or modifying text in one window may affect text in other windows - GLOBAL
  48.  * update.  Sometimes, updates to the current window are handled in the task
  49.  * routines so updates to other windows are done NOT_LOCALly.
  50.  *
  51.  * Also note that using functions copy_line and un_copy_line to change a line
  52.  * automatically adjusts the g_status.end_mem pointer.  If a function bypasses
  53.  * those functions, adjusting the g_status.end_mem pointer must be done
  54.  * explicitly.
  55.  *
  56.  * New editor name:  tde, the Thomson-Davis Editor.
  57.  * Author:           Frank Davis
  58.  * Date:             June 5, 1991, version 1.0
  59.  * Date:             July 29, 1991, version 1.1
  60.  * Date:             October 5, 1991, version 1.2
  61.  *
  62.  * This modification of Douglas Thomson's code is released into the
  63.  * public domain, Frank Davis.   You may distribute it freely.
  64.  */
  65.  
  66. #include "tdestr.h"     /* global variables */
  67. #include "define.h"
  68. #include "tdefunc.h"
  69. #include "global.h"     /* global variables */
  70.  
  71. /*
  72.  * Name:    tab_key
  73.  * Purpose: To make the necessary changes after the user types the tab key.
  74.  * Date:    June 5, 1991
  75.  * Passed:  window: information allowing access to the current window
  76.  * Notes:   If in insert mode, then this function adds the required
  77.  *           number of spaces in the file.
  78.  *          If not in insert mode, then tab simply moves the cursor right
  79.  *           the required distance.
  80.  */
  81. void tab_key( windows *window )
  82. {
  83. int spaces;      /* the spaces to move to the next tab stop */
  84. char *source;    /* source for block move to make room for c */
  85. char *dest;      /* destination for block move */
  86. int pad, len, rcol, ccol;
  87. register int i;
  88. file_infos *file;
  89. int prompt_line;
  90.  
  91.    if (*window->cursor == CONTROL_Z)
  92.       return;
  93.    prompt_line = window->bottom_line;
  94.    rcol = window->rcol;
  95.    ccol = window->ccol;
  96.    /*
  97.     * work out the number of spaces to the next tab stop
  98.     */
  99.    spaces = mode.tab_size - (rcol % mode.tab_size);
  100.  
  101.    if (mode.insert && rcol + spaces < g_display.line_length) {
  102.       copy_line( window->cursor, prompt_line );
  103.       /*
  104.        * work out how many characters need to be inserted
  105.        */
  106.       len = linelen( g_status.line_buff );
  107.       if (rcol > len)   /* padding required */
  108.          pad = rcol - len;
  109.       else
  110.          pad = 0;
  111.       if (g_status.line_buff[len] == CONTROL_Z)
  112.          ++pad;
  113.       if (len + pad + spaces >= g_display.line_length)
  114.          error( WARNING, window->bottom_line, "line too long to add" );
  115.       else {
  116.          file = window->file_info;
  117.          if (pad > 0  || spaces > 0) {
  118.             if (g_status.line_buff[len] == CONTROL_Z) {
  119.                g_status.line_buff[len] = '\n';
  120.                g_status.line_buff[len+1] = CONTROL_Z;
  121.                ++file->length;
  122.                show_size( window );
  123.                --pad;
  124.                ++len;
  125.             }
  126.             source = g_status.line_buff + rcol - pad;
  127.             dest = source + pad + spaces;
  128.             len = len + pad - rcol + 2;
  129.             memmove( dest, source, len );
  130.  
  131.             /*
  132.              * if padding was required, then put in the required spaces
  133.              */
  134.             for (i=pad; i>0; i--)
  135.                *source++ = ' ';
  136.             for (i=spaces; i>0; i--)
  137.                *source++ = ' ';
  138.          }
  139.  
  140.          file->dirty = GLOBAL;
  141.          show_changed_line( window );
  142.          rcol += spaces;
  143.          ccol += spaces;
  144.       }
  145.    } else if (rcol + spaces <= g_display.line_length) {
  146.       /*
  147.        * advance the cursor without changing the text underneath
  148.        */
  149.       rcol += spaces;
  150.       ccol += spaces;
  151.    }
  152.    check_virtual_col( window, rcol, ccol );
  153. }
  154.  
  155.  
  156. /*
  157.  * Name:    insert_newline
  158.  * Purpose: insert a newline
  159.  * Date:    June 5, 1991
  160.  * Passed:  window:   information allowing access to the current window
  161.  *          carriage_return:   TRUE if carriage return, FALSE if insert line
  162.  *          split_line:   TRUE if split line, FALSE otherwise
  163.  */
  164. void insert_newline( windows *window )
  165. {
  166. char *source;       /* source for block move to make room for c */
  167. char *dest;         /* destination for block move */
  168. int len;            /* length of current line */
  169. register int add;   /* characters to be added (usually 1 in insert mode) */
  170. int i;              /* counter for adding autoindenting */
  171. int rcol;
  172. text_ptr prev;      /* previous lines scanned for autoindent */
  173. file_infos *file;
  174. int prompt_line;
  175. long length;
  176. int carriage_return;
  177. int split_line;
  178.  
  179.    file = window->file_info;
  180.    length = file->length;
  181.    if (window->rline > length && *window->cursor != CONTROL_Z)
  182.       return;
  183.    switch (g_status.command) {
  184.       case Rturn :
  185.          carriage_return = TRUE;
  186.          split_line = FALSE;
  187.          break;
  188.       case AddLine :
  189.          split_line = carriage_return = FALSE;
  190.          break;
  191.       case SplitLine :
  192.          split_line = carriage_return = TRUE;
  193.          break;
  194.    }
  195.    window->cursor = cpf( window->cursor );
  196.    prompt_line = window->bottom_line;
  197.    copy_line( window->cursor, prompt_line );
  198.    len = linelen( g_status.line_buff );
  199.  
  200.    source = g_status.line_buff + len;
  201.    if (carriage_return || split_line) {
  202.       if (window->rcol < len)
  203.          source = g_status.line_buff + window->rcol;
  204.    }
  205.    /*
  206.     *  make room for '\n' just after source (source+1)
  207.     */
  208.    memmove( source+1, source, linelen( source )+2 );
  209.  
  210.    *source = '\n';
  211.    un_copy_line( window->cursor, window, TRUE );
  212.    adjust_windows_cursor( window, 1 );
  213.  
  214.    if (carriage_return || split_line)
  215.       update_line( window );
  216.  
  217.    file->dirty = NOT_LOCAL;
  218.    if (length == 0l)
  219.       file->dirty = GLOBAL;
  220.    else if (window->cline == prompt_line)
  221.       window_scroll_up( window->top_line, prompt_line );
  222.    else
  223.       window_scroll_down( window->cline+1, prompt_line );
  224.  
  225.    /*
  226.     * If the cursor is to move down to the next line, then update
  227.     *  the line and column appropriately.
  228.     */
  229.    if (carriage_return || split_line) {
  230.       window->cursor = find_next( window->cursor );
  231.       if (window->cline < prompt_line)
  232.          window->cline++;
  233.       window->rline++;
  234.       rcol = window->rcol;
  235.  
  236.       /*
  237.        * indentation is only required if we are in the right mode,
  238.        *  the user typed <CR>, and if there is not space followed
  239.        *  by something after the cursor.
  240.        */
  241.       if (mode.indent) {
  242.          /*
  243.           * autoindentation is required. Match the indentation of
  244.           *  the first line above that is not blank.
  245.           */
  246.          add = first_non_blank( g_status.line_buff );
  247.          if (g_status.line_buff[add] == '\n' ||
  248.                    g_status.line_buff[add] == CONTROL_Z) {
  249.             prev = cpb( window->cursor );
  250.             while ((prev = find_prev( prev )) != NULL) {
  251.                add = first_non_blank( prev );
  252.                if (prev[add] != '\n')
  253.                   break;
  254.             }
  255.          }
  256.          copy_line( window->cursor, prompt_line );
  257.          len = linelen( g_status.line_buff );
  258.          source = g_status.line_buff;
  259.          dest = source + add;
  260.          memmove( dest, source, len+add+2 );
  261.  
  262.          /*
  263.           * now put in the autoindent characters
  264.           */
  265.          for (i=add; i>0; i--)
  266.             *source++ = ' ';
  267.  
  268.          window->rcol = add;
  269.          un_copy_line( window->cursor, window, TRUE );
  270.          update_line( window );
  271.       } else
  272.          window->rcol = 0;
  273.       if (split_line) {
  274.          window->cursor = cpb( window->cursor );
  275.          window->cursor = find_prev( window->cursor );
  276.          if (window->cline > window->top_line)
  277.             window->cline--;
  278.          window->rline--;
  279.          window->rcol = rcol;
  280.       }
  281.       check_virtual_col( window, window->rcol, window->ccol );
  282.       if (file->dirty)
  283.          file->dirty = GLOBAL;
  284.    }
  285.  
  286.    /*
  287.     * record that file has been modified
  288.     */
  289.    ++file->length;
  290.    restore_marked_block( window, 1 );
  291.    show_size( window );
  292. }
  293.  
  294.  
  295. /*
  296.  * Name:    insert_overwrite
  297.  * Purpose: To make the necessary changes after the user has typed a normal
  298.  *           printable character
  299.  * Date:    June 5, 1991
  300.  * Passed:  window:   information allowing access to the current window
  301.  */
  302. void insert_overwrite( windows *window )
  303. {
  304. char *source;       /* source for block move to make room for c */
  305. char *dest;         /* destination for block move */
  306. int c;              /* text key just pressed */
  307. int len;            /* length of current line */
  308. int pad;            /* padding to add if cursor beyond end of line */
  309. int i;
  310. int add;            /* characters to be added (usually 1 in insert mode) */
  311. int line;           /* line on screen to save and show prompt */
  312. int rcol, ccol;
  313. file_infos *file;
  314.  
  315.    if (*window->cursor == CONTROL_Z || g_status.key_pressed >= 256)
  316.       return;
  317.    line = window->bottom_line;
  318.    rcol = window->rcol;
  319.    ccol = window->ccol;
  320.    /*
  321.     * first check we have room - the editor can not
  322.     *  cope with lines wider than g_display.line_length
  323.     */
  324.    if (rcol >= g_display.line_length)
  325.       error( WARNING, line, "cannot insert more characters" );
  326.    else {
  327.       c = g_status.key_pressed;
  328.       file = window->file_info;
  329.       copy_line( window->cursor, line );
  330.  
  331.       /*
  332.        * work out how many characters need to be inserted
  333.        */
  334.       len = linelen( g_status.line_buff );
  335.       if (rcol > len)   /* padding required */
  336.          pad = rcol - len;
  337.       else
  338.          pad = 0;
  339.  
  340.       /*
  341.        * if this is the last line in a file, the last character in the
  342.        * line buffer will be CONTROL_Z.  increment pad and insert a \n.
  343.        */
  344.       if (g_status.line_buff[len] == CONTROL_Z)
  345.          ++pad;
  346.  
  347.       if (mode.insert || rcol >= len)
  348.          /*
  349.           * inserted characters, or overwritten characters at the end of
  350.           *  the line, are inserted.
  351.           */
  352.          add = 1;
  353.       else
  354.          /*
  355.           *  and no extra space is required to overwrite existing characters
  356.           */
  357.          add = 0;
  358.  
  359.       /*
  360.        * check that current line would not get too long. Note that there must
  361.        *  be space for both the old line and any indentation.
  362.        */
  363.       if (len + pad + add >= g_display.line_length)
  364.          error( WARNING, line, "no more room to add" );
  365.       else {
  366.          /*
  367.           * all clear to add new character!
  368.           */
  369.  
  370.          /*
  371.           * move character to make room for whatever needs to be inserted
  372.           */
  373.          if (pad > 0  || add > 0) {
  374.             source = g_status.line_buff + len;
  375.             if (*source == CONTROL_Z) {
  376.                if (rcol > len)
  377.                   source = g_status.line_buff + rcol + 1;
  378.                *source++ = '\n';
  379.                *source   = CONTROL_Z;
  380.                ++file->length;
  381.                show_size( window );
  382.                --pad;
  383.                ++len;
  384.             }
  385.             source = g_status.line_buff + rcol - pad;
  386.             dest = source + pad + add;
  387.             len = len + pad - rcol + 2;
  388.             memmove( dest, source, len );
  389.             /*
  390.              * if padding was required, then put in the required spaces
  391.              */
  392.             for (i=pad; i>0; i--)
  393.                *source++ = ' ';
  394.          } else
  395.             source = g_status.line_buff + rcol;
  396.          /*
  397.           * now place the new character
  398.           */
  399.          *source = c;
  400.  
  401.          /*
  402.           * if we added anything, show the changed line.
  403.           */
  404.          if (pad > 0 || add > 0)
  405.             update_line( window );
  406.          else
  407.             update_char( window, c, ccol, window->cline );
  408.  
  409.          /*
  410.           * always increment the real column (rcol) and adjust the
  411.           * logical and base column as needed.
  412.           */
  413.          file->dirty = GLOBAL;
  414.          if (ccol < g_display.ncols - 1) {
  415.             ccol++;
  416.             show_changed_line( window );
  417.          } else
  418.             window->bcol++;
  419.          rcol++;
  420.  
  421.          /*
  422.           * record that file has been modified and adjust cursors and
  423.           * file start and end markers as needed.
  424.           */
  425.       }
  426.       window->rcol = rcol;
  427.       window->ccol = ccol;
  428.       file->modified = TRUE;
  429.    }
  430. }
  431.  
  432.  
  433. /*
  434.  * Name:    join_line
  435.  * Purpose: To join current line and line below at cursor
  436.  * Date:    June 5, 1991
  437.  * Passed:  window:   information allowing access to the current window
  438.  * Notes:   trunc the line then join with line below if it exists
  439.  */
  440. void join_line( windows *window )
  441. {
  442. register int len;   /* length of current line */
  443. char *source;       /* source for block move to delete word */
  444. char *dest;         /* destination for block move */
  445. text_ptr p;         /* next line in file */
  446. int pad, i;         /* padding spaces required */
  447. int cr;             /* does current line end with carriage return? */
  448. int line;           /* line on screen to save and show prompt */
  449. file_infos *file;
  450. int rcol;
  451.  
  452.    file = window->file_info;
  453.    if (window->rline > file->length || *window->cursor == CONTROL_Z)
  454.       return;
  455.    line = window->bottom_line;
  456.    rcol = window->rcol;
  457.  
  458.    window->cursor = cpf( window->cursor );
  459.    load_undo_buffer( window->cursor );
  460.    copy_line( window->cursor, window->bottom_line );
  461.    if (rcol < (len = linelen( g_status.line_buff ))) {
  462.       /*
  463.        * delete rest of line
  464.        */
  465.       dest = g_status.line_buff + rcol;
  466.  
  467.       /*
  468.        * the \n at the end of the line must NOT be deleted
  469.        */
  470.       if (g_status.line_buff[len] == '\n')
  471.          *dest++ = '\n';
  472.  
  473.       len = find_CONTROL_Z( dest );
  474.       *dest = CONTROL_Z;
  475.       un_copy_line( window->cursor, window, FALSE );
  476.       file->dirty = GLOBAL;
  477.    }
  478.    /*
  479.     * we need to combine with the next line, if any
  480.     */
  481.    if ((p = find_next( window->cursor )) != NULL && *p != CONTROL_Z) {
  482.       /*
  483.        * add padding if required
  484.        */
  485.       len = linelen( g_status.line_buff );
  486.       if (g_status.line_buff[len] == '\n')
  487.          cr = 1;
  488.       else
  489.          cr = 0;
  490.       if (rcol > len)
  491.          pad = rcol - len;
  492.       else
  493.          pad = 0;
  494.  
  495.       /*
  496.        * check room to combine lines
  497.        */
  498.       if (len + pad + cr + linelen( p ) >= g_display.line_length)
  499.          error( WARNING, line, "cannot combine lines" );
  500.       else {
  501.  
  502.          /*
  503.           * do the move
  504.           */
  505.          source = g_status.line_buff + rcol - pad;
  506.          dest = source + pad;
  507.          len = len + pad - rcol + 1 + cr;
  508.          memmove( dest, source, len );
  509.  
  510.          /*
  511.           * insert the padding
  512.           */
  513.          for (i=pad; i>0; i--)
  514.             *source++ = ' ';
  515.  
  516.          /*
  517.           * remove the \n separating the two lines.
  518.           */
  519.          i = 0;
  520.          if (*source == '\n') {
  521.             *source = CONTROL_Z;
  522.             i = -1;
  523.          }
  524.          g_status.copied = TRUE;
  525.          un_copy_line( window->cursor, window, FALSE );
  526.          adjust_windows_cursor( window, i );
  527.          --file->length;
  528.          restore_marked_block( window, -1 );
  529.          show_size( window );
  530.          file->dirty = GLOBAL;
  531.       }
  532.    }
  533. }
  534.  
  535.  
  536. /*
  537.  * Name:    word_delete
  538.  * Purpose: To delete from the cursor to the start of the next word.
  539.  * Date:    September 1, 1991
  540.  * Passed:  window:   information allowing access to the current window
  541.  * Notes:   If the cursor is at the right of the line, then combine the
  542.  *           current line with the next one, leaving the cursor where it
  543.  *           is.
  544.  *          If the cursor is on an alphanumeric character, then all
  545.  *           subsequent alphanumeric characters are deleted.
  546.  *          If the cursor is on a space, then all subsequent spaces
  547.  *           are deleted.
  548.  *          If the cursor is on a punctuation character, then all
  549.  *           subsequent punctuation characters are deleted.
  550.  */
  551. void word_delete( windows *window )
  552. {
  553. int len;            /* length of current line */
  554. register int start; /* column that next word starts in */
  555. char *source;       /* source for block move to delete word */
  556. char *dest;         /* destination for block move */
  557. int alpha;          /* is the cursor char alphanumeric? */
  558. int bottom_line;
  559. int rcol;
  560. file_infos *file;
  561. text_ptr p;
  562.  
  563.    file = window->file_info;
  564.    if (window->rline > file->length || *window->cursor == CONTROL_Z)
  565.       return;
  566.    bottom_line = window->bottom_line;
  567.    rcol = window->rcol;
  568.    window->cursor = cpf( window->cursor );
  569.    copy_line( window->cursor, bottom_line );
  570.    if (rcol >= (len = linelen( g_status.line_buff ))) {
  571.       join_line( window );
  572.       p = window->cursor + rcol;
  573.       if (*p != CONTROL_Z)
  574.          load_undo_buffer( p );
  575.    } else {
  576.       /*
  577.        * normal word delete
  578.        *
  579.        * find the start of the next word
  580.        */
  581.       start = rcol;
  582.       if (g_status.line_buff[start] == ' ') {
  583.          /*
  584.           * the cursor was on a space, so eat all consecutive spaces
  585.           *  from the cursor onwards.
  586.           */
  587.          while (g_status.line_buff[start] == ' ')
  588.             ++start;
  589.       } else {
  590.          /*
  591.           * eat all consecutive characters in the same class (spaces
  592.           *  are considered to be in the same class as the cursor
  593.           *  character)
  594.           */
  595.          alpha = myisalnum( g_status.line_buff[start++] );
  596.          while (start < len) {
  597.             if (g_status.line_buff[start] == ' ')
  598.                 /*
  599.                  * the next character that is not a space will
  600.                  *  end the delete
  601.                  */
  602.                 alpha = -1;
  603.             else if (alpha != myisalnum( g_status.line_buff[start] )) {
  604.                 if (g_status.line_buff[start] != ' ')
  605.                     break;
  606.             }
  607.             ++start;
  608.          }
  609.       }
  610.  
  611.       /*
  612.        * move text to delete word
  613.        */
  614.       source = g_status.line_buff + start;
  615.       dest = g_status.line_buff + rcol;
  616.       len = len - start + 2;
  617.       memmove( dest, source, len );
  618.       file->modified = TRUE;
  619.       file->dirty = GLOBAL;
  620.       show_changed_line( window );
  621.    }
  622. }
  623.  
  624.  
  625. /*
  626.  * Name:    dup_line
  627.  * Purpose: Duplicate current line
  628.  * Date:    June 5, 1991
  629.  * Passed:  window:   information allowing access to the current window
  630.  * Notes:   cursor stays on current line
  631.  */
  632. void dup_line( windows *window )
  633. {
  634. int len;            /* length of current line */
  635. int line;           /* line on screen to save and show prompt */
  636. long number;
  637. file_infos *file;
  638. text_ptr d, s;
  639.  
  640.    if (window->rline > window->file_info->length)
  641.       return;
  642.    line = window->bottom_line;
  643.    window->cursor = cpf( window->cursor );
  644.  
  645.    un_copy_line( window->cursor, window, TRUE );
  646.    /*
  647.     * don't dup the ^Z or a NULL line
  648.     */
  649.    if (*window->cursor != CONTROL_Z && (d=find_next( window->cursor )) !=NULL) {
  650.       file = window->file_info;
  651.  
  652.       /*
  653.        * don't use buffers to dup the line.  use hw_move to make space and
  654.        * copy current line at same time.  d is set to beginning of next line.
  655.        */
  656.       s = window->cursor;
  657.       len = linelen( s );
  658.       if (s[len] == '\n')
  659.          ++len;
  660.       number = ptoul( g_status.end_mem ) - ptoul( s );
  661.       hw_move( d, s, number );
  662.       g_status.end_mem = addltop( len, g_status.end_mem );
  663.       adjust_start_end( file, len );
  664.       addorsub_all_cursors( window, len );
  665.       adjust_windows_cursor( window, 1 );
  666.  
  667.       /*
  668.        * if current line is the bottom line, we can't see the dup line because
  669.        * cursor doesn't move and dup line is added after current line.
  670.        */
  671.       if  (window->cline != line) {
  672.          window_scroll_down( window->cline, line );
  673.          update_line( window );
  674.       }
  675.       file->dirty = NOT_LOCAL;
  676.  
  677.       /*
  678.        * record that file has been modified
  679.        */
  680.       file->modified = TRUE;
  681.       ++file->length;
  682.       restore_marked_block( window, 1 );
  683.       show_size( window );
  684.       show_avail_mem( );
  685.    } else
  686.       error( WARNING, line, "cannot duplicate line" );
  687. }
  688.  
  689.  
  690. /*
  691.  * Name:    back_space
  692.  * Purpose: To delete the character to the left of the cursor.
  693.  * Date:    June 5, 1991
  694.  * Passed:  window:   information allowing access to the current window
  695.  * Notes:   If the cursor is at the left of the line, then combine the
  696.  *           current line with the previous one.
  697.  *          If in indent mode, and the cursor is on the first non-blank
  698.  *           character of the line, then match the indentation of an
  699.  *           earlier line.
  700.  */
  701. void back_space( windows *window )
  702. {
  703. int len;            /* length of the current line */
  704. char *source;       /* source of block move to delete character */
  705. char *dest;         /* destination of block move */
  706. text_ptr p;         /* previous line in file */
  707. int plen;           /* length of previous line */
  708. int del_count;      /* number of characters to delete */
  709. int pos;            /* the position of the first non-blank char */
  710. int prompt_line;    /* line on screen to save and show prompt */
  711. register int rcol;
  712. int ccol;
  713. file_infos *file;
  714.  
  715.    file = window->file_info;
  716.    if (window->rline > file->length || *window->cursor == CONTROL_Z)
  717.       return;
  718.    prompt_line = window->bottom_line;
  719.    window->cursor = cpf( window->cursor );
  720.    copy_line( window->cursor, prompt_line );
  721.    len = linelen( g_status.line_buff );
  722.    rcol = window->rcol;
  723.    ccol = window->ccol;
  724.    if (rcol == 0) {
  725.       /*
  726.        * combine this line with the previous, if any
  727.        */
  728.       window->cursor = cpb( window->cursor );
  729.       if ((p = find_prev( window->cursor )) != NULL) {
  730.          if (len + 2 + (plen = linelen( p )) >= g_display.line_length) {
  731.             error( WARNING, prompt_line, "cannot combine lines" );
  732.             return;
  733.          }
  734.  
  735.          un_copy_line( window->cursor, window, TRUE );
  736.          copy_line( p, prompt_line );
  737.          load_undo_buffer( p );
  738.          g_status.line_buff[plen] = CONTROL_Z;
  739.          window->cursor = p;
  740.          un_copy_line( window->cursor, window, FALSE );
  741.  
  742.          /*
  743.           * make sure cursor stays on the screen, at the end of the
  744.           *  previous line
  745.           */
  746.          if (window->cline > window->top_line)
  747.             --window->cline;
  748.          --window->rline;
  749.          rcol = plen;
  750.          ccol = rcol - window->bcol;
  751.          --file->length;
  752.          restore_marked_block( window, -1 );
  753.          adjust_windows_cursor( window, -1 );
  754.          show_size( window );
  755.          check_virtual_col( window, rcol, ccol );
  756.          file->dirty = GLOBAL;
  757.       }
  758.    } else {
  759.       /*
  760.        * normal delete
  761.        *
  762.        * find out how much to delete (depends on indent mode)
  763.        */
  764.       del_count = 1;   /* the default */
  765.       if (mode.indent) {
  766.          /*
  767.           * indent only happens if the cursor is on the first
  768.           *  non-blank character of the line
  769.           */
  770.          if ((pos = first_non_blank( g_status.line_buff )) == rcol
  771.                     || g_status.line_buff[pos] == '\n'
  772.                     || g_status.line_buff[pos] == CONTROL_Z) {
  773.             /*
  774.              * now work out how much to indent
  775.              */
  776.             p = cpb( window->cursor );
  777.             for (p=find_prev( p ); p != NULL; p=find_prev( p )) {
  778.                if ((plen = first_non_blank( p )) < rcol && *(p+plen) != '\n') {
  779.                   /*
  780.                    * found the line to match
  781.                    */
  782.                   del_count = rcol - plen;
  783.                   break;
  784.                }
  785.             }
  786.          }
  787.       }
  788.  
  789.       /*
  790.        * move text to delete char(s), unless no chars actually there
  791.        */
  792.       if (rcol - del_count < len) {
  793.          dest = g_status.line_buff + rcol - del_count;
  794.          if (rcol > len) {
  795.             source = g_status.line_buff + len;
  796.             len = 2;
  797.          } else {
  798.             source = g_status.line_buff + rcol;
  799.             len = len - rcol + 2;
  800.          }
  801.          memmove( dest, source, len );
  802.       }
  803.       rcol -= del_count;
  804.       ccol -= del_count;
  805.       check_virtual_col( window, rcol, ccol );
  806.       file->dirty = GLOBAL;
  807.       show_changed_line( window );
  808.    }
  809.    file->modified = TRUE;
  810. }
  811.  
  812.  
  813. /*
  814.  * Name:    line_kill
  815.  * Purpose: To delete the line the cursor is on.
  816.  * Date:    June 5, 1991
  817.  * Passed:  window:   information allowing access to the current window
  818.  * Notes:   If *window->cursor is pointing to CONTROL_Z then do not do a
  819.  *          line kill (can't kill a NULL line).
  820.  */
  821. void line_kill( windows *window )
  822. {
  823. int i = 0;
  824. text_ptr s;         /* next line in file */
  825. windows w;
  826. file_infos *file;
  827. long length;
  828.  
  829.    file = window->file_info;
  830.    if (file->length > 0  && *window->cursor != CONTROL_Z) {
  831.  
  832.       if (g_status.copied == FALSE)
  833.          load_undo_buffer( window->cursor );
  834.       else
  835.          load_undo_buffer( g_status.line_buff );
  836.       g_status.copied = TRUE;
  837.       g_status.line_buff[0] = CONTROL_Z;
  838.       s = window->cursor = cpf( window->cursor );
  839.       /*
  840.        * if line to delete has \n at end of line then decrement file length.
  841.        */
  842.       if (*(s + linelen( s )) == '\n') {
  843.          --file->length;
  844.          --i;
  845.       }
  846.       un_copy_line( s, window, FALSE );
  847.       file->dirty = NOT_LOCAL;
  848.  
  849.       /*
  850.        * move all cursors one according to i, restore begin and end block
  851.        */
  852.       adjust_windows_cursor( window, i );
  853.       restore_marked_block( window, i );
  854.  
  855.       /*
  856.        * we are not doing a GLOBAL update, so update current window here
  857.        */
  858.       if (file->dirty == NOT_LOCAL) {
  859.          length = file->length;
  860.          window_scroll_up( window->cline, window->bottom_line );
  861.          dup_window_info( &w, window );
  862.          for (; w.cursor != NULL; w.cline++, w.rline++) {
  863.             if (w.cline == w.bottom_line) {
  864.                if (w.rline <= length && length != 1l)
  865.                   update_line( &w );
  866.                else
  867.                   show_eof( w.cline );
  868.                break;
  869.             }
  870.             w.cursor = find_next( w.cursor );
  871.          }
  872.       }
  873.       show_size( window );
  874.    }
  875. }
  876.  
  877.  
  878. /*
  879.  * Name:    char_del_under
  880.  * Purpose: To delete the character under the cursor.
  881.  * Date:    June 5, 1991
  882.  * Passed:  window:   information allowing access to the current window
  883.  * Notes:   If the cursor is beyond the end of the line, then this
  884.  *           command is ignored.
  885.  */
  886. void char_del_under( windows *window )
  887. {
  888. char *source;    /* source of block move to delete character */
  889. register int len;
  890. int rcol;
  891. file_infos *file;
  892.  
  893.    file = window->file_info;
  894.    if (window->rline > file->length || *window->cursor == CONTROL_Z)
  895.       return;
  896.    rcol = window->rcol;
  897.    copy_line( window->cursor, window->bottom_line );
  898.    if (rcol < (len = linelen( g_status.line_buff ))) {
  899.       /*
  900.        * move text to delete char using buffer
  901.        */
  902.       source = g_status.line_buff + rcol + 1;
  903.       memmove( source-1, source, len-rcol+2 );
  904.       file->dirty = GLOBAL;
  905.       file->modified = TRUE;
  906.       show_changed_line( window );
  907.    } else if (mode.sdel)
  908.       join_line( window );
  909. }
  910.  
  911.  
  912. /*
  913.  * Name:    eol_kill
  914.  * Purpose: To delete everything from the cursor to the end of the line.
  915.  * Date:    June 5, 1991
  916.  * Passed:  window:   information allowing access to the current window
  917.  * Notes:   If the cursor is beyond the end of the line, then this
  918.  *           command is ignored.
  919.  */
  920. void eol_kill( windows *window )
  921. {
  922. register char *dest;  /* the start of the delete area */
  923.  
  924.    if (window->rline > window->file_info->length || *window->cursor == CONTROL_Z)
  925.       return;
  926.    copy_line( window->cursor, window->bottom_line );
  927.    load_undo_buffer( g_status.line_buff );
  928.    if (window->rcol < linelen( g_status.line_buff )) {
  929.       /*
  930.        * truncate to delete rest of line
  931.        */
  932.       dest = g_status.line_buff + window->rcol;
  933.       *dest++ = '\n';
  934.       *dest = CONTROL_Z;
  935.       window->file_info->dirty = GLOBAL;
  936.       show_changed_line( window );
  937.    }
  938. }
  939.  
  940.  
  941. /*
  942.  * Name:    undo_line
  943.  * Purpose: To retrieve unaltered line if possible.
  944.  * Date:    June 5, 1991
  945.  * Passed:  window:   information allowing access to the current window
  946.  * Notes:   Changes are made to the line buffer so the underlying text has
  947.  *          not changed.  put the unchanged line in the line buffer.
  948.  */
  949. void undo_line( windows *window )
  950. {
  951.  
  952.    if (window->rline > window->file_info->length)
  953.       return;
  954.    if (g_status.copied) {
  955.       g_status.copied = FALSE;
  956.       copy_line( window->cursor, window->bottom_line );
  957.       window->file_info->dirty = GLOBAL;
  958.       show_changed_line( window );
  959.    }
  960. }
  961.  
  962.  
  963. /*
  964.  * Name:    undo
  965.  * Purpose: To retrieve (pop) a line from the undo stack
  966.  * Date:    September 26, 1991
  967.  * Passed:  window:   information allowing access to the current window
  968.  * Notes:   Insert an empty line into the file then pop the line in the undo
  969.  *          stack.  When we pop line 0, there are no more lines on the stack.
  970.  *          Set the stack pointer to -1 to indicate an empty stack.
  971.  */
  972. void undo( windows *window )
  973. {
  974. char *source;
  975. char *undo_line;
  976. int len;
  977. int head;
  978. file_infos *file;
  979.  
  980.    if (g_status.undo_head < 0)
  981.       return;
  982.    --g_status.undo_head;
  983.    window->cursor = cpf( window->cursor );
  984.    un_copy_line( window->cursor, window, TRUE );
  985.    copy_line( window->cursor, window->bottom_line );
  986.  
  987.    source = g_status.line_buff;
  988.    /*
  989.     *  make room for '\n'.  then, after we un_copy the g_status.line_buff, we
  990.     *  have added a line to the file.
  991.     */
  992.    memmove( source+1, source, linelen( source )+2 );
  993.    *source = '\n';
  994.    un_copy_line( window->cursor, window, TRUE );
  995.  
  996.    /*
  997.     *  ajust cursors in other windows opened to the same file.
  998.     */
  999.    adjust_windows_cursor( window, 1 );
  1000.  
  1001.    /*
  1002.     * copy the line in the top of the stack to the line_buffer.  then, copy
  1003.     * the line buffer to the file.
  1004.     */
  1005.    g_status.copied = TRUE;
  1006.    head = g_status.undo_head;
  1007.    undo_line = &g_status.undo_buffer[head][0];
  1008.    len = find_CONTROL_Z( undo_line ) + 1;
  1009.    memcpy( source, undo_line, len );
  1010.    un_copy_line( window->cursor, window, TRUE );
  1011.  
  1012.    /*
  1013.     * we have now undeleted a line.  increment the file length and display
  1014.     * it.
  1015.     */
  1016.    file = window->file_info;
  1017.    ++file->length;
  1018.    file->dirty = GLOBAL;
  1019.    show_size( window );
  1020.  
  1021.    /*
  1022.     * if g_status.undo_head == 0, we just popped the last line in the stack.
  1023.     * set the stack pointer to -1 to show there are no more lines.
  1024.     */
  1025.    if (g_status.undo_head == 0)
  1026.       g_status.undo_head = -1;
  1027. }
  1028.  
  1029.  
  1030. /*
  1031.  * Name:    beg_next_line
  1032.  * Purpose: To move the cursor to the beginning of the next line.
  1033.  * Date:    October 4, 1991
  1034.  * Passed:  window:   information allowing access to the current window
  1035.  */
  1036. void beg_next_line( windows *window )
  1037. {
  1038.    window->rcol = 0;
  1039.    check_virtual_col( window, window->rcol, window->ccol );
  1040.    move_down( window );
  1041. }
  1042.  
  1043.  
  1044. /*
  1045.  * Name:    next_line
  1046.  * Purpose: To move the cursor to the first character of the next line.
  1047.  * Date:    October 4, 1991
  1048.  * Passed:  window:   information allowing access to the current window
  1049.  */
  1050. void next_line( windows *window )
  1051. {
  1052. register int rcol;
  1053.  
  1054.    move_down( window );
  1055.    rcol = first_non_blank( window->cursor );
  1056.    if (window->cursor[rcol] == '\n')
  1057.       rcol = 0;
  1058.    check_virtual_col( window, rcol, window->ccol );
  1059. }
  1060.  
  1061.  
  1062. /*
  1063.  * Name:    goto_left
  1064.  * Purpose: To move the cursor to the left of the current line.
  1065.  * Date:    June 5, 1991
  1066.  * Passed:  window:   information allowing access to the current window
  1067.  */
  1068. void goto_left( windows *window )
  1069. {
  1070. register int rcol;
  1071.  
  1072.    if (g_status.copied)
  1073.       rcol = first_non_blank( g_status.line_buff );
  1074.    else
  1075.       rcol = first_non_blank( window->cursor );
  1076.    if (window->cursor[rcol] == '\n')
  1077.       rcol = 0;
  1078.    if (window->rcol == rcol)
  1079.       rcol = 0;
  1080.    check_virtual_col( window, rcol, window->ccol );
  1081. }
  1082.  
  1083.  
  1084. /*
  1085.  * Name:    goto_right
  1086.  * Purpose: To move the cursor to the right of the current line.
  1087.  * Date:    June 5, 1991
  1088.  * Passed:  window:   information allowing access to the current window
  1089.  */
  1090. void goto_right( windows *window )
  1091. {
  1092. register int rcol;
  1093.  
  1094.    if (g_status.copied)
  1095.       rcol = linelen( g_status.line_buff );
  1096.    else
  1097.       rcol = linelen( window->cursor );
  1098.    window->ccol = rcol - window->bcol;
  1099.    check_virtual_col( window, rcol, window->ccol );
  1100. }
  1101.  
  1102.  
  1103. /*
  1104.  * Name:    goto_top
  1105.  * Purpose: To move the cursor to the top of the current window.
  1106.  * Date:    June 5, 1991
  1107.  * Passed:  window:   information allowing access to the current window
  1108.  * Notes:   If the start of the file occurs before the top of the window,
  1109.  *           then the start of the file is moved to the top of the window.
  1110.  */
  1111. void goto_top( windows *window )
  1112. {
  1113. text_ptr cursor;  /* anticipated cursor line */
  1114.  
  1115.    un_copy_line( window->cursor, window, TRUE );
  1116.    window->cursor = cpb( window->cursor );
  1117.    for (; window->cline > window->top_line; window->cline--,window->rline--) {
  1118.       if ((cursor = find_prev( window->cursor )) == NULL)
  1119.          break;
  1120.       window->cursor = cursor;
  1121.    }
  1122. }
  1123.  
  1124.  
  1125. /*
  1126.  * Name:    goto_bottom
  1127.  * Purpose: To move the cursor to the bottom of the current window.
  1128.  * Date:    June 5, 1991
  1129.  * Passed:  window:   information allowing access to the current window
  1130.  */
  1131. void goto_bottom( windows *window )
  1132. {
  1133. text_ptr cursor;
  1134.  
  1135.    un_copy_line( window->cursor, window, TRUE );
  1136.    window->cursor = cpf( window->cursor );
  1137.    for (; window->cline < window->bottom_line; window->cline++,window->rline++) {
  1138.       if ((cursor = find_next( window->cursor )) == NULL)
  1139.          break;
  1140.       window->cursor = cursor;
  1141.    }
  1142. }
  1143.  
  1144.  
  1145. /*
  1146.  * Name:    set_tabstop
  1147.  * Purpose: To set the current interval between tab stops
  1148.  * Date:    October 1, 1989
  1149.  * Notes:   Tab interval must be reasonable, and this function will
  1150.  *           not allow tabs more than MAX_COLS / 2.
  1151.  */
  1152. void set_tabstop( windows *window )
  1153. {
  1154. register int line;
  1155. char num_str[MAX_COLS];  /* tab interval as a character string */
  1156. int tab;                 /* new tab interval */
  1157. int rc;
  1158.  
  1159.    line = window->bottom_line;
  1160.    rc = OK;
  1161.    while (rc == OK) {
  1162.       itoa( mode.tab_size, num_str, 10 );
  1163.       rc = get_name( "Tab interval: ", line, num_str, g_display.message_color );
  1164.       if (rc == OK) {
  1165.          tab = atoi( num_str );
  1166.          if (tab < MAX_COLS/2) {
  1167.             mode.tab_size = tab;
  1168.             rc = ERROR;
  1169.          } else
  1170.             error( WARNING, line, "tab size too long" );
  1171.       }
  1172.    }
  1173. }
  1174.  
  1175.  
  1176. /*
  1177.  * Name:    show_line_col
  1178.  * Purpose: show current real line and column of current cursor position
  1179.  * Date:    June 5, 1991
  1180.  * Passed:  window:   information allowing access to the current window
  1181.  * Notes:   Blank old position and display new position.  current line and
  1182.  *          column may take up to 12 columns, which allows the display of
  1183.  *          999 columns and 99,999,999 lines.
  1184.  */
  1185. void show_line_col( windows *window )
  1186. {
  1187. int i, pline;
  1188. register int k;
  1189. char line_col[20], num[10];
  1190.  
  1191.    /*
  1192.     * blank out current line:column position.
  1193.     */
  1194.    strcpy( line_col, "            " );
  1195.  
  1196.    /*
  1197.     * convert column to ascii and store in display buffer.
  1198.     */
  1199.    itoa( window->rcol+1, num, 10 );
  1200.    i = strlen( num ) - 1;
  1201.    for (k=11; i>=0; i--, k--)
  1202.       line_col[k] = num[i];
  1203.  
  1204.    /*
  1205.     * put in colon to separate line and column
  1206.     */
  1207.    line_col[k--] = ':';
  1208.  
  1209.    /*
  1210.     * convert line to ascii and store in display buffer.
  1211.     */
  1212.    ltoa( window->rline, num, 10 );
  1213.    i = strlen( num ) - 1;
  1214.    for (; i>=0; i--, k--)
  1215.       line_col[k] = num[i];
  1216.  
  1217.    /*
  1218.     * find line to start line:column display then output
  1219.     */
  1220.    pline = window->top_line - 1;
  1221.    s_output( line_col, pline, MAX_COLS-12, g_display.head_color );
  1222.  
  1223.    show_asterisk( window->file_info->modified, pline );
  1224. }
  1225.  
  1226.  
  1227. /*
  1228.  * Name:    show_asterisk
  1229.  * Purpose: give user an indication if file is dirty
  1230.  * Date:    September 16, 1991
  1231.  * Passed:  modified:   boolean - TRUE if modified, FALSE otherwise
  1232.  *          line:       line to display asterisk
  1233.  */
  1234. void show_asterisk( int modified, int line )
  1235. {
  1236. register int c;
  1237.  
  1238.    if (modified)
  1239.       c = '*';
  1240.    else
  1241.       c = ' ';
  1242.    c_output( c, 4, line, g_display.head_color );
  1243. }
  1244.  
  1245.  
  1246. /*
  1247.  * Name:    toggle_overwrite
  1248.  * Purpose: toggle overwrite-insert mode
  1249.  * Date:    September 16, 1991
  1250.  * Passed:  window
  1251.  */
  1252. void toggle_overwrite( windows *window )
  1253. {
  1254. int temp;
  1255.  
  1256.    mode.insert = !mode.insert;
  1257.    show_insert_mode( );
  1258.    if (mode.insert)
  1259.       temp = g_display.insert_cursor;
  1260.    else
  1261.       temp = g_display.overw_cursor;
  1262.    set_cursor_size( temp );
  1263. }
  1264.  
  1265. /*
  1266.  * Name:    toggle_sdel
  1267.  * Purpose: toggle stream delete mode
  1268.  * Date:    September 16, 1991
  1269.  * Passed:  window
  1270.  */
  1271. void toggle_sdel( windows *window )
  1272. {
  1273.    mode.sdel = !mode.sdel;
  1274.    show_sdelete_mode( );
  1275. }
  1276.  
  1277.  
  1278. /*
  1279.  * Name:    toggle_sdel
  1280.  * Purpose: toggle stream delete mode
  1281.  * Date:    September 16, 1991
  1282.  * Passed:  window
  1283.  */
  1284. void toggle_indent( windows *window )
  1285. {
  1286.    mode.indent = !mode.indent;
  1287.    show_indent_mode( );
  1288. }
  1289.  
  1290.  
  1291. /*
  1292.  * Name:    toggle_search_case
  1293.  * Purpose: toggle search case
  1294.  * Date:    September 16, 1991
  1295.  * Passed:  window
  1296.  */
  1297. void toggle_search_case( windows *window )
  1298. {
  1299.    if (bm.search_case == IGNORE)
  1300.       bm.search_case = MATCH;
  1301.    else
  1302.       bm.search_case = IGNORE;
  1303.    show_search_case( );
  1304.    if (bm.search_defined == OK)
  1305.       build_boyer_array( );
  1306. }
  1307.  
  1308.  
  1309. /*
  1310.  * Name:    editor
  1311.  * Purpose: Set up the editor structures and display changes as needed.
  1312.  * Date:    June 5, 1991
  1313.  * Passed:  argc:   number of command line arguments
  1314.  *          argv:   text of command line arguments
  1315.  */
  1316. void editor( int argc, char *argv[] )
  1317. {
  1318. char *name;  /* name of file to start editing */
  1319. windows *window;            /* current active window */
  1320. windows *above;             /* window above current */
  1321. windows *below;             /* window below current */
  1322. register file_infos *file;           /* temporary file structure */
  1323. int c;
  1324.  
  1325.    /*
  1326.     * Check that user specified file to edit, if not offer help
  1327.     */
  1328.    if (argc > 1)
  1329.       name = argv[1];
  1330.    else {
  1331.       g_status.rw_name[0] = '\0';
  1332.       name = g_status.rw_name;
  1333.       if (get_name( "File name to edit : ", g_display.nlines, name,
  1334.                     g_display.text_color ) != OK)
  1335.          return;
  1336.       if (*name == '\0')
  1337.          return;
  1338.    }
  1339.  
  1340.    if (edit_file( name ) == ERROR)
  1341.       return;
  1342.    if (initialize_window( ) != ERROR) {
  1343.       g_status.stop = FALSE;
  1344.       window = g_status.current_window;
  1345.       window->file_info->dirty = FALSE;
  1346.       show_window_header( window->file_info->file_name, window );
  1347.       show_size_name( window );
  1348.       show_size( window );
  1349.  
  1350.       /*
  1351.        * display the current window
  1352.        */
  1353.       display_current_window( window );
  1354.       show_modes( );
  1355.       if (mode.insert)
  1356.          c = g_display.insert_cursor;
  1357.       else
  1358.          c = g_display.overw_cursor;
  1359.       set_cursor_size( c );
  1360.    } else
  1361.       g_status.stop = TRUE;
  1362.  
  1363.    /*
  1364.     * main loop - keep updating the display and processing any commands
  1365.     *  while user has not pressed the stop key
  1366.     */
  1367.    for (; g_status.stop != TRUE;) {
  1368.       window = g_status.current_window;
  1369.  
  1370.       /*
  1371.        * update all the other windows that point to file that has been changed
  1372.        */
  1373.       above = below = window;
  1374.       while (above->prev || below->next) {
  1375.          if (above->prev) {
  1376.             above = above->prev;
  1377.             if (above->visible) {
  1378.                file = above->file_info;
  1379.                if (file->dirty == GLOBAL || file->dirty == NOT_LOCAL) {
  1380.                   display_current_window( above );
  1381.                   show_size( above );
  1382.                }
  1383.                show_asterisk( file->modified, above->top_line-1 );
  1384.             }
  1385.          }
  1386.          if (below->next) {
  1387.             below = below->next;
  1388.             if (below->visible) {
  1389.                file = below->file_info;
  1390.                if (file->dirty == GLOBAL || file->dirty == NOT_LOCAL) {
  1391.                   display_current_window( below );
  1392.                   show_size( below );
  1393.                }
  1394.                show_asterisk( file->modified, below->top_line-1 );
  1395.             }
  1396.          }
  1397.       }
  1398.       file = window->file_info;
  1399.       if (file->dirty == LOCAL || file->dirty == GLOBAL)
  1400.          display_current_window( window );
  1401.       file->dirty = FALSE;
  1402.  
  1403.       /*
  1404.        * Set the cursor position at window->ccol, window->cline.  Show the
  1405.        * user where in the file the cursor is positioned.
  1406.        */
  1407.       xygoto( window->ccol, window->cline );
  1408.       show_line_col( window );
  1409.  
  1410.       /*
  1411.        * Get a key from the user.  Look up the function assigned to that key.
  1412.        * All regular text keys are assigned to function 0.  Text characters
  1413.        * are less than 0x100, decimal 256, which includes the ASCII and
  1414.        * extended ASCII character set.
  1415.        */
  1416.       g_status.key_pressed = getkey( );
  1417.       g_status.command = getfunc( g_status.key_pressed );
  1418.       s_output( "          ", g_display.mode_line, 63, g_display.mode_color );
  1419.       if (g_status.command >= 0 && g_status.command < 77)
  1420.          (*do_it[g_status.command])( window );
  1421.    }
  1422.    cls( );
  1423.    xygoto( 0, 0 );
  1424. }
  1425.