home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / EDITOR / TDE120.ZIP / FINDREP.C < prev    next >
Encoding:
C/C++ Source or Header  |  1991-10-05  |  28.5 KB  |  961 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 - find/replace module
  10.  * Purpose: This file contains the functions relating to finding text
  11.  *           and replacing text.
  12.  *          It also contains the code for moving the cursor to various
  13.  *           other positions in the file.
  14.  * File:    findrep.c
  15.  * Author:  Douglas Thomson
  16.  * System:  this file is intended to be system-independent
  17.  * Date:    October 1, 1989
  18.  */
  19. /*********************  end of original comments   ********************/
  20.  
  21. /*
  22.  * The search and replace routines have been EXTENSIVELY rewritten.  The
  23.  * "brute force" search algorithm has been replaced by the Boyer-Moore
  24.  * string search algorithm.  This search algorithm really speeds up search
  25.  * and replace operations.
  26.  *
  27.  * If I am not mistook, seems like Boyer developed the array and Moore
  28.  * developed the search.  For those interested, the algorithm is published in:
  29.  *
  30.  *    R. S. Boyer and J. S. Moore, "A fast string searching algorithm."
  31.  *        _Communications of the ACM_  20 (No. 10): 762-772, 1977.
  32.  *
  33.  * I am not very fond of the Wordstar/TURBO type search and replace prompting.
  34.  * Once the search pattern has been defined, one only needs to press a key
  35.  * to search forwards or backwards.  The forward or backward search key may
  36.  * be pressed at any time in any file once the pattern has been entered.  Also,
  37.  * the search case may be toggled at any time in any file once the pattern has
  38.  * has been entered.
  39.  *
  40.  * New editor name:  tde, the Thomson-Davis Editor.
  41.  * Author:           Frank Davis
  42.  * Date:             June 5, 1991
  43.  *
  44.  * This modification of Douglas Thomson's code is released into the
  45.  * public domain, Frank Davis.   You may distribute it freely.
  46.  */
  47.  
  48. #include "tdestr.h"
  49. #include "common.h"
  50. #include "tdefunc.h"
  51. #include "define.h"
  52.  
  53. /*
  54.  * Name:    get_replacement_flags
  55.  * Purpose: To input find and replace flags.
  56.  * Date:    June 5, 1991
  57.  * Passed:  line:  prompt line
  58.  * Returns: OK if flags were entered, ERROR if user wanted to abort
  59.  */
  60. int  get_replacement_flags( int line )
  61. {
  62. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  63. char *find_opt = "Options  (P)rompt before replace   (N)o prompt: ";
  64. register int c;
  65. int rc, func;
  66.  
  67.    c = strlen( find_opt );
  68.    save_screen_line( 0, line, line_buff );
  69.    s_output( find_opt, line, 0, g_display.message_color );
  70.    eol_clear( c, line, g_display.text_color );
  71.    c += 2;
  72.    xygoto( c, line );
  73.    c = getkey( );
  74.    func = getfunc( c );
  75.    while (c != 'P' && c != 'p' && c != 'N' && c != 'n' && c != RTURN &&
  76.           c != ESC && func != AbortCommand) {
  77.       c = getkey( );
  78.       func = getfunc( c );
  79.    }
  80.    restore_screen_line( 0, line, line_buff );
  81.    switch (c) {
  82.       case 'P' :
  83.       case 'p' :
  84.       case RTURN :
  85.          g_status.replace_flag = PROMPT;
  86.          rc = OK;
  87.          break;
  88.       case 'N' :
  89.       case 'n' :
  90.          g_status.replace_flag = NOPROMPT;
  91.          rc = OK;
  92.          break;
  93.       default :
  94.          rc = ERROR;
  95.    }
  96.    bm.search_defined = rc;
  97.    return( rc );
  98. }
  99.  
  100.  
  101. /*
  102.  * Name:    ask_replace
  103.  * Purpose: Ask user if he want to (r)place, (s)kip, or (e)xit.
  104.  * Date:    June 5, 1991
  105.  * Returns: TRUE if user wants to replace, ERROR otherwise.
  106.  * Passed:  window: information allowing access to current window etc
  107.  *          finished: TRUE if user pressed ESC or (e)xit.
  108.  */
  109. int  ask_replace( windows *window, int *finished )
  110. {
  111. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  112. char *ask_opt = "(R)eplace  (S)kip  (E)xit";
  113. register int c;
  114. int rc, prompt_line, func;
  115.  
  116.    prompt_line = window->cline - 1;
  117.    c = 39 - (strlen( ask_opt ) >> 1);
  118.    save_screen_line( 0, prompt_line, line_buff );
  119.    s_output( ask_opt, prompt_line, c, g_display.message_color );
  120.    c = getkey( );
  121.    func = getfunc( c );
  122.    while (c != 'R' && c != 'r' && c != 'S' && c != 's' && c != 'E' && c != 'e'
  123.           && c != ESC  &&  func != AbortCommand) {
  124.       c = getkey( );
  125.       func = getfunc( c );
  126.    }
  127.    restore_screen_line( 0, prompt_line, line_buff );
  128.    switch (c) {
  129.       case 'R' :
  130.       case 'r' :
  131.          rc = OK;
  132.          break;
  133.       case 'E' :
  134.       case 'e' :
  135.          *finished = TRUE;
  136.          break;
  137.       case 'S' :
  138.       case 's' :
  139.          rc = ERROR;
  140.          break;
  141.       default :
  142.          *finished = TRUE;
  143.          rc = ERROR;
  144.          break;
  145.    }
  146.    return( rc );
  147. }
  148.  
  149.  
  150. /*
  151.  * Name:    do_replace
  152.  * Purpose: To replace text once match has been found.
  153.  * Date:    June 5, 1991
  154.  * Passed:  window: information allowing access to current window etc
  155.  *          start:  location of start of matched text
  156.  *          direction:  direction of search
  157.  */
  158. void do_replace( windows *window, text_ptr start, int direction )
  159. {
  160. int old_len;             /* length of original text */
  161. int new_len;             /* length of replacement text */
  162. int net_change;
  163. text_ptr source;         /* source of block move */
  164. text_ptr dest;           /* destination of block move */
  165. long number;             /* number of characters moved */
  166.  
  167.    old_len = strlen( g_status.pattern );
  168.    new_len = strlen( g_status.subst );
  169.  
  170.    /*
  171.     * move the text to either make room for the extra replacement text
  172.     *  or to close up the gap left
  173.     */
  174.    start = cpf( start );
  175.    source = start + old_len;
  176.    dest = start + new_len;
  177.    number = ptoul( g_status.end_mem ) - ptoul( source );
  178.    hw_move( dest, source, number );
  179.  
  180.    /*
  181.     * insert the replacement text
  182.     */
  183.    for (dest=start, source=g_status.subst; *source;)
  184.       *dest++ = *source++;
  185.  
  186.    net_change = new_len - old_len;
  187.    adjust_start_end( window->file_info, net_change );
  188.    if (direction == FORWARD)
  189.       window->rcol += net_change;
  190.    addorsub_all_cursors( window, net_change );
  191.    g_status.end_mem = addltop( (long)net_change, g_status.end_mem );
  192.    show_avail_mem( );
  193. }
  194.  
  195.  
  196. /*
  197.  * Name:    find_string
  198.  * Purpose: To set up and perform a find operation.
  199.  * Date:    June 5, 1991
  200.  * Passed:  window: information allowing access to current window etc
  201.  *          direction: find FORWARD or BACKWARD - determined by pressed key
  202.  *          new_string: search for new string to be entered by user
  203.  * Notes:   If user presses search string key then keep boyer-moore stuff.
  204.  *          To find the next occurrence either forwards or backwards all the
  205.  *          user has to do is press the "RepeatFindForward" or
  206.  *          "RepeatFindBackward" key at any time.
  207.  */
  208. void find_string( windows *window )
  209. {
  210. int direction;
  211. int new_string;
  212. char pattern[MAX_COLS];  /* text to be found */
  213. int prompt_line;
  214. text_ptr pattern_location;
  215. char *snf = "string not found";
  216.  
  217.    switch (g_status.command) {
  218.       case FindForward :
  219.          direction = FORWARD;
  220.          new_string = TRUE;
  221.          break;
  222.       case FindBackward :
  223.          direction = BACKWARD;
  224.          new_string = TRUE;
  225.          break;
  226.       case RepeatFindForward :
  227.          direction = FORWARD;
  228.          new_string = FALSE;
  229.          break;
  230.       case RepeatFindBackward :
  231.          direction = BACKWARD;
  232.          new_string = FALSE;
  233.          break;
  234.    }
  235.    un_copy_line( window->cursor, window, TRUE );
  236.    prompt_line = window->bottom_line;
  237.    /*
  238.     * get search text, using previous as default
  239.     */
  240.    if (new_string == TRUE) {
  241.       strcpy( pattern, bm.pattern );
  242.       if (get_name( "String to find: ", prompt_line, pattern,
  243.                      g_display.message_color ) != OK)
  244.          return;
  245.       bm.search_defined = OK;
  246.       strcpy( bm.pattern, pattern );
  247.  
  248.       build_boyer_array( );
  249.    }
  250.  
  251.    if (bm.search_defined == OK) {
  252.       if (direction == FORWARD) {
  253.          if ((pattern_location = forward_boyer_moore_search( window )) != NULL)
  254.             find_adjust( window, pattern_location );
  255.          else
  256.             error( WARNING, prompt_line, snf );
  257.       } else {
  258.          if ((pattern_location = backward_boyer_moore_search( window )) != NULL)
  259.             find_adjust( window, pattern_location );
  260.          else
  261.             error( WARNING, prompt_line, snf );
  262.       }
  263.    } else
  264.       error( WARNING, prompt_line, "find pattern not defined" );
  265. }
  266.  
  267.  
  268. /*
  269.  * Name:    build_boyer_array
  270.  * Purpose: To set up the boyer array for forward and backward searches.
  271.  * Date:    June 5, 1991
  272.  * Notes:   Set up one array for forward searches and another for backward
  273.  *          searches.  If user decides to ignore case then fill in array
  274.  *          with reverse case characters so both upper and lower case
  275.  *          characters are defined.
  276.  */
  277. void build_boyer_array( void )
  278. {
  279. int pl;
  280. register int i;
  281. register unsigned char *p;
  282.  
  283.    /*
  284.     * set up for forward search
  285.     */
  286.    i = pl = bm.pattern_length = strlen( bm.pattern );
  287.  
  288.    /*
  289.     * set skip index of all characters to length of string
  290.     */
  291.    memset( bm.skip_forward, i, 256 );
  292.    i--;
  293.  
  294.    /*
  295.     * for each character in string, set the skip index
  296.     */
  297.    for (p=bm.pattern; i>=0; i--, p++)
  298.       bm.skip_forward[*p] = i;
  299.    if (bm.search_case == IGNORE) {
  300.       i = pl - 1;
  301.       for (p=bm.pattern; i>=0; i--, p++) {
  302.          if (*p >= 'A' && *p <= 'Z')
  303.             bm.skip_forward[*p+32] = i;
  304.          else if (*p >= 'a' && *p <= 'z')
  305.             bm.skip_forward[*p-32] = i;
  306.       }
  307.    }
  308.  
  309.    /*
  310.     * set up for backward search
  311.     */
  312.    i = -pl;
  313.    memset( bm.skip_backward, i, 256 );
  314.    i++;
  315.    for (p=bm.pattern+pl-1; i<=0; i++, p--)
  316.       bm.skip_backward[*p] = i;
  317.    if (bm.search_case == IGNORE) {
  318.       i = -pl + 1;
  319.       for (p=bm.pattern+pl-1; i<=0; i++, p--) {
  320.          if (*p >= 'A' && *p <= 'Z')
  321.             bm.skip_backward[*p+32] = i;
  322.          else if (*p >= 'a' && *p <= 'z')
  323.             bm.skip_backward[*p-32] = i;
  324.       }
  325.    }
  326. }
  327.  
  328.  
  329. /*
  330.  * Name:    forward_boyer_moore_search
  331.  * Purpose: search forward for pattern using boyer array
  332.  * Passed:  window: information allowing access to current window etc
  333.  * Returns: position in file if found else return NULL
  334.  * Date:    June 5, 1991
  335.  * Notes:   Start searching from cursor position to end of file.  If we hit
  336.  *          end of file without a match, start searching from the beginning
  337.  *          of file to cursor position.  (do wrapped searches)
  338.  */
  339. text_ptr forward_boyer_moore_search( windows *window )
  340. {
  341. register int i;
  342. int pl;
  343. text_ptr p, q, s, search_start;
  344. long search_length;
  345.  
  346.    /*
  347.     * if cursor is beyond end of line then start at end of line
  348.     */
  349.    un_copy_line( window->cursor, window, TRUE );
  350.    window->cursor = cpf( window->cursor );
  351.    search_start = window->cursor;
  352.    i = window->rcol + 1;
  353.    pl = linelen( window->cursor );
  354.    if (i > pl)
  355.       i = pl;
  356.  
  357.    /*
  358.     * make start of search 1 character greater than current position so
  359.     * we don't repeatedly find the pattern at current position.
  360.     */
  361.    search_start += i;
  362.    s = search_start;
  363.    pl = bm.pattern_length;
  364.    i = pl - 1;
  365.  
  366.    /*
  367.     * find out how many character to search.  do standard Boyer-Moore search
  368.     */
  369.    search_length = (ptoul( window->file_info->end_text ) - 1) - ptoul( s );
  370.    p = bm.pattern;
  371.    for (search_length -= i; search_length >= 0; search_length -= i) {
  372.       s = s + i;
  373.       i = bm.skip_forward[(unsigned char)*s];
  374.       if (i == 0) {
  375.          q = addltop( 1 - pl, s );
  376.          q = cpf( q );
  377.          if (bm.search_case == MATCH)
  378.             i = _fstrncmp( q, p, pl );
  379.          else
  380.             i = _fstrnicmp( q, p, pl );
  381.          if (i == 0)
  382.             return( q );
  383.          i = 1;
  384.       }
  385.       s = cpf( s );
  386.    }
  387.  
  388.    /*
  389.     * haven't found pattern yet - now search from beginning of file
  390.     */
  391.    s_output( "wrapped...", g_display.mode_line, 63, g_display.diag_color );
  392.    s = cpf( window->file_info->start_text );
  393.    search_length = ptoul( search_start ) + 1 - ptoul( s );
  394.    i = pl - 1;
  395.    for (search_length -= i; search_length >= 0; search_length -= i) {
  396.       s = s + i;
  397.       i = bm.skip_forward[(unsigned char)*s];
  398.       if (i == 0) {
  399.          q = addltop( 1 - pl, s );
  400.          q = cpf( q );
  401.          if (bm.search_case == MATCH)
  402.             i = _fstrncmp( q, p, pl );
  403.          else
  404.             i = _fstrnicmp( q, p, pl );
  405.          if (i == 0)
  406.             return( q );
  407.          i = 1;
  408.       }
  409.       s = cpf( s );
  410.    }
  411.    /*
  412.     * pattern not found - return NULL pointer
  413.     */
  414.    return( NULL );
  415. }
  416.  
  417.  
  418. /*
  419.  * Name:    backward_boyer_moore_search
  420.  * Purpose: search backward for pattern using boyer array
  421.  * Passed:  window: information allowing access to current window etc
  422.  * Returns: position in file if found else return NULL
  423.  * Date:    June 5, 1991
  424.  * Notes:   Start searching from cursor position to beginning of file. If we
  425.  *          hit beginning end of file without a match, start searching from the
  426.  *          end of file to cursor position.  (do wrapped searches)
  427.  */
  428. text_ptr backward_boyer_moore_search( windows *window )
  429. {
  430. register int i;
  431. int pl;
  432. text_ptr p, s, search_start;
  433. long search_length;
  434.  
  435.    un_copy_line( window->cursor, window, TRUE );
  436.    i = window->rcol - 1;
  437.    i += bm.pattern_length - 1;
  438.    pl = linelen( window->cursor );
  439.    if (i > pl)
  440.       i = pl;
  441.    search_start = addltop( i, window->cursor );
  442.    s = cpb( search_start );
  443.    pl = bm.pattern_length;
  444.    i = -pl + 1;
  445.    search_length = ptoul( search_start )  -
  446.                     ptoul( window->file_info->start_text );
  447.    p = bm.pattern;
  448.    for (search_length += i; search_length >= 0; search_length += i) {
  449.       s = s + i;
  450.       i = bm.skip_backward[(unsigned char)*s];
  451.       if (i == 0) {
  452.          if (bm.search_case == MATCH)
  453.             i = _fstrncmp( s, p, pl );
  454.          else
  455.             i = _fstrnicmp( s, p, pl );
  456.          if (i == 0)
  457.             return( s );
  458.          i = -1;
  459.       }
  460.       s = cpb( s );
  461.    }
  462.  
  463.    /*
  464.     * haven't found pattern yet - now search from end of file
  465.     */
  466.    s_output( "wrapped...", g_display.mode_line, 63, g_display.diag_color );
  467.    s = addltop( -1, window->file_info->end_text );
  468.    s = cpb( s );
  469.    i = -pl + 1;
  470.    search_length = ptoul( s ) - ptoul( search_start );
  471.    for (search_length += i; search_length >= 0; search_length += i) {
  472.       s = s + i;
  473.       i = bm.skip_backward[(unsigned char)*s];
  474.       if (i == 0) {
  475.          if (bm.search_case == MATCH)
  476.             i = _fstrncmp( s, p, pl );
  477.          else
  478.             i = _fstrnicmp( s, p, pl );
  479.          if (i == 0)
  480.             return( s );
  481.          i = -1;
  482.       }
  483.       s = cpb( s );
  484.    }
  485.  
  486.    /*
  487.     * pattern not found - return NULL pointer
  488.     */
  489.    return( NULL );
  490. }
  491.  
  492.  
  493. /*
  494.  * Name:    find_adjust
  495.  * Purpose: place cursor on screen given a position in file - default cline
  496.  * Passed:  window: information allowing access to current window etc
  497.  *          found:  position anywhere in file
  498.  * Date:    June 5, 1991
  499.  * Notes:   found could be anywhere in file.  Find the start of line that
  500.  *          found is on.  Determine if start of line is behind or ahead of
  501.  *          current line.  Once that is done, it is easy to determine if found
  502.  *          is on screen.  Lastly, current cursor position becomes start of
  503.  *          found line - reposition and display.
  504.  */
  505. void find_adjust( windows *window, text_ptr found )
  506. {
  507. int rcol;
  508. long pattern_line, rline, test_line;
  509. text_ptr p, q;
  510. file_infos *file;
  511.  
  512.    file = window->file_info;
  513.    /*
  514.     * find start of line found is on.
  515.     */
  516.    found = cpb( found );
  517.    if (*(found - 1) != '\n' && *(found - 1) != CONTROL_Z)
  518.       p = find_prev( found );
  519.    else
  520.       p = found;
  521.  
  522.    /*
  523.     * find real column found is on.
  524.     */
  525.    rcol = (int)(ptoul( found ) - ptoul( p ));
  526.    rline = pattern_line = window->rline;
  527.    q = window->cursor = cpf( window->cursor );
  528.  
  529.    /*
  530.     * if p, start of found line, is greater than current line, see if
  531.     * p is between current line and bottom line on screen.
  532.     */
  533.    if (ptoul( q ) < ptoul( p )) {
  534.       while (ptoul( q ) != ptoul( p )) {
  535.          q = find_next( q );
  536.          ++pattern_line;
  537.       }
  538.       test_line = pattern_line - rline;
  539.       if ((long)window->cline + test_line <= (long)window->bottom_line)
  540.          window->cline += test_line;
  541.       else
  542.          file->dirty = LOCAL;
  543.  
  544.    /*
  545.     * if p, start of found line, is less than current line, see if
  546.     * p is between current line and top line on screen.
  547.     */
  548.    } else if (ptoul( q ) > ptoul( p )) {
  549.       q = cpb( q );
  550.       while (ptoul( q ) != ptoul( p )) {
  551.          q = find_prev( q );
  552.          --pattern_line;
  553.       }
  554.       test_line = rline - pattern_line;
  555.       if ((long)window->cline - test_line > (long)(window->top_line-1))
  556.          window->cline -= test_line;
  557.       else
  558.          file->dirty = LOCAL;
  559.       if (pattern_line < (long)(window->cline - (window->top_line-1)))
  560.          window->cline = (int)pattern_line + window->top_line - 1;
  561.    }
  562.  
  563.    /*
  564.     * cursor line becomes found line.  check if column is on screen.
  565.     */
  566.    window->cursor = p;
  567.    window->rline = pattern_line;
  568.    if (file->dirty == LOCAL && (window->cline == window->bottom_line ||
  569.                                 window->cline == window->top_line)) {
  570.       g_status.command = CenterLine;
  571.       center_window( window );
  572.    }
  573.    check_virtual_col( window, rcol, rcol );
  574. }
  575.  
  576.  
  577. /*
  578.  * Name:    replace_string
  579.  * Purpose: To set up and perform a replace operation.
  580.  * Date:    June 5, 1991
  581.  * Passed:  window: information allowing access to current window etc
  582.  *          direction:  replace forward or backward in file
  583.  */
  584. void replace_string( windows *window )
  585. {
  586. int direction;
  587. char pattern[MAX_COLS];  /* the old and replacement text */
  588. int prompt_line;
  589. int net_change;
  590. int sub_len;
  591. int file_changed;
  592. int finished;
  593. int rc;
  594. text_ptr pattern_location;
  595. text_ptr replace_start;
  596. unsigned long rs, pl;
  597. windows wp;
  598. int color;
  599. char *snf = "string not found";
  600.  
  601.    if (g_status.command == ReplaceForward)
  602.       direction = FORWARD;
  603.    else
  604.       direction = BACKWARD;
  605.    un_copy_line( window->cursor, window, TRUE );
  606.    prompt_line = window->bottom_line;
  607.    color = g_display.message_color;
  608.    /*
  609.     * get the old text, using the previous as the default
  610.     */
  611.    strcpy( pattern, g_status.pattern );
  612.    if (get_name( "String to find: ", prompt_line, pattern, color ) != OK)
  613.       return;
  614.    strcpy( g_status.pattern, pattern );
  615.  
  616.    /*
  617.     * get the replacement text, using the previous as the default
  618.     */
  619.    strcpy( pattern, g_status.subst );
  620.    if (get_name( "Replacement:    ", prompt_line, pattern, color ) != OK)
  621.       return;
  622.    strcpy( g_status.subst, pattern );
  623.    sub_len = strlen( pattern );
  624.    strcpy( bm.pattern, g_status.pattern );
  625.    net_change = sub_len - strlen( g_status.pattern );
  626.  
  627.    /*
  628.     * get the replace flags
  629.     */
  630.    if (get_replacement_flags( prompt_line ) != OK)
  631.       return;
  632.  
  633.    build_boyer_array( );
  634.    dup_window_info( &wp, window );
  635.  
  636.    finished = FALSE;
  637.    file_changed = FALSE;
  638.    if (direction == FORWARD) {
  639.       if ((replace_start = forward_boyer_moore_search( &wp )) != NULL) {
  640.          rs = ptoul( replace_start );
  641.          replace_and_display( &wp, replace_start, &finished, &file_changed,
  642.                               direction );
  643.       } else {
  644.          error( WARNING, prompt_line, snf );
  645.          finished = TRUE;
  646.       }
  647.       while (finished == FALSE) {
  648.          if ((pattern_location = forward_boyer_moore_search( &wp )) != NULL) {
  649.             pl = ptoul( pattern_location );
  650.             if (rs == pl)
  651.                finished = TRUE;
  652.             else {
  653.                rc = replace_and_display( &wp, pattern_location, &finished,
  654.                                  &file_changed, direction );
  655.                if (rc == OK && rs > pl)
  656.                   rs += net_change;
  657.             }
  658.          } else {
  659.             error( WARNING, prompt_line, snf );
  660.             finished = TRUE;
  661.          }
  662.       }
  663.    } else {
  664.       if ((replace_start = backward_boyer_moore_search( &wp )) != NULL) {
  665.          rs = ptoul( replace_start );
  666.          replace_and_display( &wp, replace_start, &finished, &file_changed,
  667.                               direction );
  668.       } else {
  669.          error( WARNING, prompt_line, snf );
  670.          finished = TRUE;
  671.       }
  672.       while (finished == FALSE) {
  673.          if ((pattern_location = backward_boyer_moore_search( &wp )) != NULL) {
  674.             pl = ptoul( pattern_location );
  675.             if (rs == pl || (pl > rs && rs > pl - sub_len))
  676.                finished = TRUE;
  677.             else {
  678.                rc = replace_and_display( &wp, pattern_location, &finished,
  679.                                  &file_changed, direction );
  680.                if (rc == OK && rs > pl)
  681.                   rs += net_change;
  682.             }
  683.          } else {
  684.             finished = TRUE;
  685.             error( WARNING, prompt_line, snf );
  686.          }
  687.       }
  688.    }
  689.    dup_window_info( window, &wp );
  690.    if (file_changed)
  691.       window->file_info->modified = TRUE;
  692. }
  693.  
  694.  
  695. /*
  696.  * Name:    replace_and_display
  697.  * Purpose: To make room for replacement string
  698.  * Date:    June 5, 1991
  699.  * Passed:  window: information allowing access to current window etc
  700.  *          pattern_location:  pointer to position of pattern in file
  701.  *          finished:  boolean - stop replacing on this occurrence
  702.  *          modified:  boolean - user may decide to skip this replacement
  703.  *          direction:  direction of search
  704.  * Notes:   Show user where pattern_location is on screen if needed.
  705.  *          Replace and display if needed.
  706.  */
  707. int  replace_and_display( windows *window, text_ptr pattern_location,
  708.                           int *finished, int *modified, int direction )
  709. {
  710. int ask_user;
  711. register int rc;
  712. file_infos *file;
  713.  
  714.    file = window->file_info;
  715.    ask_user = g_status.replace_flag;
  716.    find_adjust( window, pattern_location );
  717.    xygoto( window->ccol, window->cline );
  718.    if (file->dirty) {
  719.       display_current_window( window );
  720.       file->dirty = FALSE;
  721.    }
  722.    rc = OK;
  723.    if (ask_user == PROMPT) {
  724.       show_line_col( window );
  725.       rc = ask_replace( window, finished );
  726.    }
  727.    if (rc == OK) {
  728.       do_replace( window, pattern_location, direction );
  729.       *modified = TRUE;
  730.       file->dirty = GLOBAL;
  731.       show_changed_line( window );
  732.       file->dirty = FALSE;
  733.    }
  734.    return( rc );
  735. }
  736.  
  737.  
  738. /*
  739.  * Name:    goto_top_file
  740.  * Purpose: To move the cursor to the top of the file.
  741.  * Date:    June 5, 1991
  742.  * Passed:  window: information allowing access to current window etc
  743.  */
  744. void goto_top_file( windows *window )
  745. {
  746. text_ptr next;      /* successive lines above the cursor */
  747. int i;
  748.  
  749.    un_copy_line( window->cursor, window, TRUE );
  750.    if (window->rline != window->cline - (window->top_line-1)) {
  751.       next = cpf( window->file_info->start_text );
  752.       for (i=window->cline; i>window->top_line; i--)
  753.          next = find_next( next );
  754.       window->cursor = next;
  755.       window->rline = window->cline - (window->top_line-1);
  756.       display_current_window( window );
  757.    }
  758. }
  759.  
  760. /*
  761.  * Name:    goto_end_file
  762.  * Purpose: To move the cursor to the end of the file.
  763.  * Date:    June 5, 1991
  764.  * Passed:  window: information allowing access to current window etc
  765.  */
  766. void goto_end_file( windows *window )
  767. {
  768. text_ptr prev;
  769. int i;
  770. register int j;
  771. file_infos *file;
  772.  
  773.    un_copy_line( window->cursor, window, TRUE );
  774.    file = window->file_info;
  775.    if (file->length > window->rline + window->bottom_line - window->cline) {
  776.       prev = cpb( window->file_info->end_text ) - 1;
  777.       for (j=0,i=window->bottom_line; i>window->cline; i--, j++)
  778.          prev = find_prev( prev );
  779.       window->cursor = prev;
  780.       window->rline = file->length - j + 1;
  781.       display_current_window( window );
  782.    }
  783. }
  784.  
  785. /*
  786.  * Name:    scan_forward
  787.  * Purpose: To find the corresponding occurrence of target, ignoring
  788.  *           embedded pairs of opp and target, searching forwards.
  789.  * Date:    June 5, 1991
  790.  * Passed:  start:  position of character to be paired
  791.  *          opp:    the opposite to target, if any
  792.  *          target: the string to be found
  793.  * Returns: the location of the corresponding target in the text buffer
  794.  * Notes:   Every 8,000 characters, check pointer forward.
  795.  */
  796. text_ptr scan_forward( text_ptr start, char opp, char target )
  797. {
  798. text_ptr orig;
  799. int count = 0;  /* number of unmatched opposites found */
  800. register int check = 0;
  801. char c;
  802.  
  803.    orig = start = cpf( start );
  804.    while ((c = *++start) && (c != CONTROL_Z)) {
  805.       check++;
  806.       if (opp == c)
  807.          count++;
  808.       else if (target == c) {
  809.          if (count == 0)
  810.             break;
  811.          --count;
  812.       }
  813.       if (check > 8000) {
  814.          start = cpf( start );
  815.          check = 0;
  816.       }
  817.    }
  818.    if (c == CONTROL_Z)
  819.       start = orig;
  820.    return( start );
  821. }
  822.  
  823. /*
  824.  * Name:    scan_backward
  825.  * Purpose: To find the corresponding occurrence of target, ignoring
  826.  *           embedded pairs of opp and target, searching backwards.
  827.  * Date:    June 5, 1991
  828.  * Passed:  start:  position of character to be paired
  829.  *          opp:    the opposite to target, if any
  830.  *          target: the string to be found
  831.  * Returns: the location of the corresponding target in the text buffer
  832.  */
  833. text_ptr scan_backward( text_ptr start, char opp, char target )
  834. {
  835. text_ptr orig;
  836. int count = 0;  /* number of unmatched opposites found */
  837. register int check = 0;
  838. char c;
  839.  
  840.    orig = start = cpb( start );
  841.    while ((c = *--start) && (c != CONTROL_Z)) {
  842.       check++;
  843.       if (opp == c)
  844.          count++;
  845.       else if (target == c) {
  846.          if (count == 0)
  847.             break;
  848.          --count;
  849.       }
  850.       if (check > 8000) {
  851.          start = cpb( start );
  852.          check = 0;
  853.       }
  854.    }
  855.    if (c == CONTROL_Z)
  856.       start = orig;
  857.    return( start );
  858. }
  859.  
  860. /*
  861.  * Name:    match_pair
  862.  * Purpose: To find the corresponding pair to the character under the
  863.  *           cursor.
  864.  * Date:    June 5, 1991
  865.  * Passed:  window:   information allowing access to current window etc
  866.  * Notes:   Searching is very simple-minded, and does not cope with things
  867.  *          like brackets embedded within quoted strings.
  868.  */
  869. void match_pair( windows *window )
  870. {
  871. text_ptr orig;  /* cursor location in text */
  872. int c;
  873.  
  874.    un_copy_line( window->cursor, window, TRUE );
  875.    /*
  876.     * make sure the character under the cursor is one that has a
  877.     *  matched pair
  878.     */
  879.    if (window->rcol >= linelen( window->cursor ))
  880.       return;
  881.    window->cursor = cpf( window->cursor );
  882.    orig = window->cursor + window->rcol;
  883.    c = *orig;
  884.    if (strchr( "[]{}()", c ) == NULL)
  885.       return;
  886.  
  887.    /*
  888.     * find the matching pair
  889.     */
  890.    switch (c) {
  891.    case '[':
  892.       orig = scan_forward( orig, '[', ']' );
  893.       break;
  894.    case '(':
  895.       orig = scan_forward( orig, '(', ')' );
  896.       break;
  897.    case '{':
  898.       orig = scan_forward( orig, '{', '}' );
  899.       break;
  900.    case ']':
  901.       orig = scan_backward( orig, ']', '[' );
  902.       break;
  903.    case ')':
  904.       orig = scan_backward( orig, ')', '(' );
  905.       break;
  906.    case '}':
  907.       orig = scan_backward( orig, '}', '{' );
  908.       break;
  909.    }
  910.  
  911.    /*
  912.     * now show the user what we have found
  913.     */
  914.    find_adjust( window, orig );
  915. }
  916.  
  917. /*
  918.  * Name:    goto_line
  919.  * Purpose: To move the cursor to a particular line in the file
  920.  * Date:    June 5, 1991
  921.  * Passed:  window: information allowing access to current window etc
  922.  */
  923. void goto_line( windows *window )
  924. {
  925. long number;             /* line number selected */
  926. long i;                  /* line counter */
  927. char num_str[MAX_COLS];  /* line number as string */
  928. text_ptr p;              /* used to scan through file counting lines */
  929. int prompt_line;
  930.  
  931.    un_copy_line( window->cursor, window, TRUE );
  932.    prompt_line = window->bottom_line;
  933.    /*
  934.     * find out where we are going
  935.     */
  936.    num_str[0] = '\0';
  937.    if (get_name( "Line number: ", prompt_line, num_str,
  938.                  g_display.message_color ) != OK)
  939.       return;
  940.    number = atol( num_str );
  941.  
  942.    if (number > 0  && number <= window->file_info->length) {
  943.       p = window->cursor;
  944.       i = window->rline;
  945.       if (number < window->rline) {
  946.          p = cpb( p );
  947.          for (; i>number; i--)
  948.             p = find_prev( p );
  949.       } else {
  950.          cpf( p );
  951.          for (; i<number; i++)
  952.             p = find_next( p );
  953.       }
  954.       find_adjust( window, p );
  955.    } else {
  956.       strcat( num_str, "must be in the range 1 - " );
  957.       ltoa( window->file_info->length, num_str+25, 10 );
  958.       error( WARNING, prompt_line, num_str );
  959.    }
  960. }
  961.