home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / EDITOR / TDE120.ZIP / UTILS.C < prev    next >
Encoding:
C/C++ Source or Header  |  1991-10-05  |  51.0 KB  |  1,736 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 - miscellaneous utilities
  10.  * Purpose: This file contains miscellaneous functions that were required
  11.  *           in more than one of the other files, or were thought to be
  12.  *           likely to be used elsewhere in the future.
  13.  * File:    utils.c
  14.  * Author:  Douglas Thomson
  15.  * System:  this file is intended to be system-independent
  16.  * Date:    October 1, 1989
  17.  */
  18. /*********************  end of original comments   ********************/
  19.  
  20.  
  21. /*
  22.  * The utility routines have been EXTENSIVELY rewritten.  Update screens as
  23.  * needed.  Most times, only one line has changed.  Just show changed line
  24.  * in all windows if it is on screen.
  25.  *
  26.  * Support routines for text lines longer than screen width have been added.
  27.  * Currently support lines as long as 255 bytes.  If longer lines are needed,
  28.  * the assembly routines need to be modified since they use bytes (a byte
  29.  * goes from 255 to 0, unsigned).
  30.  *
  31.  * New editor name:  tde, the Thomson-Davis Editor.
  32.  * Author:           Frank Davis
  33.  * Date:             June 5, 1991
  34.  *
  35.  * This modification of Douglas Thomson's code is released into the
  36.  * public domain, Frank Davis.  You may distribute it freely.
  37.  */
  38.  
  39. #include "tdestr.h"
  40. #include "common.h"
  41. #include "define.h"
  42. #include "tdefunc.h"
  43. #ifdef __TURBOC__
  44.   #include <dir.h>        /* for making temporary file names etc */
  45. #endif
  46. #if defined( __MSC__ )
  47.    #include <dos.h>
  48.    #include <io.h>
  49. #endif
  50.  
  51.  
  52. /*
  53.  * Name:    myisalnum
  54.  * Purpose: To determine whether or not a character is part of a "word",
  55.  *           which in languages like Pascal means a letter, digit or
  56.  *           underscore.
  57.  * Date:    October 1, 1989
  58.  * Passed:  c: the character to be tested
  59.  * Returns: TRUE if c is an alphanumeric or '_' character, FALSE otherwise
  60.  */
  61. int  myisalnum( int c )
  62. {
  63. register int rc;
  64.  
  65.    rc = FALSE;
  66.    if (isalnum( c ) || (c == '_'))
  67.       rc = TRUE;
  68.    return( rc );
  69. }
  70.  
  71.  
  72. /*
  73.  * Name:    check_virtual_col
  74.  * Purpose: make sure real column is displayed on screen
  75.  * Date:    June 5, 1991
  76.  * Passed:  window: current window
  77.  *          rcol: real column of cursor
  78.  *          ccol: current or logical column of cursor
  79.  */
  80. void check_virtual_col( windows *window, int rcol, int ccol )
  81. {
  82. register int bcol;
  83. int ncols;
  84. file_infos *file;
  85.  
  86.    file = window->file_info;
  87.    bcol = window->bcol;
  88.    ncols = g_display.ncols - 1;
  89.  
  90.    /*
  91.     * is logical column past end of screen?
  92.     */
  93.    if (ccol > ncols) {
  94.       ccol = ccol - bcol;
  95.       if (ccol > ncols) {
  96.          ccol = ncols;
  97.          bcol = rcol - ccol;
  98.          file->dirty = LOCAL;
  99.       }
  100.  
  101.    /*
  102.     * is logical column behind start of screen?
  103.     */
  104.    } else if (ccol < 0) {
  105.       if (bcol >= -ccol)
  106.          bcol += ccol;
  107.       ccol = 0;
  108.       file->dirty = LOCAL;
  109.  
  110.    /*
  111.     * is current column < base column?
  112.     */
  113.    } else if (rcol < bcol) {
  114.       ccol = rcol;
  115.       bcol = 0;
  116.       if (ccol > ncols) {
  117.          bcol = rcol - ncols;
  118.          ccol = rcol - bcol;
  119.       }
  120.       file->dirty = LOCAL;
  121.    }
  122.  
  123.    /*
  124.     * current column + base column MUST equal real column
  125.     */
  126.    if (ccol + bcol != rcol) {
  127.       if (bcol < 0) {
  128.          bcol = rcol;
  129.          file->dirty = LOCAL;
  130.       }
  131.       ccol = rcol - bcol;
  132.       if (ccol > ncols) {
  133.          bcol = rcol - ncols;
  134.          ccol = rcol - bcol;
  135.          file->dirty = LOCAL;
  136.       }
  137.    }
  138.    if (rcol < 0) {
  139.       rcol = bcol = ccol = 0;
  140.       file->dirty = LOCAL;
  141.    }
  142.  
  143.    window->bcol = bcol;
  144.    window->ccol = ccol;
  145.    window->rcol = rcol;
  146. }
  147.  
  148.  
  149. /*
  150.  * Name:    copy_line
  151.  * Purpose: To copy the cursor line, if necessary, into the current line
  152.  *           buffer, so that changes can be made efficiently.
  153.  * Date:    June 5, 1991
  154.  * Passed:  text_line: line to be copied to line buffer
  155.  *          line: line to display error message
  156.  * Notes:   See un_copy_line, the reverse operation.  Terminate text strings
  157.  *          with CONTROL_Z.  DO NOT use the C library string functions on
  158.  *          text in g_status.line_buff.
  159.  */
  160. void copy_line( text_ptr text_line, int line )
  161. {
  162. char *d;      /* destination of copy */
  163. text_ptr s;   /* source of copy */
  164. register int len;
  165.  
  166.    if (g_status.copied == FALSE) {
  167.       g_status.copied = TRUE;
  168.       /*
  169.        * copy the cursor line to the line buffer
  170.        */
  171.       d = g_status.line_buff;
  172.       g_status.buff_line = text_line;
  173.       s = cpf( text_line );
  174.       len = linelen( s );
  175.       if (s[len] == '\n')
  176.          ++len;
  177.       if (len >= g_display.line_length) {
  178.          len = g_display.line_length;
  179.          error( WARNING, line,  "line buffer overflow - line truncated!" );
  180.       }
  181.       _fmemcpy( d, s, len );
  182.       d[len] = CONTROL_Z;
  183.    }
  184. }
  185.  
  186.  
  187. /*
  188.  * Name:    un_copy_line
  189.  * Purpose: To copy the cursor line, if necessary, from the current line
  190.  *           buffer, shifting the main text to make the right amount of
  191.  *           room.
  192.  * Date:    June 5, 1991
  193.  * Passed:  test_line:  pointer to location in file to copy line buffer
  194.  *          window:  pointer to current window info
  195.  *          del_trailing:  delete the trailing blanks at eol? TRUE or FALSE
  196.  * Notes:   For some functions, trailing spaces should not be removed when
  197.  *           returning the line buffer to the main text.  The JoinLine function
  198.  *           is a good example.  We need to leave trailing space so when we
  199.  *           join lines - the current line will extend at least up to
  200.  *           the column of the cursor.  We allow need to leave trailing space
  201.  *           during BOX block operations.
  202.  *          See copy_line, the reverse operation.
  203.  */
  204. void un_copy_line( text_ptr text_line, windows *window, int del_trailing )
  205. {
  206. char *source;    /* source for block move and for copying buffer line */
  207. text_ptr dest;   /* destination for block move and copy */
  208. text_ptr p;
  209. long number;     /* length of block move */
  210. int space;
  211. int len;         /* length of current line buffer text */
  212. int curs_len;    /* length of cursor line */
  213. int prompt_line;
  214. int i;
  215. file_infos *file;
  216.  
  217.    if (g_status.copied == TRUE) {
  218.       /*
  219.        * work out the lengths of the old cursor line (including the \n if any)
  220.        *  and the new current line buffer text.
  221.        */
  222.       prompt_line = window->bottom_line;
  223.       text_line = cpf( text_line );
  224.       curs_len = linelen( text_line );
  225.       if (text_line[curs_len] == '\n')
  226.          ++curs_len;
  227.       if (del_trailing) {
  228.          len = linelen( g_status.line_buff );
  229.          i = find_CONTROL_Z( g_status.line_buff ) + 1;
  230.          i -= len;
  231.          for (source=g_status.line_buff+len-1; len > 0; len--, source--) {
  232.             if (*source == ' ')
  233.                memmove( source, source+1, i );
  234.             else
  235.                break;
  236.          }
  237.       }
  238.       len = find_CONTROL_Z( g_status.line_buff );
  239.       space = len - curs_len;
  240.  
  241.       /*
  242.        * if the main text buffer has run out of space, then only part of the
  243.        *  current line can be moved back into the main buffer. Warn the user
  244.        *  that some of the current line has been lost
  245.        */
  246.       if (ptoul( g_status.end_mem ) + (long)space >= ptoul( g_status.max_mem )) {
  247.          error( WARNING, prompt_line, "buffer full, part line truncated" );
  248.          len = curs_len + (int) (ptoul( g_status.max_mem ) -
  249.                                  ptoul( g_status.end_mem ));
  250.       }
  251.  
  252.       /*
  253.        * move text to either make room for the extra characters in the new
  254.        *  line, or else close up the gap.
  255.        */
  256.       p = text_line + curs_len;
  257.       dest = addltop( (long)space, p );
  258.       number = ptoul( g_status.end_mem ) - ptoul( p );
  259.       hw_move( dest, p, number );
  260.       g_status.end_mem = addltop( (long)space, g_status.end_mem );
  261.  
  262.       /*
  263.        * now copy the line buffer into the space just created
  264.        */
  265.       _fmemcpy( text_line, g_status.line_buff, len );
  266.       g_status.copied = FALSE;
  267.       file = window->file_info;
  268.       file->modified = TRUE;
  269.       adjust_start_end( file, space );
  270.       addorsub_all_cursors( window, space );
  271.       show_avail_mem( );
  272.    }
  273. }
  274.  
  275.  
  276. /*
  277.  * Name:    load_undo_buffer
  278.  * Purpose: To copy the cursor line to the undo buffer.
  279.  * Date:    September 26, 1991
  280.  * Passed:  undo_line:  pointer to location in file to copy to undo buffer
  281.  * Notes:   save the last UNDO_MAX changed lines.  save the lines in a stack.
  282.  *          when  we overflow the stack, dump the oldest line.  don't worry
  283.  *          about which file the line comes from - just load the stack.
  284.  */
  285. void load_undo_buffer( text_ptr line_to_undo )
  286. {
  287. register char *s;       /*  char pointer to stack */
  288. char *d;
  289. int top;                /* top of stack */
  290. int len;
  291.  
  292.    if (g_status.undo_head < 0)
  293.       g_status.undo_head = 0;
  294.    else if (g_status.undo_head == UNDO_MAX) {
  295.       d = (char *)g_status.undo_buffer;
  296.       s = d + BUFF_SIZE;
  297.       memmove( d, s, (UNDO_MAX * BUFF_SIZE) - BUFF_SIZE );
  298.    }
  299.    len = linelen( line_to_undo );
  300.    if (line_to_undo[len] == '\n')
  301.       ++len;
  302.    top = g_status.undo_head;
  303.    if (top == UNDO_MAX)
  304.       --top;
  305.    s = &g_status.undo_buffer[top][0];
  306.    hw_move( s, line_to_undo, len );
  307.    if (s[len-1] != '\n')
  308.       s[len++] = '\n';
  309.    s[len] = CONTROL_Z;
  310.    if (g_status.undo_head < UNDO_MAX)
  311.       ++g_status.undo_head;
  312. }
  313.  
  314.  
  315. /*
  316.  * Name:    load_file
  317.  * Purpose: To read in a given file to the end of the main text buffer.
  318.  * Date:    June 5, 1991
  319.  * Passed:  name:   path name of file to be read
  320.  * Returns: OK if file read successfully
  321.  *          ERROR if any problem (such as out of buffer space)
  322.  */
  323. int  load_file( char *name )
  324. {
  325. int rc;
  326.  
  327.    /*
  328.     * make sure this gets set properly even if there is no file!
  329.     */
  330.    g_status.temp_end = g_status.end_mem;
  331.  
  332.    rc = hw_load( name, g_status.end_mem, g_status.max_mem,
  333.            &g_status.temp_end, g_display.nlines );
  334.    return( rc );
  335. }
  336.  
  337. /*
  338.  * Name:    set_prompt
  339.  * Purpose: To display a prompt, highlighted, at the bottom of the screen.
  340.  * Date:    October 1, 1989
  341.  * Passed:  prompt: prompt to be displayed
  342.  *          line:  line on which to display prompt
  343.  */
  344. void set_prompt( char *prompt, int line )
  345. {
  346. int prompt_col;
  347.  
  348.    /*
  349.     * work out where the answer should go
  350.     */
  351.    prompt_col = strlen( prompt );
  352.  
  353.    /*
  354.     * output the prompt
  355.     */
  356.    s_output( prompt, line, 0, g_display.message_color );
  357.    eol_clear( prompt_col, line, g_display.message_color );
  358.  
  359.    /*
  360.     * ensure the cursor is in the right place
  361.     */
  362.    xygoto( prompt_col, line );
  363. }
  364.  
  365. /*
  366.  * Name:    get_name
  367.  * Purpose: To prompt the user, and read the string the user enters in
  368.  *           response.
  369.  * Date:    October 1, 1989
  370.  * Passed:  prompt: prompt to offer the user
  371.  *          line:   no. of lines up from the bottom of the screen
  372.  *          name:   default answer
  373.  *          color:  color to display prompt
  374.  * Returns: name:   user's answer
  375.  *          OK if user entered something
  376.  *          ERROR if user aborted the command
  377.  * Notes:   Editing of the line is supported.
  378.  */
  379. int get_name( char *prompt, int line, char *name, int color )
  380. {
  381. int col;                /* cursor column for answer */
  382. int c;                  /* character user just typed */
  383. char *cp;               /* cursor position in answer */
  384. char *answer;           /* user's answer */
  385. int first = TRUE;       /* first character typed */
  386. register int len;       /* length of answer */
  387. int plen;               /* length of prompt */
  388. int func;               /* function of key pressed */
  389. int stop;               /* flag to stop getting characters */
  390. char *p;                /* for copying text in answer */
  391. char buffer[MAX_COLS+2];/* line on which name is being entered */
  392. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  393. int normal;
  394.  
  395.    /*
  396.     * set up prompt and default
  397.     */
  398.    strcpy( buffer, prompt );
  399.    plen = strlen( prompt );
  400.    answer = buffer + plen;
  401.    strcpy( answer, name );
  402.  
  403.    /*
  404.     * let user edit default into desired string
  405.     */
  406.    len = strlen( answer );
  407.    col = strlen( buffer );
  408.    g_status.prompt_line = line;
  409.    g_status.prompt_col = col;
  410.    cp = answer + len;
  411.    normal = g_display.text_color;
  412.    save_screen_line( 0, line, line_buff );
  413.    s_output( buffer, line, 0, color );
  414.    eol_clear( col, line, normal );
  415.    for (stop = FALSE; stop == FALSE;) {
  416.       xygoto( col, line );
  417.       c = getkey( );
  418.       func = getfunc( c );
  419.  
  420.       /*
  421.        * User may have redefined the Enter and ESC keys.  Make the Enter key
  422.        * perform a Rturn in this function. Make the ESC key do an AbortCommand.
  423.        */
  424.       if (c == RTURN)
  425.          func = Rturn;
  426.       else if (c == ESC)
  427.          func = AbortCommand;
  428.       switch (func) {
  429.          case ToggleSearchCase :
  430.             if (bm.search_case == IGNORE)
  431.                bm.search_case = MATCH;
  432.             else
  433.                bm.search_case = IGNORE;
  434.             show_search_case( );
  435.             break;
  436.          case Rturn       :
  437.          case NextLine    :
  438.          case BegNextLine :
  439.             answer[len] = '\0';
  440.             strcpy( name, answer );
  441.             /*
  442.              * finished
  443.              */
  444.             stop = TRUE;
  445.             break;
  446.          case BackSpace :
  447.             /*
  448.              * delete to left of cursor
  449.              */
  450.             if (cp > answer) {
  451.                for (p=cp-1; p < answer+len; p++) {
  452.                   *p = *(p+1);
  453.                }
  454.                --len;
  455.                --col;
  456.                --cp;
  457.                c_output( ' ', plen+len, line, normal);
  458.                s_output( cp, line, col, color );
  459.                *(answer + len) = '\0';
  460.             }
  461.             break;
  462.          case DeleteChar :
  463.             /*
  464.              * delete char under cursor
  465.              */
  466.             if (*cp) {
  467.                for (p=cp; p < answer+len; p++) {
  468.                   *p = *(p+1);
  469.                }
  470.                --len;
  471.                c_output( ' ', plen+len, line, normal);
  472.                s_output( cp, line, col, color );
  473.                *(answer + len) = '\0';
  474.             }
  475.             break;
  476.          case DeleteLine :
  477.             /*
  478.              * delete current line
  479.              */
  480.             col = plen;
  481.             cp = answer;
  482.             *cp = '\0';
  483.             len = 0;
  484.             eol_clear( col, line, normal );
  485.             break;
  486.          case AbortCommand :
  487.             stop = TRUE;
  488.             break;
  489.          case CharLeft :
  490.             /*
  491.              * move cursor left
  492.              */
  493.             if (cp > answer) {
  494.                col--;
  495.                cp--;
  496.             }
  497.             break;
  498.          case CharRight :
  499.             /*
  500.              * move cursor right
  501.              */
  502.             if (*cp) {
  503.                col++;
  504.                cp++;
  505.              }
  506.              break;
  507.          case BegOfLine :
  508.             /*
  509.              * move cursor to start of line
  510.              */
  511.             col = plen;
  512.             cp = answer;
  513.             break;
  514.          case EndOfLine :
  515.             /*
  516.              * move cursor to end of line
  517.              */
  518.             col = plen + len;
  519.             cp = answer + len;
  520.             break;
  521.          default :
  522.             if (c < 0x100) {
  523.                /*
  524.                 * insert character at cursor
  525.                 */
  526.                if (first) {
  527.                   /*
  528.                    * delete previous answer
  529.                    */
  530.                   col = plen;
  531.                   cp = answer;
  532.                   *cp = '\0';
  533.                   len = 0;
  534.                   eol_clear( col, line, normal );
  535.                }
  536.  
  537.                /*
  538.                 * insert new character
  539.                 */
  540.                if (col < g_display.ncols-1) {
  541.                   if (*cp == '\0') {
  542.                      ++len;
  543.                      *(answer + len) = '\0';
  544.                   }
  545.                   *cp = c;
  546.                   c_output( c, col, line, color );
  547.                   ++cp;
  548.                   ++col;
  549.                }
  550.             }
  551.             break;
  552.       }
  553.       first = FALSE;
  554.    }
  555.    restore_screen_line( 0, line, line_buff );
  556.    if (func == AbortCommand)
  557.       c = ERROR;
  558.    else
  559.       c = OK;
  560.    return( c );
  561. }
  562.  
  563.  
  564. /*
  565.  * Name:    get_yn
  566.  * Purpose: To input a response of yes or no.
  567.  * Date:    October 1, 1989
  568.  * Returns: the user's answer (A_??? - see tdestr.h)
  569.  */
  570. int  get_yn( void )
  571. {
  572. int c;   /* the user's response */
  573. register int rc;  /* return code */
  574.  
  575.    for (rc=-1; rc<0;) {
  576.       c = getkey( );
  577.       if (getfunc( c ) == AbortCommand || c == ESC)
  578.          rc = A_ABORT;
  579.       else {
  580.          switch ( c ) {
  581.             case 'Y':
  582.             case 'y':
  583.                rc = A_YES;
  584.                break;
  585.             case 'N':
  586.             case 'n':
  587.                rc = A_NO;
  588.                break;
  589.          }
  590.       }
  591.    }
  592.    return( rc );
  593. }
  594.  
  595.  
  596. /*
  597.  * Name:    get_oa
  598.  * Purpose: To input a response of overwrite or append.
  599.  * Date:    October 1, 1989
  600.  * Returns: the user's answer (A_??? - see tdestr.h)
  601.  */
  602. int get_oa( void )
  603. {
  604. int c;   /* the user's response */
  605. int rc;  /* return code */
  606. register int func;
  607.  
  608.    rc = 0;
  609.    while (rc != AbortCommand && rc != A_OVERWRITE && rc != A_APPEND) {
  610.       c = getkey( );
  611.       func = getfunc( c );
  612.       if (func == AbortCommand || c == ESC)
  613.          rc = AbortCommand;
  614.       switch ( c ) {
  615.          case 'O':
  616.          case 'o':
  617.             rc = A_OVERWRITE;
  618.             break;
  619.          case 'A':
  620.          case 'a':
  621.             rc = A_APPEND;
  622.             break;
  623.       }
  624.    }
  625.    return( rc );
  626. }
  627.  
  628.  
  629. /*
  630.  * Name:    show_eof
  631.  * Purpose: display eof message
  632.  * Date:    September 16, 1991
  633.  * Passed:  line: line to display "<=== eof ===>"
  634.  */
  635. void show_eof( int line )
  636. {
  637. register int color;
  638.  
  639.    color = g_display.eof_color;
  640.    eol_clear( 0, line, color );
  641.    s_output( mode.eof, line, 0, color );
  642. }
  643.  
  644. /*
  645.  * Name:    display_current_window
  646.  * Purpose: display text in current window
  647.  * Date:    June 5, 1991
  648.  * Passed:  window: current window
  649.  * Notes:   use a temporary window structure, "w", to do the dirty work.
  650.  */
  651. void display_current_window( windows *window )
  652. {
  653. text_ptr p;         /* successive lines above the cursor */
  654. int count;          /* number of lines updated so far */
  655. int number;         /* number of lines visible in window */
  656. int i;
  657. windows w;
  658. long length;
  659.  
  660.    /*
  661.     * work out how many lines need to be displayed
  662.     */
  663.    number = window->bottom_line - (window->top_line - 1);
  664.  
  665.    /*
  666.     * display the required number of lines, starting from the
  667.     *  cursor line
  668.     */
  669.    dup_window_info( &w, window );
  670.    w.cursor = cpb( w.cursor );
  671.    count = window->cline - window->top_line;
  672.    length = window->file_info->length + 1;
  673.    for (i=count; i>0; i--) {
  674.       p = find_prev( w.cursor );
  675.       if (p) {
  676.          w.cursor = p;
  677.          --w.cline;
  678.          --w.rline;
  679.       }
  680.    }
  681.    w.cursor = cpf( w.cursor );
  682.    for (i=number; i>0; i--) {
  683.       if (w.cursor) {
  684.          if (w.rline != length && length != 1l)
  685.             update_line( &w );
  686.          else
  687.             show_eof( w.cline );
  688.          w.cursor = find_next( w.cursor );
  689.       } else
  690.          eol_clear( 0, w.cline, COLOR_TEXT );
  691.       ++w.cline;
  692.       ++w.rline;
  693.    }
  694.  
  695.    show_asterisk( w.file_info->modified, w.top_line-1 );
  696. }
  697.  
  698.  
  699. /*
  700.  * Name:    redraw_screen
  701.  * Purpose: display all visible windows if some change was GLOBAL
  702.  * Date:    June 5, 1991
  703.  * Passed:  window: current window
  704.  */
  705. void redraw_screen( windows *window )
  706. {
  707. windows *above;             /* window above current */
  708. windows *below;             /* window below current */
  709.  
  710.    cls( );
  711.    /*
  712.     * display the current window
  713.     */
  714.    redraw_current_window( window );
  715.  
  716.    /*
  717.     * now update all the other windows
  718.     */
  719.    above = below = window;
  720.    while (above->prev || below->next) {
  721.       if (above->prev) {
  722.          above = above->prev;
  723.          redraw_current_window( above );
  724.       }
  725.       if (below->next) {
  726.          below = below->next;
  727.          redraw_current_window( below );
  728.       }
  729.    }
  730.    window->file_info->dirty = FALSE;
  731.    show_modes( );
  732. }
  733.  
  734.  
  735. /*
  736.  * Name:    redraw_current_window
  737.  * Purpose: redraw all info in this window
  738.  * Date:    July 13, 1991
  739.  * Passed:  window: current window
  740.  */
  741. void redraw_current_window( windows *window )
  742. {
  743.  
  744.    /*
  745.     * display the current window
  746.     */
  747.    if (window->visible) {
  748.       display_current_window( window );
  749.       show_window_header( window->file_info->file_name, window );
  750.       show_size_name( window );
  751.       show_size( window );
  752.       show_line_col( window );
  753.    }
  754. }
  755.  
  756.  
  757. /*
  758.  * Name:    show_changed_line
  759.  * Purpose: Only one line was changed in file, just show it
  760.  * Date:    June 5, 1991
  761.  * Passed:  window: current window
  762.  */
  763. void show_changed_line( windows *window )
  764. {
  765. windows *above;             /* window above current */
  766. windows *below;             /* window below current */
  767. windows w;
  768. long changed_line;
  769. long top_line, bottom_line;
  770. int line_on_screen;
  771. file_infos *file;
  772.  
  773.    file = window->file_info;
  774.    if (file->dirty == LOCAL || file->dirty == GLOBAL)
  775.       update_line( window );
  776.    changed_line = window->rline;
  777.  
  778.    /*
  779.     * now update the line in all other windows
  780.     */
  781.    if (file->dirty != LOCAL) {
  782.       above = below = window;
  783.       while (above->prev || below->next) {
  784.          if (above->prev) {
  785.             above = above->prev;
  786.             dup_window_info( &w, above );
  787.          } else if (below->next) {
  788.             below = below->next;
  789.             dup_window_info( &w, below );
  790.          }
  791.          if (w.file_info == window->file_info && w.visible) {
  792.             line_on_screen = FALSE;
  793.             top_line = w.rline - (w.cline - w.top_line);
  794.             bottom_line = w.rline + (w.bottom_line - w.cline);
  795.             if (changed_line == w.rline)
  796.                line_on_screen = TRUE;
  797.             else if (changed_line < w.rline && changed_line >= top_line) {
  798.                line_on_screen = TRUE;
  799.                w.cursor = cpb( w.cursor );
  800.                while (w.rline > changed_line) {
  801.                   w.cursor = find_prev( w.cursor );
  802.                   --w.rline;
  803.                   --w.cline;
  804.                }
  805.             } else if (changed_line > w.rline && changed_line <= bottom_line) {
  806.                line_on_screen = TRUE;
  807.                w.cursor = cpf( w.cursor );
  808.                while (w.rline < changed_line) {
  809.                   w.cursor = find_next( w.cursor );
  810.                   ++w.rline;
  811.                   ++w.cline;
  812.                }
  813.             }
  814.             if (line_on_screen)
  815.                update_line( &w );
  816.          }
  817.       }
  818.    }
  819.    file->dirty = FALSE;
  820. }
  821.  
  822.  
  823. /*
  824.  * Name:    dup_window_info
  825.  * Purpose: Copy window info from one window pointer to another
  826.  * Date:    June 5, 1991
  827.  * Passed:  dw: destination window
  828.  *          sw: source window
  829.  */
  830. void dup_window_info( windows *dw, windows *sw )
  831. {
  832.    memcpy( dw, sw, sizeof( windows ) );
  833. }
  834.  
  835.  
  836. /*
  837.  * Name:    addorsub_all_cursors
  838.  * Purpose: A change has been made - window->cursors in other windows must
  839.  *          be changed to reflect adding or subing of characters.
  840.  * Date:    June 5, 1991
  841.  * Passed:  window: current window
  842.  *          net_change:  number of bytes added or subtracted from a file
  843.  * Notes:   If a file has been changed, all of the memory pointers greater
  844.  *          than window->cursor must be adjusted by the number of characters
  845.  *          added or subtracted from the file pointed to by window.
  846.  *          If a file has been truncated in one window and there is another
  847.  *          window open to the same file and its current line is near the
  848.  *          end, the current line is reset to the last line of the file.
  849.  */
  850. void addorsub_all_cursors( windows *window, long net_change )
  851. {
  852. windows *next;
  853. file_infos *file;
  854. file_infos *next_file;
  855.  
  856.    file = window->file_info;
  857.    next = g_status.window_list;
  858.    while (next != NULL) {
  859.       if (next != window) {
  860.          next_file = next->file_info;
  861.          if (next_file == file) {
  862.             if (next->rline > window->rline)
  863.                next->cursor = addltop( net_change, next->cursor );
  864.          } else {
  865.             if (ptoul( next_file->start_text ) > ptoul( file->start_text ))
  866.                next->cursor = addltop( net_change, next->cursor );
  867.          }
  868.       }
  869.       next = next->next;
  870.    }
  871. }
  872.  
  873.  
  874. /*
  875.  * Name:    adjust_windows_cursor
  876.  * Purpose: A change has been made - window->cursors in other windows must
  877.  *          be changed to reflect adding or subing of characters.
  878.  * Date:    June 5, 1991
  879.  * Passed:  window: current window
  880.  *          line_change: number of lines added or subtracted from a file
  881.  * Notes:   If a file has been changed, all of the memory pointers greater
  882.  *          than window->cursor must be adjusted by the number of characters
  883.  *          added or subtracted from the file pointed to by window.
  884.  *          If a file has been truncated in one window and there is another
  885.  *          window open to the same file and its current line is near the
  886.  *          end, the current line is reset to the last line of the file.
  887.  */
  888. void adjust_windows_cursor( windows *window, int line_change )
  889. {
  890. windows *next;
  891. register int reset;
  892. text_ptr p;
  893. register int i;
  894. file_infos *file;
  895. file_infos *next_file;
  896.  
  897.    file = window->file_info;
  898.    next = g_status.window_list;
  899.    while (next != NULL) {
  900.       if (next != window) {
  901.          next_file = next->file_info;
  902.          if (next_file == file) {
  903.             reset = FALSE;
  904.             if (ptoul( next->cursor ) > ptoul( file->end_text ))
  905.                reset = END;
  906.             else if (ptoul( next->cursor ) < ptoul( file->start_text ))
  907.                reset = BEGIN;
  908.             else if (next->rline > window->rline) {
  909.                if (line_change) {
  910.                   p = next->cursor;
  911.                   if (line_change < 0) {
  912.                      p = cpf( p );
  913.                      for (i=line_change; i < 0 && p != NULL; i++)
  914.                         p = find_next( p );
  915.                      if (p != NULL)
  916.                         next->cursor = p;
  917.                      else
  918.                         reset = END;
  919.                   } else if (line_change > 0) {
  920.                      p = cpb( p );
  921.                      for (i=line_change; i > 0 && p != NULL; i--)
  922.                         p = find_prev( p );
  923.                      if (p != NULL)
  924.                         next->cursor = p;
  925.                      else
  926.                         reset = BEGIN;
  927.                   }
  928.                }
  929.             }
  930.             if (reset) {
  931.                if (reset == BEGIN) {
  932.                   next->cursor = next_file->start_text;
  933.                   next->rline = 1;
  934.                   next->cline = next->top_line;
  935.                } else {
  936.                   next_file->end_text = cpb( next_file->end_text );
  937.                   p = next_file->end_text - 1;
  938.                   p = find_prev( p );
  939.                   if (p != NULL)
  940.                     next->cursor = p;
  941.                   else
  942.                      next->cursor = next_file->end_text - 1;
  943.                   next->rline  = next_file->length;
  944.                }
  945.                if (next->rline < (next->cline - (next->top_line - 1)))
  946.                   next->cline = (int)next->rline + next->top_line - 1;
  947.                file->dirty = NOT_LOCAL;
  948.             }
  949.          }
  950.       }
  951.       next = next->next;
  952.    }
  953. }
  954.  
  955.  
  956. /*
  957.  * Name:    adjust_start_end
  958.  * Purpose: a file has been modified - must restore all start and end pointers
  959.  * Date:    June 5, 1991
  960.  * Passed:  mod:  pointer to modified file structure
  961.  *          net_mod:  net modifications in the source file
  962.  * Notes:   Go through the file list and adjust the start_text and end_text
  963.  *          file pointers as needed.
  964.  */
  965. void adjust_start_end( file_infos *mod_file, long net_mod )
  966. {
  967. unsigned long mst, ost;
  968. file_infos *open_file;
  969.  
  970.    mst = ptoul( mod_file->start_text );
  971.    for (open_file=g_status.file_list; open_file != NULL;
  972.              open_file=open_file->next) {
  973.       ost = ptoul( open_file->start_text );
  974.       if (ost == mst)
  975.          mod_file->end_text = addltop( net_mod, mod_file->end_text );
  976.       else if (ost > mst) {
  977.          open_file->start_text = addltop( net_mod, open_file->start_text );
  978.          open_file->end_text = addltop( net_mod, open_file->end_text );
  979.       }
  980.    }
  981. }
  982.  
  983.  
  984. /*
  985.  * Name:    first_non_blank
  986.  * Purpose: To find the column in which the first non-blank character in
  987.  *           the string occurs.
  988.  * Date:    June 5, 1991
  989.  * Passed:  s:  the string to search
  990.  * Returns: the first non-blank column
  991.  */
  992. int first_non_blank( char far *s )
  993. {
  994. register int count = 0;
  995.  
  996.    s = cpf( s );
  997.    while (*s++ == ' ')
  998.       ++count;
  999.    return( count );
  1000. }
  1001.  
  1002.  
  1003. /*
  1004.  * Name:    page_up
  1005.  * Purpose: To move the cursor one page up the window (probably more
  1006.  *           intuitive to think of the text being moved down)
  1007.  * Date:    June 5, 1991
  1008.  * Passed:  window:   information allowing access to the current window
  1009.  * Notes:   The cursor line is moved back the required number of lines
  1010.  *           towards the start of the file.
  1011.  *          If the start of the file is reached, then the movement stops.
  1012.  */
  1013. void page_up( windows *window )
  1014. {
  1015. int i;  /* count of lines scanned */
  1016. text_ptr p, q;   /* previous lines */
  1017.  
  1018.    un_copy_line( window->cursor, window, TRUE );
  1019.    if (window->rline != (window->cline - (window->top_line-1))) {
  1020.       q = cpb( window->cursor );
  1021.       i = window->cline - (window->top_line-1);
  1022.       if (( window->rline - i) < window->page) {
  1023.          i = (int)window->rline - (window->cline - (window->top_line-1));
  1024.          for (; i>0; i--) {
  1025.             if ((p = find_prev( q )) != NULL)
  1026.                q = p;
  1027.          }
  1028.          window->rline = (window->cline-(window->top_line-1)) + window->page;
  1029.       } else {
  1030.          for (i=window->page; i>0; i--) {
  1031.             if ((p = find_prev( q )) != NULL)
  1032.                q = p;
  1033.          }
  1034.       }
  1035.       window->cursor = q;
  1036.       window->rline -= window->page;
  1037.       window->file_info->dirty = LOCAL;
  1038.    }
  1039. }
  1040.  
  1041.  
  1042. /*
  1043.  * Name:    page_down
  1044.  * Purpose: To move the cursor one page down the window (probably more
  1045.  *           intuitive to think of the text being moved up)
  1046.  * Date:    June 5, 1991
  1047.  * Passed:  window:   information allowing access to the current window
  1048.  * Notes:   The cursor line is moved forwards the required number of lines
  1049.  *           towards the end of the file.
  1050.  *          If the end of the file is reached, then the movement stops.
  1051.  */
  1052. void page_down( windows *window )
  1053. {
  1054. int i, k;          /* count of lines scanned so far */
  1055. text_ptr p, q;     /* lines below cursor */
  1056.  
  1057.    un_copy_line( window->cursor, window, TRUE );
  1058.    q = cpf( window->cursor );
  1059.    k = window->cline - window->top_line;
  1060.    for (i=0; i < window->page && *q != CONTROL_Z; i++, k++) {
  1061.       p = find_next( q );
  1062.       if (p != NULL)
  1063.          q = p;
  1064.       else
  1065.          break;
  1066.    }
  1067.    if (k >= window->page) {
  1068.       window->cursor = q;
  1069.       window->rline += i;
  1070.       window->cline = window->cline + i - window->page;
  1071.       window->file_info->dirty = LOCAL;
  1072.    }
  1073. }
  1074.  
  1075.  
  1076. /*
  1077.  * Name:    scroll_down
  1078.  * Purpose: To make the necessary changes after the user has given the
  1079.  *           command to scroll down the screen.
  1080.  * Date:    June 5, 1991
  1081.  * Passed:  window: information allowing access to the current window
  1082.  * Notes:   Normally, we can just delete the top line on the window, and
  1083.  *           then move the cursor up one line (so the cursor remains at
  1084.  *           the same position in the file).
  1085.  *          However, if the cursor was already on the top line of the
  1086.  *           window, then the cursor must be moved down a line first.
  1087.  */
  1088. void scroll_down( windows *window )
  1089. {
  1090. text_ptr next;
  1091.  
  1092.    un_copy_line( window->cursor, window, TRUE );
  1093.    window->cursor = cpf( window->cursor );
  1094.    if (window->cline == window->top_line) {
  1095.       if ((next = find_next( window->cursor )) != NULL)
  1096.          window->cursor = next;
  1097.       else
  1098.          return;
  1099.       ++window->cline;
  1100.       ++window->rline;
  1101.    }
  1102.    --window->cline;
  1103.    window->file_info->dirty = LOCAL;
  1104. }
  1105.  
  1106.  
  1107. /*
  1108.  * Name:    scroll_up
  1109.  * Purpose: To make the necessary changes after the user has given the
  1110.  *           command to scroll up the screen.
  1111.  * Date:    June 5, 1991
  1112.  * Passed:  window: information allowing access to the current window
  1113.  * Notes:   Normally, we can just insert one line at the top of the window,
  1114.  *           and then move the cursor down one line (so the cursor remains at
  1115.  *           the same position in the file).
  1116.  *          However, if the cursor was already on the bottom line of the
  1117.  *           window, then the cursor must be moved up a line first.
  1118.  */
  1119. void scroll_up( windows *window )
  1120. {
  1121. text_ptr prev;
  1122.  
  1123.    un_copy_line( window->cursor, window, TRUE );
  1124.    window->cursor = cpb( window->cursor );
  1125.    if (window->rline != 1) {
  1126.       if (window->rline == (window->cline - (window->top_line -1))) {
  1127.          if ((prev = find_prev( window->cursor )) != NULL)
  1128.             window->cursor = prev;
  1129.          --window->rline;
  1130.          --window->cline;
  1131.       } else {
  1132.          if (window->cline == window->bottom_line) {
  1133.             if ((prev = find_prev( window->cursor )) != NULL)
  1134.                window->cursor = prev;
  1135.             else
  1136.                return;
  1137.             --window->cline;
  1138.             --window->rline;
  1139.          }
  1140.          ++window->cline;
  1141.          window->file_info->dirty = LOCAL;
  1142.       }
  1143.    }
  1144. }
  1145.  
  1146.  
  1147. /*
  1148.  * Name:    fixed_scroll_up
  1149.  * Purpose: To leave cursor on same logical line and scroll up text
  1150.  * Date:    September 1, 1991
  1151.  * Passed:  window: information allowing access to the current window
  1152.  * Notes:   If cursor is one first page then do not scroll.
  1153.  */
  1154. void fixed_scroll_up( windows *window )
  1155. {
  1156. text_ptr prev;
  1157.  
  1158.    un_copy_line( window->cursor, window, TRUE );
  1159.    window->cursor = cpb( window->cursor );
  1160.    if (window->rline != (window->cline+1 - window->top_line)) {
  1161.       if ((prev = find_prev( window->cursor )) != NULL) {
  1162.             window->cursor = prev;
  1163.             --window->rline;
  1164.             window->file_info->dirty = LOCAL;
  1165.       }
  1166.    }
  1167. }
  1168.  
  1169.  
  1170. /*
  1171.  * Name:    fixed_scroll_down
  1172.  * Purpose: To leave cursor on same logical line and scroll down text
  1173.  * Date:    September 1, 1991
  1174.  * Passed:  window: information allowing access to the current window
  1175.  * Notes:   If cursor is on last line in file then do not scroll.
  1176.  */
  1177. void fixed_scroll_down( windows *window )
  1178. {
  1179. text_ptr next;
  1180.  
  1181.    un_copy_line( window->cursor, window, TRUE );
  1182.    window->cursor = cpf( window->cursor );
  1183.    if ((next = find_next( window->cursor )) != NULL) {
  1184.       window->cursor = next;
  1185.       ++window->rline;
  1186.       window->file_info->dirty = LOCAL;
  1187.    }
  1188. }
  1189.  
  1190.  
  1191. /*
  1192.  * Name:    file_file
  1193.  * Purpose: To file the current file to disk.
  1194.  * Date:    September 17, 1991
  1195.  * Passed:  window:   information allowing access to the current window
  1196.  */
  1197.  void file_file( windows *window )
  1198.  {
  1199.     save_file( window );
  1200.     finish( window );
  1201.  }
  1202.  
  1203.  
  1204. /*
  1205.  * Name:    save_file
  1206.  * Purpose: To save the current file to disk.
  1207.  * Date:    June 5, 1991
  1208.  * Passed:  window:   information allowing access to the current window
  1209.  * Notes:   If anything goes wrong, then the modified flag is set.
  1210.  *          If the file is saved successfully, then modified flag is
  1211.  *           cleared.
  1212.  */
  1213. void save_file( windows *window )
  1214. {
  1215. char name[MAX_COLS]; /* name of file to be saved */
  1216. char status_line[MAX_COLS+2]; /* status line at top of window */
  1217. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  1218. register file_infos *file;
  1219. int rc;
  1220. int prompt_line;
  1221.  
  1222.    un_copy_line( window->cursor, window, TRUE );
  1223.    file = window->file_info;
  1224.    if (file->modified == FALSE)
  1225.       return;
  1226.    prompt_line = window->bottom_line;
  1227.  
  1228.    /*
  1229.     * set up file name and location of various flags
  1230.     */
  1231.    strcpy( name, file->file_name );
  1232.  
  1233.    /*
  1234.     * see if there was a file name - if not, then make the user
  1235.     *  supply one.
  1236.     */
  1237.    if (strlen( name ) == 0) {
  1238.       save_as_file( window );
  1239.       return;
  1240.    }
  1241.  
  1242.    /*
  1243.     * save the file
  1244.     */
  1245.    save_screen_line( 0, prompt_line, line_buff );
  1246.    combine_strings( status_line, "Saving '", name, "'" );
  1247.    s_output( status_line, prompt_line, 0, g_display.message_color );
  1248.    eol_clear( strlen( status_line ), prompt_line, g_display.message_color );
  1249.    file->end_text = cpb( file->end_text );
  1250.    if ((rc = hw_save( name, file->start_text, file->end_text, NOTMARKED ))
  1251.                        == ERROR) {
  1252.       combine_strings( status_line, "cannot write to '", name, "'" );
  1253.       error( WARNING, prompt_line, status_line );
  1254.    }
  1255.    restore_screen_line( 0, prompt_line, line_buff );
  1256.    if (rc != ERROR) {
  1257.       file->modified = FALSE;
  1258.       file->new_file = FALSE;
  1259.    }
  1260. }
  1261.  
  1262. /*
  1263.  * Name:    save_as_file
  1264.  * Purpose: To save the current file to disk, but under a new name.
  1265.  * Date:    June 5, 1991
  1266.  * Passed:  window:   information allowing access to the current window
  1267.  */
  1268. void save_as_file( windows *window )
  1269. {
  1270. char name[MAX_COLS];              /* new name for file */
  1271. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  1272. char status_line[MAX_COLS+2]; /* status line at top of window */
  1273. register file_infos *file;
  1274. int prompt_line;
  1275. int ok;
  1276.  
  1277.    un_copy_line( window->cursor, window, TRUE );
  1278.    /*
  1279.     * read in name, no default
  1280.     */
  1281.    prompt_line = window->bottom_line;
  1282.    file = window->file_info;
  1283.    save_screen_line( 0, prompt_line, line_buff );
  1284.    name[0] = '\0';
  1285.    if (get_name( "New file name: ", prompt_line, name,
  1286.                  g_display.message_color ) == OK) {
  1287.  
  1288.        /*
  1289.         * make sure it is OK to overwrite any existing file
  1290.         */
  1291.       ok = TRUE;
  1292.       if (hw_fattrib( name ) != ERROR) { /* file exists */
  1293.          set_prompt( "Overwrite existing file? (y/n): ", prompt_line );
  1294.          if (get_yn( ) != A_YES  ||  hw_unlink( name, prompt_line ) == ERROR)
  1295.             ok = FALSE;
  1296.       }
  1297.       if (ok == TRUE) {
  1298.          combine_strings( status_line, "Saving '", name, "'" );
  1299.          s_output( status_line, prompt_line, 0, g_display.message_color );
  1300.          eol_clear( strlen( status_line ), prompt_line,
  1301.                     g_display.message_color );
  1302.          file->end_text = cpb( file->end_text );
  1303.          if (hw_save( name, file->start_text, file->end_text, NOTMARKED )
  1304.                        == ERROR) {
  1305.             combine_strings( status_line, "cannot write to '", name, "'" );
  1306.             error( WARNING, prompt_line, status_line );
  1307.          } else
  1308.  
  1309.             /*
  1310.              * record that file is saved and not yet modified again
  1311.              */
  1312.             file->modified = FALSE;
  1313.       }
  1314.    }
  1315.    restore_screen_line( 0, prompt_line, line_buff );
  1316. }
  1317.  
  1318.  
  1319. /*
  1320.  * Name:    show_window_header
  1321.  * Purpose: To save the current file to disk, but under a new name.
  1322.  * Date:    June 5, 1991
  1323.  * Passed:  name:   name of file being edited
  1324.  *          window:   information allowing access to the current window
  1325.  * Notes:   Clear line and display file name in a lite bar
  1326.  */
  1327. void show_window_header( char *name, windows *window )
  1328. {
  1329. char status_line[MAX_COLS+2]; /* status line at top of window */
  1330. char temp[10];
  1331. char *p, *q;            /* for setting up status line */
  1332.  
  1333.    memset( status_line, ' ', MAX_COLS );
  1334.    status_line[MAX_COLS] = '\0';
  1335.    q = name;
  1336.    p = status_line + 5;
  1337.    while (*q)
  1338.       *p++ = *q++;
  1339.    itoa( window->file_info->file_no, temp, 10 );
  1340.    if (strlen( temp ) > 1)
  1341.       p = status_line + 0;
  1342.    else
  1343.       p = status_line + 1;
  1344.    q = temp;
  1345.    while (*q)
  1346.       *p++ = *q++;
  1347.    *p = window->letter;
  1348.    s_output( status_line, window->top_line-1, 0, g_display.head_color );
  1349. }
  1350.  
  1351.  
  1352. /*
  1353.  * Name:    show_size_name
  1354.  * Purpose: show 'size' line lite bar header
  1355.  * Date:    June 5, 1991
  1356.  * Passed:  window:   information allowing access to the current window
  1357.  */
  1358. void show_size_name( windows *window )
  1359. {
  1360. char size_line[MAX_COLS+2]; /* status line at top of window */
  1361.  
  1362.    strcpy( size_line, " size = " );
  1363.    s_output( size_line, window->top_line-1, 49, g_display.head_color );
  1364. }
  1365.  
  1366.  
  1367. /*
  1368.  * Name:    show_size
  1369.  * Purpose: show number of lines in file
  1370.  * Date:    June 5, 1991
  1371.  * Passed:  window:   information allowing access to the current window
  1372.  */
  1373. void show_size( windows *window )
  1374. {
  1375. char size_line[MAX_COLS+2]; /* status line at top of window */
  1376. char csize[20];
  1377. char *p, *q;            /* for setting up status line */
  1378.  
  1379.    strcpy( size_line, "        " );
  1380.    p = size_line;
  1381.    ltoa( window->file_info->length, csize, 10 );
  1382.    q = csize;
  1383.    while (*q)
  1384.       *p++ = *q++;
  1385.    *p++ = ' ';
  1386.    *p = '\0';
  1387.    s_output( size_line, window->top_line-1, 57, g_display.head_color );
  1388. }
  1389.  
  1390.  
  1391. /*
  1392.  * Name:    quit
  1393.  * Purpose: To close the current window without saving the current file.
  1394.  * Date:    June 5, 1991
  1395.  * Passed:  window: information allowing access to the current window
  1396.  * Notes:   If the file has been modified but not saved, then the user is
  1397.  *           given a second chance before the changes are discarded.
  1398.  *          Note that this is only necessary if this is the last window
  1399.  *           that refers to the file. If another window still refers to
  1400.  *           the file, then the check can be left until later.
  1401.  */
  1402. void quit( windows *window )
  1403. {
  1404. int prompt_line;
  1405. char line_buff[(MAX_COLS+2)*2]; /* buffer for char and attribute  */
  1406. register file_infos *file;
  1407. windows *wp;
  1408. int count = 0;
  1409.  
  1410.    un_copy_line( window->cursor, window, TRUE );
  1411.    prompt_line = window->bottom_line;
  1412.    file = window->file_info;
  1413.    for (wp=g_status.window_list; wp != NULL; wp=wp->next) {
  1414.       if (wp->file_info == file && wp->visible)
  1415.          ++count;
  1416.    }
  1417.    if (file->modified && count == 1) {
  1418.       save_screen_line( 0, prompt_line, line_buff );
  1419.       set_prompt( "Abandon changes? (y/n): ", prompt_line );
  1420.       if (get_yn( ) != A_YES) {
  1421.          restore_screen_line( 0, prompt_line, line_buff );
  1422.          return;
  1423.       }
  1424.    }
  1425.  
  1426.    /*
  1427.     * remove window, allocate screen lines to other windows etc
  1428.     */
  1429.    finish( window );
  1430. }
  1431.  
  1432.  
  1433. /*
  1434.  * Name:    move_up
  1435.  * Purpose: To move the cursor one line up the screen.
  1436.  * Date:    June 5, 1991
  1437.  * Passed:  window:   information allowing access to the current window
  1438.  * Notes:   If the cursor is at the top of the window, then the file must
  1439.  *           be scrolled down.
  1440.  *          If the cursor is already on the first line of the file, then
  1441.  *           this command can be ignored.
  1442.  */
  1443. void move_up( windows *window )
  1444. {
  1445. text_ptr p;   /* the previous line on the screen */
  1446.  
  1447.    un_copy_line( window->cursor, window, TRUE );
  1448.    /*
  1449.     * if no previous line, give up
  1450.     */
  1451.    window->cursor = cpb( window->cursor );
  1452.    if ((p = find_prev( window->cursor )) != NULL) {
  1453.       window->cursor = p;
  1454.       --window->rline;           /* ALWAYS decrement line counter */
  1455.       if (window->cline == window->top_line) {
  1456.          window_scroll_down( window->top_line, window->bottom_line );
  1457.          update_line( window );
  1458.       } else
  1459.          --window->cline;    /* we aren't at top of screen - so move up */
  1460.    }
  1461. }
  1462.  
  1463.  
  1464. /*
  1465.  * Name:    move_down
  1466.  * Purpose: To move the cursor one line down the screen.
  1467.  * Date:    June 5, 1991
  1468.  * Passed:  window:   information allowing access to the current window
  1469.  * Notes:   If the cursor is at the bottom of the window, then the file must
  1470.  *           be scrolled up.   If the cursor is at the bottom of the file,
  1471.  *           then scroll up until cursor is at top of screen.
  1472.  */
  1473. void move_down( windows *window )
  1474. {
  1475. text_ptr p;
  1476. register int bottom_line;
  1477.  
  1478.    un_copy_line( window->cursor, window, TRUE );
  1479.    bottom_line = window->bottom_line;
  1480.    window->cursor = cpf( window->cursor );
  1481.    if ((p = find_next( window->cursor )) != NULL) {
  1482.       window->cursor = p;
  1483.       ++window->rline;                /* ALWAYS increment line counter */
  1484.       if (window->cline == bottom_line) {
  1485.          window_scroll_up( window->top_line, bottom_line );
  1486.          if (window->rline <= window->file_info->length)
  1487.             update_line( window );
  1488.          else
  1489.             show_eof( window->cline );
  1490.       } else
  1491.          ++window->cline;     /* if not at bottom of screen move down */
  1492.    } else if (window->cline > window->top_line) {
  1493.       --window->cline;
  1494.       window_scroll_up_eof( window->top_line, bottom_line );
  1495.    }
  1496. }
  1497.  
  1498.  
  1499. /*
  1500.  * Name:    move_left
  1501.  * Purpose: To move the cursor one character to the left
  1502.  * Date:    June 5, 1991
  1503.  * Passed:  window:   information allowing access to the current window
  1504.  * Notes:   If the cursor is already at the left of the screen, then
  1505.  *           scroll horizontally if we're not at beginning of line.
  1506.  */
  1507. void move_left( windows *window )
  1508. {
  1509.    if (window->ccol > 0) {
  1510.       --window->ccol;
  1511.       --window->rcol;
  1512.    } else if (window->ccol == 0 && window->rcol > 0) {
  1513.       --window->rcol;
  1514.       --window->bcol;
  1515.       window->file_info->dirty = LOCAL;
  1516.    }
  1517. }
  1518.  
  1519.  
  1520. /*
  1521.  * Name:    move_right
  1522.  * Purpose: To move the cursor one character to the right
  1523.  * Date:    June 5, 1991
  1524.  * Passed:  window:   information allowing access to the current window
  1525.  * Notes:   If the cursor is already at the right of the screen (logical
  1526.  *          column 80) then scroll horizontally right.
  1527.  */
  1528. void move_right( windows *window )
  1529. {
  1530. int max_col;
  1531.  
  1532.    max_col = g_display.ncols - 1;
  1533.    if (window->ccol < max_col) {
  1534.       ++window->ccol;
  1535.       ++window->rcol;
  1536.    } else if (window->ccol == max_col && window->rcol < g_display.line_length) {
  1537.       ++window->rcol;
  1538.       ++window->bcol;
  1539.       window->file_info->dirty = LOCAL;
  1540.    }
  1541. }
  1542.  
  1543.  
  1544. /*
  1545.  * Name:    word_left
  1546.  * Purpose: To move the cursor one word to the left.
  1547.  * Date:    June 5, 1991
  1548.  * Passed:  window:   information allowing access to the current window
  1549.  * Notes:   Words are considered strings of letters, numbers and underscores,
  1550.  *          which must be separated by other characters.  After every 8000
  1551.  *          characters, check the pointer.
  1552.  */
  1553. void word_left( windows *window )
  1554. {
  1555. text_ptr p;   /* text pointer */
  1556. int len;      /* length of current line */
  1557. int c;        /* character at pointer */
  1558. register int check = 0;
  1559.  
  1560.    un_copy_line( window->cursor, window, TRUE );
  1561.    p = cpf( window->cursor );
  1562.    len = linelen( p );
  1563.    if (window->rcol > len)
  1564.       p += len;
  1565.    else
  1566.       p += window->rcol;
  1567.    p = cpb( p );
  1568.  
  1569.    for (c=*p;c != CONTROL_Z && myisalnum( c ); check++) {
  1570.       c = *--p;
  1571.       if (check > 8000) {
  1572.          p = cpb( p );
  1573.          check = 0;
  1574.       }
  1575.    }
  1576.    if (c == CONTROL_Z)
  1577.       return;
  1578.    for (; c != CONTROL_Z && !myisalnum( c ); check++) {
  1579.       c = *--p;
  1580.       if (check > 8000) {
  1581.          p = cpb( p );
  1582.          check = 0;
  1583.       }
  1584.    }
  1585.    if (c == CONTROL_Z)
  1586.       return;
  1587.    for (;c != CONTROL_Z && myisalnum( c ); check++) {
  1588.       c = *--p;
  1589.       if (check > 8000) {
  1590.          p = cpb( p );
  1591.          check = 0;
  1592.       }
  1593.    }
  1594.    find_adjust( window, ++p );
  1595. }
  1596.  
  1597.  
  1598. /*
  1599.  * Name:    word_right
  1600.  * Purpose: To move the cursor one word to the right.
  1601.  * Date:    June 5, 1991
  1602.  * Passed:  window:   information allowing access to the current window
  1603.  * Notes:   Words are considered strings of letters, numbers and underscores,
  1604.  *           which must be separated by other characters.  After every 8000
  1605.  *           characters, check the pointer.
  1606.  */
  1607. void word_right( windows *window )
  1608. {
  1609. int len;     /* length of current line */
  1610. text_ptr p;  /* text pointer */
  1611. int c;       /* character at pointer */
  1612. register int check = 0;
  1613.  
  1614.    un_copy_line( window->cursor, window, TRUE );
  1615.    p = cpf( window->cursor );
  1616.    len = linelen( p );
  1617.    if (window->rcol > len)
  1618.       p += len;
  1619.    else
  1620.       p += window->rcol;
  1621.    for (c=*p;c != CONTROL_Z && myisalnum( c ); check++) {
  1622.       c = *++p;
  1623.       if (check > 8000) {
  1624.          p = cpf( p );
  1625.          check = 0;
  1626.       }
  1627.    }
  1628.    for (; c != CONTROL_Z && !myisalnum( c ); check++) {
  1629.       c = *++p;
  1630.       if (check > 8000) {
  1631.          p = cpf( p );
  1632.          check = 0;
  1633.       }
  1634.    }
  1635.    if (c != CONTROL_Z)
  1636.       find_adjust( window, p );
  1637. }
  1638.  
  1639. /*
  1640.  * Name:    center_window
  1641.  * Purpose: To make the current line the center of window.
  1642.  * Date:    June 5, 1991
  1643.  * Passed:  window: information allowing access to the current window
  1644.  */
  1645. void center_window( windows *window )
  1646. {
  1647. int center;
  1648. int center_line;
  1649. int diff;
  1650. register file_infos *file;
  1651. int i;
  1652.  
  1653.    file = window->file_info;
  1654.    center = (window->bottom_line + 1 - window->top_line) / 2;
  1655.    center_line = window->top_line + center;
  1656.    diff = center_line - window->cline;
  1657.    if (g_status.command == CenterWindow) {
  1658.       un_copy_line( window->cursor, window, TRUE );
  1659.       if (diff > 0) {
  1660.          if (window->rline + diff <= file->length) {
  1661.             window->cline += diff;
  1662.             window->rline += diff;
  1663.             window->cursor = cpf( window->cursor );
  1664.             for (i=0; i<diff; i++)
  1665.                window->cursor = find_next( window->cursor );
  1666.          }
  1667.       } else if (diff < 0) {
  1668.          window->cline += diff;
  1669.          window->rline += diff;
  1670.          window->cursor = cpb( window->cursor );
  1671.          for (i=diff; i<0; i++)
  1672.             window->cursor = find_prev( window->cursor );
  1673.       }
  1674.    } else if (g_status.command == CenterLine) {
  1675.       if (diff > 0) {
  1676.          window->cline += diff;
  1677.          if ((window->cline+1 - window->top_line) > window->rline)
  1678.             window->cline = window->top_line - 1 + (int)window->rline;
  1679.          file->dirty = LOCAL;
  1680.       } else if (diff < 0) {
  1681.          window->cline = window->cline + diff;
  1682.          file->dirty = LOCAL;
  1683.       }
  1684.    }
  1685. }
  1686.  
  1687.  
  1688. /*
  1689.  * Name:    horizontal_screen_right
  1690.  * Purpose: To move the cursor one screen to the right
  1691.  * Date:    September 13, 1991
  1692.  * Passed:  window:   information allowing access to the current window
  1693.  * Notes:   Add 80 columns to the real cursor.  If the cursor is past the
  1694.  *          maximum line length then move it back.
  1695.  */
  1696. void horizontal_screen_right( windows *window )
  1697. {
  1698.  
  1699.    window->rcol += 80;
  1700.    if (window->rcol > MAX_LINE_LENGTH)
  1701.       window->rcol = MAX_LINE_LENGTH;
  1702.    else {
  1703.       window->bcol += 80;
  1704.       window->file_info->dirty = LOCAL;
  1705.    }
  1706.    check_virtual_col( window, window->rcol, window->ccol );
  1707. }
  1708.  
  1709.  
  1710. /*
  1711.  * Name:    horizontal_screen_left
  1712.  * Purpose: To move the cursor one screen to the left
  1713.  * Date:    September 13, 1991
  1714.  * Passed:  window:   information allowing access to the current window
  1715.  * Notes:   Subtract 80 columns to the real cursor.  If the cursor is less than
  1716.  *          zero then see if bcol is zero.  If bcol is not zero then make
  1717.  *          bcol zero.
  1718.  */
  1719. void horizontal_screen_left( windows *window )
  1720. {
  1721.  
  1722.    if (window->rcol - 80 < 0) {
  1723.       if (window->bcol != 0) {
  1724.          window->bcol = 0;
  1725.          window->file_info->dirty = LOCAL;
  1726.       }
  1727.    } else {
  1728.       window->rcol -= 80;
  1729.       window->bcol -= 80;
  1730.       if (window->bcol < 0)
  1731.          window->bcol = 0;
  1732.       window->file_info->dirty = LOCAL;
  1733.    }
  1734.    check_virtual_col( window, window->rcol, window->ccol );
  1735. }
  1736.