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