home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Frozen Fish 1: Amiga
/
FrozenFish-Apr94.iso
/
bbs
/
alib
/
d3xx
/
d352
/
mg.lha
/
MG
/
src.LZH
/
mg
/
re_search.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-05-23
|
16KB
|
702 lines
/*
* regular expression search commands for MicroGnuEmacs
*
* This file contains functions to implement several of gnuemacs' regular
* expression functions for MicroGnuEmacs. Several of the routines below are
* just minor rearrangements of the MicroGnuEmacs non-regular expression
* search functions. Hence some of them date back in essential structure to
* the original MicroEMACS; others are modifications of Rich Ellison's code.
* I, Peter Newton, wrote about half from scratch.
*
* Although I have nothing to do with the GNU project, these functions require
* the GNU project's regular expression package (files regex.c and regex.h).
* Hence, this file comes under the same copyright notice as the GNU
* project's code. As far as I know, the rest of MicroGnuEmacs need not
* since it may be used independently of any GNU project code. In any case,
* I certainly do not warrant either the correctness or utility of this code.
* The GNU project copyright notice follows. Don't you wish they would make
* it a bit shorter!
*
* Note: the above paragraph appears to be in error; just because it uses the
* GNU regex code does not mean that this code must also be distributed under
* the GNU General Public License. That is only true if it actually
* incorporates part of the GNU code. Since Peter Newton put the GNU
* copyright on it, that may be the case. In any case, I can't in good
* conscience delete the GNU copyright, since Peter released the code that
* way originally.
*/
/*
* Copyright (C) 1985 Richard M. Stallman
*
* This software may be redistributed under the terms described in the file
* LICENSE in this directory.
*/
#include "regexp.h"
#ifdef REGEXP
#include "def.h"
#include "line.h"
#include "buffer.h"
#include "window.h"
#ifdef ANSI
#include <string.h>
#include <stdlib.h>
#endif
#ifndef NO_PROTO
static int re_doreplace PROTO((RSIZE, char *, int));
static int re_forwsrch PROTO((VOID));
static int re_backsrch PROTO((VOID));
static int re_readpattern PROTO((char *));
static int killmatches PROTO((int));
static int countmatches PROTO((int));
#endif
#define SRCH_BEGIN (0) /* Search sub-codes. */
#define SRCH_FORW (-1)
#define SRCH_BACK (-2)
#define SRCH_NOPR (-3)
#define SRCH_ACCM (-4)
#define SRCH_MARK (-5)
char re_pat[NPAT]; /* Regex pattern */
int re_srch_lastdir = SRCH_NOPR; /* Last search flags. */
int casefoldsearch = TRUE; /* Does search ignore case ? */
/* Indexed by a character, gives the upper case equivalent of the character */
static char upcase[0400] =
{000, 001, 002, 003, 004, 005, 006, 007,
010, 011, 012, 013, 014, 015, 016, 017,
020, 021, 022, 023, 024, 025, 026, 027,
030, 031, 032, 033, 034, 035, 036, 037,
040, 041, 042, 043, 044, 045, 046, 047,
050, 051, 052, 053, 054, 055, 056, 057,
060, 061, 062, 063, 064, 065, 066, 067,
070, 071, 072, 073, 074, 075, 076, 077,
0100, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117,
0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127,
0130, 0131, 0132, 0133, 0134, 0135, 0136, 0137,
0140, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117,
0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127,
0130, 0131, 0132, 0173, 0174, 0175, 0176, 0177,
0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377
};
/*
* Search forward. Get a search string from the user, and search for it,
* starting at ".". If found, "." gets moved to just after the matched
* characters, and display does all the hard stuff. If not found, it just
* prints a message.
*/
/* ARGSUSED */
re_forwsearch(f, n)
{
register int s;
if ((s = re_readpattern("RE Search")) != TRUE)
return (s);
if (re_forwsrch() == FALSE) {
ewprintf("Search failed: \"%s\"", re_pat);
return (FALSE);
}
re_srch_lastdir = SRCH_FORW;
return (TRUE);
}
/*
* Reverse search. Get a search string from the user, and search, starting
* at "." and proceeding toward the front of the buffer. If found "." is left
* pointing at the first character of the pattern [the last character that
* was matched].
*/
/* ARGSUSED */
re_backsearch(f, n)
{
register int s;
if ((s = re_readpattern("RE Search backward")) != TRUE)
return (s);
if (re_backsrch() == FALSE) {
ewprintf("Search failed: \"%s\"", re_pat);
return (FALSE);
}
re_srch_lastdir = SRCH_BACK;
return (TRUE);
}
/*
* Search again, using the same search string and direction as the last
* search command. The direction has been saved in "srch_lastdir", so you
* know which way to go.
*/
/* ARGSUSED */
/*
* This code has problems-- some incompatibility(?) with extend.c causes
* match to fail when it should not.
*/
re_searchagain(f, n)
{
if (re_srch_lastdir == SRCH_NOPR) {
ewprintf("No last search");
return (FALSE);
}
if (re_srch_lastdir == SRCH_FORW) {
if (re_forwsrch() == FALSE) {
ewprintf("Search failed: \"%s\"", re_pat);
return (FALSE);
}
return (TRUE);
}
if (re_srch_lastdir == SRCH_BACK) {
if (re_backsrch() == FALSE) {
ewprintf("Search failed: \"%s\"", re_pat);
return (FALSE);
}
return (TRUE);
}
}
#include "regex.h"
#define BYTEWIDTH 8
/* Compiled regex goes here-- changed only when new pattern read */
static struct re_pattern_buffer re_buff;
static char fastmap[(1 << BYTEWIDTH)];
/* regs holds boundaries of matched text */
static struct re_registers regs;
/*
* Re-Query Replace. Replace strings selectively. Does a search and replace
* operation.
*/
/* ARGSUSED */
re_queryrepl(f, n)
{
register int s;
register int rcnt = 0; /* Replacements made so far */
register int plen; /* length of found string */
char news[NPAT]; /* replacement string */
/* Casefold check */
if (!casefoldsearch)
f = TRUE;
if ((s = re_readpattern("RE Query replace")) != TRUE)
return (s);
if ((s = ereply("Query replace %s with: ", news, NPAT, re_pat)) == ABORT)
return (s);
if (s == FALSE)
news[0] = '\0';
ewprintf("Query replacing %s with %s:", re_pat, news);
/*
* Search forward repeatedly, checking each time whether to insert or
* not. The "!" case makes the check always true, so it gets put
* into a tighter loop for efficiency.
*/
while (re_forwsrch() == TRUE) {
retry:
update();
switch (getkey(DISSCR)) {
case ' ':
plen = regs.end[0] - regs.start[0];
if (re_doreplace((RSIZE) plen, news, f) == FALSE)
return (FALSE);
rcnt++;
break;
case '.':
plen = regs.end[0] - regs.start[0];
if (re_doreplace((RSIZE) plen, news, f) == FALSE)
return (FALSE);
rcnt++;
goto stopsearch;
case CCHR('G'):/* ^G */
(VOID) ctrlg(FFRAND, 0);
case CCHR('['):/* ESC */
case '`':
goto stopsearch;
case '!':
do {
plen = regs.end[0] - regs.start[0];
if (re_doreplace((RSIZE) plen, news, f) == FALSE)
return (FALSE);
rcnt++;
} while (re_forwsrch() == TRUE);
goto stopsearch;
case CCHR('?'):/* To not replace */
break;
default:
ewprintf("<SP> replace, [.] rep-end, <DEL> don't, [!] repl rest <ESC> quit");
goto retry;
}
}
stopsearch:
curwp->w_flag |= WFHARD;
update();
if (rcnt == 0)
ewprintf("(No replacements done)");
else if (rcnt == 1)
ewprintf("(1 replacement done)");
else
ewprintf("(%d replacements done)", rcnt);
return TRUE;
}
/*
* Routine re_doreplace calls lreplace to make replacements needed by
* re_query replace. Its reason for existence is to deal with \1, \2. etc.
*/
/* Maximum length of replacement string */
#define REPLEN 256
static
re_doreplace(plen, st, f)
register RSIZE plen; /* length to remove */
char *st; /* replacement string */
int f; /* case hack disable */
{
int s;
int num, k;
register int j;
int more, state;
struct line *clp;
char repstr[REPLEN];
clp = curwp->w_dotp;
more = TRUE;
j = 0;
state = 0;
/* The following FSA parses the replacement string */
while (more) {
switch (state) {
case 0:
if (*st == '\\') {
st++;
state = 1;
} else if (*st == '\0')
more = FALSE;
else {
repstr[j] = *st;
j++;
if (j >= REPLEN)
return (FALSE);
st++;
}
break;
case 1:
if (*st >= '0' && *st <= '9') {
num = *st - '0';
st++;
state = 2;
} else if (*st == '\0')
more = FALSE;
else {
repstr[j] = *st;
j++;
if (j >= REPLEN)
return (FALSE);
st++;
state = 0;
}
break;
case 2:
if (*st >= '0' && *st <= '9') {
num = 10 * num + *st - '0';
st++;
} else {
if (num >= RE_NREGS)
return (FALSE);
k = regs.end[num] - regs.start[num];
if (j + k >= REPLEN)
return (FALSE);
bcopy(&(clp->l_text[regs.start[num]]), &repstr[j], k);
j += k;
if (*st == '\0')
more = FALSE;
if (*st == '\\') {
st++;
state = 1;
} else {
repstr[j] = *st;
j++;
if (j >= REPLEN)
return (FALSE);
st++;
state = 0;
}
}
break;
} /* end case */
} /* end while */
repstr[j] = '\0';
s = lreplace(plen, repstr, f);
return (s);
}
/*
* This routine does the real work of a forward search. The pattern is
* sitting in the external variable "pat". If found, dot is updated, the
* window system is notified of the change, and TRUE is returned. If the
* string isn't found, FALSE is returned.
*/
static
re_forwsrch()
{
register struct line *clp;
register int tbo;
int ntries;
int i;
clp = curwp->w_dotp;
tbo = curwp->w_doto;
if (tbo == clp->l_used)
/*
* Don't start matching off end of line-- must move to
* beginning of next line, unless at end
*/
if (clp != curbp->b_linep) {
clp = lforw(clp);
tbo = 0;
}
/*
* Note this loop does not process the last line, but this editor
* always makes the last line empty so this is good.
*/
while (clp != (curbp->b_linep)) {
ntries = llength(clp) - tbo;
i = re_search(&re_buff, ltext(clp), llength(clp), tbo, ntries, ®s);
if (i == -1) {
clp = lforw(clp);
tbo = 0;
} else {
curwp->w_doto = regs.end[0];
curwp->w_dotp = clp;
curwp->w_flag |= WFMOVE;
return (TRUE);
}
}
return (FALSE);
}
/*
* This routine does the real work of a backward search. The pattern is
* sitting in the external variable "re_pat". If found, dot is updated, the
* window system is notified of the change, and TRUE is returned. If the
* string isn't found, FALSE is returned.
*/
static
re_backsrch()
{
register struct line *clp;
register int tbo;
int ntries;
int i;
clp = curwp->w_dotp;
tbo = curwp->w_doto;
/* Start search one position to the left of dot */
tbo = tbo - 1;
if (tbo < 0) {
/* must move up one line */
clp = lback(clp);
tbo = llength(clp);
}
/*
* Note this loop does not process the last line, but this editor
* always makes the last line empty so this is good.
*/
while (clp != (curbp->b_linep)) {
ntries = tbo;
i = re_search(&re_buff, ltext(clp), llength(clp), tbo, -ntries, ®s);
if (i == -1) {
clp = lback(clp);
tbo = llength(clp);
} else {
curwp->w_doto = regs.start[0];
curwp->w_dotp = clp;
curwp->w_flag |= WFMOVE;
return (TRUE);
}
}
return (FALSE);
}
/*
* Read a pattern. Stash it in the external variable "re_pat". The "pat" is
* not updated if the user types in an empty line. If the user typed an empty
* line, and there is no old pattern, it is an error. Display the old
* pattern, in the style of Jeff Lomicka. There is some do-it-yourself
* control expansion.
*/
static
re_readpattern(prompt)
char *prompt;
{
register int s;
char tpat[NPAT];
char *message;
if (re_pat[0] == '\0')
s = ereply("%s: ", tpat, NPAT, prompt);
else
s = ereply("%s: (default %s) ", tpat, NPAT, prompt, re_pat);
if (s == TRUE) {
/* New pattern given */
(VOID) strcpy(re_pat, tpat);
re_buff.allocated = 40;
re_buff.buffer = (char *) malloc(re_buff.allocated);
re_buff.fastmap = fastmap;
if (casefoldsearch)
re_buff.translate = upcase;
else
re_buff.translate = '\0';
message = re_compile_pattern(re_pat, strlen(re_pat), &re_buff);
if (message != '\0') {
ewprintf("Regex Error: %s", message);
re_pat[0] = '\0';
return (FALSE);
}
re_compile_fastmap(&re_buff);
} else if (s == FALSE && re_pat[0] != '\0')
/* Just using old pattern */
s = TRUE;
return (s);
}
/*
* Cause case to not matter in searches. This is the default. If called
* with argument cause case to matter.
*/
setcasefold(f, n)
{
if (f & FFARG) {
casefoldsearch = FALSE;
ewprintf("Case-fold-search unset");
} else {
casefoldsearch = TRUE;
ewprintf("Case-fold-search set");
}
/*
* Invalidate the regular expression pattern since I'm too lazy to
* recompile it.
*/
re_pat[0] = '\0';
return (TRUE);
} /* end setcasefold */
/*
* Delete all lines after dot that contain a string matching regex
*/
delmatchlines(f, n)
{
int s;
if ((s = re_readpattern("Flush lines (containing match for regexp)")) != TRUE)
return (s);
s = killmatches(TRUE);
return (s);
}
/*
* Delete all lines after dot that don't contain a string matching regex
*/
delnonmatchlines(f, n)
{
int s;
if ((s = re_readpattern("Keep lines (containing match for regexp)")) != TRUE)
return (s);
s = killmatches(FALSE);
return (s);
}
/* This function does the work of deleting matching lines */
static
killmatches(cond)
int cond;
{
int s, i;
int count = 0;
struct line *clp;
clp = curwp->w_dotp;
if (curwp->w_doto == llength(clp))
/* Consider dot on next line */
clp = lforw(clp);
while (clp != (curbp->b_linep)) {
/* see if line matches */
i = re_search(&re_buff, ltext(clp), llength(clp), 0, llength(clp),
®s);
/* Delete line when appropriate */
if ((cond == FALSE && i == -1) || (cond == TRUE && i != -1)) {
curwp->w_doto = 0;
curwp->w_dotp = clp;
count++;
s = fdelete(llength(clp) + 1, FALSE);
clp = curwp->w_dotp;
curwp->w_flag |= WFMOVE;
if (s == FALSE)
return (FALSE);
} else
clp = lforw(clp);
}
ewprintf("%d line(s) deleted", count);
if (count > 0)
curwp->w_flag |= WFMOVE;
return (TRUE);
}
/*
* Count lines matching regex
*/
cntmatchlines(f, n)
{
int s;
if ((s = re_readpattern("Count lines (matching regexp)")) != TRUE)
return (s);
s = countmatches(TRUE);
return (s);
}
/*
* Count lines that fail to match regex
*/
cntnonmatchlines(f, n)
{
int s;
if ((s = re_readpattern("Count lines (not matching regexp)")) != TRUE)
return (s);
s = countmatches(FALSE);
return (s);
}
/* This function does the work of counting matching lines */
static
countmatches(cond)
int cond;
{
int i;
int count = 0;
struct line *clp;
clp = curwp->w_dotp;
if (curwp->w_doto == llength(clp))
/* Consider dot on next line */
clp = lforw(clp);
while (clp != (curbp->b_linep)) {
/* see if line matches */
i = re_search(&re_buff, ltext(clp), llength(clp), 0, llength(clp),
®s);
/* Count line when appropriate */
if ((cond == FALSE && i == -1) || (cond == TRUE && i != -1))
count++;
clp = lforw(clp);
}
if (cond)
ewprintf("Number of lines matching: %d", count);
else
ewprintf("Number of lines not matching: %d", count);
return (TRUE);
}
#else
#include "nullfile.h"
#endif