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