home *** CD-ROM | disk | FTP | other *** search
/ vsiftp.vmssoftware.com / VSIPUBLIC@vsiftp.vmssoftware.com.tar / FREEWARE / FREEWARE40.ZIP / pine / pico / search.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-04-06  |  16.0 KB  |  447 lines

  1. #if    !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: search.c,v 4.3 1993/06/15 21:14:23 erez Exp $";
  3. #endif
  4. /*
  5.  * Program:    Searching routines
  6.  *
  7.  *
  8.  * Michael Seibel
  9.  * Networks and Distributed Computing
  10.  * Computing and Communications
  11.  * University of Washington
  12.  * Administration Builiding, AG-44
  13.  * Seattle, Washington, 98195, USA
  14.  * Internet: mikes@cac.washington.edu
  15.  *
  16.  * Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  17.  *
  18.  * Copyright 1991-1993  University of Washington
  19.  *
  20.  *  Permission to use, copy, modify, and distribute this software and its
  21.  * documentation for any purpose and without fee to the University of
  22.  * Washington is hereby granted, provided that the above copyright notice
  23.  * appears in all copies and that both the above copyright notice and this
  24.  * permission notice appear in supporting documentation, and that the name
  25.  * of the University of Washington not be used in advertising or publicity
  26.  * pertaining to distribution of the software without specific, written
  27.  * prior permission.  This software is made available "as is", and
  28.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  29.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  30.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  31.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  32.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  33.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  34.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  35.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  36.  *
  37.  * Pine and Pico are trademarks of the University of Washington.
  38.  * No commercial use of these trademarks may be made without prior
  39.  * written permission of the University of Washington.
  40.  *
  41.  */
  42. /*
  43.  * The functions in this file implement commands that search in the forward
  44.  * and backward directions. There are no special characters in the search
  45.  * strings. Probably should have a regular expression search, or something
  46.  * like that.
  47.  *
  48.  */
  49.  
  50. #include        <stdio.h>
  51. #include    "osdep.h"
  52. #include    "estruct.h"
  53. #include    "pico.h"
  54. #include        "edef.h"
  55. #ifdef HEBREW
  56. #include "hebrew.h"
  57. #endif
  58. #define  Char  unsigned char
  59.  
  60. #ifdef    ANSI
  61.     int eq(int, int);
  62.     int expandp(char *, char *, int);
  63. #else
  64.     int eq();
  65.     int expandp();
  66. #endif
  67.  
  68.  
  69. /*
  70.  * Search forward. Get a search string from the user, and search, beginning at
  71.  * ".", for the string. If found, reset the "." to be just after the match
  72.  * string, and [perhaps] repaint the display. Bound to "C-S".
  73.  */
  74.  
  75. /*    string search input parameters    */
  76.  
  77. #define    PTBEG    1    /* leave the point at the begining on search */
  78. #define    PTEND    2    /* leave the point at the end on search */
  79.  
  80.  
  81.  
  82.  
  83. static char *SearchHelpText[] = {
  84. "Help for Search Command",
  85. " ",
  86. "\tEnter the words or characters you would like to search",
  87. "~\tfor, then press ~R~e~t~u~r~n.  The search then takes place.",
  88. "\tWhen the characters or words that you entered ",
  89. "\tare found, the buffer will be redisplayed with the cursor ",
  90. "\tat the beginning of the selected text.",
  91. " ",
  92. "\tThe most recent string for which a search was made is",
  93. "\tdisplayed in the \"Search\" prompt between the square",
  94. "\tbrackets.  This string is the default search prompt.",
  95. "~        Hitting only ~R~e~t~u~r~n or at the prompt will cause the",
  96. "\tsearch to be made with the default value.",
  97. "  ",
  98. "\tThe text search is not case sensitive, and will examine the",
  99. "\tentire message.",
  100. "  ",
  101. "\tShould the search fail, a message will be displayed.",
  102. "  ",
  103. "End of Search Help.",
  104. "  ",
  105. NULL
  106. };
  107.  
  108.  
  109. /*
  110.  * Compare two characters. The "bc" comes from the buffer. It has it's case
  111.  * folded out. The "pc" is from the pattern.
  112.  */
  113. eq(bc, pc)
  114. int bc;
  115. int pc;
  116. {
  117.     if ((curwp->w_bufp->b_mode & MDEXACT) == 0){
  118.     if (bc>='a' && bc<='z')
  119.       bc -= 0x20;
  120.  
  121.     if (pc>='a' && pc<='z')
  122.       pc -= 0x20;
  123.     }
  124.  
  125.     return(bc == pc);
  126. }
  127.  
  128.  
  129. forwsearch(f, n)
  130. {
  131.     register int status;
  132.     int wrapt = FALSE;
  133.     Char show[512];
  134.  
  135.     /* resolve the repeat count */
  136.     if (n == 0)
  137.       n = 1;
  138.  
  139.     if (n < 1)            /* search backwards */
  140.       return(0);
  141.  
  142.     /* ask the user for the text of a pattern */
  143.     while(1){
  144.     wkeyhelp("GC0000000000","Get Help,Cancel");
  145.     if(Pmaster == NULL)
  146.       sgarbk = TRUE;
  147.  
  148. #ifndef HEBREW
  149.     status = readpattern("Search");
  150. #else
  151.     if(!compose_heb)
  152.       status = readpattern("Search");      
  153.     else
  154.       status = readpattern("hcraeS");      
  155.     
  156. #endif
  157.     if (status == TRUE){
  158.         break;
  159.     }
  160.     else{
  161.         switch(status){
  162.           case HELPCH:            /* help requested */
  163.         if(Pmaster)
  164.           (*Pmaster->helper)(Pmaster->search_help,
  165.                      "Help for Searching", 1);
  166.         else
  167.           pico_help(SearchHelpText, "Help for Searching", 1);
  168.           case (CTRL|'L'):            /* redraw requested */
  169.         refresh(FALSE, 1);
  170.         update();
  171.         break;
  172.           default:
  173.         curwp->w_flag |= WFMODE;
  174.         if(status == ABORT)
  175.           emlwrite("Search Cancelled", NULL);
  176.         else
  177.           mlerase();
  178.         return(FALSE);
  179.         }
  180.     }
  181.     }
  182.  
  183.     curwp->w_flag |= WFMODE;
  184.  
  185.     /*
  186.      * This was added late in the game and is kind of a hack.
  187.      * What is wanted is an easy way to move immediately to the top or
  188.      * bottom of the buffer.  This makes it not-too-obvious, but saves
  189.      * key commands.
  190.      */
  191.     if(pat[0] == '\\'){
  192.     if(!strcmp(&pat[1], "top")){
  193.         gotobob(0, 1);
  194.         mlerase();
  195.         return(1);
  196.     }
  197.  
  198.     if(!strcmp(&pat[1], "bottom")){
  199.         gotoeob(0, 1);
  200.         mlerase();
  201.         return(1);
  202.     }
  203.     }
  204.  
  205.     /*
  206.      * This code is kind of dumb.  What I want is successive C-W 's to 
  207.      * move dot to successive occurences of the pattern.  So, if dot is
  208.      * already sitting at the beginning of the pattern, then we'll move
  209.      * forward a char before beginning the search.  We'll let the
  210.      * automatic wrapping handle putting the dot back in the right 
  211.      * place...
  212.      */
  213.     status = 0;        /* using "status" as int temporarily! */
  214.     while(1){
  215.     if(pat[status] == '\0'){
  216.         forwchar(0, 1);
  217.         break;        /* find next occurance! */
  218.     }
  219.  
  220.     if(status + curwp->w_doto >= llength(curwp->w_dotp) ||
  221.        !eq(pat[status],lgetc(curwp->w_dotp, curwp->w_doto + status).c))
  222.       break;        /* do nothing! */
  223.     status++;
  224.     }
  225.  
  226.     /* search for the pattern */
  227.     
  228.     while (n-- > 0) {
  229.     if((status = forscan(&wrapt,&pat[0],PTBEG)) == FALSE)
  230.       break;
  231.     }
  232.  
  233.     /* and complain if not there */
  234.     if (status == FALSE){
  235.     expandp(&pat[0], &show[0], 50);
  236. #ifdef HEBREW
  237.     if(compose_heb)reverse_line(show,strlen(show));
  238. #endif
  239.     emlwrite("\"%s\" not found", show);
  240.     } 
  241.     else if(wrapt == TRUE){
  242.     emlwrite("Search Wrapped");
  243.     }
  244.     else if(status == TRUE)
  245.       emlwrite("");
  246.  
  247.     return(status);
  248. }
  249.  
  250.  
  251. /*
  252.  * Read a pattern. Stash it in the external variable "pat". The "pat" is not
  253.  * updated if the user types in an empty line. If the user typed an empty line,
  254.  * and there is no old pattern, it is an error. Display the old pattern, in the
  255.  * style of Jeff Lomicka. There is some do-it-yourself control expansion.
  256.  * change to using <ESC> to delemit the end-of-pattern to allow <NL>s in
  257.  * the search string.
  258.  */
  259. readpattern(prompt)
  260. Char *prompt;
  261. {
  262.     register int s;
  263.     Char tpat[NPAT+20];
  264.  
  265.     strcpy(tpat, prompt);    /* copy prompt to output string */
  266.         if(pat[0] != '\0'){
  267.         strcat(tpat, " [");    /* build new prompt string */
  268.         expandp(&pat[0], &tpat[strlen(tpat)], NPAT/2);    /* add old pattern */
  269.         strcat(tpat, "]");
  270.         }
  271.     strcat(tpat, " : ");
  272.  
  273. #ifdef HEBREW
  274.     if(compose_heb)search_mode=1;
  275. #endif
  276.     s = mlreplyd(tpat, tpat, NPAT, QNORML);     /* Read pattern */
  277. #ifdef HEBREW
  278.     if(compose_heb)search_mode=0;
  279. #endif
  280.  
  281.     if (s == TRUE)                /* Specified */
  282.         strcpy(pat, tpat);
  283.     else if (s == FALSE && pat[0] != 0)    /* CR, but old one */
  284.         s = TRUE;
  285.  
  286.     return(s);
  287. }
  288.  
  289.  
  290. forscan(wrapt,patrn,leavep)    /*    search forward for a <patrn>    */
  291. int    *wrapt;        /* boolean indicating search wrapped */
  292. Char *patrn;        /* string to scan for */
  293. int leavep;        /* place to leave point
  294.                 PTBEG = begining of match
  295.                 PTEND = at end of match        */
  296.  
  297. {
  298.     register LINE *curline;        /* current line during scan */
  299.     register int curoff;        /* position within current line */
  300.     register LINE *lastline;    /* last line position during scan */
  301.     register int lastoff;        /* position within last line */
  302.     register int c;            /* character at current position */
  303.     register LINE *matchline;    /* current line during matching */
  304.     register int matchoff;        /* position in matching line */
  305.     register Char *patptr;        /* pointer into pattern */
  306.     register int stopoff;        /* offset to stop search */
  307.     register LINE *stopline;    /* line to stop search */
  308.  
  309.     *wrapt = FALSE;
  310.  
  311.     /*
  312.      * the idea is to set the character to end the search at the 
  313.      * next character in the buffer.  thus, let the search wrap
  314.      * completely around the buffer.
  315.      * 
  316.      * first, test to see if we are at the end of the line, 
  317.      * otherwise start searching on the next character. 
  318.      */
  319.     if(curwp->w_doto == llength(curwp->w_dotp)){
  320.         /*
  321.          * dot is not on end of a line
  322.          * start at 0 offset of the next line
  323.          */
  324.         stopoff = curoff  = 0;
  325.         stopline = curline = lforw(curwp->w_dotp);
  326.         if (curwp->w_dotp == curbp->b_linep)
  327.           *wrapt = TRUE;
  328.     }
  329.     else{
  330.         stopoff = curoff  = curwp->w_doto;
  331.         stopline = curline = curwp->w_dotp;
  332.     }
  333.  
  334.     /* scan each character until we hit the head link record */
  335.  
  336.     /*
  337.      * maybe wrapping is a good idea
  338.      */
  339.     while (curline){
  340.  
  341.         if (curline == curbp->b_linep)
  342.             *wrapt = TRUE;
  343.  
  344.         /* save the current position in case we need to
  345.            restore it on a match            */
  346.  
  347.         lastline = curline;
  348.         lastoff = curoff;
  349.  
  350.         /* get the current character resolving EOLs */
  351.  
  352.         if (curoff == llength(curline)) {    /* if at EOL */
  353.             curline = lforw(curline);    /* skip to next line */
  354.             curoff = 0;
  355.             c = '\n';            /* and return a <NL> */
  356.         } else
  357.             c = lgetc(curline, curoff++).c;    /* get the char */
  358.  
  359.         /* test it against first char in pattern */
  360.         if (eq(c, patrn[0]) != FALSE) {    /* if we find it..*/
  361.             /* setup match pointers */
  362.             matchline = curline;
  363.             matchoff = curoff;
  364.             patptr = &patrn[0];
  365.  
  366.             /* scan through patrn for a match */
  367.             while (*++patptr != 0) {
  368.                 /* advance all the pointers */
  369.                 if (matchoff == llength(matchline)) {
  370.                     /* advance past EOL */
  371.                     matchline = lforw(matchline);
  372.                     matchoff = 0;
  373.                     c = '\n';
  374.                 } else
  375.                     c = lgetc(matchline, matchoff++).c;
  376.  
  377.                 /* and test it against the pattern */
  378.                 if (eq(*patptr, c) == FALSE)
  379.                     goto fail;
  380.             }
  381.  
  382.             /* A SUCCESSFULL MATCH!!! */
  383.             /* reset the global "." pointers */
  384.             if (leavep == PTEND) {    /* at end of string */
  385.                 curwp->w_dotp = matchline;
  386.                 curwp->w_doto = matchoff;
  387.             } else {        /* at begining of string */
  388.                 curwp->w_dotp = lastline;
  389.                 curwp->w_doto = lastoff;
  390.             }
  391.             curwp->w_flag |= WFMOVE; /* flag that we have moved */
  392.             return(TRUE);
  393.  
  394.         }
  395. fail:;            /* continue to search */
  396.         if((curline == stopline) && (curoff == stopoff))
  397.             break;            /* searched everywhere... */
  398.     }
  399.     /* we could not find a match */
  400.  
  401.     return(FALSE);
  402. }
  403.  
  404. /*     expandp:    expand control key sequences for output        */
  405.  
  406. expandp(srcstr, deststr, maxlength)
  407.  
  408. Char *srcstr;    /* string to expand */
  409. Char *deststr;    /* destination of expanded string */
  410. int maxlength;    /* maximum chars in destination */
  411.  
  412. {
  413.     Char c;        /* current char to translate */
  414.  
  415.     /* scan through the string */
  416.     while ((c = *srcstr++) != 0) {
  417.         if (c == '\n') {        /* its an EOL */
  418.             *deststr++ = '<';
  419.             *deststr++ = 'N';
  420.             *deststr++ = 'L';
  421.             *deststr++ = '>';
  422.             maxlength -= 4;
  423.         } else if (c < 0x20 || c == 0x7f) {    /* control character */
  424.             *deststr++ = '^';
  425.             *deststr++ = c ^ 0x40;
  426.             maxlength -= 2;
  427.         } else if (c == '%') {
  428.             *deststr++ = '%';
  429.             *deststr++ = '%';
  430.             maxlength -= 2;
  431.  
  432.         } else {            /* any other character */
  433.             *deststr++ = c;
  434.             maxlength--;
  435.         }
  436.  
  437.         /* check for maxlength */
  438.         if (maxlength < 4) {
  439.             *deststr++ = '$';
  440.             *deststr = '\0';
  441.             return(FALSE);
  442.         }
  443.     }
  444.     *deststr = '\0';
  445.     return(TRUE);
  446. }