home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
rtsi.com
/
2014.01.www.rtsi.com.tar
/
www.rtsi.com
/
OS9
/
OSK
/
CMDS
/
memacs400_src.lzh
/
MEMACS400
/
SRC
/
search.c
< prev
next >
Wrap
Text File
|
1996-04-25
|
43KB
|
1,867 lines
/*
* The functions in this file implement commands that search in the forward
* and backward directions.
*
* (History comments formerly here have been moved to history.c)
*/
#include <stdio.h>
#include "estruct.h"
#include "eproto.h"
#include "edef.h"
#include "elang.h"
#ifdef OSK
#define direct direct0
#endif
#define SEARCH_HIGHLIGHT 10 /* mark # used to highlight */
#define MAGIC_JUMP_TABLES 1 /* Jump tables in MAGIC mode */
#if MAGIC
static int group_count;
static int group_len[MAXGROUPS];
static REGION group_reg[MAXGROUPS];
#endif
#if WINDOW_MSWIN
static int o = 0; /* For longop() calls.*/
#endif
/*
* forwsearch -- Search forward. Get a search string from the user, and
* search for the string. If found, reset the "." to be just after
* the match string, and (perhaps) repaint the display.
*/
#if PROTO
int PASCAL NEAR forwsearch(int f, int n)
#else
int PASCAL NEAR forwsearch( f, n)
int f;
int n;
#endif
{
register int status;
/* If n is negative, search backwards.
* Otherwise proceed by asking for the search string.
*/
if (n < 0)
return (backsearch(f, -n));
/* Ask the user for the text of a pattern. If the response is
* TRUE (responses other than FALSE are possible), search for
* pattern for up to n times, as long as the pattern is there
* to be found.
*/
if ((status = readpattern(TEXT78, (char *)&pat[0], TRUE)) == TRUE)
status = forwhunt(f, n);
/* "Search" */
return (status);
}
/*
* forwhunt -- Search forward for a previously acquired search string.
* If found, reset the "." to be just after the match string,
* and (perhaps) repaint the display.
*/
#if PROTO
int PASCAL NEAR forwhunt(int f, int n)
#else
int PASCAL NEAR forwhunt( f, n)
int f;
int n;
#endif
{
register int spoint = PTEND;
register int status;
if (n < 0) /* search backwards */
return (backhunt(f, -n));
/* Make sure a pattern exists, or that we didn't switch
* into MAGIC mode after we entered the pattern.
*/
if (pat[0] == '\0') {
mlwrite(TEXT80);
/* "No pattern set" */
return FALSE;
}
#if MAGIC
if ((curwp->w_bufp->b_mode & MDMAGIC) && mcpat[0].mc_type == MCNIL) {
if (!mcstr())
return FALSE;
}
#endif
/*
* Do one extra search to get us past our current
* match, if the search type has us at the start
* of a match, instead of after a match.
*/
if (searchtype == SRBEGIN) {
spoint = PTBEG;
if (lastflag & CFSRCH)
n = (n > 2)? (n + 1): 2;
}
#if MAGIC
#if 0
if (magical && (curwp->w_bufp->b_mode & MDMAGIC))
status = mcscanner(&mcpat[0], FORWARD, spoint, n);
else
status = mcscanner(&mcdeltapat[0], FORWARD, spoint, n);
#else
status = mcscanner((magical && (curwp->w_bufp->b_mode & MDMAGIC))?
&mcpat[0]: &mcdeltapat[0], FORWARD, spoint, n);
#endif
#else
status = scanner(FORWARD, spoint, n);
#endif
/* Complain if not there.
*/
if (status == FALSE)
mlwrite(TEXT79);
/* "Not found" */
thisflag |= CFSRCH;
return (status);
}
/*
* backsearch -- 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).
*/
#if PROTO
int PASCAL NEAR backsearch(int f, int n)
#else
int PASCAL NEAR backsearch( f, n)
int f;
int n;
#endif
{
register int status;
/* If n is negative, search forwards.
* Otherwise proceed by asking for the search string.
*/
if (n < 0)
return (forwsearch(f, -n));
/* Ask the user for the text of a pattern. If the response is
* TRUE (responses other than FALSE are possible), search for
* pattern for up to n times, as long as the pattern is there
* to be found.
*/
if ((status = readpattern(TEXT81, (char *)&pat[0], TRUE)) == TRUE)
status = backhunt(f, n);
/* "Reverse search" */
return (status);
}
/*
* backhunt -- Reverse search for a previously acquired search string,
* 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).
*/
#if PROTO
int PASCAL NEAR backhunt(int f, int n)
#else
int PASCAL NEAR backhunt( f, n)
int f;
int n;
#endif
{
register int spoint = PTBEG;
register int status;
if (n < 0)
return (forwhunt(f, -n));
/* Make sure a pattern exists, or that we didn't switch
* into MAGIC mode after we entered the pattern.
*/
if (tap[0] == '\0') {
mlwrite(TEXT80);
/* "No pattern set" */
return FALSE;
}
#if MAGIC
if ((curwp->w_bufp->b_mode & MDMAGIC) && tapcm[0].mc_type == MCNIL) {
if (!mcstr())
return FALSE;
}
#endif
/*
* Do one extra search to get us past our current
* match, if the search type has us at the start
* of a match, instead of after a match.
*/
if (searchtype == SREND) {
spoint = PTEND;
if (lastflag & CFSRCH)
n = (n > 2)? (n + 1): 2;
}
#if MAGIC
#if 0
if (magical && (curwp->w_bufp->b_mode & MDMAGIC))
status = mcscanner(&tapcm[0], REVERSE, spoint, n);
else
status = mcscanner(&tapatledcm[0], REVERSE, spoint, n);
#else
status = mcscanner((magical && (curwp->w_bufp->b_mode & MDMAGIC))?
&tapcm[0]: &tapatledcm[0], REVERSE, spoint, n);
#endif
#else
status = scanner(REVERSE, spoint, n);
#endif
/* Complain if not there.
*/
if (status == FALSE)
mlwrite(TEXT79);
/* "Not found" */
thisflag |= CFSRCH;
return (status);
}
#if MAGIC
/*
* mcscanner -- Search for a meta-pattern in either direction. If found,
* reset the "." to be at the start or just after the match string,
* and (perhaps) repaint the display.
*/
#if PROTO
int PASCAL NEAR mcscanner(MC *mcpatrn, int direct, int beg_or_end, int repeats)
#else
int PASCAL NEAR mcscanner( mcpatrn, direct, beg_or_end, repeats)
MC *mcpatrn;
int direct;
int beg_or_end;
int repeats;
#endif
{
LINE *curline; /* current line during scan */
int curoff; /* position within current line */
DELTA *tbl; /* structure with jump info */
int patlenadd;
int jump;
/* If we are going in reverse, then the 'end' is actually
* the beginning of the pattern. Toggle it.
*/
beg_or_end ^= direct;
#if MAGIC_JUMP_TABLES
if (mcpatrn->mc_type == JMPTABLE) {
tbl = mcpatrn->u.jmptable;
jump = tbl->jump;
patlenadd = tbl->patlen;
mcpatrn++;
}
else
tbl = NULL;
#endif
/* Setup local scan pointers to global ".".
*/
curline = curwp->w_dotp;
curoff = curwp->w_doto;
/* Scan each character until we hit the head link record.
*/
while (!boundry(curline, curoff, direct)) {
/* Save the current position in case we need to
* restore it on a match, and initialize matchlen to
* zero in case we are doing a search for replacement.
*/
matchline = curline;
matchoff = curoff;
matchlen = 0;
#if MAGIC_JUMP_TABLES
/*
* If the first thing to look for is a constant string
* that has been made into a jump table, use the fast
* scan routines. If that fails, then we have nothing
* to match and return immediately.
*/
if (tbl != NULL) {
if (!fbound(tbl, patlenadd, &curline, &curoff, direct)) {
do
{
if (direct == FORWARD)
movelocalpoint(-patlenadd, &curoff, &curline);
else
movelocalpoint(patlenadd + 1, &curoff, &curline);
matchline = curline;
matchoff = curoff;
if ((matchlen = liteq(&curline, &curoff, direct, tbl->patrn)) > 0)
break;
} while (!fbound(tbl, jump, &curline, &curoff, direct));
}
if (matchlen == 0)
return FALSE;
}
#endif
#if WINDOW_MSWIN
/* to lower overhead, only 1/100 calls to longop */
if (--o < 0) {
longop(TRUE);
o = 100;
}
#endif
if (amatch(mcpatrn, direct, &curline, &curoff)) {
/* A SUCCESSFULL MATCH!!!
* Flag that we have moved,
* reset the global "." pointers.
*/
curwp->w_markp[SEARCH_HIGHLIGHT] = matchline;
curwp->w_marko[SEARCH_HIGHLIGHT] = matchoff;
curwp->w_markp[SEARCH_HIGHLIGHT+1] = curline;
curwp->w_marko[SEARCH_HIGHLIGHT+1] = curoff;
curwp->w_flag |= WFMOVE;
if (beg_or_end == PTEND) /* at end of string */
{
curwp->w_dotp = curline;
curwp->w_doto = curoff;
}
else /* at beginning of string */
{
curwp->w_dotp = matchline;
curwp->w_doto = matchoff;
}
/* If we're heading in reverse, set the "match"
* pointers to the start of the string, for savematch().
*/
if (direct == REVERSE) {
matchline = curline;
matchoff = curoff;
}
if (savematch() == ABORT)
return ABORT;
/* Continue scanning if we haven't found
* the nth match.
*/
if (--repeats <= 0)
return TRUE;
}
/* Advance the cursor.
*/
nextch(&curline, &curoff, direct);
}
return FALSE; /* We could not find a match.*/
}
/*
* amatch -- Search for a meta-pattern in either direction. Update
* the passed-in LINE and offset values if successful.
* Based on the recursive routine amatch() (for "anchored match")
* in Kernighan & Plauger's "Software Tools".
*/
#if PROTO
int PASCAL NEAR amatch(MC *mcptr, int direct, LINE **pcwline, int *pcwoff)
#else
int PASCAL NEAR amatch( mcptr, direct, pcwline, pcwoff)
MC *mcptr;
int direct;
LINE **pcwline;
int *pcwoff;
#endif
{
LINE *curline; /* current line during scan */
int curoff; /* position within current line */
int pre_matchlen; /* matchlen before a recursive amatch() call.*/
int cl_matchlen; /* number of chars matched in a local closure.*/
int cl_min; /* minimum number of chars matched in closure.*/
int cl_type; /* Which closure type?.*/
/* Set up local scan pointers to ".",
* and set up our local character counting
* variable, which can correct matchlen on
* a failed partial match.
*/
curline = *pcwline;
curoff = *pcwoff;
cl_matchlen = 0;
/*
* Loop through the meta-pattern, laughing all the way.
*/
while (mcptr->mc_type != MCNIL) {
/* Is the current meta-character modified
* by a closure?
*/
if (cl_type = (mcptr->mc_type & ALLCLOS)) {
/* Minimum number of characters that may
* match is 0 or 1.
*/
cl_min = (cl_type == CLOSURE_1);
if (cl_type == ZEROONE)
{
if (mceq(nextch(&curline, &curoff, direct), mcptr))
{
nextch(&curline, &curoff, direct);
cl_matchlen++;
}
}
else
{
/* Match as many characters as possible
* against the current meta-character.
*/
while (mceq(nextch(&curline, &curoff, direct), mcptr))
cl_matchlen++;
}
/* We are now at the character that made us
* fail. Try to match the rest of the pattern.
* Shrink the closure by one for each failure.
*/
mcptr++;
matchlen += cl_matchlen;
for (;;)
{
if (cl_matchlen < cl_min) {
matchlen -= cl_matchlen;
return FALSE;
}
nextch(&curline, &curoff, direct ^ REVERSE);
pre_matchlen = matchlen;
if (amatch(mcptr, direct, &curline, &curoff))
goto success;
matchlen = pre_matchlen - 1;
cl_matchlen--;
}
}
else if (mcptr->mc_type == GRPBEG) {
group_reg[mcptr->u.group_no].r_offset = curoff;
group_reg[mcptr->u.group_no].r_linep = curline;
group_reg[mcptr->u.group_no].r_size = (direct == FORWARD)? -matchlen: matchlen;
}
else if (mcptr->mc_type == GRPEND) {
group_len[mcptr->u.group_no] = (direct == FORWARD)? matchlen: -matchlen;
}
else if (mcptr->mc_type == BOL) {
if (curoff != 0)
return FALSE;
}
else if (mcptr->mc_type == EOL) {
if (curoff != lused(curline))
return FALSE;
}
else if (mcptr->mc_type == BOWRD) {
if (!isinword(lgetc(curline, curoff)))
return FALSE;
if (curoff != 0 &&
isinword(lgetc(curline, curoff - 1)))
return FALSE;
}
else if (mcptr->mc_type == EOWRD) {
if (isinword(lgetc(curline, curoff)))
return FALSE;
if (curoff == 0 ||
!isinword(lgetc(curline, curoff - 1)))
return FALSE;
}
else if (mcptr->mc_type == LITSTRING) {
if ((pre_matchlen = liteq(&curline, &curoff, direct, mcptr->u.lstring)) == 0)
return FALSE;
matchlen += pre_matchlen;
}
else
{
/* A character to compare against
* the meta-character equal function.
* If we still match, increment the length.
*/
if (!mceq(nextch(&curline, &curoff, direct), mcptr))
return FALSE;
matchlen++;
}
mcptr++;
} /* End of mcptr loop.*/
/* A SUCCESSFULL MATCH!!!
* Reset the "." pointers.
*/
success:
*pcwline = curline;
*pcwoff = curoff;
return (TRUE);
}
#else
/*
* scanner -- Search for a pattern in either direction. If found,
* reset the "." to be at the start or just after the match string,
* and (perhaps) repaint the display.
* Fast version using simplified version of Boyer and Moore
* Software-Practice and Experience, vol 10, 501-506 (1980)
*/
#if PROTO
int PASCAL NEAR scanner(int direct, int beg_or_end, int repeats)
#else
int PASCAL NEAR scanner( direct, beg_or_end, repeats)
int direct;
int beg_or_end;
int repeats;
#endif
{
DELTA *tbl; /* structure holding the jump info */
char *patrn; /* string to scan for */
LINE *curline; /* current line during scan */
int curoff; /* position within current line */
int jump; /* next offset */
int patlenadd;
/* If we are going in reverse, then the 'end' is actually
* the beginning of the pattern. Toggle it.
*/
beg_or_end ^= direct;
/* Another directional problem: if we are searching
* forwards, use the pattern and the jump size in
* deltapat. Otherwise, use the reversed pattern
* pattern and the jump size in tapatled.
*/
if (direct == FORWARD) {
tbl = &deltapat;
patrn = deltapat.patrn;
patlenadd = deltapat.patlen;
jump = deltapat.jump;
}
else
{
tbl = &tapatled;
patrn = tapatled.patrn;
patlenadd = tapatled.patlen;
jump = tapatled.jump;
}
/* Set up local pointers to global ".".
*/
curline = curwp->w_dotp;
curoff = curwp->w_doto;
/* Scan each character until we hit the head link record.
* Get the character resolving newlines, offset
* by the pattern length, i.e. the last character of the
* potential match.
*/
if (!fbound(tbl, patlenadd, &curline, &curoff, direct)) {
do
{
/* Set up the scanning pointers, and save
* the current position in case we match
* the search string at this point.
*/
if (direct == FORWARD)
movelocalpoint(-patlenadd, &curoff, &curline);
else
movelocalpoint(patlenadd + 1, &curoff, &curline);
matchline = curline;
matchoff = curoff;
if (liteq(&curline, &curoff, direct, patrn) == 0)
goto fail;
/* A SUCCESSFULL MATCH!!!
* Flag that we have moved and reset the
* global "." pointers.
*/
curwp->w_markp[SEARCH_HIGHLIGHT] = matchline;
curwp->w_marko[SEARCH_HIGHLIGHT] = matchoff;
curwp->w_markp[SEARCH_HIGHLIGHT+1] = curline;
curwp->w_marko[SEARCH_HIGHLIGHT+1] = curoff;
curwp->w_flag |= WFMOVE;
if (beg_or_end == PTEND) /* at end of string */
{
curwp->w_dotp = curline;
curwp->w_doto = curoff;
}
else /* at beginning of string */
{
curwp->w_dotp = matchline;
curwp->w_doto = matchoff;
}
/* If we're heading in reverse, set the "match"
* pointers to the start of the string, for savematch().
*/
if (direct == REVERSE) {
matchline = curline;
matchoff = curoff;
}
matchlen = patlenadd + 1;
if (savematch() == ABORT)
return ABORT;
/* Continue scanning if we haven't found
* the nth match.
*/
if (--repeats <= 0)
return (TRUE);
fail:; /* continue to search */
} while (!fbound(tbl, jump, &curline, &curoff, direct));
}
return FALSE; /* We could not find a match */
}
#endif
/*
* fbound -- Return information depending on whether we have hit a boundry
* (and may therefore search no further) or if a trailing character
* of the search string has been found. See boundry() for search
* restrictions.
*/
#if PROTO
int PASCAL NEAR fbound(DELTA *tbl, int jump, LINE **pcurline, int *pcuroff, int dir)
#else
int PASCAL NEAR fbound( tbl, jump, pcurline, pcuroff, dir)
DELTA *tbl;
int jump;
LINE **pcurline;
int *pcuroff;
int dir;
#endif
{
int curoff;
LINE *curline;
curline = *pcurline;
curoff = *pcuroff;
if (dir == FORWARD) {
while (jump != 0) {
#if WINDOW_MSWIN
/* to lower overhead, only 1/100 calls to longop */
if (--o < 0) {
longop(TRUE);
o = 100;
}
#endif
if (movelocalpoint(jump, &curoff, &curline))
return (TRUE); /* hit end of buffer */
if (curoff == lused(curline))
jump = tbl->delta[(int) '\r'];
else
jump = tbl->delta[(int) lgetc(curline, curoff)];
}
}
else /* Reverse.*/
{
jump++; /* allow for offset in reverse */
while (jump != 0) {
#if WINDOW_MSWIN
/* to lower overhead, only 1/100 calls to longop */
if (--o < 0) {
longop(TRUE);
o = 100;
}
#endif
if (movelocalpoint(-jump, &curoff, &curline))
return (TRUE); /* hit end of buffer */
if (curoff == lused(curline))
jump = tbl->delta[(int) '\r'];
else
jump = tbl->delta[(int) lgetc(curline, curoff)];
}
}
*pcurline = curline;
*pcuroff = curoff;
return FALSE;
}
/*
* movelocalpoint -- Take the passed-in offset and LINE pointers, and
* adjust them by n characters. If boundry is hit, leave the pointers
* alone and return TRUE. Otherwise all went well, and return FALSE.
*/
#if PROTO
int PASCAL NEAR movelocalpoint(int n, int *pcuroff, LINE **pcurline)
#else
int PASCAL NEAR movelocalpoint( n, pcuroff, pcurline)
int n;
int *pcuroff;
LINE **pcurline;
#endif
{
register int spare;
register int curoff;
register LINE *curline;
curline = *pcurline;
curoff = *pcuroff + n;
if (n > 0) {
if (curline == curbp->b_linep)
return TRUE; /* hit end of buffer */
while ((spare = curoff - lused(curline)) > 0) {
curline = lforw(curline);/* skip to next line */
curoff = spare - 1;
if (curline == curbp->b_linep)
return (TRUE); /* hit end of buffer */
}
}
else {
while (curoff < 0) {
curline = lback(curline); /* skip back a line */
curoff += lused(curline) + 1;
if (curline == curbp->b_linep)
return (TRUE); /* hit end of buffer */
}
}
*pcurline = curline;
*pcuroff = curoff;
return FALSE;
}
/*
* make_delta -- Create the delta tables.
*/
#if PROTO
VOID make_delta(char *pstring, DELTA *tbl)
#else
VOID make_delta( pstring, tbl)
char *pstring;
DELTA *tbl;
#endif
{
int j, jump_by, ch;
strcpy(tbl->patrn, pstring);
jump_by = strlen(pstring);
for (j = 0; j < HICHAR; j++)
tbl->delta[j] = jump_by;
jump_by -= 1;
/* Now put in the characters contained
* in the pattern, duplicating the CASE.
*/
for (j = 0; j < jump_by; j++) {
ch = *pstring++;
if (is_letter(ch))
tbl->delta[(unsigned char) chcase(ch)] = jump_by - j;
tbl->delta[ch] = jump_by - j;
}
/* The last character (left over from the loop above) will
* have the pattern length, unless there are duplicates of
* it. Get the number to jump from the delta array, and
* overwrite with zeroes in delta duplicating the CASE.
*/
ch = *pstring;
tbl->patlen = jump_by;
tbl->jump = jump_by + tbl->delta[ch];
if (is_letter(ch))
tbl->delta[(unsigned char) chcase(ch)] = 0;
tbl->delta[ch] = 0;
}
/*
* setjtable -- Settting up search delta forward and delta backward
* tables. The reverse search string and string lengths are
* set here, for table initialization and for substitution
* purposes. The default for any character to jump is the
* pattern length.
*/
VOID PASCAL NEAR setjtable()
{
make_delta(pat, &deltapat);
make_delta(strrev(strcpy((char *)tap, (char *)pat)), &tapatled);
}
/*
* eq -- Compare two characters. The "bc" comes from the buffer, "pc"
* from the pattern. If we are not in EXACT mode, fold out the case.
*/
int PASCAL NEAR eq(bc, pc)
register unsigned char bc;
register unsigned char pc;
{
if ((curwp->w_bufp->b_mode & MDEXACT) == 0) {
if (is_lower(bc))
bc = chcase(bc);
if (is_lower(pc))
pc = chcase(pc);
}
return (bc == pc);
}
/*
* readpattern -- Read a pattern. Stash it in apat. If it is the
* search string (which means that the global variable pat[]
* has been passed in), create the reverse pattern and the magic
* pattern, assuming we are in MAGIC mode (and #defined that way).
*
* Apat 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. Change to using
* <META> to delimit the end-of-pattern to allow <NL>s in the search
* string.
*/
#if PROTO
int PASCAL NEAR readpattern(char *prompt, char apat[], int srch)
#else
int PASCAL NEAR readpattern( prompt, apat, srch)
char *prompt;
char apat[];
int srch;
#endif
{
register int status;
char tpat[NPAT+20];
mlprompt(prompt, apat, sterm);
/* Read a pattern. Either we get one,
* or we just get the META charater, and use the previous pattern.
* Then, if it's the search string, create the delta tables.
* *Then*, make the meta-pattern, if we are defined that way.
*/
if ((status = nextarg(NULL, tpat, NPAT, sterm)) == TRUE) {
lastflag &= ~CFSRCH;
strcpy(apat, tpat);
if (srch)
setjtable();
}
else if (status == FALSE && apat[0] != 0) /* Old one */
status = TRUE;
#if MAGIC
/* Only make the meta-pattern if in magic mode, since the
* pattern in question might have an invalid meta combination.
*/
if (status == TRUE)
if ((curwp->w_bufp->b_mode & MDMAGIC) == 0) {
mcclear();
rmcclear();
}
else
status = srch? mcstr(): rmcstr();
#endif
return (status);
}
/*
* savematch -- We found the pattern? Let's save it away.
*/
int PASCAL NEAR savematch()
{
register int j;
REGION tmpreg;
if ((patmatch = reroom(patmatch, matchlen + 1)) == NULL) {
mlabort(TEXT94);
/* "%%Out of memory" */
return ABORT;
}
tmpreg.r_offset = matchoff;
tmpreg.r_linep = matchline;
tmpreg.r_size = matchlen;
#if MAGIC == 0
regtostr(patmatch, &tmpreg);
#else
/*
* Save the groups.
*/
grpmatch[0] = regtostr(patmatch, &tmpreg);
for (j = 1; j <= group_count; j++)
{
group_reg[j].r_size += group_len[j];
if ((grpmatch[j] = reroom(grpmatch[j], group_reg[j].r_size + 1)) == NULL) {
mlabort(TEXT94);
/* "%%Out of memory" */
return ABORT;
}
regtostr(grpmatch[j], &group_reg[j]);
}
#endif
return TRUE;
}
/*
* boundry -- Return information depending on whether we may search no
* further. Beginning of file and end of file are the obvious
* cases, but we may want to add further optional boundry restrictions
* in future, a' la VMS EDT. At the moment, just return (TRUE) or
* FALSE depending on if a boundry is hit (ouch).
*/
#if PROTO
int PASCAL NEAR boundry(LINE *curline, int curoff, int dir)
#else
int PASCAL NEAR boundry( curline, curoff, dir)
LINE *curline;
int curoff;
int dir;
#endif
{
register int border;
if (dir == FORWARD) {
border = (curoff == lused(curline)) &&
(lforw(curline) == curbp->b_linep);
}
else
{
border = (curoff == 0) &&
(lback(curline) == curbp->b_linep);
}
return (border);
}
/*
* nextch -- retrieve the next/previous character in the buffer,
* and advance/retreat the point.
* The order in which this is done is significant, and depends
* upon the direction of the search. Forward searches look at
* the current character and move, reverse searches move and
* look at the character.
*/
#if PROTO
int PASCAL NEAR nextch(LINE **pcurline, int *pcuroff, int dir)
#else
int PASCAL NEAR nextch( pcurline, pcuroff, dir)
LINE **pcurline;
int *pcuroff;
int dir;
#endif
{
register LINE *curline;
register int curoff;
register int c;
curline = *pcurline;
curoff = *pcuroff;
if (dir == FORWARD) {
if (curoff == lused(curline)) /* if at EOL */
{
curline = lforw(curline); /* skip to next line */
curoff = 0;
c = '\r'; /* and return a <NL> */
}
else
c = lgetc(curline, curoff++); /* get the char */
}
else /* Reverse.*/
{
if (curoff == 0) {
curline = lback(curline);
curoff = lused(curline);
c = '\r';
}
else
c = lgetc(curline, --curoff);
}
*pcurline = curline;
*pcuroff = curoff;
return (c);
}
/*
* liteq -- compare the string versus the current characters in the line.
* Returns 0 (no match) or the number of characters matched.
*/
#if PROTO
int PASCAL NEAR liteq(LINE **curline, int *curpos, int direct, char *lstring)
#else
int PASCAL NEAR liteq( curline, curpos, direct, lstring)
LINE **curline;
int *curpos;
int direct;
char *lstring;
#endif
{
LINE *scanline = *curline;
int scanpos = *curpos;
register int c;
register int count = 0;
while ((c = (unsigned char)(*lstring++)) != '\0') {
if (!eq(c, nextch(&scanline, &scanpos, direct)))
return 0;
count++;
}
*curline = scanline;
*curpos = scanpos;
return count;
}
#if MAGIC
/*
* mcstr -- Set up the 'magic' array. The closure symbol is taken as
* a literal character when (1) it is the first character in the
* pattern, and (2) when preceded by a symbol that does not allow
* closure, such as beginning or end of line symbol, or another
* closure symbol.
*
* Coding comment (jmg): yes, i know i have gotos that are, strictly
* speaking, unnecessary. But right now we are so cramped for
* code space that i will grab what i can in order to remain
* within the 64K limit. C compilers actually do very little
* in the way of optimizing - they expect you to do that.
*/
int PASCAL NEAR mcstr()
{
MC *mcptr, *rtpcm;
DELTA *tbl;
char *patptr;
int pchr;
int status = TRUE;
int does_closure = FALSE;
int mj = 0;
int group_stack[MAXGROUPS];
int stacklevel = 0;
/* If we had metacharacters in the MC array previously,
* free up any bitmaps that may have been allocated, and
* reset magical.
*/
if (magical)
mcclear();
mcptr = &mcpat[0];
patptr = (char *)&pat[0];
while ((pchr = *patptr) && status) {
switch (pchr) {
case MC_CCL:
status = cclmake(&patptr, mcptr);
magical = TRUE;
does_closure = TRUE;
break;
case MC_BOL:
/* If the BOL character isn't the
* first in the pattern, we assume
* it's a literal instead.
*/
if (mj != 0)
goto litcase;
mcptr->mc_type = BOL;
magical = TRUE;
break;
case MC_EOL:
/* If the EOL character isn't the
* last in the pattern, we assume
* it's a literal instead.
*/
if (*(patptr + 1) != '\0')
goto litcase;
mcptr->mc_type = EOL;
magical = TRUE;
break;
case MC_ANY:
mcptr->mc_type = ANY;
magical = TRUE;
does_closure = TRUE;
break;
case MC_CLOSURE:
case MC_CLOSURE_1:
case MC_ZEROONE:
/* Does the closure symbol mean closure here?
* If so, back up to the previous element
* and indicate it is enclosed.
*/
if (does_closure == FALSE)
goto litcase;
mj--;
mcptr--;
if (pchr == MC_CLOSURE)
mcptr->mc_type |= CLOSURE;
else if (pchr == MC_CLOSURE_1)
mcptr->mc_type |= CLOSURE_1;
else
mcptr->mc_type |= ZEROONE;
magical = TRUE;
does_closure = FALSE;
break;
case MC_ESC:
/* No break between here and LITCHAR if
* the next character is to be taken literally.
*/
magical = TRUE;
pchr = *++patptr;
if (pchr == MC_GRPBEG) {
/* Start of a group. Indicate it, and
* set magical.
*/
if (++group_count < MAXGROUPS) {
mcptr->mc_type = GRPBEG;
mcptr->u.group_no = group_count;
group_stack[stacklevel++] = group_count;
does_closure = FALSE;
}
else
{
mlwrite(TEXT221);
/* "Too many groups" */
status = FALSE;
}
break;
}
else if (pchr == MC_GRPEND) {
/* If we've no groups to close, assume
* a literal character. Otherwise,
* indicate the end of a group.
*/
if (stacklevel > 0) {
mcptr->u.group_no = group_stack[--stacklevel];
mcptr->mc_type = GRPEND;
does_closure = FALSE;
break;
}
}
else if (pchr == MC_BOWRD) {
mcptr->mc_type = BOWRD;
does_closure = FALSE;
break;
}
else if (pchr == MC_EOWRD) {
mcptr->mc_type = EOWRD;
does_closure = FALSE;
break;
}
else if (pchr == '\0') {
pchr = '\\';
--patptr;
}
default:
litcase:
status = litmake(&patptr, mcptr);
does_closure = TRUE;
break;
} /* End of switch.*/
mcptr++;
patptr++;
mj++;
} /* End of while.*/
/* Close off the meta-string, then set up the reverse array,
* if the status is good.
*
* If the status is not good, free up any bitmaps or strings,
* and make mcpat[0].mc_type MCNIL.
*
* Please note the structure assignment - your compiler may
* not like that.
*/
mcptr->mc_type = MCNIL;
if (stacklevel) {
status = FALSE;
mlwrite(TEXT222);
/* "Group not ended" */
}
if (status) {
rtpcm = &tapcm[0];
while (--mj >= 0) {
*rtpcm = *--mcptr;
if (rtpcm->mc_type == LITSTRING) {
if ((rtpcm->u.lstring = copystr(mcptr->u.lstring)) == NULL) {
status = FALSE;
break;
}
strrev(rtpcm->u.lstring);
}
rtpcm++;
}
rtpcm->mc_type = MCNIL;
}
if (status) {
#if MAGIC_JUMP_TABLES
/*
* Now see if we can use the fast jump tables instead
* of a brute-force string search, if the first or
* last meta-character types are strings.
*/
if (mcpat[0].mc_type == LITSTRING)
{
if ((tbl = (DELTA *) room(sizeof(DELTA))) != NULL)
{
make_delta(mcpat[0].u.lstring, tbl);
free(mcpat[0].u.lstring);
mcpat[0].u.jmptable = tbl;
mcpat[0].mc_type = JMPTABLE;
}
}
if (tapcm[0].mc_type == LITSTRING)
{
if ((tbl = (DELTA *) room(sizeof(DELTA))) != NULL)
{
make_delta(tapcm[0].u.lstring, tbl);
free(tapcm[0].u.lstring);
tapcm[0].u.jmptable = tbl;
tapcm[0].mc_type = JMPTABLE;
}
}
#endif
}
else
mcclear();
#if DEBUG_SEARCH
mc_list(0,0);
#endif
return (status);
}
/*
* mcclear -- Free up any CCL bitmaps, and MCNIL the MC search arrays.
*/
VOID PASCAL NEAR mcclear()
{
register MC *mcptr;
register int j;
/*
* Free up any memory allocated for the meta-characters:
* bitmaps, strings, or the DELTA jmptables.
*/
mcptr = &mcpat[0];
while (mcptr->mc_type != MCNIL) {
if ((mcptr->mc_type == CCL) || (mcptr->mc_type == NCCL))
free(mcptr->u.cclmap);
else if (mcptr->mc_type == LITSTRING)
free(mcptr->u.lstring);
else if (mcptr->mc_type == JMPTABLE)
free(mcptr->u.jmptable);
mcptr++;
}
/*
* Do the same for the reverse pattern, with the exception of
* of the bitmaps. The reverse pattern simply 'borrowed'
* the forward pattern's bitmaps, which by now have been freed.
*/
mcptr = &tapcm[0];
while (mcptr->mc_type != MCNIL) {
if (mcptr->mc_type == LITSTRING)
free(mcptr->u.lstring);
else if (mcptr->mc_type == JMPTABLE)
free(mcptr->u.jmptable);
mcptr++;
}
mcpat[0].mc_type = tapcm[0].mc_type = MCNIL;
/*
* Remember that grpmatch[0] == patmatch.
*/
for (j = 0; j < MAXGROUPS; j++) {
if (grpmatch[j] != NULL) {
free(grpmatch[j]);
grpmatch[j] = NULL;
}
}
patmatch = NULL;
group_count = 0;
magical = FALSE;
}
/*
* mceq -- meta-character equality with a character. In Kernighan & Plauger's
* Software Tools, this is the function omatch(), but i felt there were
* too many functions with the 'match' name already.
*/
#if PROTO
int PASCAL NEAR mceq(unsigned char bc, MC *mt)
#else
int PASCAL NEAR mceq( bc, mt)
unsigned char bc;
MC *mt;
#endif
{
register int result;
switch (mt->mc_type & MASKCLO) {
case LITCHAR:
result = (unsigned char) eq(bc, (unsigned char) mt->u.lchar);
break;
case ANY:
result = (bc != '\r');
break;
case CCL:
if (!(result = biteq(bc, mt->u.cclmap))) {
if ((curwp->w_bufp->b_mode & MDEXACT) == 0 &&
(is_letter(bc)))
result = biteq(chcase(bc), mt->u.cclmap);
}
break;
case NCCL:
result = !biteq(bc, mt->u.cclmap);
if ((curwp->w_bufp->b_mode & MDEXACT) == 0 &&
(is_letter(bc)))
result &= !biteq(chcase(bc), mt->u.cclmap);
break;
default:
mlwrite(TEXT95, mt->mc_type);
/* "%%mceq: what is %d?" */
result = FALSE;
break;
} /* End of switch.*/
return (result);
}
/*
* cclmake -- create the bitmap for the character class.
* ppatptr is left pointing to the end-of-character-class character,
* so that a loop may automatically increment with safety.
*/
#if PROTO
int PASCAL NEAR cclmake(char **ppatptr, MC *mcptr)
#else
int PASCAL NEAR cclmake( ppatptr, mcptr)
char **ppatptr;
MC *mcptr;
#endif
{
EBITMAP bmap;
register char *patptr;
register int pchr, ochr;
if ((bmap = (EBITMAP) room(BMAPSIZE)) == NULL) {
mlabort(TEXT94);
/* "%%Out of memory" */
mcptr->mc_type = MCNIL;
return FALSE;
}
memset(bmap, 0, BMAPSIZE);
mcptr->u.cclmap = bmap;
patptr = *ppatptr;
ochr = MC_CCL;
/*
* Test the initial character(s) in ccl for
* special cases - negate ccl, or an end ccl
* character as a first character. Anything
* else gets set in the bitmap.
*/
if (*++patptr == MC_NCCL) {
patptr++;
mcptr->mc_type = NCCL;
}
else
mcptr->mc_type = CCL;
if ((pchr = *patptr) == MC_ECCL) {
mlwrite(TEXT96);
/* "%%No characters in character class" */
free(bmap);
return FALSE;
}
while (pchr != MC_ECCL && pchr != '\0') {
switch (pchr) {
/* Range character loses its meaning if it is
* the first or last character in the class. We
* also treat it as un-ordinary if the order is
* wrong, e.g. "z-a".
*/
case MC_RCCL:
pchr = *(patptr + 1);
if (ochr == MC_CCL || pchr == MC_ECCL ||
ochr > pchr)
setbit(MC_RCCL, bmap);
else
{
do {
setbit(++ochr, bmap);
} while (ochr < pchr);
patptr++;
}
break;
/* Note: no break between case MC_ESC and the default.
*/
case MC_ESC:
pchr = *++patptr;
default:
setbit(pchr, bmap);
break;
}
ochr = pchr;
pchr = *++patptr;
}
*ppatptr = patptr;
if (pchr == '\0') {
mlwrite(TEXT97);
/* "%%Character class not ended" */
free(bmap);
return FALSE;
}
return TRUE;
}
/*
* litmake -- create the literal string from the collection of characters.
* If there is only one character in the collection, then no memory
* needs to be allocated.
*
* Please Note: If new meta-characters are added (see estruct.h) then
* you will also need to update this function!
*/
#if PROTO
int PASCAL NEAR litmake(char **ppatptr, MC *mcptr)
#else
int PASCAL NEAR litmake( ppatptr, mcptr)
char **ppatptr;
MC *mcptr;
#endif
{
char collect[NPAT + 1];
int collect_len;
register int pchr;
register char *patptr;
/*
* The reason this function was called was because a literal
* character was encountered, so collect it immediately.
*/
collect[0] = *(patptr = *ppatptr);
collect_len = 1;
/*
* Now loop through the pattern, collecting characters until
* we run into a meta-character.
*/
while (pchr = *++patptr)
{
/*
* If the current character is a closure character,
* then the previous character cannot be part of the
* collected string (it will be modified by closure).
* Back up one, if it is not solo.
*/
if (pchr == MC_CLOSURE || pchr == MC_CLOSURE_1 ||
pchr == MC_ZEROONE)
{
if (collect_len > 1) {
collect_len--;
patptr--;
}
break;
}
/*
* The single-character meta-characters...
*/
if (pchr == MC_ANY || pchr == MC_CCL ||
pchr == MC_BOL || pchr == MC_EOL)
break;
/*
* See if an escaped character is part of a meta-character
* or not. If not, then advance the pointer to collect the
* next character, if there is a next character.
*/
if (pchr == MC_ESC) {
pchr = *(patptr + 1);
if (pchr == MC_GRPBEG || pchr == MC_GRPEND ||
pchr == MC_BOWRD || pchr == MC_EOWRD)
break;
if (pchr != '\0')
patptr++;
magical = TRUE;
}
collect[collect_len++] = *patptr;
}
/*
* Finished collecting characters, so either make a string out
* of them, or a simple character.
*/
if (collect_len == 1) {
mcptr->u.lchar = collect[0];
mcptr->mc_type = LITCHAR;
}
else
{
collect[collect_len] = '\0';
if ((mcptr->u.lstring = copystr(collect)) == NULL)
mcptr->mc_type = MCNIL;
else
mcptr->mc_type = LITSTRING;
}
/*
* Back up one so that the calling function will
* increment onto the character we halted on.
*/
*ppatptr = patptr -1;
return (mcptr->mc_type != MCNIL);
}
/*
* biteq -- is the character in the bitmap?
*/
#if PROTO
int PASCAL NEAR biteq(int bc, EBITMAP cclmap)
#else
int PASCAL NEAR biteq( bc, cclmap)
int bc;
EBITMAP cclmap;
#endif
{
if ((unsigned)bc >= HICHAR)
return FALSE;
return ( (*(cclmap + (bc >> 3)) & BIT(bc & 7))? TRUE: FALSE );
}
/*
* setbit -- Set a bit (ON only) in the bitmap.
*/
#if PROTO
VOID PASCAL NEAR setbit(int bc, EBITMAP cclmap)
#else
VOID PASCAL NEAR setbit( bc, cclmap)
int bc;
EBITMAP cclmap;
#endif
{
if ((unsigned)bc < HICHAR)
*(cclmap + (bc >> 3)) |= BIT(bc & 7);
}
#endif
#if DEBUG_SEARCH
#if PROTO
int PASCAL NEAR mc_list(int f, int n)
#else
int PASCAL NEAR mc_list( f, n)
int f;
int n;
#endif
{
MC *mcptr;
BUFFER *patbuf; /* buffer containing pattern list */
char pline[NPAT*2]; /* text buffer to hold current line */
char cstr[2]; /* to turn single characters into strings.*/
int status; /* return status from subcommands */
int j;
/* prepare and clear the buffer holding the meta-character list */
patbuf = bfind("[Debug Search Metacharacters]", TRUE, BFINVS);
if (patbuf == NULL) {
mlwrite(TEXT137);
/* "Cannot create buffer" */
return FALSE;
}
patbuf->b_mode |= MDVIEW;
if ((status = bclear(patbuf)) != TRUE) /* Blow old text away */
return(status);
/* add in the header text */
strcpy(pline, " Pattern = \"");
strcat(pline, pat);
strcat(pline, "\" (");
strcat(pline, int_asc(deltapat.jump));
strcat(pline, ", ");
strcat(pline, int_asc(deltapat.patlen));
strcat(pline, ")");
/* scan through the regular expression pattern */
cstr[0] = cstr[1] = '\0';
mcptr = &mcpat[0];
for (j = 0; j < 2; j++)
{
if (addline(patbuf, " ") == FALSE
|| addline(patbuf, pline) == FALSE
|| addline(patbuf, "-----------------------") == FALSE
|| addline(patbuf, "Closure MC Type") == FALSE
|| addline(patbuf, "-----------------------") == FALSE)
return(FALSE);
while (mcptr->mc_type != MCNIL) {
if ((mcptr->mc_type) & CLOSURE)
strcpy(pline, "Zero to many ");
else if ((mcptr->mc_type) & CLOSURE_1)
strcpy(pline, "One to many ");
else if ((mcptr->mc_type) & ZEROONE)
strcpy(pline, "Optional ");
else
strcpy(pline, " ");
/* next, the meta-character type */
mctype_cat(pline, (mcptr->mc_type) & MASKCLO);
/* and some additional information */
switch ((mcptr->mc_type) & MASKCLO) {
case JMPTABLE:
strcat(pline, "\"");
strcat(pline, mcptr->u.jmptable->patrn);
strcat(pline, "\" (");
strcat(pline, int_asc(mcptr->u.jmptable->jump));
strcat(pline, ", ");
strcat(pline, int_asc(mcptr->u.jmptable->patlen));
strcat(pline, ")");
break;
case LITSTRING:
strcat(pline, "\"");
strcat(pline, mcptr->u.lstring);
strcat(pline, "\"");
break;
case LITCHAR:
cstr[0] = mcptr->u.lchar;
strcat(pline, "\"");
strcat(pline, cstr);
strcat(pline, "\"");
break;
case GRPBEG:
cstr[0] = mcptr->u.group_no + '0';
strcat(pline, cstr);
break;
case GRPEND:
cstr[0] = mcptr->u.group_no + '0';
strcat(pline, cstr);
break;
}
/* terminate and add the built line into the buffer */
if (addline(patbuf, pline) == FALSE)
return(FALSE);
mcptr++;
}
/* add in the header text */
strcpy(pline, " Reverse Pattern = \"");
strcat(pline, tap);
strcat(pline, "\" (");
strcat(pline, int_asc(tapatled.jump));
strcat(pline, ", ");
strcat(pline, int_asc(tapatled.patlen));
strcat(pline, ")");
mcptr = &tapcm[0];
}
return(wpopup(patbuf));
}
#if PROTO
int PASCAL NEAR rmc_list(int f, int n)
#else
int PASCAL NEAR rmc_list( f, n)
int f;
int n;
#endif
{
RMC *rmcptr;
BUFFER *patbuf; /* buffer containing pattern list */
char pline[NPAT+32]; /* text buffer to hold current line */
char cstr[2]; /* to turn single characters into strings.*/
int status; /* return status from subcommands */
/* prepare and clear the buffer holding the meta-character list */
patbuf = bfind("[Debug Replace Metacharacters]", TRUE, BFINVS);
if (patbuf == NULL) {
mlwrite(TEXT137);
/* "Cannot create buffer" */
return FALSE;
}
patbuf->b_mode |= MDVIEW;
if ((status = bclear(patbuf)) != TRUE) /* Blow old text away */
return(status);
/* add in the header text */
strcpy(pline, "Replacement Pattern = \"");
strcat(pline, rpat);
strcat(pline, "\"");
/* scan through the regular expression pattern */
cstr[0] = cstr[1] = '\0';
rmcptr = &rmcpat[0];
if (addline(patbuf, " ") == FALSE
|| addline(patbuf, pline) == FALSE
|| addline(patbuf, "-----------------------") == FALSE
|| addline(patbuf, "RMC Type") == FALSE
|| addline(patbuf, "-----------------------") == FALSE)
return(FALSE);
while (rmcptr->mc_type != MCNIL) {
mctype_cat(pline, rmcptr->mc_type);
/* and some additional information */
switch (rmcptr->mc_type) {
case LITSTRING:
strcat(pline, "\"");
strcat(pline, rmcptr->u.rstr);
strcat(pline, "\"");
break;
case GROUP:
cstr[0] = rmcptr->u.group_no + '0';
strcat(pline, cstr);
break;
}
/* terminate and add the built line into the buffer */
if (addline(patbuf, pline) == FALSE)
return(FALSE);
rmcptr++;
}
return(wpopup(patbuf));
}
#if PROTO
VOID PASCAL NEAR mctype_cat(char pline[], int mc_type)
#else
VOID PASCAL NEAR mctype_cat( pline, mc_type)
char pline[];
int mc_type;
#endif
{
switch (mc_type) {
case JMPTABLE:
strcat(pline, "JMPTABLE ");
break;
case LITSTRING:
strcat(pline, "LITSTRING ");
break;
case LITCHAR:
strcat(pline, "LITCHAR ");
break;
case ANY:
strcat(pline, "ANY ");
break;
case CCL:
strcat(pline, "CCL ");
break;
case NCCL:
strcat(pline, "NCCL ");
break;
case BOL:
strcat(pline, "BOL ");
break;
case EOL:
strcat(pline, "EOL ");
break;
case BOWRD:
strcat(pline, "BOWORD ");
break;
case EOWRD:
strcat(pline, "EOWORD ");
break;
case GRPBEG:
strcat(pline, "GRPBEG ");
break;
case GRPEND:
strcat(pline, "GRPEND ");
break;
case GROUP:
strcat(pline, "GROUP ");
break;
case DITTO:
strcat(pline, "DITTO ");
break;
default:
strcat(pline, "Unknown type");
break;
}
}
#endif