home *** CD-ROM | disk | FTP | other *** search
/ Black Box 4 / BlackBox.cdr / editors / tde150.arj / ED.C < prev    next >
C/C++ Source or Header  |  1992-04-01  |  55KB  |  1,772 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.  *
  67.  * This modification of Douglas Thomson's code is released into the
  68.  * public domain, Frank Davis.   You may distribute it freely.
  69.  */
  70.  
  71. #include "tdestr.h"     /* global variables */
  72. #include "define.h"
  73. #include "tdefunc.h"
  74. #include "global.h"     /* global variables */
  75. #include "default.h"
  76.  
  77.  
  78. /*
  79.  * Name:    tab_key
  80.  * Purpose: To make the necessary changes after the user types the tab key.
  81.  * Date:    June 5, 1991
  82.  * Passed:  window: information allowing access to the current window
  83.  * Notes:   If in insert mode, then this function adds the required
  84.  *           number of spaces in the file.
  85.  *          If not in insert mode, then tab simply moves the cursor right
  86.  *           the required distance.
  87.  */
  88. void tab_key( WINDOW *window )
  89. {
  90. int spaces;      /* the spaces to move to the next tab stop */
  91. char *source;    /* source for block move to make room for c */
  92. char *dest;      /* destination for block move */
  93. int pad, len;
  94. register int rcol;
  95. int old_bcol;
  96. register WINDOW *win;  /* put window pointer in a register */
  97.  
  98.    win  = window;
  99.    if (*win->cursor == CONTROL_Z)
  100.       return;
  101.    rcol = win->rcol;
  102.    old_bcol = win->bcol;
  103.    show_ruler_char( win );
  104.    /*
  105.     * work out the number of spaces to the next tab stop
  106.     */
  107.    spaces = mode.tab_size - (rcol % mode.tab_size);
  108.  
  109.    if (mode.insert && rcol + spaces < g_display.line_length) {
  110.       copy_line( win->cursor, win->bottom_line );
  111.       /*
  112.        * work out how many characters need to be inserted
  113.        */
  114.       len = linelen( g_status.line_buff );
  115.       pad = rcol > len ? rcol - len : 0;
  116.       if (g_status.line_buff[len] == CONTROL_Z)
  117.          ++pad;
  118.       if (len + pad + spaces >= g_display.line_length)
  119.          error( WARNING, win->bottom_line, "line too long to add" );
  120.       else {
  121.          if (pad > 0  || spaces > 0) {
  122.             if (g_status.line_buff[len] == CONTROL_Z) {
  123.                g_status.line_buff[len] = '\n';
  124.                g_status.line_buff[len+1] = CONTROL_Z;
  125.                ++win->file_info->length;
  126.                show_size( window );
  127.                --pad;
  128.                ++len;
  129.             }
  130.             source = g_status.line_buff + rcol - pad;
  131.             dest = source + pad + spaces;
  132.             memmove( dest, source, len+pad-rcol+2 );
  133.  
  134.             /*
  135.              * if padding was required, then put in the required spaces
  136.              */
  137.             memset( source, ' ', pad + spaces );
  138.          }
  139.  
  140.          win->file_info->dirty = GLOBAL;
  141.          show_changed_line( win );
  142.          rcol += spaces;
  143.          win->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.       win->ccol += spaces;
  151.    }
  152.    check_virtual_col( win, rcol, win->ccol );
  153.    if (old_bcol != win->bcol) {
  154.       make_ruler( win );
  155.       show_ruler( win );
  156.    }
  157. }
  158.  
  159.  
  160. /*
  161.  * Name:    backtab
  162.  * Purpose: To make the necessary changes after the user presses the backtab.
  163.  * Date:    November 1, 1991
  164.  * Passed:  window: information allowing access to the current window
  165.  * Notes:   If in insert mode, then this function subs the required
  166.  *           number of spaces in the file.
  167.  *          If not in insert mode, then tab simply moves the cursor left
  168.  *           the required distance.
  169.  */
  170. void backtab( WINDOW *window )
  171. {
  172. int spaces;      /* the spaces to move to the next tab stop */
  173. char *source;    /* source for block move to make room for c */
  174. char *dest;      /* destination for block move */
  175. int pad, len;
  176. register int rcol;
  177. int old_bcol;
  178. register WINDOW *win;  /* put window pointer in a register */
  179.  
  180.    win  = window;
  181.    rcol = win->rcol;
  182.    if (*win->cursor == CONTROL_Z || win->rcol == 0)
  183.       return;
  184.    old_bcol = win->bcol;
  185.    show_ruler_char( win );
  186.    /*
  187.     * work out the number of spaces to the previous tab stop
  188.     */
  189.    spaces = win->rcol % mode.tab_size;
  190.    if (spaces == 0)
  191.       spaces = mode.tab_size;
  192.    copy_line( win->cursor, win->bottom_line );
  193.    len = linelen( g_status.line_buff );
  194.    if (mode.insert && rcol - spaces < len) {
  195.       pad = rcol > len ? rcol - len : 0;
  196.       if (g_status.line_buff[len] == CONTROL_Z)
  197.          ++pad;
  198.       if (pad > 0  || spaces > 0) {
  199.          if (g_status.line_buff[len] == CONTROL_Z) {
  200.             g_status.line_buff[len] = '\n';
  201.             g_status.line_buff[len+1] = CONTROL_Z;
  202.             ++win->file_info->length;
  203.             show_size( win );
  204.             --pad;
  205.             ++len;
  206.          }
  207.          /*
  208.           * if padding was required, then put in the required spaces
  209.           */
  210.          if (pad > 0) {
  211.             source = g_status.line_buff + rcol - pad;
  212.             dest = source + pad;
  213.             memmove( dest, source, pad+2 );
  214.             memset( source, ' ', pad );
  215.          }
  216.          source = g_status.line_buff + rcol;
  217.          dest = source - spaces;
  218.          memmove( dest, source, len+pad-rcol+2 );
  219.       }
  220.  
  221.       win->file_info->dirty = GLOBAL;
  222.       show_changed_line( win );
  223.       rcol -= spaces;
  224.       win->ccol -= spaces;
  225.    } else {
  226.       /*
  227.        * move the cursor without changing the text underneath
  228.        */
  229.       rcol -= spaces;
  230.       if (rcol < 0)
  231.          rcol = 0;
  232.       win->ccol -= spaces;
  233.    }
  234.    check_virtual_col( win, rcol, win->ccol );
  235.    if (old_bcol != win->bcol) {
  236.       make_ruler( win );
  237.       show_ruler( win );
  238.    }
  239. }
  240.  
  241.  
  242. /*
  243.  * Name:    insert_newline
  244.  * Purpose: insert a newline
  245.  * Date:    June 5, 1991
  246.  * Passed:  window:   information allowing access to the current window
  247.  * Notes:   There a several ways to insert a line into a file:  1) pressing
  248.  *          a key, 2) word wrap, 3) any others?
  249.  *          When doing word wrap or format paragraph, don't show any changes.
  250.  *            Wait until the function finishes then show all changes at once.
  251.  */
  252. void insert_newline( WINDOW *window )
  253. {
  254. char *source;       /* source for block move to make room for c */
  255. char *dest;         /* destination for block move */
  256. int len;            /* length of current line */
  257. register int add;   /* characters to be added (usually 1 in insert mode) */
  258. int rcol;
  259. text_ptr prev;      /* previous lines scanned for autoindent */
  260. long length;
  261. int carriage_return;
  262. int split_line;
  263. int wordwrap;
  264. int dirty;
  265. int old_bcol;
  266. register WINDOW *win;  /* put window pointer in a register */
  267.  
  268.    win = window;
  269.    length = win->file_info->length;
  270.    if (win->rline > length && *win->cursor != CONTROL_Z)
  271.       return;
  272.    wordwrap = mode.word_wrap;
  273.    switch (g_status.command) {
  274.       case WordWrap:
  275.          wordwrap = TRUE;
  276.          carriage_return = TRUE;
  277.          split_line = FALSE;
  278.          break;
  279.       case Rturn :
  280.          show_ruler_char( win );
  281.          carriage_return = TRUE;
  282.          split_line = FALSE;
  283.          break;
  284.       case AddLine :
  285.          split_line = carriage_return = FALSE;
  286.          break;
  287.       case SplitLine :
  288.          split_line = carriage_return = TRUE;
  289.          break;
  290.    }
  291.  
  292.    /*
  293.     * make window temporarily invisible to the un_copy_line function
  294.     */
  295.    win->visible = FALSE;
  296.    win->cursor = cpf( win->cursor );
  297.    copy_line( win->cursor, win->bottom_line );
  298.    len = linelen( g_status.line_buff );
  299.  
  300.    source = g_status.line_buff + len;
  301.    if (carriage_return || split_line) {
  302.       if (win->rcol < len)
  303.          source = g_status.line_buff + win->rcol;
  304.    }
  305.    /*
  306.     *  make room for '\n' just after source (source+1)
  307.     */
  308.    memmove( source+1, source, linelen( source )+2 );
  309.  
  310.    *source = '\n';
  311.    un_copy_line( win->cursor, win, TRUE );
  312.    adjust_windows_cursor( win, 1 );
  313.  
  314.    ++win->file_info->length;
  315.    win->file_info->dirty = NOT_LOCAL;
  316.    if (length == 0l || wordwrap || win->cline == win->bottom_line)
  317.       win->file_info->dirty = GLOBAL;
  318.    else if (!split_line)
  319.       update_line( win );
  320.  
  321.    /*
  322.     * If the cursor is to move down to the next line, then update
  323.     *  the line and column appropriately.
  324.     */
  325.    if (carriage_return || split_line) {
  326.       dirty = win->file_info->dirty;
  327.       win->cursor = find_next( win->cursor );
  328.       if (win->cline < win->bottom_line)
  329.          win->cline++;
  330.       win->rline++;
  331.       rcol = win->rcol;
  332.       old_bcol = win->bcol;
  333.  
  334.       /*
  335.        * indentation is only required if we are in the right mode,
  336.        *  the user typed <CR>, and if there is not space followed
  337.        *  by something after the cursor.
  338.        */
  339.       if (mode.indent || wordwrap) {
  340.          /*
  341.           * autoindentation is required. Match the indentation of
  342.           *  the first line above that is not blank.
  343.           */
  344.          if (wordwrap) {
  345.             prev = cpb( win->cursor );
  346.             prev = find_prev( prev );
  347.             if (prev == NULL)
  348.                add = mode.left_margin;
  349.             else if (linelen( prev ) == 0)
  350.                add = mode.parg_margin;
  351.             else
  352.                add = mode.left_margin;
  353.          } else {
  354.             add = first_non_blank( g_status.line_buff );
  355.             if (g_status.line_buff[add] == '\n' ||
  356.                       g_status.line_buff[add] == CONTROL_Z) {
  357.                prev = cpb( win->cursor );
  358.                while ((prev = find_prev( prev )) != NULL) {
  359.                   add = first_non_blank( prev );
  360.                   if (prev[add] != '\n')
  361.                      break;
  362.                }
  363.             }
  364.          }
  365.          copy_line( win->cursor, win->bottom_line );
  366.          len = linelen( g_status.line_buff );
  367.          source = g_status.line_buff;
  368.          if (len + add > MAX_LINE_LENGTH)
  369.             add = MAX_LINE_LENGTH - len;
  370.          dest = source + add;
  371.          memmove( dest, source, len+2 );
  372.  
  373.          /*
  374.           * now put in the autoindent characters
  375.           */
  376.          memset( source, ' ', add );
  377.          win->rcol = add;
  378.          un_copy_line( win->cursor, win, TRUE );
  379.       } else
  380.          win->rcol = 0;
  381.       if (split_line) {
  382.          win->cursor = cpb( win->cursor );
  383.          win->cursor = find_prev( win->cursor );
  384.          if (win->cline > win->top_line + window->ruler)
  385.             win->cline--;
  386.          win->rline--;
  387.          win->rcol = rcol;
  388.       }
  389.       check_virtual_col( win, win->rcol, win->ccol );
  390.       if (dirty == GLOBAL || win->file_info->dirty == LOCAL || wordwrap)
  391.          win->file_info->dirty = GLOBAL;
  392.       else
  393.          win->file_info->dirty = dirty;
  394.    }
  395.  
  396.    /*
  397.     * record that file has been modified
  398.     */
  399.    if (win->file_info->dirty != GLOBAL)
  400.       my_scroll_down( win );
  401.    restore_marked_block( win, 1 );
  402.    show_size( win );
  403.    win->visible = TRUE;
  404.    if (old_bcol != win->bcol) {
  405.       make_ruler( win );
  406.       show_ruler( win );
  407.    }
  408. }
  409.  
  410.  
  411. /*
  412.  * Name:    insert_overwrite
  413.  * Purpose: To make the necessary changes after the user has typed a normal
  414.  *           printable character
  415.  * Date:    June 5, 1991
  416.  * Passed:  window:   information allowing access to the current window
  417.  */
  418. void insert_overwrite( WINDOW *window )
  419. {
  420. char *source;       /* source for block move to make room for c */
  421. char *dest;         /* destination for block move */
  422. int len;            /* length of current line */
  423. int pad;            /* padding to add if cursor beyond end of line */
  424. int add;            /* characters to be added (usually 1 in insert mode) */
  425. register int rcol;
  426. register WINDOW *win;  /* put window pointer in a register */
  427.  
  428.    win = window;
  429.    if (*win->cursor == CONTROL_Z || g_status.key_pressed >= 256)
  430.       return;
  431.    rcol = win->rcol;
  432.    /*
  433.     * first check we have room - the editor can not
  434.     *  cope with lines wider than g_display.line_length
  435.     */
  436.    if (rcol >= g_display.line_length)
  437.       error( WARNING, win->bottom_line, "cannot insert more characters" );
  438.    else {
  439.       copy_line( win->cursor, win->bottom_line );
  440.  
  441.       /*
  442.        * work out how many characters need to be inserted
  443.        */
  444.       len = linelen( g_status.line_buff );
  445.       pad = rcol > len ? rcol - len : 0;
  446.  
  447.       /*
  448.        * if this is the last line in a file, the last character in the
  449.        * line buffer will be CONTROL_Z.  increment pad and insert a \n.
  450.        */
  451.       if (g_status.line_buff[len] == CONTROL_Z)
  452.          ++pad;
  453.  
  454.       if (mode.insert || rcol >= len)
  455.          /*
  456.           * inserted characters, or overwritten characters at the end of
  457.           *  the line, are inserted.
  458.           */
  459.          add = 1;
  460.       else
  461.          /*
  462.           *  and no extra space is required to overwrite existing characters
  463.           */
  464.          add = 0;
  465.  
  466.       /*
  467.        * check that current line would not get too long.
  468.        */
  469.       if (len + pad + add >= g_display.line_length)
  470.          error( WARNING, win->bottom_line, "no more room to add" );
  471.       else {
  472.  
  473.          /*
  474.           * make room for whatever needs to be inserted
  475.           */
  476.          if (pad > 0  || add > 0) {
  477.             source = g_status.line_buff + len;
  478.             if (*source == CONTROL_Z) {
  479.                if (rcol > len)
  480.                   source = g_status.line_buff + rcol + 1;
  481.                *source++ = '\n';
  482.                *source   = CONTROL_Z;
  483.                ++win->file_info->length;
  484.                show_size( win );
  485.                --pad;
  486.                ++len;
  487.             }
  488.             source = g_status.line_buff + rcol - pad;
  489.             dest = source + pad + add;
  490.             memmove( dest, source, len + pad - rcol + 2 );
  491.             /*
  492.              * put in the required padding
  493.              */
  494.             memset( source, ' ', pad );
  495.          }
  496.          g_status.line_buff[rcol] = (char)g_status.key_pressed;
  497.  
  498.          /*
  499.           * always increment the real column (rcol) then adjust the
  500.           * logical and base column as needed.   show the changed line
  501.           * in all but the LOCAL window.  In the LOCAL window, there are
  502.           * two cases:  1) update the line, or 2) redraw the window if
  503.           * cursor goes too far right.
  504.           */
  505.          win->file_info->dirty = NOT_LOCAL;
  506.          show_changed_line( win );
  507.          if (win->ccol < win->end_col) {
  508.             show_curl_line( win );
  509.             show_ruler_char( win );
  510.             win->ccol++;
  511.          } else {
  512.             win->bcol++;
  513.             win->file_info->dirty = LOCAL;
  514.             make_ruler( win );
  515.             show_ruler( win );
  516.          }
  517.          rcol++;
  518.       }
  519.  
  520.       /*
  521.        * record that file has been modified and adjust cursors,
  522.        * file start and end pointers as needed.
  523.        */
  524.       check_virtual_col( win, rcol, win->ccol );
  525.       win->file_info->modified = TRUE;
  526.       if (mode.word_wrap) {
  527.          g_status.command = WordWrap;
  528.          word_wrap( win );
  529.       }
  530.    }
  531. }
  532.  
  533.  
  534. /*
  535.  * Name:    join_line
  536.  * Purpose: To join current line and line below at cursor
  537.  * Date:    June 5, 1991
  538.  * Passed:  window:   information allowing access to the current window
  539.  * Notes:   trunc the line then join with line below if it exists
  540.  */
  541. void join_line( WINDOW *window )
  542. {
  543. register int len;   /* length of current line */
  544. char *source;       /* source for block move to delete word */
  545. char *dest;         /* destination for block move */
  546. text_ptr p;         /* next line in file */
  547. int pad, i;         /* padding spaces required */
  548. int cr;             /* does current line end with carriage return? */
  549. register WINDOW *win;  /* put window pointer in a register */
  550.  
  551.    win = window;
  552.    if (win->rline > win->file_info->length || *win->cursor == CONTROL_Z)
  553.       return;
  554.  
  555.    win->cursor = cpf( win->cursor );
  556.    load_undo_buffer( win->cursor );
  557.    copy_line( win->cursor, win->bottom_line );
  558.    if (win->rcol < (len = linelen( g_status.line_buff ))) {
  559.       /*
  560.        * delete rest of line
  561.        */
  562.       dest = g_status.line_buff + win->rcol;
  563.  
  564.       if (g_status.line_buff[len] == '\n')
  565.          *dest++ = '\n';
  566.  
  567.       *dest = CONTROL_Z;
  568.       un_copy_line( win->cursor, win, FALSE );
  569.       win->file_info->dirty = GLOBAL;
  570.    }
  571.    /*
  572.     * we need to combine with the next line, if any
  573.     */
  574.    if ((p = find_next( win->cursor )) != NULL && *p != CONTROL_Z) {
  575.       /*
  576.        * add padding if required
  577.        */
  578.       len = linelen( g_status.line_buff );
  579.       cr  = g_status.line_buff[len] == '\n' ? 1 : 0;
  580.       pad = win->rcol > len ? win->rcol - len : 0;
  581.  
  582.       /*
  583.        * check room to combine lines
  584.        */
  585.       if (len + pad + cr + linelen( p ) >= g_display.line_length)
  586.          error( WARNING, win->bottom_line, "cannot combine lines" );
  587.       else {
  588.  
  589.          /*
  590.           * do the move
  591.           */
  592.          source = g_status.line_buff + win->rcol - pad;
  593.          dest   = source + pad;
  594.          memmove( dest, source, len + pad - win->rcol + 1 + cr );
  595.  
  596.          /*
  597.           * insert the padding
  598.           */
  599.          while (pad--)
  600.             *source++ = ' ';
  601.  
  602.          /*
  603.           * remove the \n separating the two lines.
  604.           */
  605.          i = 0;
  606.          if (*source == '\n') {
  607.             *source = CONTROL_Z;
  608.             i = -1;
  609.          }
  610.          g_status.copied = TRUE;
  611.          un_copy_line( win->cursor, win, FALSE );
  612.          adjust_windows_cursor( win, i );
  613.          --win->file_info->length;
  614.          restore_marked_block( win, -1 );
  615.          show_size( win );
  616.          win->file_info->dirty = GLOBAL;
  617.       }
  618.    }
  619. }
  620.  
  621.  
  622. /*
  623.  * Name:    word_delete
  624.  * Purpose: To delete from the cursor to the start of the next word.
  625.  * Date:    September 1, 1991
  626.  * Passed:  window:   information allowing access to the current window
  627.  * Notes:   If the cursor is at the right of the line, then combine the
  628.  *           current line with the next one, leaving the cursor where it
  629.  *           is.
  630.  *          If the cursor is on an alphanumeric character, then all
  631.  *           subsequent alphanumeric characters are deleted.
  632.  *          If the cursor is on a space, then all subsequent spaces
  633.  *           are deleted.
  634.  *          If the cursor is on a punctuation character, then all
  635.  *           subsequent punctuation characters are deleted.
  636.  */
  637. void word_delete( WINDOW *window )
  638. {
  639. int len;            /* length of current line */
  640. register int start; /* column that next word starts in */
  641. char *source;       /* source for block move to delete word */
  642. char *dest;         /* destination for block move */
  643. int alpha;          /* is the cursor char alphanumeric? */
  644. text_ptr p;
  645. register WINDOW *win;  /* put window pointer in a register */
  646.  
  647.    win = window;
  648.    if (win->rline > win->file_info->length || *win->cursor == CONTROL_Z)
  649.       return;
  650.    win->cursor = cpf( win->cursor );
  651.    copy_line( win->cursor, win->bottom_line );
  652.    if (win->rcol >= (len = linelen( g_status.line_buff ))) {
  653.       join_line( win );
  654.       p = win->cursor + win->rcol;
  655.       if (*p != CONTROL_Z)
  656.          load_undo_buffer( p );
  657.    } else {
  658.       /*
  659.        * normal word delete
  660.        *
  661.        * find the start of the next word
  662.        */
  663.       start = win->rcol;
  664.       if (g_status.line_buff[start] == ' ') {
  665.          /*
  666.           * the cursor was on a space, so eat all consecutive spaces
  667.           *  from the cursor onwards.
  668.           */
  669.          while (g_status.line_buff[start] == ' ')
  670.             ++start;
  671.       } else {
  672.          /*
  673.           * eat all consecutive characters in the same class (spaces
  674.           *  are considered to be in the same class as the cursor
  675.           *  character)
  676.           */
  677.          alpha = myisalnum( g_status.line_buff[start++] );
  678.          while (start < len) {
  679.             if (g_status.line_buff[start] == ' ')
  680.                 /*
  681.                  * the next character that is not a space will
  682.                  *  end the delete
  683.                  */
  684.                 alpha = -1;
  685.             else if (alpha != myisalnum( g_status.line_buff[start] )) {
  686.                 if (g_status.line_buff[start] != ' ')
  687.                     break;
  688.             }
  689.             ++start;
  690.          }
  691.       }
  692.  
  693.       /*
  694.        * move text to delete word
  695.        */
  696.       source = g_status.line_buff + start;
  697.       dest = g_status.line_buff + win->rcol;
  698.       memmove( dest, source, len-start+2 );
  699.       win->file_info->modified = TRUE;
  700.       win->file_info->dirty = GLOBAL;
  701.       if (g_status.command == WordDelete)
  702.          show_changed_line( win );
  703.    }
  704. }
  705.  
  706.  
  707. /*
  708.  * Name:    dup_line
  709.  * Purpose: Duplicate current line
  710.  * Date:    June 5, 1991
  711.  * Passed:  window:   information allowing access to the current window
  712.  * Notes:   cursor stays on current line
  713.  */
  714. void dup_line( WINDOW *window )
  715. {
  716. register int len;       /* length of current line */
  717. long number;
  718. text_ptr d, s;
  719. register WINDOW *win;  /* put window pointer in a register */
  720.  
  721.    win = window;
  722.    if (win->rline > win->file_info->length)
  723.       return;
  724.    win->cursor = cpf( win->cursor );
  725.  
  726.    un_copy_line( win->cursor, win, TRUE );
  727.    /*
  728.     * don't dup the ^Z or a NULL line
  729.     */
  730.    if (*win->cursor != CONTROL_Z && (d=find_next( win->cursor )) != NULL) {
  731.  
  732.       /*
  733.        * don't use buffers to dup the line.  use hw_move to make space and
  734.        * copy current line at same time.  d is set to beginning of next line.
  735.        */
  736.       s = win->cursor;
  737.       len = linelen( s );
  738.       if (s[len] == '\n')
  739.          ++len;
  740.       number = ptoul( g_status.end_mem ) - ptoul( s );
  741.       hw_move( d, s, number );
  742.       ++win->file_info->length;
  743.       g_status.end_mem = addltop( len, g_status.end_mem );
  744.       adjust_start_end( win->file_info, len );
  745.       addorsub_all_cursors( win, len );
  746.       adjust_windows_cursor( win, 1 );
  747.  
  748.       /*
  749.        * if current line is the bottom line, we can't see the dup line because
  750.        * cursor doesn't move and dup line is added after current line.
  751.        */
  752.       if  (win->cline != win->bottom_line)
  753.          my_scroll_down( win );
  754.       win->file_info->dirty = NOT_LOCAL;
  755.  
  756.       /*
  757.        * record that file has been modified
  758.        */
  759.       win->file_info->modified = TRUE;
  760.       restore_marked_block( win, 1 );
  761.       show_size( win );
  762.       show_avail_mem( );
  763.    } else
  764.       error( WARNING, win->bottom_line, "cannot duplicate line" );
  765. }
  766.  
  767.  
  768. /*
  769.  * Name:    back_space
  770.  * Purpose: To delete the character to the left of the cursor.
  771.  * Date:    June 5, 1991
  772.  * Passed:  window:   information allowing access to the current window
  773.  * Notes:   If the cursor is at the left of the line, then combine the
  774.  *           current line with the previous one.
  775.  *          If in indent mode, and the cursor is on the first non-blank
  776.  *           character of the line, then match the indentation of an
  777.  *           earlier line.
  778.  */
  779. void back_space( WINDOW *window )
  780. {
  781. int len;            /* length of the current line */
  782. char *source;       /* source of block move to delete character */
  783. char *dest;         /* destination of block move */
  784. text_ptr p;         /* previous line in file */
  785. int plen;           /* length of previous line */
  786. int del_count;      /* number of characters to delete */
  787. int pos;            /* the position of the first non-blank char */
  788. register int rcol;
  789. int ccol;
  790. int old_bcol;
  791. register WINDOW *win;  /* put window pointer in a register */
  792.  
  793.    win = window;
  794.    if (win->rline > win->file_info->length || *win->cursor == CONTROL_Z)
  795.       return;
  796.    win->cursor = cpf( win->cursor );
  797.    copy_line( win->cursor, win->bottom_line );
  798.    len = linelen( g_status.line_buff );
  799.    rcol = win->rcol;
  800.    ccol = win->ccol;
  801.    old_bcol = win->bcol;
  802.    if (rcol == 0) {
  803.       /*
  804.        * combine this line with the previous, if any
  805.        */
  806.       win->cursor = cpb( win->cursor );
  807.       if ((p = find_prev( win->cursor )) != NULL) {
  808.          if (len + 2 + (plen = linelen( p )) >= g_display.line_length) {
  809.             error( WARNING, win->bottom_line, "cannot combine lines" );
  810.             return;
  811.          }
  812.  
  813.          un_copy_line( win->cursor, win, TRUE );
  814.          copy_line( p, win->bottom_line );
  815.          load_undo_buffer( p );
  816.          g_status.line_buff[plen] = CONTROL_Z;
  817.          win->cursor = p;
  818.          un_copy_line( win->cursor, win, FALSE );
  819.  
  820.          /*
  821.           * make sure cursor stays on the screen, at the end of the
  822.           *  previous line
  823.           */
  824.          if (win->cline > win->top_line + win->ruler)
  825.             --win->cline;
  826.          --win->rline;
  827.          rcol = plen;
  828.          ccol = rcol - win->bcol;
  829.          --win->file_info->length;
  830.          restore_marked_block( win, -1 );
  831.          adjust_windows_cursor( win, -1 );
  832.          show_size( win );
  833.          check_virtual_col( win, rcol, ccol );
  834.          win->file_info->dirty = GLOBAL;
  835.          make_ruler( win );
  836.          show_ruler( win );
  837.       }
  838.    } else {
  839.       /*
  840.        * normal delete
  841.        *
  842.        * find out how much to delete (depends on indent mode)
  843.        */
  844.       del_count = 1;   /* the default */
  845.       if (mode.indent) {
  846.          /*
  847.           * indent only happens if the cursor is on the first
  848.           *  non-blank character of the line
  849.           */
  850.          if ((pos = first_non_blank( g_status.line_buff )) == rcol
  851.                     || g_status.line_buff[pos] == '\n'
  852.                     || g_status.line_buff[pos] == CONTROL_Z) {
  853.             /*
  854.              * now work out how much to indent
  855.              */
  856.             p = cpb( win->cursor );
  857.             for (p=find_prev( p ); p != NULL; p=find_prev( p )) {
  858.                if ((plen=first_non_blank( p )) < rcol && *(p+plen)!='\n') {
  859.                   /*
  860.                    * found the line to match
  861.                    */
  862.                   del_count = rcol - plen;
  863.                   break;
  864.                }
  865.             }
  866.          }
  867.       }
  868.  
  869.       /*
  870.        * move text to delete char(s), unless no chars actually there
  871.        */
  872.       if (rcol - del_count < len) {
  873.          dest = g_status.line_buff + rcol - del_count;
  874.          if (rcol > len) {
  875.             source = g_status.line_buff + len;
  876.             len = 2;
  877.          } else {
  878.             source = g_status.line_buff + rcol;
  879.             len = len - rcol + 2;
  880.          }
  881.          memmove( dest, source, len );
  882.       }
  883.       rcol -= del_count;
  884.       ccol -= del_count;
  885.       win->file_info->dirty = NOT_LOCAL;
  886.       show_ruler_char( win );
  887.       show_changed_line( win );
  888.       check_virtual_col( win, rcol, ccol );
  889.       if (!win->file_info->dirty)
  890.          show_curl_line( win );
  891.       if (old_bcol != win->bcol) {
  892.          make_ruler( win );
  893.          show_ruler( win );
  894.       }
  895.    }
  896.    win->file_info->modified = TRUE;
  897. }
  898.  
  899.  
  900. /*
  901.  * Name:    line_kill
  902.  * Purpose: To delete the line the cursor is on.
  903.  * Date:    June 5, 1991
  904.  * Passed:  window:   information allowing access to the current window
  905.  * Notes:   If *window->cursor is pointing to CONTROL_Z then do not do a
  906.  *          line kill (can't kill a NULL line).
  907.  */
  908. void line_kill( WINDOW *window )
  909. {
  910. int i = 0;
  911. text_ptr s;         /* next line in file */
  912. register WINDOW *win;  /* put window pointer in a register */
  913.  
  914.    win = window;
  915.    if (win->file_info->length > 0  && *win->cursor != CONTROL_Z) {
  916.       load_undo_buffer( g_status.copied ? g_status.line_buff : win->cursor);
  917.       g_status.copied = TRUE;
  918.       g_status.line_buff[0] = CONTROL_Z;
  919.       s = win->cursor = cpf( win->cursor );
  920.       /*
  921.        * if line to delete has \n at end of line then decrement file length.
  922.        */
  923.       if (*(s + linelen( s )) == '\n') {
  924.          --win->file_info->length;
  925.          --i;
  926.       }
  927.       un_copy_line( s, win, FALSE );
  928.       win->file_info->dirty = NOT_LOCAL;
  929.  
  930.       /*
  931.        * move all cursors one according to i, restore begin and end block
  932.        */
  933.       adjust_windows_cursor( win, i );
  934.       restore_marked_block( win, i );
  935.  
  936.       /*
  937.        * we are not doing a GLOBAL update, so update current window here
  938.        */
  939.       if (win->file_info->dirty == NOT_LOCAL)
  940.          my_scroll_down( win );
  941.       show_size( win );
  942.    }
  943. }
  944.  
  945.  
  946. /*
  947.  * Name:    char_del_under
  948.  * Purpose: To delete the character under the cursor.
  949.  * Date:    June 5, 1991
  950.  * Passed:  window:   information allowing access to the current window
  951.  * Notes:   If the cursor is beyond the end of the line, then this
  952.  *           command is ignored.
  953.  *          DeleteChar and StreamDeleteChar use this function.
  954.  */
  955. void char_del_under( WINDOW *window )
  956. {
  957. char *source;    /* source of block move to delete character */
  958. register int len;
  959. register WINDOW *win;  /* put window pointer in a register */
  960.  
  961.    win = window;
  962.    if (win->rline > win->file_info->length || *win->cursor == CONTROL_Z)
  963.       return;
  964.    copy_line( win->cursor, win->bottom_line );
  965.    if (win->rcol < (len = linelen( g_status.line_buff ))) {
  966.       /*
  967.        * move text to delete using buffer
  968.        */
  969.       source = g_status.line_buff + win->rcol + 1;
  970.       memmove( source-1, source, len - win->rcol + 2 );
  971.       win->file_info->dirty    = GLOBAL;
  972.       win->file_info->modified = TRUE;
  973.       show_changed_line( win );
  974.    } else if (g_status.command == StreamDeleteChar)
  975.       join_line( win );
  976. }
  977.  
  978.  
  979. /*
  980.  * Name:    eol_kill
  981.  * Purpose: To delete everything from the cursor to the end of the line.
  982.  * Date:    June 5, 1991
  983.  * Passed:  window:   information allowing access to the current window
  984.  * Notes:   If the cursor is beyond the end of the line, then this
  985.  *           command is ignored.
  986.  */
  987. void eol_kill( WINDOW *window )
  988. {
  989. register char *dest;  /* the start of the delete area */
  990. register WINDOW *win;  /* put window pointer in a register */
  991.  
  992.    win = window;
  993.    if (win->rline > win->file_info->length || *win->cursor == CONTROL_Z)
  994.       return;
  995.    copy_line( win->cursor, win->bottom_line );
  996.    load_undo_buffer( g_status.line_buff );
  997.    if (win->rcol < linelen( g_status.line_buff )) {
  998.       /*
  999.        * truncate to delete rest of line
  1000.        */
  1001.       dest = g_status.line_buff + win->rcol;
  1002.       *dest++ = '\n';
  1003.       *dest = CONTROL_Z;
  1004.       win->file_info->dirty = GLOBAL;
  1005.       show_changed_line( win );
  1006.    }
  1007. }
  1008.  
  1009.  
  1010. /*
  1011.  * Name:    undo_line
  1012.  * Purpose: To retrieve unaltered line if possible.
  1013.  * Date:    June 5, 1991
  1014.  * Passed:  window:   information allowing access to the current window
  1015.  * Notes:   Changes are made to the line buffer so the underlying text has
  1016.  *          not changed.  Put the unchanged line from the file into the
  1017.  *          line buffer and display it.
  1018.  */
  1019. void undo_line( WINDOW *window )
  1020. {
  1021. register WINDOW *win;  /* put window pointer in a register */
  1022.  
  1023.    win = window;
  1024.    if (win->rline > win->file_info->length)
  1025.       return;
  1026.    if (g_status.copied) {
  1027.       g_status.copied = FALSE;
  1028.       copy_line( win->cursor, win->bottom_line );
  1029.       win->file_info->dirty = GLOBAL;
  1030.       show_changed_line( win );
  1031.    }
  1032. }
  1033.  
  1034.  
  1035. /*
  1036.  * Name:    undo
  1037.  * Purpose: To retrieve (pop) a line from the undo stack
  1038.  * Date:    September 26, 1991
  1039.  * Passed:  window:   information allowing access to the current window
  1040.  * Notes:   Insert an empty line into the file then pop the line in the undo
  1041.  *          stack.  When we pop line 0, there are no more lines on the stack.
  1042.  *          Set the stack pointer to -1 to indicate an empty stack.
  1043.  */
  1044. void undo( WINDOW *window )
  1045. {
  1046. char *source;
  1047. char *undo_line;
  1048. int len;
  1049. int head;
  1050. register WINDOW *win;  /* put window pointer in a register */
  1051.  
  1052.    if (g_status.undo_head < 0)
  1053.       return;
  1054.    --g_status.undo_head;
  1055.    win = window;
  1056.    win->cursor = cpf( win->cursor );
  1057.    un_copy_line( win->cursor, win, TRUE );
  1058.    copy_line( win->cursor, win->bottom_line );
  1059.  
  1060.    source = g_status.line_buff;
  1061.    /*
  1062.     *  make room for '\n'.  then, after we un_copy the g_status.line_buff, we
  1063.     *  have added a line to the file.
  1064.     */
  1065.    memmove( source+1, source, linelen( source )+2 );
  1066.    *source = '\n';
  1067.    un_copy_line( win->cursor, win, TRUE );
  1068.  
  1069.    /*
  1070.     *  ajust cursors in other windows opened to the same file.
  1071.     */
  1072.    adjust_windows_cursor( win, 1 );
  1073.  
  1074.    /*
  1075.     * copy the line in the top of the stack to the line_buffer.  then, copy
  1076.     * the line buffer to the file.
  1077.     */
  1078.    g_status.copied = TRUE;
  1079.    head = g_status.undo_head;
  1080.    undo_line = &g_status.undo_buffer[head][0];
  1081.    len = find_CONTROL_Z( undo_line ) + 1;
  1082.    memcpy( source, undo_line, len );
  1083.    un_copy_line( win->cursor, win, TRUE );
  1084.  
  1085.    /*
  1086.     * we have now undeleted a line.  increment the file length and display
  1087.     * it.
  1088.     */
  1089.    win->file_info->length++;
  1090.    win->file_info->dirty = GLOBAL;
  1091.    show_size( win );
  1092.  
  1093.    /*
  1094.     * if g_status.undo_head == 0, we just popped the last line in the stack.
  1095.     * set the stack pointer to -1 to show there are no more lines.
  1096.     */
  1097.    if (g_status.undo_head == 0)
  1098.       g_status.undo_head = -1;
  1099. }
  1100.  
  1101.  
  1102. /*
  1103.  * Name:    beg_next_line
  1104.  * Purpose: To move the cursor to the beginning of the next line.
  1105.  * Date:    October 4, 1991
  1106.  * Passed:  window:   information allowing access to the current window
  1107.  */
  1108. void beg_next_line( WINDOW *window )
  1109. {
  1110.    window->rcol = 0;
  1111.    check_virtual_col( window, window->rcol, window->ccol );
  1112.    move_down( window );
  1113.    make_ruler( window );
  1114.    show_ruler( window );
  1115. }
  1116.  
  1117.  
  1118. /*
  1119.  * Name:    next_line
  1120.  * Purpose: To move the cursor to the first character of the next line.
  1121.  * Date:    October 4, 1991
  1122.  * Passed:  window:   information allowing access to the current window
  1123.  */
  1124. void next_line( WINDOW *window )
  1125. {
  1126. register int rcol;
  1127. register WINDOW *win;  /* put window pointer in a register */
  1128.  
  1129.    win = window;
  1130.    move_down( win );
  1131.    rcol = first_non_blank( win->cursor );
  1132.    if (win->cursor[rcol] == '\n')
  1133.       rcol = 0;
  1134.    check_virtual_col( win, rcol, win->ccol );
  1135.    make_ruler( win );
  1136.    show_ruler( win );
  1137. }
  1138.  
  1139.  
  1140. /*
  1141.  * Name:    home
  1142.  * Purpose: To move the cursor to the left of the current line.
  1143.  * Date:    June 5, 1991
  1144.  * Passed:  window:   information allowing access to the current window
  1145.  * Notes:   this routine is made a little more complicated with cursor sync.
  1146.  *            if the g_status.copied flag is set we need to see from what file
  1147.  *            the line_buff was copied.
  1148.  */
  1149. void home( WINDOW *window )
  1150. {
  1151. register int rcol;
  1152. register WINDOW *win;  /* put window pointer in a register */
  1153.  
  1154.    win = window;
  1155.    if (g_status.copied && win->file_info == g_status.current_window->file_info){
  1156.       rcol = first_non_blank( g_status.line_buff );
  1157.       if (g_status.line_buff[rcol] == '\n')
  1158.          rcol = 0;
  1159.    } else {
  1160.       win->cursor = cpf( win->cursor );
  1161.       rcol = first_non_blank( win->cursor );
  1162.       if (win->cursor[rcol] == '\n')
  1163.          rcol = 0;
  1164.    }
  1165.    if (win->rcol == rcol)
  1166.       rcol = 0;
  1167.    check_virtual_col( win, rcol, win->ccol );
  1168.    sync( win );
  1169.    make_ruler( win );
  1170.    show_ruler( win );
  1171. }
  1172.  
  1173.  
  1174. /*
  1175.  * Name:    goto_right
  1176.  * Purpose: To move the cursor to the right of the current line.
  1177.  * Date:    June 5, 1991
  1178.  * Passed:  window:   information allowing access to the current window
  1179.  * Notes:   this routine is made a little more complicated with cursor sync.
  1180.  *            if the g_status.copied flag is set we need to see from what file
  1181.  *            the line_buff was copied.
  1182.  */
  1183. void goto_right( WINDOW *window )
  1184. {
  1185. register int rcol;
  1186. register WINDOW *win;  /* put window pointer in a register */
  1187.  
  1188.    win = window;
  1189.    if (g_status.copied) {
  1190.       if (win->file_info == g_status.current_window->file_info)
  1191.          rcol = linelen( g_status.line_buff );
  1192.       else
  1193.          rcol = linelen( win->cursor );
  1194.    } else
  1195.       rcol = linelen( win->cursor );
  1196.    win->ccol = win->start_col + rcol - win->bcol;
  1197.    check_virtual_col( win, rcol, win->ccol );
  1198.    sync( win );
  1199.    make_ruler( win );
  1200.    show_ruler( win );
  1201. }
  1202.  
  1203.  
  1204. /*
  1205.  * Name:    goto_top
  1206.  * Purpose: To move the cursor to the top of the current window.
  1207.  * Date:    June 5, 1991
  1208.  * Passed:  window:   information allowing access to the current window
  1209.  * Notes:   If the start of the file occurs before the top of the window,
  1210.  *           then the start of the file is moved to the top of the window.
  1211.  */
  1212. void goto_top( WINDOW *window )
  1213. {
  1214. text_ptr cursor;  /* anticipated cursor line */
  1215. register WINDOW *win;  /* put window pointer in a register */
  1216.  
  1217.    win = window;
  1218.    un_copy_line( win->cursor, win, TRUE );
  1219.    update_line( win );
  1220.    win->cursor = cpb( win->cursor );
  1221.    for (; win->cline > win->top_line+win->ruler; win->cline--,win->rline--) {
  1222.       if ((cursor = find_prev( win->cursor )) == NULL)
  1223.          break;
  1224.       win->cursor = cursor;
  1225.    }
  1226.    show_curl_line( win );
  1227.    sync( win );
  1228. }
  1229.  
  1230.  
  1231. /*
  1232.  * Name:    goto_bottom
  1233.  * Purpose: To move the cursor to the bottom of the current window.
  1234.  * Date:    June 5, 1991
  1235.  * Passed:  window:   information allowing access to the current window
  1236.  */
  1237. void goto_bottom( WINDOW *window )
  1238. {
  1239. text_ptr cursor;
  1240. register WINDOW *win;  /* put window pointer in a register */
  1241.  
  1242.    win = window;
  1243.    un_copy_line( win->cursor, win, TRUE );
  1244.    update_line( win );
  1245.    win->cursor = cpf( win->cursor );
  1246.    for (; win->cline < win->bottom_line; win->cline++,win->rline++) {
  1247.       if ((cursor = find_next( win->cursor )) == NULL)
  1248.          break;
  1249.       win->cursor = cursor;
  1250.    }
  1251.    show_curl_line( win );
  1252.    sync( win );
  1253. }
  1254.  
  1255.  
  1256. /*
  1257.  * Name:    set_tabstop
  1258.  * Purpose: To set the current interval between tab stops
  1259.  * Date:    October 1, 1989
  1260.  * Notes:   Tab interval must be reasonable, and this function will
  1261.  *           not allow tabs more than MAX_COLS / 2.
  1262.  */
  1263. void set_tabstop( WINDOW *window )
  1264. {
  1265. char num_str[MAX_COLS];  /* tab interval as a character string */
  1266. int tab;                 /* new tab interval */
  1267. register int rc;
  1268.  
  1269.    itoa( mode.tab_size, num_str, 10 );
  1270.    rc = get_name( "Tab interval: ", window->bottom_line, num_str,
  1271.                   g_display.message_color );
  1272.    if (rc == OK) {
  1273.       tab = atoi( num_str );
  1274.       if (tab < MAX_COLS/2)
  1275.          mode.tab_size = tab;
  1276.       else
  1277.          error( WARNING, window->bottom_line, "tab size too long" );
  1278.    }
  1279. }
  1280.  
  1281.  
  1282. /*
  1283.  * Name:    show_line_col
  1284.  * Purpose: show current real line and column of current cursor position
  1285.  * Date:    June 5, 1991
  1286.  * Passed:  window:   information allowing access to the current window
  1287.  * Notes:   Blank old position and display new position.  current line and
  1288.  *          column may take up to 11 columns, which allows the display of
  1289.  *          999 columns and 9,999,999 lines.
  1290.  */
  1291. void show_line_col( WINDOW *window )
  1292. {
  1293. int i;
  1294. register int k;
  1295. char line_col[20], num[10];
  1296.  
  1297.    /*
  1298.     * blank out current line:column position.
  1299.     */
  1300.    memset( line_col, ' ', 12 );
  1301.    line_col[12] = '\0';
  1302.  
  1303.    /*
  1304.     * convert column to ascii and store in display buffer.
  1305.     */
  1306.    itoa( window->rcol+1, num, 10 );
  1307.    i = strlen( num ) - 1;
  1308.    for (k=11; i>=0; i--, k--)
  1309.       line_col[k] = num[i];
  1310.  
  1311.    /*
  1312.     * put in colon to separate line and column
  1313.     */
  1314.    line_col[k--] = ':';
  1315.  
  1316.    /*
  1317.     * convert line to ascii and store in display buffer.
  1318.     */
  1319.    ltoa( window->rline, num, 10 );
  1320.    i = strlen( num ) - 1;
  1321.    for (; i>=0; i--, k--)
  1322.       line_col[k] = num[i];
  1323.  
  1324.    /*
  1325.     * find line to start line:column display then output
  1326.     */
  1327.    s_output( line_col, window->top_line-1, window->end_col-11,
  1328.              g_display.head_color );
  1329.    show_asterisk( window );
  1330. }
  1331.  
  1332.  
  1333. /*
  1334.  * Name:    show_asterisk
  1335.  * Purpose: give user an indication if file is dirty
  1336.  * Date:    September 16, 1991
  1337.  * Passed:  window:   current window info
  1338.  */
  1339. void show_asterisk( WINDOW *window )
  1340. {
  1341.    c_output( window->file_info->modified ? '*' : ' ', window->start_col+4,
  1342.              window->top_line-1, g_display.head_color );
  1343. }
  1344.  
  1345.  
  1346. /*
  1347.  * Name:    toggle_overwrite
  1348.  * Purpose: toggle overwrite-insert mode
  1349.  * Date:    September 16, 1991
  1350.  * Passed:  arg_filler:  argument to satify function prototype
  1351.  */
  1352. void toggle_overwrite( WINDOW *arg_filler )
  1353. {
  1354.    mode.insert = !mode.insert;
  1355.    show_insert_mode( );
  1356.    set_cursor_size( mode.insert ? g_display.insert_cursor :
  1357.                     g_display.overw_cursor );
  1358. }
  1359.  
  1360.  
  1361. /*
  1362.  * Name:    toggle_indent
  1363.  * Purpose: toggle indent mode
  1364.  * Date:    September 16, 1991
  1365.  * Passed:  arg_filler:  argument to satify function prototype
  1366.  */
  1367. void toggle_indent( WINDOW *arg_filler )
  1368. {
  1369.    mode.indent = !mode.indent;
  1370.    show_indent_mode( );
  1371. }
  1372.  
  1373.  
  1374. /*
  1375.  * Name:    set_left_margin
  1376.  * Purpose: set left margin for word wrap
  1377.  * Date:    November 27, 1991
  1378.  * Passed:  window
  1379.  */
  1380. void set_left_margin( WINDOW *window )
  1381. {
  1382. register int rc;
  1383. char temp[80];
  1384.  
  1385.    itoa( mode.left_margin + 1, temp, 10 );
  1386.    rc = get_name("Enter new left margin (must be less than right margin) : ",
  1387.                    window->bottom_line, temp, g_display.message_color );
  1388.    if (rc == OK) {
  1389.       rc = atoi( temp ) - 1;
  1390.       if (rc < 0 || rc >= mode.right_margin)
  1391.          error( WARNING, window->bottom_line, "Left margin out of range." );
  1392.       else {
  1393.          mode.left_margin = rc;
  1394.          show_all_rulers( );
  1395.       }
  1396.    }
  1397. }
  1398.  
  1399.  
  1400. /*
  1401.  * Name:    set_right_margin
  1402.  * Purpose: set right margin for word wrap
  1403.  * Date:    November 27, 1991
  1404.  * Passed:  window
  1405.  */
  1406. void set_right_margin( WINDOW *window )
  1407. {
  1408. register int rc;
  1409. char temp[80];
  1410.  
  1411.    itoa( mode.right_margin + 1, temp, 10 );
  1412.    rc = get_name("Enter new right margin (must be greater than left margin) : ",
  1413.                    window->bottom_line, temp, g_display.message_color );
  1414.    if (rc == OK) {
  1415.       rc = atoi( temp ) - 1;
  1416.       if (rc <= mode.left_margin || rc > MAX_LINE_LENGTH)
  1417.          error( WARNING, window->bottom_line, "Right margin out of range." );
  1418.       else {
  1419.          mode.right_margin = rc;
  1420.          show_all_rulers( );
  1421.       }
  1422.    }
  1423. }
  1424.  
  1425.  
  1426. /*
  1427.  * Name:    set_paragraph_margin
  1428.  * Purpose: set column to begin paragraph
  1429.  * Date:    November 27, 1991
  1430.  * Passed:  window
  1431.  * Notes:   paragraph may be indented, flush, or offset.
  1432.  */
  1433. void set_paragraph_margin( WINDOW *window )
  1434. {
  1435. register int rc;
  1436. char temp[80];
  1437.  
  1438.    itoa( mode.parg_margin + 1, temp, 10 );
  1439.    rc = get_name("Enter paragraph margin (must be less than right margin) : ",
  1440.                    window->bottom_line, temp, g_display.message_color );
  1441.    if (rc == OK) {
  1442.       rc = atoi( temp ) - 1;
  1443.       if (rc < 0 || rc >= mode.right_margin)
  1444.          error( WARNING, window->bottom_line, "Paragraph margin out of range.");
  1445.       else {
  1446.          mode.parg_margin = rc;
  1447.          show_all_rulers( );
  1448.       }
  1449.    }
  1450. }
  1451.  
  1452.  
  1453. /*
  1454.  * Name:    toggle_crlf
  1455.  * Purpose: toggle crlf mode
  1456.  * Date:    November 27, 1991
  1457.  * Passed:  arg_filler:  argument to satify function prototype
  1458.  */
  1459. void toggle_crlf( WINDOW *arg_filler )
  1460. {
  1461.    mode.crlf = (mode.crlf == CRLF) ? LF : CRLF;
  1462.    show_crlf_mode( );
  1463. }
  1464.  
  1465.  
  1466. /*
  1467.  * Name:    toggle_ww
  1468.  * Purpose: toggle word wrap mode
  1469.  * Date:    November 27, 1991
  1470.  * Passed:  arg_filler:  argument to satify function prototype
  1471.  */
  1472. void toggle_ww( WINDOW *arg_filler )
  1473. {
  1474.    mode.word_wrap = !mode.word_wrap;
  1475.    show_wordwrap_mode( );
  1476. }
  1477.  
  1478.  
  1479. /*
  1480.  * Name:    toggle_trailing
  1481.  * Purpose: toggle eleminating trainling space at eol
  1482.  * Date:    November 25, 1991
  1483.  * Passed:  arg_filler:  argument to satify function prototype
  1484.  */
  1485. void toggle_trailing( WINDOW *arg_filler )
  1486. {
  1487.    mode.trailing = !mode.trailing;
  1488.    show_trailing( );
  1489. }
  1490.  
  1491.  
  1492. /*
  1493.  * Name:    toggle_z
  1494.  * Purpose: toggle writing control z at eof
  1495.  * Date:    November 25, 1991
  1496.  * Passed:  arg_filler:  argument to satify function prototype
  1497.  */
  1498. void toggle_z( WINDOW *arg_filler )
  1499. {
  1500.    mode.control_z = !mode.control_z;
  1501.    show_control_z( );
  1502. }
  1503.  
  1504.  
  1505. /*
  1506.  * Name:    toggle_eol
  1507.  * Purpose: toggle writing eol character at eol
  1508.  * Date:    November 25, 1991
  1509.  * Passed:  arg_filler:  argument to satify function prototype
  1510.  */
  1511. void toggle_eol( WINDOW *arg_filler )
  1512. {
  1513. register file_infos *file;
  1514.  
  1515.    mode.show_eol = !mode.show_eol;
  1516.    for (file=g_status.file_list; file != NULL; file=file->next)
  1517.       file->dirty = GLOBAL;
  1518. }
  1519.  
  1520.  
  1521. /*
  1522.  * Name:    toggle_search_case
  1523.  * Purpose: toggle search case
  1524.  * Date:    September 16, 1991
  1525.  * Passed:  arg_filler:  argument to satify function prototype
  1526.  */
  1527. void toggle_search_case( WINDOW *arg_filler )
  1528. {
  1529.    bm.search_case = (bm.search_case == IGNORE) ? MATCH : IGNORE;
  1530.    show_search_case( );
  1531.    if (bm.search_defined == OK)
  1532.       build_boyer_array( );
  1533. }
  1534.  
  1535.  
  1536. /*
  1537.  * Name:    toggle_sync
  1538.  * Purpose: toggle sync mode
  1539.  * Date:    January 15, 1992
  1540.  * Passed:  arg_filler:  argument to satify function prototype
  1541.  */
  1542. void toggle_sync( WINDOW *arg_filler )
  1543. {
  1544.    mode.sync = !mode.sync;
  1545.    show_sync_mode( );
  1546. }
  1547.  
  1548.  
  1549. /*
  1550.  * Name:    toggle_ruler
  1551.  * Purpose: toggle ruler
  1552.  * Date:    March 5, 1992
  1553.  * Passed:  arg_filler:  argument to satify function prototype
  1554.  */
  1555. void toggle_ruler( WINDOW *arg_filler )
  1556. {
  1557. register WINDOW *wp;
  1558.  
  1559.    mode.ruler = !mode.ruler;
  1560.    wp = g_status.window_list;
  1561.    while (wp != NULL) {
  1562.       if (mode.ruler) {
  1563.          if (wp->bottom_line - wp->top_line >0) {
  1564.             if (wp->cline == wp->top_line)
  1565.                ++wp->cline;
  1566.             if (wp->cline > wp->bottom_line)
  1567.                wp->cline = wp->bottom_line;
  1568.             wp->ruler = TRUE;
  1569.          } else
  1570.             wp->ruler = FALSE;
  1571.       } else {
  1572.          if (wp->rline == ((wp->cline - wp->ruler) - (wp->top_line - 1)))
  1573.             --wp->cline;
  1574.          if (wp->cline < wp->top_line)
  1575.             wp->cline = wp->top_line;
  1576.          wp->ruler = FALSE;
  1577.       }
  1578.       make_ruler( wp );
  1579.       setup_window( wp );
  1580.       if (wp->visible)
  1581.          redraw_current_window( wp );
  1582.       wp = wp->next;
  1583.    }
  1584. }
  1585.  
  1586.  
  1587. /*
  1588.  * Name:    sync
  1589.  * Purpose: carry out cursor movements in all windows opened to the same file
  1590.  * Date:    January 15, 1992
  1591.  * Passed:  window
  1592.  * Notes:   switch sync semaphore when we do this so we don't get into a
  1593.  *          recursive loop.  All cursor movement commands un_copy_line before
  1594.  *          moving the cursor off the current line.   You MUST make certain
  1595.  *          that the current line is uncopied in the task routines before
  1596.  *          calling sync.
  1597.  */
  1598. void sync( WINDOW *window )
  1599. {
  1600. register WINDOW *wp;
  1601. register file_infos *fp;
  1602.  
  1603.    if (mode.sync && mode.sync_sem) {
  1604.       mode.sync_sem = FALSE;
  1605.       wp = g_status.window_list;
  1606.       while (wp != NULL) {
  1607.          if (wp->visible && wp != window) {
  1608.             (*do_it[g_status.command])( wp );
  1609.             show_line_col( wp );
  1610.             show_ruler_pointer( wp );
  1611.          }
  1612.          wp = wp->next;
  1613.       }
  1614.       mode.sync_sem = TRUE;
  1615.       fp = g_status.file_list;
  1616.       while (fp != NULL) {
  1617.          if (fp->dirty != FALSE)
  1618.             fp->dirty = GLOBAL;
  1619.          fp = fp->next;
  1620.       }
  1621.    }
  1622. }
  1623.  
  1624.  
  1625. /*
  1626.  * Name:    editor
  1627.  * Purpose: Set up the editor structures and display changes as needed.
  1628.  * Date:    June 5, 1991
  1629.  * Notes:   Master editor routine.
  1630.  */
  1631. void editor( )
  1632. {
  1633. char *name;  /* name of file to start editing */
  1634. register WINDOW *window;            /* current active window */
  1635. int c;
  1636.  
  1637.    /*
  1638.     * Check that user specified file to edit, if not offer help
  1639.     */
  1640.    if (g_status.argc > 1)
  1641.       c = edit_next_file( g_status.current_window );
  1642.    else {
  1643.       name = g_status.rw_name;
  1644.       *name = '\0';
  1645.       c = get_name( "File name to edit : ", g_display.nlines, name,
  1646.                       g_display.text_color );
  1647.       if (c == ERROR || *name == '\0')
  1648.          return;
  1649.       if (c == OK)
  1650.          c = attempt_edit_display( name, GLOBAL );
  1651.    }
  1652.    g_status.stop = c == OK ? FALSE : TRUE;
  1653.    if (c == OK)
  1654.       set_cursor_size( mode.insert ? g_display.insert_cursor :
  1655.                        g_display.overw_cursor );
  1656.  
  1657.    /*
  1658.     * main loop - keep updating the display and processing any commands
  1659.     *  while user has not pressed the stop key
  1660.     */
  1661.    for (; g_status.stop != TRUE;) {
  1662.       window = g_status.current_window;
  1663.       display_dirty_windows( window );
  1664.       ceh.flag = OK;
  1665.  
  1666.       /*
  1667.        * Get a key from the user.  Look up the function assigned to that key.
  1668.        * All regular text keys are assigned to function 0.  Text characters
  1669.        * are less than 0x100, decimal 256, which includes the ASCII and
  1670.        * extended ASCII character set.
  1671.        */
  1672.       g_status.key_pressed = getkey( );
  1673.       g_status.command = getfunc( g_status.key_pressed );
  1674.       if (g_status.wrapped) {
  1675.          g_status.wrapped = FALSE;
  1676.          show_search_message( CLR_SEARCH, g_display.mode_color );
  1677.       }
  1678.       if (g_status.command >= 0 && g_status.command < NUM_FUNCS) {
  1679.          record_keys( window->bottom_line );
  1680.          (*do_it[g_status.command])( window );
  1681.       }
  1682.    }
  1683.    cls( );
  1684.    xygoto( 0, 0 );
  1685. }
  1686.  
  1687.  
  1688. /*
  1689.  * Name:    display_dirty_windows
  1690.  * Purpose: Set up the editor structures and display changes as needed.
  1691.  * Date:    June 5, 1991
  1692.  * Notes:   Display all windows with dirty files.
  1693.  */
  1694. void display_dirty_windows( WINDOW *window )
  1695. {
  1696. register WINDOW *below;         /* window below current */
  1697. register WINDOW *above;         /* window above current */
  1698. file_infos *file;               /* temporary file structure */
  1699.  
  1700.    /*
  1701.     * update all windows that point to any file that has been changed
  1702.     */
  1703.    above = below = window;
  1704.    while (above->prev || below->next) {
  1705.       if (above->prev) {
  1706.          above = above->prev;
  1707.          if (above->visible) {
  1708.             file = above->file_info;
  1709.             if (file->dirty == GLOBAL || file->dirty == NOT_LOCAL) {
  1710.                display_current_window( above );
  1711.                show_size( above );
  1712.             }
  1713.             show_asterisk( above );
  1714.          }
  1715.       }
  1716.       if (below->next) {
  1717.          below = below->next;
  1718.          if (below->visible) {
  1719.             file = below->file_info;
  1720.             if (file->dirty == GLOBAL || file->dirty == NOT_LOCAL) {
  1721.                display_current_window( below );
  1722.                show_size( below );
  1723.             }
  1724.             show_asterisk( below );
  1725.          }
  1726.       }
  1727.    }
  1728.    file = window->file_info;
  1729.    if (file->dirty == LOCAL || file->dirty == GLOBAL)
  1730.       display_current_window( window );
  1731.    for (file=g_status.file_list; file != NULL; file=file->next)
  1732.       file->dirty = FALSE;
  1733.  
  1734.    /*
  1735.     * Set the cursor position at window->ccol, window->cline.  Show the
  1736.     * user where in the file the cursor is positioned.
  1737.     */
  1738.    xygoto( window->ccol, window->cline );
  1739.    show_line_col( window );
  1740.    show_ruler_pointer( window );
  1741. }
  1742.  
  1743.  
  1744. /*
  1745.  * Name:    play_back
  1746.  * Purpose: play back a series of keystrokes assigned to key
  1747.  * Date:    April 1, 1992
  1748.  */
  1749. void play_back( WINDOW *window )
  1750. {
  1751. int next;
  1752. int key;
  1753.  
  1754.    next = macro.first_stroke[g_status.key_pressed-256];
  1755.    key = macro.strokes[next].key;
  1756.    if (key != MAX_KEYS+1  &&  key != -1) {
  1757.       do {
  1758.          window = g_status.current_window;
  1759.          display_dirty_windows( window );
  1760.          ceh.flag = OK;
  1761.          g_status.key_pressed = macro.strokes[next].key;
  1762.          g_status.command = getfunc( g_status.key_pressed );
  1763.          if (g_status.wrapped) {
  1764.             g_status.wrapped = FALSE;
  1765.             show_search_message( CLR_SEARCH, g_display.mode_color );
  1766.          }
  1767.          if (g_status.command >= 0 && g_status.command < NUM_FUNCS)
  1768.              (*do_it[g_status.command])( window );
  1769.       } while ((next = macro.strokes[next].next) != -1);
  1770.    }
  1771. }
  1772.