home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_300 / 331_01 / search.c < prev    next >
Text File  |  1990-06-14  |  15KB  |  535 lines

  1. /*
  2. HEADER:         CUG999.04;
  3. TITLE:          GED (nee QED) screen editor -- part 4;
  4. DATE:           10/08/86;
  5.  
  6. DESCRIPTION:    "Find, alter, and repeat commands for the GED editor.";
  7. KEYWORDS:       find, alter, repeat, pattern matching, search, replace;
  8. FILENAME:       GED4.C;
  9. AUTHORS:        G. Nigel Gilbert, James W. Haefner, Mel Tearle, G. Osborn;
  10. COMPILERS:      DeSmet C;
  11. */
  12.  
  13. /*
  14.      e/qed/ged  screen editor
  15.  
  16.     (C) G. Nigel Gilbert, MICROLOGY, 1981
  17.            August-December 1981
  18.  
  19.     Modified:  Aug-Dec   1984:   BDS-C 'e'(vers 4.6a) to 'qe' (J.W. Haefner)
  20.                March     1985:   BDS-C 'qe' to DeSmet-C 'qed' (J.W. Haefner)
  21.                May       1986:   converted to ged - Mel Tearle
  22.  
  23.     FILE:      ged4.c
  24.  
  25.     FUNCTIONS: info, findorrep, dofindrep, find
  26.  
  27.     PURPOSE:   perform find, alter and repeat commands
  28.  
  29. */
  30.  
  31.  
  32. #include <stdio.h>
  33. #include "ged.h"
  34.  
  35. /* ^Q quick key services */
  36.  
  37. char spatt[81];
  38. char changeto[81];
  39.  
  40. info()
  41. {
  42.     unsigned char c;
  43.  
  44.     puttext();
  45.     putmess("|F|ind, |A|lter, |K|ontext/files, |P|aragraph");
  46.     while ( (c = getlow()) != 'a' && c != 'f' && c != 'k' &&
  47.         c != 'p' && c != ESCKEY );
  48.     if (c == ESCKEY)
  49.         return;
  50.     switch (c)  {
  51.     case 'f':
  52.         findorrep(0);
  53.         break;
  54.     case 'a':
  55.         findorrep(1);
  56.         break;
  57. /* the wordstar ^QY clear to end of line command.  disabled here because
  58.  * this version uses ^Y to delete from cursor to end of line
  59.  *
  60.  *  case 'y':
  61.  *      cleareol(cursorx,cursory);
  62.  *      altered = YES;
  63.  *      text[charn] = '\0';
  64.  *      break;
  65.  */
  66.     case 'k':
  67.         envir();
  68.         break;
  69.     case 'p':
  70.         putmess("(F1 = dot help)  Delete dot commands while reforming?  (Y/N)");
  71.         while ( (c = getlow()) != 'y' && c != 'n' && c != ESCKEY && c != F1KEY);
  72.         if (c == ESCKEY)
  73.             return;
  74.         if (c == F1KEY) {
  75.             helpdot();
  76.             return;
  77.         }
  78.         if (c == 'y')
  79.             roff(0);
  80.         else
  81.             roff(1);
  82.         break;
  83.     }
  84. }
  85.  
  86.  
  87. /* initiate string search or search and replace.  Called by F2 key
  88.  * with mode = 0 or from above.
  89.  * F2 str F3  search back
  90.  * F2 str F4  search fwd
  91.  * F2 str1 F2 str2 F3 replace backward
  92.  * F2 str1 F2 str2 F4 replace forward
  93.  * F3 & F4 & ^L  resume
  94.  */
  95. findorrep(mode)
  96. int mode;
  97. {
  98.     int i, cnt;
  99.     int c;
  100.     char tpat[80];
  101.     puttext();
  102.  
  103.     putmess("|Find|? ");
  104.     c = scans(tpat,80);
  105.     if ( c == ESCKEY || !tpat[0] )
  106.         return;
  107.     strcpy(spatt, tpat);  /* move valid string to permanent storage */
  108.     if (c == F2KEY)
  109.         replace = YES;
  110.     else
  111.         replace = mode;
  112.  
  113.     if ( replace )  {
  114.         putmess("Alter to? ");
  115.         c = scans(changeto,80);  /* null replacemente allowed */
  116.         if (!ctrl) {
  117.             if (c == ESCKEY)
  118.                 return;     /* no escape if <esc> can be embedded */
  119.             for (i = 0; (changeto[i]); i++) {
  120.                 if (changeto[i] < ' ') {
  121.                     error("Use -C option to embed ctrl codes (F7)");
  122.                     return;
  123.                 }
  124.             }
  125.         }
  126.     }
  127.     else {
  128.         changeto[0] = '\0';
  129.     }
  130.  
  131. /* Global is the only option because this would be an awkward way to
  132.  * replace one word.
  133.  * To do the entire document it is necessary to use the <home> key first
  134.  * to jump to line 1.
  135.  * It is planned to add a wrap at end of file option, probably as the default.
  136.  */
  137.  
  138. /*defaults */
  139.     nocheck = NO;
  140.     cnt  = 0;
  141.     findir = 1;
  142.  
  143.     if ( c == F3KEY) {
  144.         findir = -1;
  145.     }
  146.     else if (c == F4KEY) {
  147.         findir = 1;
  148.     }
  149. /* string terminated by <ret> */
  150.     else {
  151.         if ( replace )
  152.             putmess("|B|ackward,  |W|ithout asking, |count| ");
  153.         else
  154.             putmess("|<ret>|, |B|ackwards, |count| ");
  155.         if ( scans(opts,5) == ESCKEY )
  156.             return;
  157.  
  158.         for ( i = 0; ( c = opts[i]); i++ )  {
  159.             switch(tolower(c)) {
  160.             case 'b':
  161.                 findir = -1;
  162.                 break;
  163.             case 'w':
  164.                 nocheck = YES;
  165.                 break;
  166.             default:
  167.                 if ( c >= '0' && c <= '9' )
  168.                     cnt = cnt*10+c-'0';
  169.             }
  170.         }
  171.     }
  172.     if ( cnt == 0 )  {
  173.         if (replace)
  174.             cnt = -1; /* unlimited */
  175.         else
  176.             cnt = 1;
  177.     }
  178.     dofindrep(cnt,findir);
  179. }
  180.  
  181.  
  182.  
  183. /* Resume a search or search and replace
  184.  * Called by F4, F5, ^L, and from above.
  185.  * All searchs and search/replace operations wrap at beginning and end of file.
  186.  * The first possible match is the first char beyond
  187.  * the cursor position.  The last possible match is at the initial
  188.  * cursor position.
  189.  */
  190. int ncline, count, oldlen, newlen;
  191.  
  192. dofindrep(count1,dir)
  193. int count1,dir;
  194. {
  195.     int i, j, l2, s0, ocharn, ocline, ncharn;
  196.     char c;
  197.     char buf[81+35];
  198.  
  199.  
  200.     puttext();
  201.     count = count1;
  202.     findir = dir;  /* F2 and F3 keys permanently change directin */
  203.     oldlen = strlen(spatt);
  204.     ocline = cline;
  205.     ncline = cline;
  206.     ocharn = charn;
  207.     ncharn = charn;
  208.     if (findir > 0) {
  209.         newlen = 1;    /* don't start at the cursor */
  210.  
  211.         putmess1("    ", 28, 5);
  212.         while (i = find(charn+newlen, cline, LLIM, lastl)) {
  213.             if (i < 0)
  214.                 return;
  215.             if (!dorep())
  216.                 return;
  217.             ncline = cline;
  218.             ncharn = charn;
  219.         }
  220.         putmess1("EOF", 28, 5);
  221.         wait(1);
  222.         puttext();
  223.         charn = newlen = 0;
  224.         cline = 1;
  225.         plast = -1;
  226.         gettext(cline, 0);
  227.         while (i = find(charn+newlen, cline, ocharn, ocline)) {
  228.             if (i < 0)
  229.                 return;
  230.             s0 = strlen(text);
  231.             if (!dorep())
  232.                 return;
  233.             if (cline == ocline)
  234.                 ocharn += strlen(text) - s0;
  235.             ncline = cline;
  236.             ncharn = charn;
  237.         }
  238.     }
  239.     else {
  240.         s0 = strlen(text);
  241.         while (i = find(charn-1, cline, 0, cline)) {
  242.             if (i < 0)
  243.                 return;
  244.             if (!dorep())
  245.                 return;
  246.             ncline = cline;
  247.             ncharn = charn;
  248.         }
  249.         ocharn += strlen(text) - s0; /* terminal point changes with replacemets */
  250.         charn = 0;
  251.         putmess1("    ", 28, 5);
  252.         while (i = find(charn-1, cline, 0, 1)) {
  253.             if (i < 0)
  254.                 return;
  255.             if (!dorep())
  256.                 return;
  257.             ncline = cline;
  258.             ncharn = charn;
  259.         }
  260.         puttext();
  261.         putmess1("BOF", 28, 5);
  262.         wait(1);
  263.         cline = lastl;
  264.         plast = -1;
  265.         gettext(cline, 0);
  266.         charn=strlen(text);
  267.         while (i = find(charn-1, cline, ocharn, ocline)) {
  268.             if (i < 0) {
  269.                 return;
  270.             }
  271.             if (!dorep())
  272.                 return;
  273.             ncline = cline;
  274.             ncharn = charn;
  275.         }
  276.     }
  277.     strcpy(buf,"Search for '");
  278.     strcat(buf,spatt);
  279.     strcat(buf,"' fails");
  280.     error1(buf);
  281.     wait(2);
  282.     putstatusline(ncline);
  283.     charn = ncharn;
  284.     moveline(ncline-cline);
  285.     curson(YES);
  286.     return;
  287. }
  288.  
  289. int dorep()
  290. {
  291.     int i, j;
  292.     char c;
  293.  
  294.     if (!replace)
  295.         return 0;
  296.  
  297.  
  298.     if ( nocheck )
  299.         c = 'y';
  300.     else  {
  301.         blankedmess = YES;
  302.         putlineno(cline);
  303.         putmess1("Replace  |<esc>|/|Y|/|N| ?", 34, 37);
  304.         do {
  305.             gotoxy(55,0);  /* position just after prompt */
  306.             for (i=0; i < 3000; i++);
  307.             resetpcursor();
  308.             for (i=0; i < 3000; i++);
  309.         }
  310.         while (chkbuf() == 0);
  311.  
  312.         c = testlow();
  313.     }
  314.     switch(c)  {
  315.     case 'y' :
  316.         newlen = strlen(changeto);   /* null replacement allowed */
  317.         if ( strlen(text) + newlen - oldlen + 1 > LLIM )  {
  318.             error(" Line would be too long ");
  319.             return;
  320.         }
  321. /* delete the old word */
  322.         for ( j = charn; (text[j] = text[j + oldlen]); j++ )
  323.             ;
  324.  
  325. /* make room. move '\0' also, for strlen+1 moves */
  326.         j = strlen(text);
  327.         for (  ; j >= charn;  j-- )
  328.             text[j+newlen]=text[j];
  329.  
  330.         j = charn;
  331.         for ( i = 0; (c=changeto[i]); i++ )
  332.             text[j++] = c;
  333.  
  334. /* the line is not stored immediately because there may be other changes
  335.                    in it */
  336.         altered = YES;
  337.         rewrite( charn );
  338.         sync(charn);
  339.         break;
  340.     default:   /* no */
  341.         newlen = 1;
  342.         break;
  343.     case ESCKEY :
  344. /* move off the string start so it will be picked up on a resume */
  345.         movechar(-findir);
  346.         error1(" Search stopped.  ^L, F3, F4 to resume. ");
  347.         wait(1);
  348.         return 0;
  349.         break;
  350.     }
  351.     count--;
  352.     if (count == -2)
  353.         count = -1;   /* -1 = unlimited count */
  354.     if (count = 0)
  355.         return 0;
  356.     else
  357.         return 1;
  358. }
  359.  
  360. /* find 'spatt', searching backwards ( findir==-1)
  361.   or forwards (findir==1).  The search starts at text[cp].
  362.   If cp is -1 and the search is backward the search begins at the
  363.   end of the preceeding line.  If forward and off the end of the
  364.   line the search begins on the following line.  Lines between line1
  365.   and line2 are searched.
  366.  
  367.   Returns 1 if found, 0 if not found, -1 if aborted.
  368.  
  369.   No wild cards in this version.  When the wildcard option is added
  370.   a different and slower search algorithm should be used, retaining
  371.   this code if no wild cards are specified.  In that way performance
  372.   is not lost for the normal case.
  373.  
  374.   On a 5 MHz IBM PC this version searches text of typical line length
  375.   at 30,000 characters per second, provided that all of it is in RAM.
  376.   This performance is important for large documents.
  377.  */
  378. int find(cp, line1, cp2, line2)      /* local function */
  379. int cp, line1, cp2, line2;
  380. {
  381.     unsigned char testkey();
  382.     int   i, j, k, m, fline, oldcharn, interupt, linecount;
  383.     char  *s, pattch1, *p, *t, *padr, *getline();
  384.     char *strstr();
  385.     long jj;
  386.  
  387. /* the cursor is on the line being searched, which is not on the screen */
  388.     curson(NO);
  389.     fline = line1;
  390.     oldcharn = charn;
  391.     interupt = NO;
  392.     linecount = cline % 100;
  393.     pattch1 = spatt[0];
  394.  
  395.     if ( findir == 1 ) {
  396.         if ( cp >= strlen(text) ) {
  397.             fline++;
  398.             cp = 0;
  399.         }
  400. /* first search from the cursor+1 to line end of the first line, then all
  401.  * of the following lines.  The library routines are used where
  402.  * possible because they are often faster.
  403.  */
  404.  
  405.         while ( fline <= line2 )  {
  406. /* return null if spatt not in line */
  407.             if (fline == cline)
  408.                 p = text;
  409.             else
  410.                 p = getline(fline);
  411.  
  412.             s = strstr(p+cp, spatt);
  413.             if (!(s == NULL)) {
  414.                 cp = s-p;
  415.                 if (fline == line2 && cp > cp2)
  416.                     goto fail;
  417.                 else
  418.                     goto  foundit;
  419.             }
  420.  
  421. /* Check for input key occasionally.  The search operation is slowed
  422.  * excessively if this test is made on every line.  The test
  423.  * is made on every replacement, though.
  424.  */
  425.             if ( (fline % 200) == 0 || fline == cline) {
  426.                 if (chkbuf() != 0) {
  427.                     inbufp = 0;    /* discard the keystroke */
  428.                     interupt = YES;
  429.                     goto  interrupted;
  430.                 }
  431.             }
  432.             fline++;
  433.             cp=0;  /* cp not always set by above loop */
  434.         }
  435.     }
  436. /* search backward */
  437.     else {
  438.         if (cp < 0)
  439.             fline--;
  440.  
  441.         while ( fline >= line2 )  {
  442.  
  443. /* do a fast forward test to find if the string is in the line,
  444.  * then search all or a portion of the line backward.
  445.  */
  446.             if (fline == cline)
  447.                 padr = text;
  448.             else
  449.                 padr = getline(fline);
  450.             if (cp < 0)
  451.                 cp = strlen(padr) - 1;    /* -1 <= cp */
  452.             t = strstr(padr, spatt);
  453.             if (!(t == NULL)) {
  454.                 for ( s = padr; cp >= 0; cp--) {
  455.                     if ( *( p = s+cp) == pattch1 )  {
  456.                         for ( t = &spatt[1], p++; *t && (*p == *t); p++, t++ )
  457.                             ;
  458.                         if (*t == '\0') {
  459.                             if (fline == line2 && cp < cp2)
  460.                                 goto fail;
  461.                             else
  462.                                 goto  foundit;
  463.                         }
  464.                     }
  465.                 }
  466.             }
  467.             if (fline % 200 == 0 || fline == cline) {
  468.                 if (chkbuf() != 0) {
  469.                     inbufp = 0;   /* discard the keystroke */
  470.                     interupt = YES;
  471.                     goto  interrupted;
  472.                 }
  473.             }
  474.             cp = -1;
  475.             fline--;  /* do next line */
  476.         }
  477. /* line loop falls through.  not found. */
  478.     }
  479. fail:;
  480.     return 0;
  481.  
  482. interrupted:;
  483.     error1("Search aborted");
  484.     wait(1);
  485.     putstatusline(ncline);
  486.     moveline(ncline-cline);
  487.     curson(YES);
  488.     return -1;
  489.  
  490. /* only one copy of the text buffer is stored below by moveline, regardless of
  491.  * the number of changes in it.
  492.  */
  493.  
  494. foundit:;
  495. /* position the screen horizontally so both the new and old strings are
  496.  * visible in context.
  497.  */
  498.     i = strlen(changeto);   /* newlen not current */
  499.     k = (i > newlen) ? i : newlen;
  500.     m = cp + k;
  501.  
  502.     if (cp < charn) {
  503.         if (cp < SWIDTH-k)
  504.             calcoffset(0);
  505.         else
  506.             calcoffset(cp-10);
  507.     }
  508.     else {
  509. /* favor no offet.  if offset required anyway then show a little more */
  510.         if (m > SWIDTH)
  511.             m += 10;
  512.         calcoffset(m);
  513.     }
  514.  
  515.     if (fline != cline || plast < 0) {  /* scroll vertically if necessary */
  516.         charn = 0;   /* required by a consistancy check in puttext() */
  517.         puttext();     /* store the changes */
  518.         if (fline > plast+2 || fline < pfirst - 2)
  519.             cursory = topline + (SHEIGHT-topline)/2;  /* re-optimize cursor position if new text area */
  520.         charn = cp;
  521.         moveline(fline-cline);
  522.     }
  523.     else {
  524.         sync(cp);  /* won't cause horz scroll */
  525.         rewrite(0);
  526.     }
  527.     putstatusline(cline);
  528.     blankedmess = NO;
  529.     curson(YES);
  530.     return  1;
  531. }
  532.  
  533.  
  534.  
  535.