Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!mnetor!uunet!husc6!mit-eddie!ll-xn!adelie!necntc!ncoast!allbery From: nwd@j.cc.purdue.edu (Daniel Lawrence) Newsgroups: comp.sources.misc Subject: MicroEmacs 3.9 (Part 10 of 16) Message-ID: <5686@ncoast.UUCP> Date: Mon, 16-Nov-87 21:33:24 EST Article-I.D.: ncoast.5686 Posted: Mon Nov 16 21:33:24 1987 Date-Received: Fri, 20-Nov-87 06:41:51 EST Sender: allbery@ncoast.UUCP Lines: 1785 Approved: allbery@ncoast.UUCP X-Archive: comp.sources.misc/microemacs-3.9/9 # This is a shar archive. # Remove everything above this line. # Run the file through sh, not csh. # (type `sh mes.10') # If you do not see the message # `mes.10 completed!' # then the file was incomplete. echo extracting - region.c sed 's/^X//' > region.c << 'FRIDAY_NIGHT' X/* X * The routines in this file X * deal with the region, that magic space X * between "." and mark. Some functions are X * commands. Some functions are just for X * internal use. X */ X#include X#include "estruct.h" X#include "edef.h" X X#if MEGAMAX & ST520 Xoverlay "region" X#endif X X/* X * Kill the region. Ask "getregion" X * to figure out the bounds of the region. X * Move "." to the start, and kill the characters. X * Bound to "C-W". X */ Xkillregion(f, n) X{ X register int s; X REGION region; X X if (curbp->b_mode&MDVIEW) /* don't allow this command if */ X return(rdonly()); /* we are in read only mode */ X if ((s=getregion(®ion)) != TRUE) X return (s); X if ((lastflag&CFKILL) == 0) /* This is a kill type */ X kdelete(); /* command, so do magic */ X thisflag |= CFKILL; /* kill buffer stuff. */ X curwp->w_dotp = region.r_linep; X curwp->w_doto = region.r_offset; X return (ldelete(region.r_size, TRUE)); X} X X/* X * Copy all of the characters in the X * region to the kill buffer. Don't move dot X * at all. This is a bit like a kill region followed X * by a yank. Bound to "M-W". X */ Xcopyregion(f, n) X{ X register LINE *linep; X register int loffs; X register int s; X REGION region; X X if ((s=getregion(®ion)) != TRUE) X return (s); X if ((lastflag&CFKILL) == 0) /* Kill type command. */ X kdelete(); X thisflag |= CFKILL; X linep = region.r_linep; /* Current line. */ X loffs = region.r_offset; /* Current offset. */ X while (region.r_size--) { X if (loffs == llength(linep)) { /* End of line. */ X if ((s=kinsert('\n')) != TRUE) X return (s); X linep = lforw(linep); X loffs = 0; X } else { /* Middle of line. */ X if ((s=kinsert(lgetc(linep, loffs))) != TRUE) X return (s); X ++loffs; X } X } X mlwrite("[region copied]"); X return (TRUE); X} X X/* X * Lower case region. Zap all of the upper X * case characters in the region to lower case. Use X * the region code to set the limits. Scan the buffer, X * doing the changes. Call "lchange" to ensure that X * redisplay is done in all buffers. Bound to X * "C-X C-L". X */ Xlowerregion(f, n) X{ X register LINE *linep; X register int loffs; X register int c; X register int s; X REGION region; X X if (curbp->b_mode&MDVIEW) /* don't allow this command if */ X return(rdonly()); /* we are in read only mode */ X if ((s=getregion(®ion)) != TRUE) X return (s); X lchange(WFHARD); X linep = region.r_linep; X loffs = region.r_offset; X while (region.r_size--) { X if (loffs == llength(linep)) { X linep = lforw(linep); X loffs = 0; X } else { X c = lgetc(linep, loffs); X if (c>='A' && c<='Z') X lputc(linep, loffs, c+'a'-'A'); X ++loffs; X } X } X return (TRUE); X} X X/* X * Upper case region. Zap all of the lower X * case characters in the region to upper case. Use X * the region code to set the limits. Scan the buffer, X * doing the changes. Call "lchange" to ensure that X * redisplay is done in all buffers. Bound to X * "C-X C-L". X */ Xupperregion(f, n) X{ X register LINE *linep; X register int loffs; X register int c; X register int s; X REGION region; X X if (curbp->b_mode&MDVIEW) /* don't allow this command if */ X return(rdonly()); /* we are in read only mode */ X if ((s=getregion(®ion)) != TRUE) X return (s); X lchange(WFHARD); X linep = region.r_linep; X loffs = region.r_offset; X while (region.r_size--) { X if (loffs == llength(linep)) { X linep = lforw(linep); X loffs = 0; X } else { X c = lgetc(linep, loffs); X if (c>='a' && c<='z') X lputc(linep, loffs, c-'a'+'A'); X ++loffs; X } X } X return (TRUE); X} X X/* X * This routine figures out the X * bounds of the region in the current window, and X * fills in the fields of the "REGION" structure pointed X * to by "rp". Because the dot and mark are usually very X * close together, we scan outward from dot looking for X * mark. This should save time. Return a standard code. X * Callers of this routine should be prepared to get X * an "ABORT" status; we might make this have the X * conform thing later. X */ Xgetregion(rp) Xregister REGION *rp; X{ X register LINE *flp; X register LINE *blp; X long fsize; X long bsize; X X if (curwp->w_markp == NULL) { X mlwrite("No mark set in this window"); X return (FALSE); X } X if (curwp->w_dotp == curwp->w_markp) { X rp->r_linep = curwp->w_dotp; X if (curwp->w_doto < curwp->w_marko) { X rp->r_offset = curwp->w_doto; X rp->r_size = (long)(curwp->w_marko-curwp->w_doto); X } else { X rp->r_offset = curwp->w_marko; X rp->r_size = (long)(curwp->w_doto-curwp->w_marko); X } X return (TRUE); X } X blp = curwp->w_dotp; X bsize = (long)curwp->w_doto; X flp = curwp->w_dotp; X fsize = (long)(llength(flp)-curwp->w_doto+1); X while (flp!=curbp->b_linep || lback(blp)!=curbp->b_linep) { X if (flp != curbp->b_linep) { X flp = lforw(flp); X if (flp == curwp->w_markp) { X rp->r_linep = curwp->w_dotp; X rp->r_offset = curwp->w_doto; X rp->r_size = fsize+curwp->w_marko; X return (TRUE); X } X fsize += llength(flp)+1; X } X if (lback(blp) != curbp->b_linep) { X blp = lback(blp); X bsize += llength(blp)+1; X if (blp == curwp->w_markp) { X rp->r_linep = blp; X rp->r_offset = curwp->w_marko; X rp->r_size = bsize - curwp->w_marko; X return (TRUE); X } X } X } X mlwrite("Bug: lost mark"); X return (FALSE); X} X FRIDAY_NIGHT echo extracting - search.c sed 's/^X//' > search.c << 'FRIDAY_NIGHT' 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 * April 1987: John M. Gamble X * Deleted the "if (n == 0) n = 1;" statements in front of the X * search/hunt routines. Since we now use a do loop, these X * checks are unnecessary. Consolidated common code into the X * function delins(). Renamed global mclen matchlen, X * and added the globals matchline, matchoff, patmatch, and X * mlenold. X * This gave us the ability to unreplace regular expression searches, X * and to put the matched string into an evironment variable. X * SOON TO COME: Meta-replacement characters! X * X * 25-apr-87 DML X * - cleaned up an unneccessary if/else in forwsearch() and X * backsearch() X * - savematch() failed to malloc room for the terminating byte X * of the match string (stomp...stomp...). It does now. Also X * it now returns gracefully if malloc fails X * X * July 1987: John M. Gamble X * Set the variables matchlen and matchoff in the 'unreplace' X * section of replaces(). The function savematch() would X * get confused if you replaced, unreplaced, then replaced X * again (serves you right for being so wishy-washy...) X * X * August 1987: John M. Gamble X * Put in new function rmcstr() to create the replacement X * meta-character array. Modified delins() so that it knows X * whether or not to make use of the array. And, put in the X * appropriate new structures and variables. X */ X X#include X#include "estruct.h" X#include "edef.h" X X#if LATTICE X#define void int X#endif X Xstatic int readpattern(); Xstatic int replaces(); Xstatic int nextch(); X#if MAGIC Xstatic int cclmake(); Xstatic int mcstr(); Xstatic int rmcstr(); Xstatic int mceq(); Xstatic void setbit(); Xstatic int amatch(); Xstatic int biteq(); Xstatic BITMAP clearbits(); X#endif X X/* X * forwsearch -- Search forward. Get a search string from the user, and X * search for the string. If found, reset the "." to be just after X * the match string, and (perhaps) repaint the display. X */ Xforwsearch(f, n) Xint f, n; /* default flag / numeric argument */ X{ X register int status = TRUE; X X /* If n is negative, search backwards. X * Otherwise proceed by asking for the search string. X */ X if (n < 0) X return(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 for as long as X * n is positive (n == 0 will go through once, which X * is just fine). X */ X if ((status = readpattern("Search", &pat[0], TRUE)) == TRUE) { X do { 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 /* Save away the match, or complain X * if not there. X */ X if (status == TRUE) X savematch(); X else X mlwrite("Not found"); X } X return(status); X} X X/* X * forwhunt -- Search forward for a previously acquired search string. X * If found, reset the "." to be just after the match string, X * and (perhaps) repaint the display. X */ X Xforwhunt(f, n) Xint f, n; /* default flag / numeric argument */ X{ X register int status = TRUE; X X 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 for as long as X * n is positive (n == 0 will go through once, which X * is just fine). 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 /* Save away the match, or complain X * if not there. X */ X if (status == TRUE) X savematch(); X else 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) Xint f, n; /* default flag / numeric argument */ X{ X register int status = TRUE; X X /* If n is negative, search forwards. X * Otherwise proceed by asking for the search string. X */ X if (n < 0) X return(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 for as long as X * n is positive (n == 0 will go through once, which X * is just fine). X */ X if ((status = readpattern("Reverse search", &pat[0], TRUE)) == TRUE) { X do { 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 /* Save away the match, or complain X * if not there. X */ X if (status == TRUE) X savematch(); X else 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) Xint f, n; /* default flag / numeric argument */ X{ X register int status = TRUE; X X 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 for as long as X * n is positive (n == 0 will go through once, which X * is just fine). 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 /* Save away the match, or complain X * if not there. X */ X if (status == TRUE) X savematch(); X else X mlwrite("Not found"); X X return(status); X} X X#if MAGIC X/* X * mcscanner -- Search for a meta-pattern in either direction. If found, X * reset the "." to be at the start or just after the match string, X * and (perhaps) repaint the display. 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 LINE *curline; /* current line during scan */ X int curoff; /* position within current 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 /* X * Save the old matchlen length, in case it is X * very different (closure) from the old length. X * This is important for query-replace undo X * command. X */ X mlenold = matchlen; 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 matchlen to X * zero in case we are doing a search for replacement. X */ X matchline = curline; X matchoff = curoff; X matchlen = 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 = matchline; X curwp->w_doto = matchoff; X } X X curwp->w_flag |= WFMOVE; /* flag that we have moved */ X return TRUE; X } X X /* Advance the cursor. X */ X 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 matchlen += 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 matchlen++; 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. If found, X * reset the "." to be at the start or just after the match string, X * and (perhaps) repaint the display. X */ Xint scanner(patrn, direct, beg_or_end) Xunsigned char *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 unsigned char *patptr; /* pointer into pattern */ X LINE *curline; /* current line during scan */ X int curoff; /* position within current line */ X LINE *scanline; /* current line during scanning */ X int scanoff; /* position in scanned 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 /* Set up local 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 match X * the search string at this point. X */ X matchline = curline; X matchoff = 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 scanning pointers. X */ X scanline = curline; X scanoff = curoff; X patptr = &patrn[0]; X X /* Scan through the pattern for a match. X */ X while (*++patptr != '\0') X { X c = nextch(&scanline, &scanoff, 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 = scanline; X curwp->w_doto = scanoff; X } X else /* at beginning of string */ X { X curwp->w_dotp = matchline; X curwp->w_doto = matchoff; 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, and remember X * the length for substitution purposes. X */ X rvstrcpy(tap, apat); X mlenold = matchlen = strlen(apat); X } 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 { X mcclear(); X rmcclear(); X } X else X status = srch? mcstr(): rmcstr(); X#endif X } X else if (status == FALSE && apat[0] != 0) /* Old one */ X status = TRUE; X X return(status); X} X X/* X * savematch -- We found the pattern? Let's save it away. X */ Xsavematch() X{ X register char *ptr; /* pointer to last match string */ X register int j; X LINE *curline; /* line of last match */ X int curoff; /* offset " " */ X X /* Free any existing match string, then X * attempt to allocate a new one. X */ X if (patmatch != NULL) X free(patmatch); X X ptr = patmatch = malloc(matchlen + 1); X X if (ptr != NULL) X { X curoff = matchoff; X curline = matchline; X X for (j = 0; j < matchlen; j++) X *ptr++ = nextch(&curline, &curoff, FORWARD); X X *ptr = '\0'; X } 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 status; /* success flag on pattern inputs */ X register int rlength; /* length of replacement string */ 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 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 length of the replacement string. X */ 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[matchlen - 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, X * matchlen is reset to the true length of X * the 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 } 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(TRUE); /* 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 savematch(); 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 pprompt; 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 status = delins(rlength, patmatch, FALSE); X if (status != TRUE) X return (status); X X /* Record one less substitution, X * backup, save our place, and X * reprompt. X */ X --numsub; X backchar(FALSE, mlenold); X matchline = curwp->w_dotp; X matchoff = curwp->w_doto; 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, and insert its X * replacement. X */ X status = delins(matchlen, &rpat[0], TRUE); X if (status != TRUE) X return (status); X X /* Save our position, since we may X * 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 * delins -- Delete a specified length from the current point X * then either insert the string directly, or make use of X * replacement meta-array. X */ Xdelins(dlength, instr, use_meta) Xint dlength; Xchar *instr; Xint use_meta; X{ X int status; X#if MAGIC X RMC *rmcptr; X#endif X X /* Zap what we gotta, X * and insert its replacement. X */ X if ((status = ldelete((long) dlength, FALSE)) != TRUE) X mlwrite("%%ERROR while deleting"); X else X#if MAGIC X if ((rmagical && use_meta) && X (curwp->w_bufp->b_mode & MDMAGIC) != 0) { X rmcptr = &rmcpat[0]; X while (rmcptr->mc_type != MCNIL && status == TRUE) { X if (rmcptr->mc_type == LITCHAR) X status = linstr(rmcptr->rstr); X else X status = linstr(patmatch); X rmcptr++; X } X } else X#endif X status = linstr(instr); X X return(status); 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 unsigned 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 mcclear(); 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, and call mcclear() X * to free any other bitmaps. 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 | TURBO | 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 mcclear(); X } X X return(status); X} X X/* X * rmcstr -- Set up the replacement 'magic' array. Note that if there X * are no meta-characters encountered in the replacement string, X * the array is never actually created - we will just use the X * character array rpat[] as the replacement string. X */ Xstatic int rmcstr() X{ X RMC *rmcptr; X char *patptr; X int status = TRUE; X int mj; X X patptr = &rpat[0]; X rmcptr = &rmcpat[0]; X mj = 0; X rmagical = FALSE; X X while (*patptr && status == TRUE) X { X switch (*patptr) X { X case MC_DITTO: X X /* If there were non-magical characters X * in the string before reaching this X * character, plunk it in the replacement X * array before processing the current X * meta-character. X */ X if (mj != 0) X { X rmcptr->mc_type = LITCHAR; X if ((rmcptr->rstr = malloc(mj + 1)) == NULL) X { X mlwrite("%%Out of memory"); X status = FALSE; X break; X } X strncpy(rmcptr->rstr, patptr - mj, mj); X rmcptr++; X mj = 0; X } X rmcptr->mc_type = DITTO; X rmcptr++; X rmagical = TRUE; X break; X X case MC_ESC: X rmcptr->mc_type = LITCHAR; X X /* We malloc mj plus two here, instead X * of one, because we have to count the X * current character. X */ X if ((rmcptr->rstr = malloc(mj + 2)) == NULL) X { X mlwrite("%%Out of memory"); X status = FALSE; X break; X } X X strncpy(rmcptr->rstr, patptr - mj, mj + 1); X X /* If MC_ESC is not the last character X * in the string, find out what it is X * escaping, and overwrite the last X * character with it. X */ X if (*(patptr + 1) != '\0') X *((rmcptr->rstr) + mj) = *++patptr; X X rmcptr++; X mj = 0; X rmagical = TRUE; X break; X X default: X mj++; X } X patptr++; X } X X if (rmagical && mj > 0) X { X rmcptr->mc_type = LITCHAR; X if ((rmcptr->rstr = malloc(mj + 1)) == NULL) X { X mlwrite("%%Out of memory."); X status = FALSE; X } X strncpy(rmcptr->rstr, patptr - mj, mj); X rmcptr++; X } X X rmcptr->mc_type = MCNIL; X} X X/* X * mcclear -- Free up any CCL bitmaps, and MCNIL the MC search arrays. X */ Xmcclear() 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 mcpat[0].mc_type = tapcm[0].mc_type = MCNIL; X} X X/* X * rmcclear -- Free up any strings, and MCNIL the RMC array. X */ Xrmcclear() X{ X register RMC *rmcptr; X X rmcptr = &rmcpat[0]; X X while (rmcptr->mc_type != MCNIL) X { X if (rmcptr->mc_type == LITCHAR) X free(rmcptr->rstr); X rmcptr++; X } X X rmcpat[0].mc_type = MCNIL; 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 * setbit -- Set a bit (ON only) in the bitmap. X */ Xstatic void setbit(bc, cclmap) Xint bc; XBITMAP cclmap; X{ X if (bc < HICHAR) X *(cclmap + (bc >> 3)) |= BIT(bc & 7); X} X#endif FRIDAY_NIGHT echo mes.10 completed! # That's all folks!