Subject: v09i044: MicroEMACS, version 3.8b, Part12/14 Newsgroups: mod.sources Approved: rs@mirror.TMC.COM Submitted by: ihnp4!itivax!duncan!lawrence (Daniel Lawrence) Mod.sources: Volume 9, Issue 44 Archive-name: uemacs3.8b/Part12 #! /bin/sh # This is a shell archive. Remove anything before this line, # then unpack it by saving it in a file and typing "sh file". # If this archive is complete, you will see the message: # "End of archive 12 (of 14)." # Contents: search.c PATH=/bin:/usr/bin:/usr/ucb ; export PATH echo shar: Extracting \"search.c\" \(30773 characters\) if test -f search.c ; then echo shar: Will not over-write existing file \"search.c\" else sed "s/^X//" >search.c <<'END_OF_search.c' X/* X * The functions in this file implement commands that search in the forward X * and backward directions. There are no special characters in the search X * strings. Probably should have a regular expression search, or something X * like that. X * X * Aug. 1986 John M. Gamble: X * Made forward and reverse search use the same scan routine. X * X * Added a limited number of regular expressions - 'any', X * 'character class', 'closure', 'beginning of line', and X * 'end of line'. X * X * Replacement metacharacters will have to wait for a re-write of X * the replaces function, and a new variation of ldelete(). X * X * For those curious as to my references, i made use of X * Kernighan & Plauger's "Software Tools." X * I deliberately did not look at any published grep or editor X * source (aside from this one) for inspiration. I did make use of X * Allen Hollub's bitmap routines as published in Doctor Dobb's Journal, X * June, 1985 and modified them for the limited needs of character class X * matching. Any inefficiences, bugs, stupid coding examples, etc., X * are therefore my own responsibility. X */ X X#include X#include "estruct.h" X#include "edef.h" X#include "esearch.h" X X/* X * Reversed pattern array. X */ Xchar tap[NPAT]; X X#if MAGIC X/* X * The variable magical determines if there are actual X * metacharacters in the string - if not, then we don't X * have to use the slower MAGIC mode search functions. X * X * The variable mclen holds the length of the matched X * string - used by the replace functions. X * X * The arrays mcpat and tapcm hold the MC and reversed X * MC search structures. X */ Xshort int magical = FALSE; Xint mclen = 0; XMC mcpat[NPAT]; XMC tapcm[NPAT]; X#endif X X/* X * forwsearch -- Search forward. Get a search string from the user, and X * search, beginning at ".", for the string. If found, reset the "." X * to be just after the match string, and (perhaps) repaint the display. X */ X Xforwsearch(f, n) X{ X register int status = TRUE; X X /* Resolve the repeat count. X */ X if (n == 0) X n = 1; X X /* If n is negative, search backwards. X * Otherwise proceed by asking for the search string. X */ X if (n < 0) X status = backsearch(f, -n); X X /* Ask the user for the text of a pattern. If the X * response is TRUE (responses other than FALSE are X * possible), search for the pattern. X */ X else if ((status = readpattern("Search", &pat[0], TRUE)) == TRUE) X { X do X { X#if MAGIC X if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0) X status = mcscanner(&mcpat[0], FORWARD, PTEND); X else X#endif X status = scanner(&pat[0], FORWARD, PTEND); X } while ((--n > 0) && status); X X /* ...and complain if not there. X */ X if (status == FALSE) X mlwrite("Not found"); X } X return(status); X} X X/* X * forwhunt -- Search forward for a previously acquired search string, X * beginning at ".". If found, reset the "." to be just after X * the match string, and (perhaps) repaint the display. X */ X Xforwhunt(f, n) X{ X register int status = TRUE; X X /* Resolve the repeat count. X */ X if (n == 0) X n = 1; X else if (n < 0) /* search backwards */ X return(backhunt(f, -n)); X X /* Make sure a pattern exists, or that we didn't switch X * into MAGIC mode until after we entered the pattern. X */ X if (pat[0] == '\0') X { X mlwrite("No pattern set"); X return FALSE; X } X#if MAGIC X if ((curwp->w_bufp->b_mode & MDMAGIC) != 0 && X mcpat[0].mc_type == MCNIL) X { X if (!mcstr()) X return FALSE; X } X#endif X X /* Search for the pattern... X */ X do X { X#if MAGIC X if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0) X status = mcscanner(&mcpat[0], FORWARD, PTEND); X else X#endif X status = scanner(&pat[0], FORWARD, PTEND); X } while ((--n > 0) && status); X X /* ...and complain if not there. X */ X if (status == FALSE) X mlwrite("Not found"); X X return(status); X} X X/* X * backsearch -- Reverse search. Get a search string from the user, and X * search, starting at "." and proceeding toward the front of the buffer. X * If found "." is left pointing at the first character of the pattern X * (the last character that was matched). X */ Xbacksearch(f, n) X{ X register int status = TRUE; X X /* Resolve null and negative arguments. X */ X if (n == 0) X n = 1; X X /* If n is negative, search forwards. X * Otherwise proceed by asking for the search string. X */ X if (n < 0) X status = forwsearch(f, -n); X X /* Ask the user for the text of a pattern. If the X * response is TRUE (responses other than FALSE are X * possible), search for the pattern. X */ X else if ((status = readpattern("Reverse search", &pat[0], TRUE)) == TRUE) X { X do X { X#if MAGIC X if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0) X status = mcscanner(&tapcm[0], REVERSE, PTBEG); X else X#endif X status = scanner(&tap[0], REVERSE, PTBEG); X } while ((--n > 0) && status); X X /* ...and complain if not there. X */ X if (status == FALSE) X mlwrite("Not found"); X } X return(status); X} X X/* X * backhunt -- Reverse search for a previously acquired search string, X * starting at "." and proceeding toward the front of the buffer. X * If found "." is left pointing at the first character of the pattern X * (the last character that was matched). X */ Xbackhunt(f, n) X{ X register int status = TRUE; X X /* Resolve null and negative arguments. X */ X if (n == 0) X n = 1; X else if (n < 0) X return(forwhunt(f, -n)); X X /* Make sure a pattern exists, or that we didn't switch X * into MAGIC mode until after we entered the pattern. X */ X if (tap[0] == '\0') X { X mlwrite("No pattern set"); X return FALSE; X } X#if MAGIC X if ((curwp->w_bufp->b_mode & MDMAGIC) != 0 && X tapcm[0].mc_type == MCNIL) X { X if (!mcstr()) X return FALSE; X } X#endif X X /* Go search for it... X */ X do X { X#if MAGIC X if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0) X status = mcscanner(&tapcm[0], REVERSE, PTBEG); X else X#endif X status = scanner(&tap[0], REVERSE, PTBEG); X } while ((--n > 0) && status); X X /* ...and complain if not there. X */ X if (status == FALSE) X mlwrite("Not found"); X X return(status); X} X X#if MAGIC X/* X * mcscanner -- Search for a meta-pattern in either direction. X */ Xint mcscanner(mcpatrn, direct, beg_or_end) XMC *mcpatrn; /* pointer into pattern */ Xint direct; /* which way to go.*/ Xint beg_or_end; /* put point at beginning or end of pattern.*/ X{ X register LINE *lastline; /* last line position during scan */ X register int lastoff; /* position within last line */ X LINE *curline; /* current line during scan */ X int curoff; /* position within current line */ X int c; /* (dummy) char at current position */ X X /* If we are going in reverse, then the 'end' is actually X * the beginning of the pattern. Toggle it. X */ X beg_or_end ^= direct; X X /* Setup local scan pointers to global ".". X */ X curline = curwp->w_dotp; X curoff = curwp->w_doto; X X /* Scan each character until we hit the head link record. X */ X while (!boundry(curline, curoff, direct)) X { X /* Save the current position in case we need to X * restore it on a match, and initialize mclen to X * zero in case we are doing a search for replacement. X */ X lastline = curline; X lastoff = curoff; X mclen = 0; X X if (amatch(mcpatrn, direct, &curline, &curoff)) X { X /* A SUCCESSFULL MATCH!!! X * reset the global "." pointers. X */ X if (beg_or_end == PTEND) /* at end of string */ X { X curwp->w_dotp = curline; X curwp->w_doto = curoff; X } X else /* at beginning of string */ X { X curwp->w_dotp = lastline; X curwp->w_doto = lastoff; X } X X curwp->w_flag |= WFMOVE; /* flag that we have moved */ X return TRUE; X } X X /* Advance the cursor. X */ X c = nextch(&curline, &curoff, direct); X } X X return FALSE; /* We could not find a match.*/ X} X X/* X * amatch -- Search for a meta-pattern in either direction. Based on the X * recursive routine amatch() (for "anchored match") in X * Kernighan & Plauger's "Software Tools". X */ Xstatic int amatch(mcptr, direct, pcwline, pcwoff) Xregister MC *mcptr; /* string to scan for */ Xint direct; /* which way to go.*/ XLINE **pcwline; /* current line during scan */ Xint *pcwoff; /* position within current line */ X{ X register int c; /* character at current position */ X LINE *curline; /* current line during scan */ X int curoff; /* position within current line */ X int nchars; X X /* Set up local scan pointers to ".", and get X * the current character. Then loop around X * the pattern pointer until success or failure. X */ X curline = *pcwline; X curoff = *pcwoff; X X /* The beginning-of-line and end-of-line metacharacters X * do not compare against characters, they compare X * against positions. X * BOL is guaranteed to be at the start of the pattern X * for forward searches, and at the end of the pattern X * for reverse searches. The reverse is true for EOL. X * So, for a start, we check for them on entry. X */ X if (mcptr->mc_type == BOL) X { X if (curoff != 0) X return FALSE; X mcptr++; X } X X if (mcptr->mc_type == EOL) X { X if (curoff != llength(curline)) X return FALSE; X mcptr++; X } X X while (mcptr->mc_type != MCNIL) X { X c = nextch(&curline, &curoff, direct); X X if (mcptr->mc_type & CLOSURE) X { X /* Try to match as many characters as possible X * against the current meta-character. A X * newline never matches a closure. X */ X nchars = 0; X while (c != '\n' && mceq(c, mcptr)) X { X c = nextch(&curline, &curoff, direct); X nchars++; X } X X /* We are now at the character that made us X * fail. Try to match the rest of the pattern. X * Shrink the closure by one for each failure. X * Since closure matches *zero* or more occurences X * of a pattern, a match may start even if the X * previous loop matched no characters. X */ X mcptr++; X X for (;;) X { X c = nextch(&curline, &curoff, direct ^ REVERSE); X X if (amatch(mcptr, direct, &curline, &curoff)) X { X mclen += nchars; X goto success; X } X X if (nchars-- == 0) X return FALSE; X } X } X else /* Not closure.*/ X { X /* The only way we'd get a BOL metacharacter X * at this point is at the end of the reversed pattern. X * The only way we'd get an EOL metacharacter X * here is at the end of a regular pattern. X * So if we match one or the other, and are at X * the appropriate position, we are guaranteed success X * (since the next pattern character has to be MCNIL). X * Before we report success, however, we back up by X * one character, so as to leave the cursor in the X * correct position. For example, a search for ")$" X * will leave the cursor at the end of the line, while X * a search for ")" will leave the cursor at the X * beginning of the next line. This follows the X * notion that the meta-character '$' (and likewise X * '^') match positions, not characters. X */ X if (mcptr->mc_type == BOL) X if (curoff == llength(curline)) X { X c = nextch(&curline, &curoff, X direct ^ REVERSE); X goto success; X } X else X return FALSE; X X if (mcptr->mc_type == EOL) X if (curoff == 0) X { X c = nextch(&curline, &curoff, X direct ^ REVERSE); X goto success; X } X else X return FALSE; X X /* Neither BOL nor EOL, so go through X * the meta-character equal function. X */ X if (!mceq(c, mcptr)) X return FALSE; X } X X /* Increment the length counter and X * advance the pattern pointer. X */ X mclen++; X mcptr++; X } /* End of mcptr loop.*/ X X /* A SUCCESSFULL MATCH!!! X * Reset the "." pointers. X */ Xsuccess: X *pcwline = curline; X *pcwoff = curoff; X X return TRUE; X} X#endif X X/* X * scanner -- Search for a pattern in either direction. X */ Xint scanner(patrn, direct, beg_or_end) Xchar *patrn; /* string to scan for */ Xint direct; /* which way to go.*/ Xint beg_or_end; /* put point at beginning or end of pattern.*/ X{ X register int c; /* character at current position */ X register char *patptr; /* pointer into pattern */ X register LINE *lastline; /* last line position during scan */ X register int lastoff; /* position within last line */ X LINE *curline; /* current line during scan */ X int curoff; /* position within current line */ X LINE *matchline; /* current line during matching */ X int matchoff; /* position in matching line */ X X /* If we are going in reverse, then the 'end' is actually X * the beginning of the pattern. Toggle it. X */ X beg_or_end ^= direct; X X /* Setup local scan pointers to global ".". X */ X curline = curwp->w_dotp; X curoff = curwp->w_doto; X X /* Scan each character until we hit the head link record. X */ X while (!boundry(curline, curoff, direct)) X { X /* Save the current position in case we need to X * restore it on a match. X */ X lastline = curline; X lastoff = curoff; X X /* Get the character resolving newlines, and X * test it against first char in pattern. X */ X c = nextch(&curline, &curoff, direct); X X if (eq(c, patrn[0])) /* if we find it..*/ X { X /* Setup match pointers. X */ X matchline = curline; X matchoff = curoff; X patptr = &patrn[0]; X X /* Scan through the pattern for a match. X */ X while (*++patptr != '\0') X { X c = nextch(&matchline, &matchoff, direct); X X if (!eq(c, *patptr)) X goto fail; X } X X /* A SUCCESSFULL MATCH!!! X * reset the global "." pointers X */ X if (beg_or_end == PTEND) /* at end of string */ X { X curwp->w_dotp = matchline; X curwp->w_doto = matchoff; X } X else /* at beginning of string */ X { X curwp->w_dotp = lastline; X curwp->w_doto = lastoff; X } X X curwp->w_flag |= WFMOVE; /* Flag that we have moved.*/ X return TRUE; X X } Xfail:; /* continue to search */ X } X X return FALSE; /* We could not find a match */ X} X X/* X * eq -- Compare two characters. The "bc" comes from the buffer, "pc" X * from the pattern. If we are not in EXACT mode, fold out the case. X */ Xint eq(bc, pc) Xregister int bc; Xregister int pc; X{ X if ((curwp->w_bufp->b_mode & MDEXACT) == 0) X { X if (islower(bc)) X bc ^= DIFCASE; X X if (islower(pc)) X pc ^= DIFCASE; X } X X return (bc == pc); X} X X/* X * readpattern -- Read a pattern. Stash it in apat. If it is the X * search string, create the reverse pattern and the magic X * pattern, assuming we are in MAGIC mode (and defined that way). X * Apat is not updated if the user types in an empty line. If X * the user typed an empty line, and there is no old pattern, it is X * an error. Display the old pattern, in the style of Jeff Lomicka. X * There is some do-it-yourself control expansion. Change to using X * to delimit the end-of-pattern to allow s in the search X * string. X */ Xstatic int readpattern(prompt, apat, srch) Xchar *prompt; Xchar apat[]; Xint srch; X{ X int status; X char tpat[NPAT+20]; X X strcpy(tpat, prompt); /* copy prompt to output string */ X strcat(tpat, " ["); /* build new prompt string */ X expandp(&apat[0], &tpat[strlen(tpat)], NPAT/2); /* add old pattern */ X strcat(tpat, "]: "); X X /* Read a pattern. Either we get one, X * or we just get the META charater, and use the previous pattern. X * Then, if it's the search string, make a reversed pattern. X * *Then*, make the meta-pattern, if we are defined that way. X */ X if ((status = mlreplyt(tpat, tpat, NPAT, metac)) == TRUE) X { X strcpy(apat, tpat); X if (srch) /* If we are doing the search string.*/ X { X /* Reverse string copy. X */ X rvstrcpy(tap, apat); X#if MAGIC X /* Only make the meta-pattern if in magic mode, X * since the pattern in question might have an X * invalid meta combination. X */ X if ((curwp->w_bufp->b_mode & MDMAGIC) == 0) X mcpat[0].mc_type = tapcm[0].mc_type = MCNIL; X else X status = mcstr(); X#endif X } X } X else if (status == FALSE && apat[0] != 0) /* Old one */ X status = TRUE; X X return(status); X} X X/* X * rvstrcpy -- Reverse string copy. X */ Xrvstrcpy(rvstr, str) Xregister char *rvstr, *str; X{ X register int i; X X str += (i = strlen(str)); X X while (i-- > 0) X *rvstr++ = *--str; X X *rvstr = '\0'; X} X X/* X * sreplace -- Search and replace. X */ Xsreplace(f, n) Xint f; /* default flag */ Xint n; /* # of repetitions wanted */ X{ X return(replaces(FALSE, f, n)); X} X X/* X * qreplace -- search and replace with query. X */ Xqreplace(f, n) Xint f; /* default flag */ Xint n; /* # of repetitions wanted */ X{ X return(replaces(TRUE, f, n)); X} X X/* X * replaces -- Search for a string and replace it with another X * string. Query might be enabled (according to kind). X */ Xstatic int replaces(kind, f, n) Xint kind; /* Query enabled flag */ Xint f; /* default flag */ Xint n; /* # of repetitions wanted */ X{ X register int i; /* loop index */ X register int status; /* success flag on pattern inputs */ X register int slength, X rlength; /* length of search and replace strings */ X register int numsub; /* number of substitutions */ X register int nummatch; /* number of found matches */ X int nlflag; /* last char of search string a ? */ X int nlrepl; /* was a replace done on the last line? */ X char tmpc; /* temporary character */ X char c; /* input char for query */ X char tpat[NPAT]; /* temporary to hold search pattern */ X LINE *origline; /* original "." position */ X int origoff; /* and offset (for . query option) */ X LINE *lastline; /* position of last replace and */ X int lastoff; /* offset (for 'u' query option) */ X X if (curbp->b_mode & MDVIEW) /* don't allow this command if */ X return(rdonly()); /* we are in read only mode */ X X /* Check for negative repetitions. X */ X if (f && n < 0) X return(FALSE); X X /* Ask the user for the text of a pattern. X */ X if ((status = readpattern( X (kind == FALSE ? "Replace" : "Query replace"), &pat[0], TRUE)) X != TRUE) X return(status); X X /* Ask for the replacement string. X */ X if ((status = readpattern("with", &rpat[0], FALSE)) == ABORT) X return(status); X X /* Find the lengths of the strings. X */ X slength = strlen(&pat[0]); X rlength = strlen(&rpat[0]); X X /* Set up flags so we can make sure not to do a recursive X * replace on the last line. X */ X nlflag = (pat[slength - 1] == '\n'); X nlrepl = FALSE; X X if (kind) X { X /* Build query replace question string. X */ X strcpy(tpat, "Replace '"); X expandp(&pat[0], &tpat[strlen(tpat)], NPAT/3); X strcat(tpat, "' with '"); X expandp(&rpat[0], &tpat[strlen(tpat)], NPAT/3); X strcat(tpat, "'? "); X X /* Initialize last replaced pointers. X */ X lastline = NULL; X lastoff = 0; X } X X /* Save original . position, init the number of matches and X * substitutions, and scan through the file. X */ X origline = curwp->w_dotp; X origoff = curwp->w_doto; X numsub = 0; X nummatch = 0; X X while ( (f == FALSE || n > nummatch) && X (nlflag == FALSE || nlrepl == FALSE) ) X { X /* Search for the pattern. X * If we search with a regular expression, also save X * the true length of matched string. X */ X#if MAGIC X if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0) X { X if (!mcscanner(&mcpat[0], FORWARD, PTBEG)) X break; X slength = mclen; X } X else X#endif X if (!scanner(&pat[0], FORWARD, PTBEG)) X break; /* all done */ X X ++nummatch; /* Increment # of matches */ X X /* Check if we are on the last line. X */ X nlrepl = (lforw(curwp->w_dotp) == curwp->w_bufp->b_linep); X X /* Check for query. X */ X if (kind) X { X /* Get the query. X */ Xpprompt: mlwrite(&tpat[0], &pat[0], &rpat[0]); Xqprompt: X update(); /* show the proposed place to change */ X c = tgetc(); /* and input */ X mlwrite(""); /* and clear it */ X X /* And respond appropriately. X */ X switch (c) X { X case 'y': /* yes, substitute */ X case ' ': X break; X X case 'n': /* no, onword */ X forwchar(FALSE, 1); X continue; X X case '!': /* yes/stop asking */ X kind = FALSE; X break; X X case 'u': /* undo last and re-prompt */ X X /* Restore old position. X */ X if (lastline == NULL) X { X /* There is nothing to undo. X */ X TTbeep(); X goto qprompt; X } X curwp->w_dotp = lastline; X curwp->w_doto = lastoff; X lastline = NULL; X lastoff = 0; X X /* Delete the new string. X */ X backchar(FALSE, rlength); X if (!ldelete(rlength, FALSE)) X { X mlwrite("%%ERROR while deleting"); X return(FALSE); X } X X /* And put in the old one. X */ X for (i = 0; i < slength; i++) X { X tmpc = pat[i]; X status = (tmpc == '\n'? X lnewline(): X linsert(1, tmpc)); X X /* Insertion error? X */ X if (!status) X { X mlwrite("%%Out of memory while inserting"); X return(FALSE); X } X } X X /* Record one less substitution, X * backup, and reprompt. X */ X --numsub; X backchar(FALSE, slength); X goto pprompt; X X case '.': /* abort! and return */ X /* restore old position */ X curwp->w_dotp = origline; X curwp->w_doto = origoff; X curwp->w_flag |= WFMOVE; X X case BELL: /* abort! and stay */ X mlwrite("Aborted!"); X return(FALSE); X X default: /* bitch and beep */ X TTbeep(); X X case '?': /* help me */ X mlwrite( X"(Y)es, (N)o, (!)Do rest, (U)ndo last, (^G)Abort, (.)Abort back, (?)Help: "); X goto qprompt; X X } /* end of switch */ X } /* end of "if kind" */ X X /* X * Delete the sucker. X */ X if (!ldelete(slength, FALSE)) X { X mlwrite("%%ERROR while deleteing"); X return(FALSE); X } X X /* And insert its replacement. X */ X for (i = 0; i < rlength; i++) X { X tmpc = rpat[i]; X status = (tmpc == '\n'? lnewline(): linsert(1, tmpc)); X X /* Insertion error? X */ X if (!status) X { X mlwrite("%%Out of memory while inserting"); X return(FALSE); X } X } X X /* Save where we are if we might undo this.... X */ X if (kind) X { X lastline = curwp->w_dotp; X lastoff = curwp->w_doto; X } X X numsub++; /* increment # of substitutions */ X } X X /* And report the results. X */ X mlwrite("%d substitutions", numsub); X return(TRUE); X} X X/* X * expandp -- Expand control key sequences for output. X */ Xexpandp(srcstr, deststr, maxlength) Xchar *srcstr; /* string to expand */ Xchar *deststr; /* destination of expanded string */ Xint maxlength; /* maximum chars in destination */ X{ X char c; /* current char to translate */ X X /* Scan through the string. X */ X while ((c = *srcstr++) != 0) X { X if (c == '\n') /* it's a newline */ X { X *deststr++ = '<'; X *deststr++ = 'N'; X *deststr++ = 'L'; X *deststr++ = '>'; X maxlength -= 4; X } X else if (c < 0x20 || c == 0x7f) /* control character */ X { X *deststr++ = '^'; X *deststr++ = c ^ 0x40; X maxlength -= 2; X } X else if (c == '%') X { X *deststr++ = '%'; X *deststr++ = '%'; X maxlength -= 2; X } X else /* any other character */ X { X *deststr++ = c; X maxlength--; X } X X /* check for maxlength */ X if (maxlength < 4) X { X *deststr++ = '$'; X *deststr = '\0'; X return(FALSE); X } X } X *deststr = '\0'; X return(TRUE); X} X X/* X * boundry -- Return information depending on whether we may search no X * further. Beginning of file and end of file are the obvious X * cases, but we may want to add further optional boundry restrictions X * in future, a' la VMS EDT. At the moment, just return TRUE or X * FALSE depending on if a boundry is hit (ouch). X */ Xint boundry(curline, curoff, dir) XLINE *curline; Xint curoff, dir; X{ X register int border; X X if (dir == FORWARD) X { X border = (curoff == llength(curline)) && X (lforw(curline) == curbp->b_linep); X } X else X { X border = (curoff == 0) && X (lback(curline) == curbp->b_linep); X } X return (border); X} X X/* X * nextch -- retrieve the next/previous character in the buffer, X * and advance/retreat the point. X * The order in which this is done is significant, and depends X * upon the direction of the search. Forward searches look at X * the current character and move, reverse searches move and X * look at the character. X */ Xstatic int nextch(pcurline, pcuroff, dir) XLINE **pcurline; Xint *pcuroff; Xint dir; X{ X register LINE *curline; X register int curoff; X register int c; X X curline = *pcurline; X curoff = *pcuroff; X X if (dir == FORWARD) X { X if (curoff == llength(curline)) /* if at EOL */ X { X curline = lforw(curline); /* skip to next line */ X curoff = 0; X c = '\n'; /* and return a */ X } X else X c = lgetc(curline, curoff++); /* get the char */ X } X else /* Reverse.*/ X { X if (curoff == 0) X { X curline = lback(curline); X curoff = llength(curline); X c = '\n'; X } X else X c = lgetc(curline, --curoff); X X } X *pcurline = curline; X *pcuroff = curoff; X X return (c); X} X X#if MAGIC X/* X * mcstr -- Set up the 'magic' array. The closure symbol is taken as X * a literal character when (1) it is the first character in the X * pattern, and (2) when preceded by a symbol that does not allow X * closure, such as a newline, beginning of line symbol, or another X * closure symbol. X * X * Coding comment (jmg): yes, i know i have gotos that are, strictly X * speaking, unnecessary. But right now we are so cramped for X * code space that i will grab what i can in order to remain X * within the 64K limit. C compilers actually do very little X * in the way of optimizing - they expect you to do that. X */ Xstatic int mcstr() X{ X MC *mcptr, *rtpcm; X char *patptr; X int mj; X int pchr; X int status = TRUE; X int does_closure = FALSE; X X /* If we had metacharacters in the MC array previously, X * free up any bitmaps that may have been allocated. X */ X if (magical) X freebits(); X X magical = FALSE; X mj = 0; X mcptr = &mcpat[0]; X patptr = &pat[0]; X X while ((pchr = *patptr) && status) X { X switch (pchr) X { X case MC_CCL: X status = cclmake(&patptr, mcptr); X magical = TRUE; X does_closure = TRUE; X break; X case MC_BOL: X if (mj != 0) X goto litcase; X X mcptr->mc_type = BOL; X magical = TRUE; X does_closure = FALSE; X break; X case MC_EOL: X if (*(patptr + 1) != '\0') X goto litcase; X X mcptr->mc_type = EOL; X magical = TRUE; X does_closure = FALSE; X break; X case MC_ANY: X mcptr->mc_type = ANY; X magical = TRUE; X does_closure = TRUE; X break; X case MC_CLOSURE: X /* Does the closure symbol mean closure here? X * If so, back up to the previous element X * and indicate it is enclosed. X */ X if (!does_closure) X goto litcase; X mj--; X mcptr--; X mcptr->mc_type |= CLOSURE; X magical = TRUE; X does_closure = FALSE; X break; X X /* Note: no break between MC_ESC case and the default. X */ X case MC_ESC: X if (*(patptr + 1) != '\0') X { X pchr = *++patptr; X magical = TRUE; X } X default: Xlitcase: mcptr->mc_type = LITCHAR; X mcptr->u.lchar = pchr; X does_closure = (pchr != '\n'); X break; X } /* End of switch.*/ X mcptr++; X patptr++; X mj++; X } /* End of while.*/ X X /* Close off the meta-string. X */ X mcptr->mc_type = MCNIL; X X /* Set up the reverse array, if the status is good. Please note the X * structure assignment - your compiler may not like that. X * If the status is not good, nil out the meta-pattern. X * The only way the status would be bad is from the cclmake() X * routine, and the bitmap for that member is guarenteed to be X * freed. So we stomp a MCNIL value there, call freebits() to X * free any other bitmaps, and set the zeroth array to MCNIL. X */ X if (status) X { X rtpcm = &tapcm[0]; X while (--mj >= 0) X { X#if LATTICE X movmem(--mcptr, rtpcm++, sizeof (MC)); X#endif X X#if MWC86 | AZTEC | MSC | VMS | USG | BSD | V7 X *rtpcm++ = *--mcptr; X#endif X } X rtpcm->mc_type = MCNIL; X } X else X { X (--mcptr)->mc_type = MCNIL; X freebits(); X mcpat[0].mc_type = tapcm[0].mc_type = MCNIL; X } X X return(status); X} X X/* X * mceq -- meta-character equality with a character. In Kernighan & Plauger's X * Software Tools, this is the function omatch(), but i felt there X * were too many functions with the 'match' name already. X */ Xstatic int mceq(bc, mt) Xint bc; XMC *mt; X{ X register int result; X X switch (mt->mc_type & MASKCL) X { X case LITCHAR: X result = eq(bc, mt->u.lchar); X break; X X case ANY: X result = (bc != '\n'); X break; X X case CCL: X if (!(result = biteq(bc, mt->u.cclmap))) X { X if ((curwp->w_bufp->b_mode & MDEXACT) == 0 && X (isletter(bc))) X { X result = biteq(CHCASE(bc), mt->u.cclmap); X } X } X break; X X case NCCL: X result = !biteq(bc, mt->u.cclmap); X X if ((curwp->w_bufp->b_mode & MDEXACT) == 0 && X (isletter(bc))) X { X result &= !biteq(CHCASE(bc), mt->u.cclmap); X } X break; X X default: X mlwrite("mceq: what is %d?", mt->mc_type); X result = FALSE; X break; X X } /* End of switch.*/ X X return (result); X} X X/* X * cclmake -- create the bitmap for the character class. X * ppatptr is left pointing to the end-of-character-class character, X * so that a loop may automatically increment with safety. X */ Xstatic int cclmake(ppatptr, mcptr) Xchar **ppatptr; XMC *mcptr; X{ X BITMAP clearbits(); X BITMAP bmap; X register char *patptr; X register int pchr, ochr; X X if ((bmap = clearbits()) == NULL) X { X mlwrite("%%Out of memory"); X return FALSE; X } X X mcptr->u.cclmap = bmap; X patptr = *ppatptr; X X /* X * Test the initial character(s) in ccl for X * special cases - negate ccl, or an end ccl X * character as a first character. Anything X * else gets set in the bitmap. X */ X if (*++patptr == MC_NCCL) X { X patptr++; X mcptr->mc_type = NCCL; X } X else X mcptr->mc_type = CCL; X X if ((ochr = *patptr) == MC_ECCL) X { X mlwrite("%%No characters in character class"); X return (FALSE); X } X else X { X if (ochr == MC_ESC) X ochr = *++patptr; X X setbit(ochr, bmap); X patptr++; X } X X while (ochr != '\0' && (pchr = *patptr) != MC_ECCL) X { X switch (pchr) X { X /* Range character loses its meaning X * if it is the last character in X * the class. X */ X case MC_RCCL: X if (*(patptr + 1) == MC_ECCL) X setbit(pchr, bmap); X else X { X pchr = *++patptr; X while (++ochr <= pchr) X setbit(ochr, bmap); X } X break; X X /* Note: no break between case MC_ESC and the default. X */ X case MC_ESC: X pchr = *++patptr; X default: X setbit(pchr, bmap); X break; X } X patptr++; X ochr = pchr; X } X X *ppatptr = patptr; X X if (ochr == '\0') X { X mlwrite("%%Character class not ended"); X free(bmap); X return FALSE; X } X return TRUE; X} X X/* X * biteq -- is the character in the bitmap? X */ Xstatic int biteq(bc, cclmap) Xint bc; XBITMAP cclmap; X{ X if (bc >= HICHAR) X return FALSE; X X return( (*(cclmap + (bc >> 3)) & BIT(bc & 7))? TRUE: FALSE ); X} X X/* X * clearbits -- Allocate and zero out a CCL bitmap. X */ Xstatic BITMAP clearbits() X{ X char *malloc(); X X BITMAP cclstart, cclmap; X register int j; X X if ((cclmap = cclstart = (BITMAP) malloc(HIBYTE)) != NULL) X for (j = 0; j < HIBYTE; j++) X *cclmap++ = 0; X X return (cclstart); X} X X/* X * freebits -- Free up any CCL bitmaps. X */ Xstatic freebits() X{ X register MC *mcptr; X X mcptr = &mcpat[0]; X X while (mcptr->mc_type != MCNIL) X { X if ((mcptr->mc_type & MASKCL) == CCL || X (mcptr->mc_type & MASKCL) == NCCL) X free(mcptr->u.cclmap); X mcptr++; X } X} X X/* X * setbit -- Set a bit (ON only) in the bitmap. X */ Xstatic setbit(bc, cclmap) Xint bc; XBITMAP cclmap; X{ X if (bc < HICHAR) X *(cclmap + (bc >> 3)) |= BIT(bc & 7); X} X#endif END_OF_search.c if test 30773 -ne `wc -c