mirror of
https://github.com/rfivet/uemacs.git
synced 2024-12-18 07:16:23 -05:00
1523 lines
36 KiB
C
1523 lines
36 KiB
C
/* search.c -- implements search.h */
|
|
#include "search.h"
|
|
|
|
/* search.c
|
|
*
|
|
* The functions in this file implement commands that search in the forward
|
|
* and backward directions. There are no special characters in the search
|
|
* strings. Probably should have a regular expression search, or something
|
|
* like that.
|
|
*
|
|
* Aug. 1986 John M. Gamble:
|
|
* Made forward and reverse search use the same scan routine.
|
|
*
|
|
* Added a limited number of regular expressions - 'any',
|
|
* 'character class', 'closure', 'beginning of line', and
|
|
* 'end of line'.
|
|
*
|
|
* Replacement metacharacters will have to wait for a re-write of
|
|
* the replaces function, and a new variation of ldelete().
|
|
*
|
|
* For those curious as to my references, i made use of
|
|
* Kernighan & Plauger's "Software Tools."
|
|
* I deliberately did not look at any published grep or editor
|
|
* source (aside from this one) for inspiration. I did make use of
|
|
* Allen Hollub's bitmap routines as published in Doctor Dobb's Journal,
|
|
* June, 1985 and modified them for the limited needs of character class
|
|
* matching. Any inefficiences, bugs, stupid coding examples, etc.,
|
|
* are therefore my own responsibility.
|
|
*
|
|
* April 1987: John M. Gamble
|
|
* Deleted the "if (n == 0) n = 1;" statements in front of the
|
|
* search/hunt routines. Since we now use a do loop, these
|
|
* checks are unnecessary. Consolidated common code into the
|
|
* function delins(). Renamed global mclen matchlen,
|
|
* and added the globals matchline, matchoff, patmatch, and
|
|
* mlenold.
|
|
* This gave us the ability to unreplace regular expression searches,
|
|
* and to put the matched string into an evironment variable.
|
|
* SOON TO COME: Meta-replacement characters!
|
|
*
|
|
* 25-apr-87 DML
|
|
* - cleaned up an unneccessary if/else in forwsearch() and
|
|
* backsearch()
|
|
* - savematch() failed to malloc room for the terminating byte
|
|
* of the match string (stomp...stomp...). It does now. Also
|
|
* it now returns gracefully if malloc fails
|
|
*
|
|
* July 1987: John M. Gamble
|
|
* Set the variables matchlen and matchoff in the 'unreplace'
|
|
* section of replaces(). The function savematch() would
|
|
* get confused if you replaced, unreplaced, then replaced
|
|
* again (serves you right for being so wishy-washy...)
|
|
*
|
|
* August 1987: John M. Gamble
|
|
* Put in new function rmcstr() to create the replacement
|
|
* meta-character array. Modified delins() so that it knows
|
|
* whether or not to make use of the array. And, put in the
|
|
* appropriate new structures and variables.
|
|
*
|
|
* Modified by Petri Kutvonen
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "basic.h"
|
|
#include "buffer.h"
|
|
#include "display.h"
|
|
#include "estruct.h"
|
|
#include "edef.h"
|
|
#include "input.h"
|
|
#include "line.h"
|
|
#include "log.h"
|
|
#include "window.h"
|
|
|
|
#if defined(MAGIC)
|
|
/*
|
|
* The variables magical and rmagical determine if there
|
|
* were actual metacharacters in the search and replace strings -
|
|
* if not, then we don't have to use the slower MAGIC mode
|
|
* search functions.
|
|
*/
|
|
static short int magical;
|
|
static short int rmagical;
|
|
static struct magic mcpat[NPAT]; /* The magic pattern. */
|
|
static struct magic tapcm[NPAT]; /* The reversed magic patterni. */
|
|
static struct magic_replacement rmcpat[NPAT]; /* The replacement magic array. */
|
|
#endif
|
|
|
|
static int amatch(struct magic *mcptr, int direct, struct line **pcwline, int *pcwoff);
|
|
static int readpattern(char *prompt, char *apat, int srch);
|
|
static int replaces(int kind, int f, int n);
|
|
static int nextch(struct line **pcurline, int *pcuroff, int dir);
|
|
static int mcstr(void);
|
|
static int rmcstr(void);
|
|
static int mceq(int bc, struct magic *mt);
|
|
static int cclmake(char **ppatptr, struct magic *mcptr);
|
|
static int biteq(int bc, char *cclmap);
|
|
static char *clearbits(void);
|
|
static void setbit(int bc, char *cclmap);
|
|
|
|
/*
|
|
* 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.
|
|
*
|
|
* int f, n; default flag / numeric argument
|
|
*/
|
|
int forwsearch(int f, int n)
|
|
{
|
|
int status = TRUE;
|
|
|
|
/* 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 the pattern for as long as
|
|
* n is positive (n == 0 will go through once, which
|
|
* is just fine).
|
|
*/
|
|
if ((status = readpattern("Search", &pat[0], TRUE)) == TRUE) {
|
|
do {
|
|
#if MAGIC
|
|
if ((magical
|
|
&& curwp->w_bufp->b_mode & MDMAGIC) != 0)
|
|
status =
|
|
mcscanner(&mcpat[0], FORWARD, PTEND);
|
|
else
|
|
#endif
|
|
status = scanner(&pat[0], FORWARD, PTEND);
|
|
} while ((--n > 0) && status);
|
|
|
|
/* Save away the match, or complain
|
|
* if not there.
|
|
*/
|
|
if (status == TRUE)
|
|
savematch();
|
|
else
|
|
mlwrite("Not found");
|
|
}
|
|
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.
|
|
*
|
|
* int f, n; default flag / numeric argument
|
|
*/
|
|
int forwhunt(int f, int n)
|
|
{
|
|
int status = TRUE;
|
|
|
|
if (n < 0) /* search backwards */
|
|
return backhunt(f, -n);
|
|
|
|
/* Make sure a pattern exists, or that we didn't switch
|
|
* into MAGIC mode until after we entered the pattern.
|
|
*/
|
|
if (pat[0] == '\0') {
|
|
mlwrite("No pattern set");
|
|
return FALSE;
|
|
}
|
|
#if MAGIC
|
|
if ((curwp->w_bufp->b_mode & MDMAGIC) != 0 &&
|
|
mcpat[0].mc_type == MCNIL) {
|
|
if (!mcstr())
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
/* Search for the pattern for as long as
|
|
* n is positive (n == 0 will go through once, which
|
|
* is just fine).
|
|
*/
|
|
do {
|
|
#if MAGIC
|
|
if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0)
|
|
status = mcscanner(&mcpat[0], FORWARD, PTEND);
|
|
else
|
|
#endif
|
|
status = scanner(&pat[0], FORWARD, PTEND);
|
|
} while ((--n > 0) && status);
|
|
|
|
/* Save away the match, or complain
|
|
* if not there.
|
|
*/
|
|
if (status == TRUE)
|
|
savematch();
|
|
else
|
|
mlwrite("Not found");
|
|
|
|
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).
|
|
*
|
|
* int f, n; default flag / numeric argument
|
|
*/
|
|
int backsearch(int f, int n)
|
|
{
|
|
int status = TRUE;
|
|
|
|
/* 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 the pattern for as long as
|
|
* n is positive (n == 0 will go through once, which
|
|
* is just fine).
|
|
*/
|
|
if ((status =
|
|
readpattern("Reverse search", &pat[0], TRUE)) == TRUE) {
|
|
do {
|
|
#if MAGIC
|
|
if ((magical
|
|
&& curwp->w_bufp->b_mode & MDMAGIC) != 0)
|
|
status =
|
|
mcscanner(&tapcm[0], REVERSE, PTBEG);
|
|
else
|
|
#endif
|
|
status = scanner(&tap[0], REVERSE, PTBEG);
|
|
} while ((--n > 0) && status);
|
|
|
|
/* Save away the match, or complain
|
|
* if not there.
|
|
*/
|
|
if (status == TRUE)
|
|
savematch();
|
|
else
|
|
mlwrite("Not found");
|
|
}
|
|
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).
|
|
*
|
|
* int f, n; default flag / numeric argument
|
|
*/
|
|
int backhunt(int f, int n)
|
|
{
|
|
int status = TRUE;
|
|
|
|
if (n < 0)
|
|
return forwhunt(f, -n);
|
|
|
|
/* Make sure a pattern exists, or that we didn't switch
|
|
* into MAGIC mode until after we entered the pattern.
|
|
*/
|
|
if (tap[0] == '\0') {
|
|
mlwrite("No pattern set");
|
|
return FALSE;
|
|
}
|
|
#if MAGIC
|
|
if ((curwp->w_bufp->b_mode & MDMAGIC) != 0 &&
|
|
tapcm[0].mc_type == MCNIL) {
|
|
if (!mcstr())
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
/* Go search for it for as long as
|
|
* n is positive (n == 0 will go through once, which
|
|
* is just fine).
|
|
*/
|
|
do {
|
|
#if MAGIC
|
|
if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0)
|
|
status = mcscanner(&tapcm[0], REVERSE, PTBEG);
|
|
else
|
|
#endif
|
|
status = scanner(&tap[0], REVERSE, PTBEG);
|
|
} while ((--n > 0) && status);
|
|
|
|
/* Save away the match, or complain
|
|
* if not there.
|
|
*/
|
|
if (status == TRUE)
|
|
savematch();
|
|
else
|
|
mlwrite("Not found");
|
|
|
|
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.
|
|
*
|
|
* struct magic *mcpatrn; pointer into pattern
|
|
* int direct; which way to go.
|
|
* int beg_or_end; put point at beginning or end of pattern.
|
|
*/
|
|
int mcscanner(struct magic *mcpatrn, int direct, int beg_or_end)
|
|
{
|
|
struct line *curline; /* current line during scan */
|
|
int curoff; /* position within current line */
|
|
|
|
/* If we are going in reverse, then the 'end' is actually
|
|
* the beginning of the pattern. Toggle it.
|
|
*/
|
|
beg_or_end ^= direct;
|
|
|
|
/*
|
|
* Save the old matchlen length, in case it is
|
|
* very different (closure) from the old length.
|
|
* This is important for query-replace undo
|
|
* command.
|
|
*/
|
|
mlenold = matchlen;
|
|
|
|
/* 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 (amatch(mcpatrn, direct, &curline, &curoff)) {
|
|
/* A SUCCESSFULL MATCH!!!
|
|
* reset the global "." pointers.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
curwp->w_flag |= WFMOVE; /* flag that we have moved */
|
|
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. Based on the
|
|
* recursive routine amatch() (for "anchored match") in
|
|
* Kernighan & Plauger's "Software Tools".
|
|
*
|
|
* struct magic *mcptr; string to scan for
|
|
* int direct; which way to go.
|
|
* struct line **pcwline; current line during scan
|
|
* int *pcwoff; position within current line
|
|
*/
|
|
static int amatch(struct magic *mcptr, int direct, struct line **pcwline, int *pcwoff)
|
|
{
|
|
int c; /* character at current position */
|
|
struct line *curline; /* current line during scan */
|
|
int curoff; /* position within current line */
|
|
int nchars;
|
|
|
|
/* Set up local scan pointers to ".", and get
|
|
* the current character. Then loop around
|
|
* the pattern pointer until success or failure.
|
|
*/
|
|
curline = *pcwline;
|
|
curoff = *pcwoff;
|
|
|
|
/* The beginning-of-line and end-of-line metacharacters
|
|
* do not compare against characters, they compare
|
|
* against positions.
|
|
* BOL is guaranteed to be at the start of the pattern
|
|
* for forward searches, and at the end of the pattern
|
|
* for reverse searches. The reverse is true for EOL.
|
|
* So, for a start, we check for them on entry.
|
|
*/
|
|
if (mcptr->mc_type == BOL) {
|
|
if (curoff != 0)
|
|
return FALSE;
|
|
mcptr++;
|
|
}
|
|
|
|
if (mcptr->mc_type == EOL) {
|
|
if (curoff != llength(curline))
|
|
return FALSE;
|
|
mcptr++;
|
|
}
|
|
|
|
while (mcptr->mc_type != MCNIL) {
|
|
c = nextch(&curline, &curoff, direct);
|
|
|
|
if (mcptr->mc_type & CLOSURE) {
|
|
/* Try to match as many characters as possible
|
|
* against the current meta-character. A
|
|
* newline never matches a closure.
|
|
*/
|
|
nchars = 0;
|
|
while (c != '\n' && mceq(c, mcptr)) {
|
|
c = nextch(&curline, &curoff, direct);
|
|
nchars++;
|
|
}
|
|
|
|
/* 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.
|
|
* Since closure matches *zero* or more occurences
|
|
* of a pattern, a match may start even if the
|
|
* previous loop matched no characters.
|
|
*/
|
|
mcptr++;
|
|
|
|
for (;;) {
|
|
c = nextch(&curline, &curoff,
|
|
direct ^ REVERSE);
|
|
|
|
if (amatch
|
|
(mcptr, direct, &curline, &curoff)) {
|
|
matchlen += nchars;
|
|
goto success;
|
|
}
|
|
|
|
if (nchars-- == 0)
|
|
return FALSE;
|
|
}
|
|
} else { /* Not closure. */
|
|
|
|
/* The only way we'd get a BOL metacharacter
|
|
* at this point is at the end of the reversed pattern.
|
|
* The only way we'd get an EOL metacharacter
|
|
* here is at the end of a regular pattern.
|
|
* So if we match one or the other, and are at
|
|
* the appropriate position, we are guaranteed success
|
|
* (since the next pattern character has to be MCNIL).
|
|
* Before we report success, however, we back up by
|
|
* one character, so as to leave the cursor in the
|
|
* correct position. For example, a search for ")$"
|
|
* will leave the cursor at the end of the line, while
|
|
* a search for ")<NL>" will leave the cursor at the
|
|
* beginning of the next line. This follows the
|
|
* notion that the meta-character '$' (and likewise
|
|
* '^') match positions, not characters.
|
|
*/
|
|
if (mcptr->mc_type == BOL) {
|
|
if (curoff == llength(curline)) {
|
|
c = nextch(&curline, &curoff,
|
|
direct ^ REVERSE);
|
|
goto success;
|
|
} else
|
|
return FALSE;
|
|
}
|
|
|
|
if (mcptr->mc_type == EOL) {
|
|
if (curoff == 0) {
|
|
c = nextch(&curline, &curoff,
|
|
direct ^ REVERSE);
|
|
goto success;
|
|
} else
|
|
return FALSE;
|
|
}
|
|
|
|
/* Neither BOL nor EOL, so go through
|
|
* the meta-character equal function.
|
|
*/
|
|
if (!mceq(c, mcptr))
|
|
return FALSE;
|
|
}
|
|
|
|
/* Increment the length counter and
|
|
* advance the pattern pointer.
|
|
*/
|
|
matchlen++;
|
|
mcptr++;
|
|
} /* End of mcptr loop. */
|
|
|
|
/* A SUCCESSFULL MATCH!!!
|
|
* Reset the "." pointers.
|
|
*/
|
|
success:
|
|
*pcwline = curline;
|
|
*pcwoff = curoff;
|
|
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* 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.
|
|
*
|
|
* unsigned char *patrn; string to scan for
|
|
* int direct; which way to go.
|
|
* int beg_or_end; put point at beginning or end of pattern.
|
|
*/
|
|
int scanner(const char *patrn, int direct, int beg_or_end)
|
|
{
|
|
int c; /* character at current position */
|
|
const char *patptr; /* pointer into pattern */
|
|
struct line *curline; /* current line during scan */
|
|
int curoff; /* position within current line */
|
|
struct line *scanline; /* current line during scanning */
|
|
int scanoff; /* position in scanned line */
|
|
|
|
/* If we are going in reverse, then the 'end' is actually
|
|
* the beginning of the pattern. Toggle it.
|
|
*/
|
|
beg_or_end ^= direct;
|
|
|
|
/* Set up local 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 match
|
|
* the search string at this point.
|
|
*/
|
|
matchline = curline;
|
|
matchoff = curoff;
|
|
|
|
/* Get the character resolving newlines, and
|
|
* test it against first char in pattern.
|
|
*/
|
|
c = nextch(&curline, &curoff, direct);
|
|
|
|
if (eq(c, patrn[0])) { /* if we find it.. */
|
|
/* Setup scanning pointers.
|
|
*/
|
|
scanline = curline;
|
|
scanoff = curoff;
|
|
patptr = &patrn[0];
|
|
|
|
/* Scan through the pattern for a match.
|
|
*/
|
|
while (*++patptr != '\0') {
|
|
c = nextch(&scanline, &scanoff, direct);
|
|
|
|
if (!eq(c, *patptr))
|
|
goto fail;
|
|
}
|
|
|
|
/* A SUCCESSFULL MATCH!!!
|
|
* reset the global "." pointers
|
|
*/
|
|
if (beg_or_end == PTEND) { /* at end of string */
|
|
curwp->w_dotp = scanline;
|
|
curwp->w_doto = scanoff;
|
|
} else { /* at beginning of string */
|
|
|
|
curwp->w_dotp = matchline;
|
|
curwp->w_doto = matchoff;
|
|
}
|
|
|
|
curwp->w_flag |= WFMOVE; /* Flag that we have moved. */
|
|
return TRUE;
|
|
|
|
}
|
|
fail:; /* continue to search */
|
|
}
|
|
|
|
return FALSE; /* We could not find a match */
|
|
}
|
|
|
|
/*
|
|
* 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 eq(unsigned char bc, unsigned char pc)
|
|
{
|
|
if ((curwp->w_bufp->b_mode & MDEXACT) == 0) {
|
|
if (islower(bc))
|
|
bc ^= DIFCASE;
|
|
|
|
if (islower(pc))
|
|
pc ^= DIFCASE;
|
|
}
|
|
|
|
return bc == pc;
|
|
}
|
|
|
|
/*
|
|
* readpattern -- Read a pattern. Stash it in apat. If it is the
|
|
* search string, 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.
|
|
*/
|
|
static int readpattern(char *prompt, char *apat, int srch)
|
|
{
|
|
int status;
|
|
char tpat[NPAT + 20];
|
|
|
|
strcpy(tpat, prompt); /* copy prompt to output string */
|
|
strcat(tpat, " ("); /* build new prompt string */
|
|
expandp(&apat[0], &tpat[strlen(tpat)], NPAT / 2); /* add old pattern */
|
|
strcat(tpat, ")<Meta>: ");
|
|
|
|
/* 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, make a reversed pattern.
|
|
* *Then*, make the meta-pattern, if we are defined that way.
|
|
*/
|
|
if ((status = mlreplyt(tpat, tpat, NPAT, metac)) == TRUE) {
|
|
strcpy(apat, tpat);
|
|
if (srch) { /* If we are doing the search string. */
|
|
/* Reverse string copy, and remember
|
|
* the length for substitution purposes.
|
|
*/
|
|
rvstrcpy(tap, apat);
|
|
mlenold = matchlen = strlen(apat);
|
|
}
|
|
#if MAGIC
|
|
/* Only make the meta-pattern if in magic mode,
|
|
* since the pattern in question might have an
|
|
* invalid meta combination.
|
|
*/
|
|
if ((curwp->w_bufp->b_mode & MDMAGIC) == 0) {
|
|
mcclear();
|
|
rmcclear();
|
|
} else
|
|
status = srch ? mcstr() : rmcstr();
|
|
#endif
|
|
} else if (status == FALSE && apat[0] != 0) /* Old one */
|
|
status = TRUE;
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* savematch -- We found the pattern? Let's save it away.
|
|
*/
|
|
void savematch(void)
|
|
{
|
|
char *ptr; /* pointer to last match string */
|
|
int j;
|
|
struct line *curline; /* line of last match */
|
|
int curoff; /* offset " " */
|
|
|
|
/* Free any existing match string, then
|
|
* attempt to allocate a new one.
|
|
*/
|
|
if (patmatch != NULL)
|
|
free(patmatch);
|
|
|
|
ptr = patmatch = malloc(matchlen + 1);
|
|
|
|
if (ptr != NULL) {
|
|
curoff = matchoff;
|
|
curline = matchline;
|
|
|
|
for (j = 0; j < matchlen; j++)
|
|
*ptr++ = nextch(&curline, &curoff, FORWARD);
|
|
|
|
*ptr = '\0';
|
|
}
|
|
}
|
|
|
|
/*
|
|
* rvstrcpy -- Reverse string copy.
|
|
*/
|
|
void rvstrcpy(char *rvstr, char *str)
|
|
{
|
|
int i;
|
|
|
|
str += (i = strlen(str));
|
|
|
|
while (i-- > 0)
|
|
*rvstr++ = *--str;
|
|
|
|
*rvstr = '\0';
|
|
}
|
|
|
|
/*
|
|
* sreplace -- Search and replace.
|
|
*
|
|
* int f; default flag
|
|
* int n; # of repetitions wanted
|
|
*/
|
|
int sreplace(int f, int n)
|
|
{
|
|
return replaces(FALSE, f, n);
|
|
}
|
|
|
|
/*
|
|
* qreplace -- search and replace with query.
|
|
*
|
|
* int f; default flag
|
|
* int n; # of repetitions wanted
|
|
*/
|
|
int qreplace(int f, int n)
|
|
{
|
|
return replaces(TRUE, f, n);
|
|
}
|
|
|
|
/*
|
|
* replaces -- Search for a string and replace it with another
|
|
* string. Query might be enabled (according to kind).
|
|
*
|
|
* int kind; Query enabled flag
|
|
* int f; default flag
|
|
* int n; # of repetitions wanted
|
|
*/
|
|
static int replaces(int kind, int f, int n)
|
|
{
|
|
int status; /* success flag on pattern inputs */
|
|
int rlength; /* length of replacement string */
|
|
int numsub; /* number of substitutions */
|
|
int nummatch; /* number of found matches */
|
|
int nlflag; /* last char of search string a <NL>? */
|
|
int nlrepl; /* was a replace done on the last line? */
|
|
char c; /* input char for query */
|
|
char tpat[NPAT]; /* temporary to hold search pattern */
|
|
struct line *origline; /* original "." position */
|
|
int origoff; /* and offset (for . query option) */
|
|
struct line *lastline; /* position of last replace and */
|
|
int lastoff; /* offset (for 'u' query option) */
|
|
|
|
/* rfi */
|
|
lastline = NULL ;
|
|
lastoff = 0 ;
|
|
|
|
if (curbp->b_mode & MDVIEW) /* don't allow this command if */
|
|
return rdonly(); /* we are in read only mode */
|
|
|
|
/* Check for negative repetitions.
|
|
*/
|
|
if (f && n < 0)
|
|
return FALSE;
|
|
|
|
/* Ask the user for the text of a pattern.
|
|
*/
|
|
if ((status = readpattern((kind ==
|
|
FALSE ? "Replace" : "Query replace"),
|
|
&pat[0], TRUE))
|
|
!= TRUE)
|
|
return status;
|
|
|
|
/* Ask for the replacement string.
|
|
*/
|
|
if ((status = readpattern("with", &rpat[0], FALSE)) == ABORT)
|
|
return status;
|
|
|
|
/* Find the length of the replacement string.
|
|
*/
|
|
rlength = strlen(&rpat[0]);
|
|
|
|
/* Set up flags so we can make sure not to do a recursive
|
|
* replace on the last line.
|
|
*/
|
|
nlflag = (pat[matchlen - 1] == '\n');
|
|
nlrepl = FALSE;
|
|
|
|
if (kind) {
|
|
/* Build query replace question string.
|
|
*/
|
|
strcpy(tpat, "Replace '");
|
|
expandp(&pat[0], &tpat[strlen(tpat)], NPAT / 3);
|
|
strcat(tpat, "' with '");
|
|
expandp(&rpat[0], &tpat[strlen(tpat)], NPAT / 3);
|
|
strcat(tpat, "'? ");
|
|
|
|
/* Initialize last replaced pointers.
|
|
*/
|
|
lastline = NULL;
|
|
lastoff = 0;
|
|
}
|
|
|
|
/* Save original . position, init the number of matches and
|
|
* substitutions, and scan through the file.
|
|
*/
|
|
origline = curwp->w_dotp;
|
|
origoff = curwp->w_doto;
|
|
numsub = 0;
|
|
nummatch = 0;
|
|
|
|
while ((f == FALSE || n > nummatch) &&
|
|
(nlflag == FALSE || nlrepl == FALSE)) {
|
|
/* Search for the pattern.
|
|
* If we search with a regular expression,
|
|
* matchlen is reset to the true length of
|
|
* the matched string.
|
|
*/
|
|
#if MAGIC
|
|
if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0) {
|
|
if (!mcscanner(&mcpat[0], FORWARD, PTBEG))
|
|
break;
|
|
} else
|
|
#endif
|
|
if (!scanner(&pat[0], FORWARD, PTBEG))
|
|
break; /* all done */
|
|
|
|
++nummatch; /* Increment # of matches */
|
|
|
|
/* Check if we are on the last line.
|
|
*/
|
|
nlrepl = (lforw(curwp->w_dotp) == curwp->w_bufp->b_linep);
|
|
|
|
/* Check for query.
|
|
*/
|
|
if (kind) {
|
|
/* Get the query.
|
|
*/
|
|
pprompt:mlwrite(&tpat[0], &pat[0],
|
|
&rpat[0]);
|
|
qprompt:
|
|
update(TRUE); /* show the proposed place to change */
|
|
c = tgetc(); /* and input */
|
|
mlwrite(""); /* and clear it */
|
|
|
|
/* And respond appropriately.
|
|
*/
|
|
switch (c) {
|
|
#if PKCODE
|
|
case 'Y':
|
|
#endif
|
|
case 'y': /* yes, substitute */
|
|
case ' ':
|
|
savematch();
|
|
break;
|
|
|
|
#if PKCODE
|
|
case 'N':
|
|
#endif
|
|
case 'n': /* no, onword */
|
|
forwchar(FALSE, 1);
|
|
continue;
|
|
|
|
case '!': /* yes/stop asking */
|
|
kind = FALSE;
|
|
break;
|
|
|
|
#if PKCODE
|
|
case 'U':
|
|
#endif
|
|
case 'u': /* undo last and re-prompt */
|
|
|
|
/* Restore old position.
|
|
*/
|
|
if (lastline == NULL) {
|
|
/* There is nothing to undo.
|
|
*/
|
|
TTbeep();
|
|
goto pprompt;
|
|
}
|
|
curwp->w_dotp = lastline;
|
|
curwp->w_doto = lastoff;
|
|
lastline = NULL;
|
|
lastoff = 0;
|
|
|
|
/* Delete the new string.
|
|
*/
|
|
backchar(FALSE, rlength);
|
|
#if PKCODE
|
|
matchline = curwp->w_dotp;
|
|
matchoff = curwp->w_doto;
|
|
#endif
|
|
status = delins(rlength, patmatch, FALSE);
|
|
if (status != TRUE)
|
|
return status;
|
|
|
|
/* Record one less substitution,
|
|
* backup, save our place, and
|
|
* reprompt.
|
|
*/
|
|
--numsub;
|
|
backchar(FALSE, mlenold);
|
|
matchline = curwp->w_dotp;
|
|
matchoff = curwp->w_doto;
|
|
goto pprompt;
|
|
|
|
case '.': /* abort! and return */
|
|
/* restore old position */
|
|
curwp->w_dotp = origline;
|
|
curwp->w_doto = origoff;
|
|
curwp->w_flag |= WFMOVE;
|
|
|
|
case BELL: /* abort! and stay */
|
|
mlwrite("Aborted!");
|
|
return FALSE;
|
|
|
|
default: /* bitch and beep */
|
|
TTbeep();
|
|
|
|
case '?': /* help me */
|
|
mlwrite
|
|
("(Y)es, (N)o, (!)Do rest, (U)ndo last, (^G)Abort, (.)Abort back, (?)Help: ");
|
|
goto qprompt;
|
|
|
|
} /* end of switch */
|
|
}
|
|
|
|
/* end of "if kind" */
|
|
/*
|
|
* Delete the sucker, and insert its
|
|
* replacement.
|
|
*/
|
|
status = delins(matchlen, &rpat[0], TRUE);
|
|
if (status != TRUE)
|
|
return status;
|
|
|
|
/* Save our position, since we may
|
|
* undo this.
|
|
*/
|
|
if (kind) {
|
|
lastline = curwp->w_dotp;
|
|
lastoff = curwp->w_doto;
|
|
}
|
|
|
|
numsub++; /* increment # of substitutions */
|
|
}
|
|
|
|
/* And report the results.
|
|
*/
|
|
mlwrite("%d substitutions", numsub);
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* delins -- Delete a specified length from the current point
|
|
* then either insert the string directly, or make use of
|
|
* replacement meta-array.
|
|
*/
|
|
int delins(int dlength, char *instr, int use_meta)
|
|
{
|
|
int status;
|
|
#if MAGIC
|
|
struct magic_replacement *rmcptr;
|
|
#endif
|
|
|
|
/* Zap what we gotta,
|
|
* and insert its replacement.
|
|
*/
|
|
if ((status = ldelete((long) dlength, FALSE)) != TRUE)
|
|
mlwrite("%%ERROR while deleting");
|
|
else
|
|
#if MAGIC
|
|
if ((rmagical && use_meta) &&
|
|
(curwp->w_bufp->b_mode & MDMAGIC) != 0) {
|
|
rmcptr = &rmcpat[0];
|
|
while (rmcptr->mc_type != MCNIL && status == TRUE) {
|
|
if (rmcptr->mc_type == LITCHAR)
|
|
status = linstr(rmcptr->rstr);
|
|
else
|
|
status = linstr(patmatch);
|
|
rmcptr++;
|
|
}
|
|
} else
|
|
#endif
|
|
status = linstr(instr);
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* expandp -- Expand control key sequences for output.
|
|
*
|
|
* char *srcstr; string to expand
|
|
* char *deststr; destination of expanded string
|
|
* int maxlength; maximum chars in destination
|
|
*/
|
|
int expandp(char *srcstr, char *deststr, int maxlength)
|
|
{
|
|
unsigned char c; /* current char to translate */
|
|
|
|
/* Scan through the string.
|
|
*/
|
|
while ((c = *srcstr++) != 0) {
|
|
if (c == '\n') { /* it's a newline */
|
|
*deststr++ = '<';
|
|
*deststr++ = 'N';
|
|
*deststr++ = 'L';
|
|
*deststr++ = '>';
|
|
maxlength -= 4;
|
|
}
|
|
#if PKCODE
|
|
else if ((c > 0 && c < 0x20) || c == 0x7f) /* control character */
|
|
#else
|
|
else if (c < 0x20 || c == 0x7f) /* control character */
|
|
#endif
|
|
{
|
|
*deststr++ = '^';
|
|
*deststr++ = c ^ 0x40;
|
|
maxlength -= 2;
|
|
} else if (c == '%') {
|
|
*deststr++ = '%';
|
|
*deststr++ = '%';
|
|
maxlength -= 2;
|
|
} else { /* any other character */
|
|
|
|
*deststr++ = c;
|
|
maxlength--;
|
|
}
|
|
|
|
/* check for maxlength */
|
|
if (maxlength < 4) {
|
|
*deststr++ = '$';
|
|
*deststr = '\0';
|
|
return FALSE;
|
|
}
|
|
}
|
|
*deststr = '\0';
|
|
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).
|
|
*/
|
|
int boundry(struct line *curline, int curoff, int dir)
|
|
{
|
|
int border;
|
|
|
|
if (dir == FORWARD) {
|
|
border = (curoff == llength(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.
|
|
*/
|
|
static int nextch(struct line **pcurline, int *pcuroff, int dir)
|
|
{
|
|
struct line *curline;
|
|
int curoff;
|
|
int c;
|
|
|
|
curline = *pcurline;
|
|
curoff = *pcuroff;
|
|
|
|
if (dir == FORWARD) {
|
|
if (curoff == llength(curline)) { /* if at EOL */
|
|
curline = lforw(curline); /* skip to next line */
|
|
curoff = 0;
|
|
c = '\n'; /* and return a <NL> */
|
|
} else
|
|
c = lgetc(curline, curoff++); /* get the char */
|
|
} else { /* Reverse. */
|
|
|
|
if (curoff == 0) {
|
|
curline = lback(curline);
|
|
curoff = llength(curline);
|
|
c = '\n';
|
|
} else
|
|
c = lgetc(curline, --curoff);
|
|
|
|
}
|
|
*pcurline = curline;
|
|
*pcuroff = curoff;
|
|
|
|
return c;
|
|
}
|
|
|
|
#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 a newline, beginning 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.
|
|
*/
|
|
static int mcstr(void)
|
|
{
|
|
struct magic *mcptr, *rtpcm;
|
|
char *patptr;
|
|
int mj;
|
|
int pchr;
|
|
int status = TRUE;
|
|
int does_closure = FALSE;
|
|
|
|
/* If we had metacharacters in the struct magic array previously,
|
|
* free up any bitmaps that may have been allocated.
|
|
*/
|
|
if (magical)
|
|
mcclear();
|
|
|
|
magical = FALSE;
|
|
mj = 0;
|
|
mcptr = &mcpat[0];
|
|
patptr = &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 (mj != 0)
|
|
goto litcase;
|
|
|
|
mcptr->mc_type = BOL;
|
|
magical = TRUE;
|
|
does_closure = FALSE;
|
|
break;
|
|
case MC_EOL:
|
|
if (*(patptr + 1) != '\0')
|
|
goto litcase;
|
|
|
|
mcptr->mc_type = EOL;
|
|
magical = TRUE;
|
|
does_closure = FALSE;
|
|
break;
|
|
case MC_ANY:
|
|
mcptr->mc_type = ANY;
|
|
magical = TRUE;
|
|
does_closure = TRUE;
|
|
break;
|
|
case MC_CLOSURE:
|
|
/* Does the closure symbol mean closure here?
|
|
* If so, back up to the previous element
|
|
* and indicate it is enclosed.
|
|
*/
|
|
if (!does_closure)
|
|
goto litcase;
|
|
mj--;
|
|
mcptr--;
|
|
mcptr->mc_type |= CLOSURE;
|
|
magical = TRUE;
|
|
does_closure = FALSE;
|
|
break;
|
|
|
|
/* Note: no break between MC_ESC case and the default.
|
|
*/
|
|
case MC_ESC:
|
|
if (*(patptr + 1) != '\0') {
|
|
pchr = *++patptr;
|
|
magical = TRUE;
|
|
}
|
|
default:
|
|
litcase:mcptr->mc_type =
|
|
LITCHAR;
|
|
mcptr->u.lchar = pchr;
|
|
does_closure = (pchr != '\n');
|
|
break;
|
|
} /* End of switch. */
|
|
mcptr++;
|
|
patptr++;
|
|
mj++;
|
|
} /* End of while. */
|
|
|
|
/* Close off the meta-string.
|
|
*/
|
|
mcptr->mc_type = MCNIL;
|
|
|
|
/* Set up the reverse array, if the status is good. Please note the
|
|
* structure assignment - your compiler may not like that.
|
|
* If the status is not good, nil out the meta-pattern.
|
|
* The only way the status would be bad is from the cclmake()
|
|
* routine, and the bitmap for that member is guarenteed to be
|
|
* freed. So we stomp a MCNIL value there, and call mcclear()
|
|
* to free any other bitmaps.
|
|
*/
|
|
if (status) {
|
|
rtpcm = &tapcm[0];
|
|
while (--mj >= 0) {
|
|
#if MSC | TURBO | VMS | USG | BSD | V7
|
|
*rtpcm++ = *--mcptr;
|
|
#endif
|
|
}
|
|
rtpcm->mc_type = MCNIL;
|
|
} else {
|
|
(--mcptr)->mc_type = MCNIL;
|
|
mcclear();
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* rmcstr -- Set up the replacement 'magic' array. Note that if there
|
|
* are no meta-characters encountered in the replacement string,
|
|
* the array is never actually created - we will just use the
|
|
* character array rpat[] as the replacement string.
|
|
*/
|
|
static int rmcstr(void)
|
|
{
|
|
struct magic_replacement *rmcptr;
|
|
char *patptr;
|
|
int status = TRUE;
|
|
int mj;
|
|
|
|
patptr = &rpat[0];
|
|
rmcptr = &rmcpat[0];
|
|
mj = 0;
|
|
rmagical = FALSE;
|
|
|
|
while (*patptr && status == TRUE) {
|
|
switch (*patptr) {
|
|
case MC_DITTO:
|
|
|
|
/* If there were non-magical characters
|
|
* in the string before reaching this
|
|
* character, plunk it in the replacement
|
|
* array before processing the current
|
|
* meta-character.
|
|
*/
|
|
if (mj != 0) {
|
|
rmcptr->mc_type = LITCHAR;
|
|
if ((rmcptr->rstr =
|
|
malloc(mj + 1)) == NULL) {
|
|
mlwrite("%%Out of memory");
|
|
status = FALSE;
|
|
break;
|
|
}
|
|
strncpy(rmcptr->rstr, patptr - mj, mj);
|
|
rmcptr++;
|
|
mj = 0;
|
|
}
|
|
rmcptr->mc_type = DITTO;
|
|
rmcptr++;
|
|
rmagical = TRUE;
|
|
break;
|
|
|
|
case MC_ESC:
|
|
rmcptr->mc_type = LITCHAR;
|
|
|
|
/* We malloc mj plus two here, instead
|
|
* of one, because we have to count the
|
|
* current character.
|
|
*/
|
|
if ((rmcptr->rstr = malloc(mj + 2)) == NULL) {
|
|
mlwrite("%%Out of memory");
|
|
status = FALSE;
|
|
break;
|
|
}
|
|
|
|
strncpy(rmcptr->rstr, patptr - mj, mj + 1);
|
|
|
|
/* If MC_ESC is not the last character
|
|
* in the string, find out what it is
|
|
* escaping, and overwrite the last
|
|
* character with it.
|
|
*/
|
|
if (*(patptr + 1) != '\0')
|
|
*((rmcptr->rstr) + mj) = *++patptr;
|
|
|
|
rmcptr++;
|
|
mj = 0;
|
|
rmagical = TRUE;
|
|
break;
|
|
|
|
default:
|
|
mj++;
|
|
}
|
|
patptr++;
|
|
}
|
|
|
|
if (rmagical && mj > 0) {
|
|
rmcptr->mc_type = LITCHAR;
|
|
if ((rmcptr->rstr = malloc(mj + 1)) == NULL) {
|
|
mlwrite("%%Out of memory.");
|
|
status = FALSE;
|
|
}
|
|
strncpy(rmcptr->rstr, patptr - mj, mj);
|
|
rmcptr++;
|
|
}
|
|
|
|
rmcptr->mc_type = MCNIL;
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* mcclear -- Free up any CCL bitmaps, and MCNIL the struct magic search arrays.
|
|
*/
|
|
void mcclear(void)
|
|
{
|
|
struct magic *mcptr;
|
|
|
|
mcptr = &mcpat[0];
|
|
|
|
while (mcptr->mc_type != MCNIL) {
|
|
if ((mcptr->mc_type & MASKCL) == CCL ||
|
|
(mcptr->mc_type & MASKCL) == NCCL)
|
|
free(mcptr->u.cclmap);
|
|
mcptr++;
|
|
}
|
|
mcpat[0].mc_type = tapcm[0].mc_type = MCNIL;
|
|
}
|
|
|
|
/*
|
|
* rmcclear -- Free up any strings, and MCNIL the struct magic_replacement array.
|
|
*/
|
|
void rmcclear(void)
|
|
{
|
|
struct magic_replacement *rmcptr;
|
|
|
|
rmcptr = &rmcpat[0];
|
|
|
|
while (rmcptr->mc_type != MCNIL) {
|
|
if (rmcptr->mc_type == LITCHAR)
|
|
free(rmcptr->rstr);
|
|
rmcptr++;
|
|
}
|
|
|
|
rmcpat[0].mc_type = MCNIL;
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
static int mceq(int bc, struct magic *mt)
|
|
{
|
|
int result;
|
|
|
|
#if PKCODE
|
|
bc = bc & 0xFF;
|
|
#endif
|
|
switch (mt->mc_type & MASKCL) {
|
|
case LITCHAR:
|
|
result = eq(bc, mt->u.lchar);
|
|
break;
|
|
|
|
case ANY:
|
|
result = (bc != '\n');
|
|
break;
|
|
|
|
case CCL:
|
|
if (!(result = biteq(bc, mt->u.cclmap))) {
|
|
if ((curwp->w_bufp->b_mode & MDEXACT) == 0 &&
|
|
(isletter(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 &&
|
|
(isletter(bc))) {
|
|
result &= !biteq(CHCASE(bc), mt->u.cclmap);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
mlwrite("mceq: what is %d?", mt->mc_type);
|
|
result = FALSE;
|
|
break;
|
|
|
|
} /* End of switch. */
|
|
|
|
return result;
|
|
}
|
|
|
|
extern char *clearbits(void);
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
static int cclmake(char **ppatptr, struct magic *mcptr)
|
|
{
|
|
char *bmap;
|
|
char *patptr;
|
|
int pchr, ochr;
|
|
|
|
if ((bmap = clearbits()) == NULL) {
|
|
mlwrite("%%Out of memory");
|
|
return FALSE;
|
|
}
|
|
|
|
mcptr->u.cclmap = bmap;
|
|
patptr = *ppatptr;
|
|
|
|
/*
|
|
* 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 ((ochr = *patptr) == MC_ECCL) {
|
|
mlwrite("%%No characters in character class");
|
|
return FALSE;
|
|
} else {
|
|
if (ochr == MC_ESC)
|
|
ochr = *++patptr;
|
|
|
|
setbit(ochr, bmap);
|
|
patptr++;
|
|
}
|
|
|
|
while (ochr != '\0' && (pchr = *patptr) != MC_ECCL) {
|
|
switch (pchr) {
|
|
/* Range character loses its meaning
|
|
* if it is the last character in
|
|
* the class.
|
|
*/
|
|
case MC_RCCL:
|
|
if (*(patptr + 1) == MC_ECCL)
|
|
setbit(pchr, bmap);
|
|
else {
|
|
pchr = *++patptr;
|
|
while (++ochr <= pchr)
|
|
setbit(ochr, bmap);
|
|
}
|
|
break;
|
|
|
|
/* Note: no break between case MC_ESC and the default.
|
|
*/
|
|
case MC_ESC:
|
|
pchr = *++patptr;
|
|
default:
|
|
setbit(pchr, bmap);
|
|
break;
|
|
}
|
|
patptr++;
|
|
ochr = pchr;
|
|
}
|
|
|
|
*ppatptr = patptr;
|
|
|
|
if (ochr == '\0') {
|
|
mlwrite("%%Character class not ended");
|
|
free(bmap);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* biteq -- is the character in the bitmap?
|
|
*/
|
|
static int biteq(int bc, char *cclmap)
|
|
{
|
|
#if PKCODE
|
|
bc = bc & 0xFF;
|
|
#endif
|
|
if (bc >= HICHAR)
|
|
return FALSE;
|
|
|
|
return (*(cclmap + (bc >> 3)) & BIT(bc & 7)) ? TRUE : FALSE;
|
|
}
|
|
|
|
/*
|
|
* clearbits -- Allocate and zero out a CCL bitmap.
|
|
*/
|
|
static char *clearbits(void)
|
|
{
|
|
char *cclstart;
|
|
char *cclmap;
|
|
int i;
|
|
|
|
if ((cclmap = cclstart = (char *)malloc(HIBYTE)) != NULL) {
|
|
for (i = 0; i < HIBYTE; i++)
|
|
*cclmap++ = 0;
|
|
}
|
|
return cclstart;
|
|
}
|
|
|
|
/*
|
|
* setbit -- Set a bit (ON only) in the bitmap.
|
|
*/
|
|
static void setbit(int bc, char *cclmap)
|
|
{
|
|
#if PKCODE
|
|
bc = bc & 0xFF;
|
|
#endif
|
|
if (bc < HICHAR)
|
|
*(cclmap + (bc >> 3)) |= BIT(bc & 7);
|
|
}
|
|
#endif
|