Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!mnetor!uunet!seismo!husc6!think!bloom-beacon!mit-hermes!iuvax!isrnix!mr From: mr@isrnix.UUCP (a.k.a regoli@silver.bacs.indiana.edu) Newsgroups: comp.sys.ibm.pc Subject: Repost: EMACS 3.8L Source (Part 7 of 9) Message-ID: <875@isrnix.UUCP> Date: Fri, 31-Jul-87 15:29:49 EDT Article-I.D.: isrnix.875 Posted: Fri Jul 31 15:29:49 1987 Date-Received: Sun, 2-Aug-87 06:36:23 EDT Sender: mr@isrnix.UUCP Organization: indiana university, bloomington Lines: 2726 #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # random.c # region.c # search.c # This archive created: Fri Jul 31 13:54:40 1987 # By: michael regoli (indiana university, bloomington) export PATH; PATH=/bin:/usr/bin:$PATH echo shar: "extracting 'random.c'" '(0 character)' if test -f 'random.c' then echo shar: "will not over-write existing file 'random.c'" else cat << \SHAR_EOF > 'random.c' /* * This file contains the command processing functions for a number of random * commands. There is no functional grouping here, for sure. */ #include #include "estruct.h" #include "edef.h" int tabsize; /* Tab size (0: use real tabs) */ /* * Set fill column to n. */ setfillcol(f, n) { fillcol = n; mlwrite("[Fill column is %d]",n); return(TRUE); } /* * Display the current position of the cursor, in origin 1 X-Y coordinates, * the character that is under the cursor (in hex), and the fraction of the * text that is before the cursor. The displayed column is not the current * column, but the column that would be used on an infinite width display. * Normally this is bound to "C-X =". */ showcpos(f, n) { register LINE *lp; /* current line */ register long numchars; /* # of chars in file */ register int numlines; /* # of lines in file */ register long predchars; /* # chars preceding point */ register int predlines; /* # lines preceding point */ register int curchar; /* character under cursor */ int ratio; int col; int savepos; /* temp save for current offset */ int ecol; /* column pos/end of current line */ /* starting at the beginning of the buffer */ lp = lforw(curbp->b_linep); /* start counting chars and lines */ numchars = 0; numlines = 0; while (lp != curbp->b_linep) { /* if we are on the current line, record it */ if (lp == curwp->w_dotp) { predlines = numlines; predchars = numchars + curwp->w_doto; if ((curwp->w_doto) == llength(lp)) curchar = '\n'; else curchar = lgetc(lp, curwp->w_doto); } /* on to the next line */ ++numlines; numchars += llength(lp) + 1; lp = lforw(lp); } /* if at end of file, record it */ if (curwp->w_dotp == curbp->b_linep) { predlines = numlines; predchars = numchars; } /* Get real column and end-of-line column. */ col = getccol(FALSE); savepos = curwp->w_doto; curwp->w_doto = llength(curwp->w_dotp); ecol = getccol(FALSE); curwp->w_doto = savepos; ratio = 0; /* Ratio before dot. */ if (numchars != 0) ratio = (100L*predchars) / numchars; /* summarize and report the info */ mlwrite("Line %d/%d Col %d/%d Char %D/%D (%d%%) char = 0x%x", predlines+1, numlines+1, col, ecol, predchars, numchars, ratio, curchar); return (TRUE); } getcline() /* get the current line number */ { register LINE *lp; /* current line */ register int numlines; /* # of lines before point */ /* starting at the beginning of the buffer */ lp = lforw(curbp->b_linep); /* start counting lines */ numlines = 0; while (lp != curbp->b_linep) { /* if we are on the current line, record it */ if (lp == curwp->w_dotp) break; ++numlines; lp = lforw(lp); } /* and return the resulting count */ return(numlines + 1); } /* * Return current column. Stop at first non-blank given TRUE argument. */ getccol(bflg) int bflg; { register int c, i, col; col = 0; for (i=0; iw_doto; ++i) { c = lgetc(curwp->w_dotp, i); if (c!=' ' && c!='\t' && bflg) break; if (c == '\t') col |= 0x07; else if (c<0x20 || c==0x7F) ++col; ++col; } return(col); } /* * Set current column. */ setccol(pos) int pos; /* position to set cursor */ { register int c; /* character being scanned */ register int i; /* index into current line */ register int col; /* current cursor column */ register int llen; /* length of line in bytes */ col = 0; llen = llength(curwp->w_dotp); /* scan the line until we are at or past the target column */ for (i = 0; i < llen; ++i) { /* upon reaching the target, drop out */ if (col >= pos) break; /* advance one character */ c = lgetc(curwp->w_dotp, i); if (c == '\t') col |= 0x07; else if (c<0x20 || c==0x7F) ++col; ++col; } /* if not long enough... */ if (col < pos) return(FALSE); /* otherwise...set us at the new position */ curwp->w_doto = i; return(TRUE); } /* * Twiddle the two characters on either side of dot. If dot is at the end of * the line twiddle the two characters before it. Return with an error if dot * is at the beginning of line; it seems to be a bit pointless to make this * work. This fixes up a very common typo with a single stroke. Normally bound * to "C-T". This always works within a line, so "WFEDIT" is good enough. */ twiddle(f, n) { register LINE *dotp; register int doto; register int cl; register int cr; if (curbp->b_mode&MDVIEW) /* don't allow this command if */ return(rdonly()); /* we are in read only mode */ dotp = curwp->w_dotp; doto = curwp->w_doto; if (doto==llength(dotp) && --doto<0) return (FALSE); cr = lgetc(dotp, doto); if (--doto < 0) return (FALSE); cl = lgetc(dotp, doto); lputc(dotp, doto+0, cr); lputc(dotp, doto+1, cl); lchange(WFEDIT); return (TRUE); } /* * Quote the next character, and insert it into the buffer. All the characters * are taken literally, with the exception of the newline, which always has * its line splitting meaning. The character is always read, even if it is * inserted 0 times, for regularity. Bound to "C-Q" */ quote(f, n) { register int s; register int c; if (curbp->b_mode&MDVIEW) /* don't allow this command if */ return(rdonly()); /* we are in read only mode */ c = tgetc(); if (n < 0) return (FALSE); if (n == 0) return (TRUE); if (c == '\n') { do { s = lnewline(); } while (s==TRUE && --n); return (s); } return (linsert(n, c)); } /* * Set tab size if given non-default argument (n <> 1). Otherwise, insert a * tab into file. If given argument, n, of zero, change to true tabs. * If n > 1, simulate tab stop every n-characters using spaces. This has to be * done in this slightly funny way because the tab (in ASCII) has been turned * into "C-I" (in 10 bit code) already. Bound to "C-I". */ tab(f, n) { if (n < 0) return (FALSE); if (n == 0 || n > 1) { tabsize = n; return(TRUE); } if (! tabsize) return(linsert(1, '\t')); return(linsert(tabsize - (getccol(FALSE) % tabsize), ' ')); } #if AEDIT detab(f, n) /* change tabs to spaces */ int f,n; /* default flag and numeric repeat count */ { register int inc; /* increment to next line [sgn(n)] */ if (curbp->b_mode&MDVIEW) /* don't allow this command if */ return(rdonly()); /* we are in read only mode */ if (f == FALSE) n = 1; /* loop thru detabbing n lines */ inc = ((n > 0) ? 1 : -1); while (n) { curwp->w_doto = 0; /* start at the beginning */ /* detab the entire current line */ while (curwp->w_doto < llength(curwp->w_dotp)) { /* if we have a tab */ if (lgetc(curwp->w_dotp, curwp->w_doto) == '\t') { ldelete(1, FALSE); insspace(TRUE, 8 - (curwp->w_doto & 7)); } forwchar(FALSE, 1); } /* advance/or back to the next line */ forwline(TRUE, inc); n -= inc; } curwp->w_doto = 0; /* to the begining of the line */ thisflag &= ~CFCPCN; /* flag that this resets the goal column */ lchange(WFEDIT); /* yes, we have made at least an edit */ return(TRUE); } entab(f, n) /* change spaces to tabs where posible */ int f,n; /* default flag and numeric repeat count */ { register int inc; /* increment to next line [sgn(n)] */ register int fspace; /* pointer to first space if in a run */ register int ccol; /* current cursor column */ register char cchar; /* current character */ if (curbp->b_mode&MDVIEW) /* don't allow this command if */ return(rdonly()); /* we are in read only mode */ if (f == FALSE) n = 1; /* loop thru entabbing n lines */ inc = ((n > 0) ? 1 : -1); while (n) { curwp->w_doto = 0; /* start at the beginning */ /* entab the entire current line */ fspace = -1; ccol = 0; while (curwp->w_doto < llength(curwp->w_dotp)) { /* see if it is time to compress */ if ((fspace >= 0) && (nextab(fspace) <= ccol)) if (ccol - fspace < 2) fspace = -1; else { /* there is a bug here dealing with mixed space/tabed lines.......it will get fixed */ backchar(TRUE, ccol - fspace); ldelete(ccol - fspace, FALSE); linsert(1, '\t'); fspace = -1; } /* get the current character */ cchar = lgetc(curwp->w_dotp, curwp->w_doto); switch (cchar) { case '\t': /* a tab...count em up */ ccol = nextab(ccol); break; case ' ': /* a space...compress? */ if (fspace == -1) fspace = ccol; ccol++; break; default: /* any other char...just count */ ccol++; fspace = -1; break; } forwchar(FALSE, 1); } /* advance/or back to the next line */ forwline(TRUE, inc); n -= inc; } curwp->w_doto = 0; /* to the begining of the line */ thisflag &= ~CFCPCN; /* flag that this resets the goal column */ lchange(WFEDIT); /* yes, we have made at least an edit */ return(TRUE); } trim(f, n) /* trim trailing whitespace from the point to eol */ int f,n; /* default flag and numeric repeat count */ { register LINE *lp; /* current line pointer */ register int offset; /* original line offset position */ register int length; /* current length */ register int inc; /* increment to next line [sgn(n)] */ if (curbp->b_mode&MDVIEW) /* don't allow this command if */ return(rdonly()); /* we are in read only mode */ if (f == FALSE) n = 1; /* loop thru trimming n lines */ inc = ((n > 0) ? 1 : -1); while (n) { lp = curwp->w_dotp; /* find current line text */ offset = curwp->w_doto; /* save original offset */ length = lp->l_used; /* find current length */ /* trim the current line */ while (length > offset) { if (lgetc(lp, length-1) != ' ' && lgetc(lp, length-1) != '\t') break; length--; } lp->l_used = length; /* advance/or back to the next line */ forwline(TRUE, inc); n -= inc; } lchange(WFEDIT); thisflag &= ~CFCPCN; /* flag that this resets the goal column */ return(TRUE); } #endif /* * Open up some blank space. The basic plan is to insert a bunch of newlines, * and then back up over them. Everything is done by the subcommand * procerssors. They even handle the looping. Normally this is bound to "C-O". */ openline(f, n) { register int i; register int s; if (curbp->b_mode&MDVIEW) /* don't allow this command if */ return(rdonly()); /* we are in read only mode */ if (n < 0) return (FALSE); if (n == 0) return (TRUE); i = n; /* Insert newlines. */ do { s = lnewline(); } while (s==TRUE && --i); if (s == TRUE) /* Then back up overtop */ s = backchar(f, n); /* of them all. */ return (s); } /* * Insert a newline. Bound to "C-M". If we are in CMODE, do automatic * indentation as specified. */ newline(f, n) { register int s; if (curbp->b_mode&MDVIEW) /* don't allow this command if */ return(rdonly()); /* we are in read only mode */ if (n < 0) return (FALSE); /* if we are in C mode and this is a default */ if (n == 1 && (curbp->b_mode & MDCMOD) && curwp->w_dotp != curbp->b_linep) return(cinsert()); /* * If a newline was typed, fill column is defined, the argument is non- * negative, wrap mode is enabled, and we are now past fill column, * and we are not read-only, perform word wrap. */ if ((curwp->w_bufp->b_mode & MDWRAP) && fillcol > 0 && getccol(FALSE) > fillcol && (curwp->w_bufp->b_mode & MDVIEW) == FALSE) execute(META|SPEC|'W', FALSE, 1); /* insert some lines */ while (n--) { if ((s=lnewline()) != TRUE) return (s); } return (TRUE); } cinsert() /* insert a newline and indentation for C */ { register char *cptr; /* string pointer into text to copy */ register int tptr; /* index to scan into line */ register int bracef; /* was there a brace at the end of line? */ register int i; char ichar[NSTRING]; /* buffer to hold indent of last line */ /* grab a pointer to text to copy indentation from */ cptr = &curwp->w_dotp->l_text[0]; /* check for a brace */ tptr = curwp->w_doto - 1; bracef = (cptr[tptr] == '{'); /* save the indent of the previous line */ i = 0; while ((i < tptr) && (cptr[i] == ' ' || cptr[i] == '\t') && (i < NSTRING - 1)) { ichar[i] = cptr[i]; ++i; } ichar[i] = 0; /* terminate it */ /* put in the newline */ if (lnewline() == FALSE) return(FALSE); /* and the saved indentation */ i = 0; while (ichar[i]) linsert(1, ichar[i++]); /* and one more tab for a brace */ if (bracef) tab(FALSE, 1); return(TRUE); } insbrace(n, c) /* insert a brace into the text here...we are in CMODE */ int n; /* repeat count */ int c; /* brace to insert (always { for now) */ { register int ch; /* last character before input */ register int i; register int target; /* column brace should go after */ /* if we are at the beginning of the line, no go */ if (curwp->w_doto == 0) return(linsert(n,c)); /* scan to see if all space before this is white space */ for (i = curwp->w_doto - 1; i >= 0; --i) { ch = lgetc(curwp->w_dotp, i); if (ch != ' ' && ch != '\t') return(linsert(n, c)); } /* delete back first */ target = getccol(FALSE); /* calc where we will delete to */ target -= 1; target -= target % (tabsize == 0 ? 8 : tabsize); while (getccol(FALSE) > target) backdel(FALSE, 1); /* and insert the required brace(s) */ return(linsert(n, c)); } inspound() /* insert a # into the text here...we are in CMODE */ { register int ch; /* last character before input */ register int i; /* if we are at the beginning of the line, no go */ if (curwp->w_doto == 0) return(linsert(1,'#')); /* scan to see if all space before this is white space */ for (i = curwp->w_doto - 1; i >= 0; --i) { ch = lgetc(curwp->w_dotp, i); if (ch != ' ' && ch != '\t') return(linsert(1, '#')); } /* delete back first */ while (getccol(FALSE) >= 1) backdel(FALSE, 1); /* and insert the required pound */ return(linsert(1, '#')); } /* * Delete blank lines around dot. What this command does depends if dot is * sitting on a blank line. If dot is sitting on a blank line, this command * deletes all the blank lines above and below the current line. If it is * sitting on a non blank line then it deletes all of the blank lines after * the line. Normally this command is bound to "C-X C-O". Any argument is * ignored. */ deblank(f, n) { register LINE *lp1; register LINE *lp2; long nld; if (curbp->b_mode&MDVIEW) /* don't allow this command if */ return(rdonly()); /* we are in read only mode */ lp1 = curwp->w_dotp; while (llength(lp1)==0 && (lp2=lback(lp1))!=curbp->b_linep) lp1 = lp2; lp2 = lp1; nld = 0; while ((lp2=lforw(lp2))!=curbp->b_linep && llength(lp2)==0) ++nld; if (nld == 0) return (TRUE); curwp->w_dotp = lforw(lp1); curwp->w_doto = 0; return (ldelete(nld, FALSE)); } /* * Insert a newline, then enough tabs and spaces to duplicate the indentation * of the previous line. Assumes tabs are every eight characters. Quite simple. * Figure out the indentation of the current line. Insert a newline by calling * the standard routine. Insert the indentation by inserting the right number * of tabs and spaces. Return TRUE if all ok. Return FALSE if one of the * subcomands failed. Normally bound to "C-J". */ indent(f, n) { register int nicol; register int c; register int i; if (curbp->b_mode&MDVIEW) /* don't allow this command if */ return(rdonly()); /* we are in read only mode */ if (n < 0) return (FALSE); while (n--) { nicol = 0; for (i=0; iw_dotp); ++i) { c = lgetc(curwp->w_dotp, i); if (c!=' ' && c!='\t') break; if (c == '\t') nicol |= 0x07; ++nicol; } if (lnewline() == FALSE || ((i=nicol/8)!=0 && linsert(i, '\t')==FALSE) || ((i=nicol%8)!=0 && linsert(i, ' ')==FALSE)) return (FALSE); } return (TRUE); } /* * Delete forward. This is real easy, because the basic delete routine does * all of the work. Watches for negative arguments, and does the right thing. * If any argument is present, it kills rather than deletes, to prevent loss * of text if typed with a big argument. Normally bound to "C-D". */ forwdel(f, n) { if (curbp->b_mode&MDVIEW) /* don't allow this command if */ return(rdonly()); /* we are in read only mode */ if (n < 0) return (backdel(f, -n)); if (f != FALSE) { /* Really a kill. */ if ((lastflag&CFKILL) == 0) kdelete(); thisflag |= CFKILL; } return (ldelete((long)n, f)); } /* * Delete backwards. This is quite easy too, because it's all done with other * functions. Just move the cursor back, and delete forwards. Like delete * forward, this actually does a kill if presented with an argument. Bound to * both "RUBOUT" and "C-H". */ backdel(f, n) { register int s; if (curbp->b_mode&MDVIEW) /* don't allow this command if */ return(rdonly()); /* we are in read only mode */ if (n < 0) return (forwdel(f, -n)); if (f != FALSE) { /* Really a kill. */ if ((lastflag&CFKILL) == 0) kdelete(); thisflag |= CFKILL; } if ((s=backchar(f, n)) == TRUE) s = ldelete((long)n, f); return (s); } /* * Kill text. If called without an argument, it kills from dot to the end of * the line, unless it is at the end of the line, when it kills the newline. * If called with an argument of 0, it kills from the start of the line to dot. * If called with a positive argument, it kills from dot forward over that * number of newlines. If called with a negative argument it kills backwards * that number of newlines. Normally bound to "C-K". */ killtext(f, n) { register LINE *nextp; long chunk; if (curbp->b_mode&MDVIEW) /* don't allow this command if */ return(rdonly()); /* we are in read only mode */ if ((lastflag&CFKILL) == 0) /* Clear kill buffer if */ kdelete(); /* last wasn't a kill. */ thisflag |= CFKILL; if (f == FALSE) { chunk = llength(curwp->w_dotp)-curwp->w_doto; if (chunk == 0) chunk = 1; } else if (n == 0) { chunk = curwp->w_doto; curwp->w_doto = 0; } else if (n > 0) { chunk = llength(curwp->w_dotp)-curwp->w_doto+1; nextp = lforw(curwp->w_dotp); while (--n) { if (nextp == curbp->b_linep) return (FALSE); chunk += llength(nextp)+1; nextp = lforw(nextp); } } else { mlwrite("neg kill"); return (FALSE); } return(ldelete(chunk, TRUE)); } setmode(f, n) /* prompt and set an editor mode */ int f, n; /* default and argument */ { adjustmode(TRUE, FALSE); } delmode(f, n) /* prompt and delete an editor mode */ int f, n; /* default and argument */ { adjustmode(FALSE, FALSE); } setgmode(f, n) /* prompt and set a global editor mode */ int f, n; /* default and argument */ { adjustmode(TRUE, TRUE); } delgmode(f, n) /* prompt and delete a global editor mode */ int f, n; /* default and argument */ { adjustmode(FALSE, TRUE); } adjustmode(kind, global) /* change the editor mode status */ int kind; /* true = set, false = delete */ int global; /* true = global flag, false = current buffer flag */ { register char *scan; /* scanning pointer to convert prompt */ register int i; /* loop index */ register status; /* error return on input */ #if COLOR register int uflag; /* was modename uppercase? */ #endif char prompt[50]; /* string to prompt user with */ char cbuf[NPAT]; /* buffer to recieve mode name into */ /* build the proper prompt string */ if (global) strcpy(prompt,"Global mode to "); else strcpy(prompt,"Mode to "); if (kind == TRUE) strcat(prompt, "add: "); else strcat(prompt, "delete: "); /* prompt the user and get an answer */ status = mlreply(prompt, cbuf, NPAT - 1); if (status != TRUE) return(status); /* make it uppercase */ scan = cbuf; #if COLOR uflag = (*scan >= 'A' && *scan <= 'Z'); #endif while (*scan != 0) { if (*scan >= 'a' && *scan <= 'z') *scan = *scan - 32; scan++; } /* test it first against the colors we know */ for (i=0; iw_fcolor = i; else if (global) gbcolor = i; else curwp->w_bcolor = i; curwp->w_flag |= WFCOLR; #endif mlerase(); return(TRUE); } } /* test it against the modes we know */ for (i=0; i < NUMMODES; i++) { if (strcmp(cbuf, modename[i]) == 0) { /* finding a match, we process it */ if (kind == TRUE) if (global) gmode |= (1 << i); else curwp->w_bufp->b_mode |= (1 << i); else if (global) gmode &= ~(1 << i); else curwp->w_bufp->b_mode &= ~(1 << i); /* display new mode line */ if (global == 0) upmode(); mlerase(); /* erase the junk */ return(TRUE); } } mlwrite("No such mode!"); return(FALSE); } /* This function simply clears the message line, mainly for macro usage */ clrmes(f, n) int f, n; /* arguments ignored */ { mlforce(""); return(TRUE); } /* This function writes a string on the message line mainly for macro usage */ writemsg(f, n) int f, n; /* arguments ignored */ { register char *sp; /* pointer into buf to expand %s */ register char *np; /* ptr into nbuf */ register int status; char buf[NPAT]; /* buffer to recieve message into */ char nbuf[NPAT*2]; /* buffer to expand string into */ if ((status = mlreply("Message to write: ", buf, NPAT - 1)) != TRUE) return(status); /* expand all '%' to "%%" so mlwrite won't expect arguments */ sp = buf; np = nbuf; while (*sp) { *np++ = *sp; if (*sp++ == '%') *np++ = '%'; } *np = '\0'; /* write the message out */ mlforce(nbuf); return(TRUE); } #if CFENCE /* the cursor is moved to a matching fence */ getfence(f, n) int f, n; /* not used */ { register LINE *oldlp; /* original line pointer */ register int oldoff; /* and offset */ register int sdir; /* direction of search (1/-1) */ register int count; /* current fence level count */ register char ch; /* fence type to match against */ register char ofence; /* open fence */ register char c; /* current character in scan */ /* save the original cursor position */ oldlp = curwp->w_dotp; oldoff = curwp->w_doto; /* get the current character */ if (oldoff == llength(oldlp)) ch = '\n'; else ch = lgetc(oldlp, oldoff); /* setup proper matching fence */ switch (ch) { case '(': ofence = ')'; sdir = FORWARD; break; case '{': ofence = '}'; sdir = FORWARD; break; case '[': ofence = ']'; sdir = FORWARD; break; case ')': ofence = '('; sdir = REVERSE; break; case '}': ofence = '{'; sdir = REVERSE; break; case ']': ofence = '['; sdir = REVERSE; break; default: TTbeep(); return(FALSE); } /* set up for scan */ count = 1; if (sdir == REVERSE) backchar(FALSE, 1); else forwchar(FALSE, 1); /* scan until we find it, or reach the end of file */ while (count > 0) { if (curwp->w_doto == llength(curwp->w_dotp)) c = '\n'; else c = lgetc(curwp->w_dotp, curwp->w_doto); if (c == ch) ++count; if (c == ofence) --count; if (sdir == FORWARD) forwchar(FALSE, 1); else backchar(FALSE, 1); if (boundry(curwp->w_dotp, curwp->w_doto, sdir)) break; } /* if count is zero, we have a match, move the sucker */ if (count == 0) { if (sdir == FORWARD) backchar(FALSE, 1); else forwchar(FALSE, 1); curwp->w_flag |= WFMOVE; return(TRUE); } /* restore the current position */ curwp->w_dotp = oldlp; curwp->w_doto = oldoff; TTbeep(); return(FALSE); } #endif /* Close fences are matched against their partners, and if on screen the cursor briefly lights there */ fmatch(ch) char ch; /* fence type to match against */ { register LINE *oldlp; /* original line pointer */ register int oldoff; /* and offset */ register LINE *toplp; /* top line in current window */ register int count; /* current fence level count */ register char opench; /* open fence */ register char c; /* current character in scan */ register int i; /* first get the display update out there */ update(FALSE); /* save the original cursor position */ oldlp = curwp->w_dotp; oldoff = curwp->w_doto; /* setup proper open fence for passed close fence */ if (ch == ')') opench = '('; else if (ch == '}') opench = '{'; else opench = '['; /* find the top line and set up for scan */ toplp = curwp->w_linep->l_bp; count = 1; backchar(FALSE, 2); /* scan back until we find it, or reach past the top of the window */ while (count > 0 && curwp->w_dotp != toplp) { if (curwp->w_doto == llength(curwp->w_dotp)) c = '\n'; else c = lgetc(curwp->w_dotp, curwp->w_doto); if (c == ch) ++count; if (c == opench) --count; backchar(FALSE, 1); if (curwp->w_dotp == curwp->w_bufp->b_linep->l_fp && curwp->w_doto == 0) break; } /* if count is zero, we have a match, display the sucker */ /* there is a real machine dependant timing problem here we have yet to solve......... */ if (count == 0) { forwchar(FALSE, 1); for (i = 0; i < term.t_pause; i++) update(FALSE); } /* restore the current position */ curwp->w_dotp = oldlp; curwp->w_doto = oldoff; return(TRUE); } istring(f, n) /* ask for and insert a string into the current buffer at the current point */ int f, n; /* ignored arguments */ { register char *tp; /* pointer into string to add */ register int status; /* status return code */ char tstring[NPAT+1]; /* string to add */ /* ask for string to insert */ status = mlreplyt("String to insert: ", tstring, NPAT, metac); if (status != TRUE) return(status); if (f == FALSE) n = 1; if (n < 0) n = - n; /* insert it */ while (n--) { tp = &tstring[0]; while (*tp) { if (*tp == 0x0a) status = lnewline(); else status = linsert(1, *tp); ++tp; if (status != TRUE) return(status); } } return(TRUE); } SHAR_EOF chmod +x 'random.c' fi echo shar: "extracting 'region.c'" '(0 character)' if test -f 'region.c' then echo shar: "will not over-write existing file 'region.c'" else cat << \SHAR_EOF > 'region.c' /* * The routines in this file * deal with the region, that magic space * between "." and mark. Some functions are * commands. Some functions are just for * internal use. */ #include #include "estruct.h" #include "edef.h" #if MEGAMAX & ST520 overlay "region" #endif /* * Kill the region. Ask "getregion" * to figure out the bounds of the region. * Move "." to the start, and kill the characters. * Bound to "C-W". */ killregion(f, n) { register int s; REGION region; if (curbp->b_mode&MDVIEW) /* don't allow this command if */ return(rdonly()); /* we are in read only mode */ if ((s=getregion(®ion)) != TRUE) return (s); if ((lastflag&CFKILL) == 0) /* This is a kill type */ kdelete(); /* command, so do magic */ thisflag |= CFKILL; /* kill buffer stuff. */ curwp->w_dotp = region.r_linep; curwp->w_doto = region.r_offset; return (ldelete(region.r_size, TRUE)); } /* * Copy all of the characters in the * region to the kill buffer. Don't move dot * at all. This is a bit like a kill region followed * by a yank. Bound to "M-W". */ copyregion(f, n) { register LINE *linep; register int loffs; register int s; REGION region; if ((s=getregion(®ion)) != TRUE) return (s); if ((lastflag&CFKILL) == 0) /* Kill type command. */ kdelete(); thisflag |= CFKILL; linep = region.r_linep; /* Current line. */ loffs = region.r_offset; /* Current offset. */ while (region.r_size--) { if (loffs == llength(linep)) { /* End of line. */ if ((s=kinsert('\n')) != TRUE) return (s); linep = lforw(linep); loffs = 0; } else { /* Middle of line. */ if ((s=kinsert(lgetc(linep, loffs))) != TRUE) return (s); ++loffs; } } return (TRUE); } /* * Lower case region. Zap all of the upper * case characters in the region to lower case. Use * the region code to set the limits. Scan the buffer, * doing the changes. Call "lchange" to ensure that * redisplay is done in all buffers. Bound to * "C-X C-L". */ lowerregion(f, n) { register LINE *linep; register int loffs; register int c; register int s; REGION region; if (curbp->b_mode&MDVIEW) /* don't allow this command if */ return(rdonly()); /* we are in read only mode */ if ((s=getregion(®ion)) != TRUE) return (s); lchange(WFHARD); linep = region.r_linep; loffs = region.r_offset; while (region.r_size--) { if (loffs == llength(linep)) { linep = lforw(linep); loffs = 0; } else { c = lgetc(linep, loffs); if (c>='A' && c<='Z') lputc(linep, loffs, c+'a'-'A'); ++loffs; } } return (TRUE); } /* * Upper case region. Zap all of the lower * case characters in the region to upper case. Use * the region code to set the limits. Scan the buffer, * doing the changes. Call "lchange" to ensure that * redisplay is done in all buffers. Bound to * "C-X C-L". */ upperregion(f, n) { register LINE *linep; register int loffs; register int c; register int s; REGION region; if (curbp->b_mode&MDVIEW) /* don't allow this command if */ return(rdonly()); /* we are in read only mode */ if ((s=getregion(®ion)) != TRUE) return (s); lchange(WFHARD); linep = region.r_linep; loffs = region.r_offset; while (region.r_size--) { if (loffs == llength(linep)) { linep = lforw(linep); loffs = 0; } else { c = lgetc(linep, loffs); if (c>='a' && c<='z') lputc(linep, loffs, c-'a'+'A'); ++loffs; } } return (TRUE); } /* * This routine figures out the * bounds of the region in the current window, and * fills in the fields of the "REGION" structure pointed * to by "rp". Because the dot and mark are usually very * close together, we scan outward from dot looking for * mark. This should save time. Return a standard code. * Callers of this routine should be prepared to get * an "ABORT" status; we might make this have the * conform thing later. */ getregion(rp) register REGION *rp; { register LINE *flp; register LINE *blp; long fsize; long bsize; if (curwp->w_markp == NULL) { mlwrite("No mark set in this window"); return (FALSE); } if (curwp->w_dotp == curwp->w_markp) { rp->r_linep = curwp->w_dotp; if (curwp->w_doto < curwp->w_marko) { rp->r_offset = curwp->w_doto; rp->r_size = (long)(curwp->w_marko-curwp->w_doto); } else { rp->r_offset = curwp->w_marko; rp->r_size = (long)(curwp->w_doto-curwp->w_marko); } return (TRUE); } blp = curwp->w_dotp; bsize = (long)curwp->w_doto; flp = curwp->w_dotp; fsize = (long)(llength(flp)-curwp->w_doto+1); while (flp!=curbp->b_linep || lback(blp)!=curbp->b_linep) { if (flp != curbp->b_linep) { flp = lforw(flp); if (flp == curwp->w_markp) { rp->r_linep = curwp->w_dotp; rp->r_offset = curwp->w_doto; rp->r_size = fsize+curwp->w_marko; return (TRUE); } fsize += llength(flp)+1; } if (lback(blp) != curbp->b_linep) { blp = lback(blp); bsize += llength(blp)+1; if (blp == curwp->w_markp) { rp->r_linep = blp; rp->r_offset = curwp->w_marko; rp->r_size = bsize - curwp->w_marko; return (TRUE); } } } mlwrite("Bug: lost mark"); return (FALSE); } SHAR_EOF chmod +x 'region.c' fi echo shar: "extracting 'search.c'" '(0 character)' if test -f 'search.c' then echo shar: "will not over-write existing file 'search.c'" else cat << \SHAR_EOF > '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 */ #include #include "estruct.h" #include "edef.h" /* * 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. */ forwsearch(f, n) int f, n; /* default flag / numeric argument */ { register 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. */ forwhunt(f, n) int f, n; /* default flag / numeric argument */ { register 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). */ backsearch(f, n) int f, n; /* default flag / numeric argument */ { register 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). */ backhunt(f, n) int f, n; /* default flag / numeric argument */ { register 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. */ int mcscanner(mcpatrn, direct, beg_or_end) MC *mcpatrn; /* pointer into pattern */ int direct; /* which way to go.*/ int beg_or_end; /* put point at beginning or end of pattern.*/ { LINE *curline; /* current line during scan */ int curoff; /* position within current line */ int c; /* (dummy) char at current position */ /* 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 * horribly different (closure) from the old length. * This is terribly 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. */ c = 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". */ static int amatch(mcptr, direct, pcwline, pcwoff) register MC *mcptr; /* string to scan for */ int direct; /* which way to go.*/ LINE **pcwline; /* current line during scan */ int *pcwoff; /* position within current line */ { register int c; /* character at current position */ 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 ")" 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. */ int scanner(patrn, direct, beg_or_end) char *patrn; /* string to scan for */ int direct; /* which way to go.*/ int beg_or_end; /* put point at beginning or end of pattern.*/ { register int c; /* character at current position */ register char *patptr; /* pointer into pattern */ LINE *curline; /* current line during scan */ int curoff; /* position within current line */ 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(bc, pc) register int bc; register int 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 * to delimit the end-of-pattern to allow s in the search * string. */ static int readpattern(prompt, apat, srch) 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, "]: "); /* 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.*/ { matchlen = strlen(apat); /* Reverse string copy. */ rvstrcpy(tap, 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(); else status = mcstr(); #endif } } else if (status == FALSE && apat[0] != 0) /* Old one */ status = TRUE; return(status); } /* * savematch -- We found the pattern? Let's save it away. */ savematch() { register char *ptr; /* ptr into malloced last match string */ register int j; /* index */ LINE *curline; /* line of last match */ int curoff; /* offset " " */ /* free any existing match string */ if (patmatch != NULL) free(patmatch); /* attempt to allocate a new one */ ptr = patmatch = malloc(matchlen + 1); if (ptr == NULL) return; /* save the match! */ curoff = matchoff; curline = matchline; for (j = 0; j < matchlen; j++) *ptr++ = nextch(&curline, &curoff, FORWARD); /* null terminate the match string */ *ptr = '\0'; } /* * rvstrcpy -- Reverse string copy. */ rvstrcpy(rvstr, str) register char *rvstr, *str; { register int i; str += (i = strlen(str)); while (i-- > 0) *rvstr++ = *--str; *rvstr = '\0'; } /* * sreplace -- Search and replace. */ sreplace(f, n) int f; /* default flag */ int n; /* # of repetitions wanted */ { return(replaces(FALSE, f, n)); } /* * qreplace -- search and replace with query. */ qreplace(f, n) int f; /* default flag */ int n; /* # of repetitions wanted */ { return(replaces(TRUE, f, n)); } /* * replaces -- Search for a string and replace it with another * string. Query might be enabled (according to kind). */ static int replaces(kind, f, n) int kind; /* Query enabled flag */ int f; /* default flag */ int n; /* # of repetitions wanted */ { register int status; /* success flag on pattern inputs */ register int rlength; /* length of replacement string */ register int numsub; /* number of substitutions */ register int nummatch; /* number of found matches */ int nlflag; /* last char of search string a ? */ int nlrepl; /* was a replace done on the last line? */ char c; /* input char for query */ char tpat[NPAT]; /* temporary to hold search pattern */ LINE *origline; /* original "." position */ int origoff; /* and offset (for . query option) */ LINE *lastline; /* position of last replace and */ int lastoff; /* offset (for 'u' query option) */ 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) { case 'y': /* yes, substitute */ case ' ': savematch(); break; case 'n': /* no, onword */ forwchar(FALSE, 1); continue; case '!': /* yes/stop asking */ kind = FALSE; break; 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); status = delins(rlength, patmatch); if (status != TRUE) return (status); /* Record one less substitution, * backup, and reprompt. */ --numsub; backchar(FALSE, mlenold); 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]); if (status != TRUE) return (status); /* Save where we are if we might 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 insert the string. */ delins(dlength, instr) int dlength; char *instr; { int status; char tmpc; /* Zap what we gotta, * and insert its replacement. */ if (!(status = ldelete((long) dlength, FALSE))) { mlwrite("%%ERROR while deleting"); return(FALSE); } else while (tmpc = *instr) { status = (tmpc == '\n'? lnewline(): linsert(1, tmpc)); /* Insertion error? */ if (!status) { mlwrite("%%Out of memory while inserting"); break; } instr++; } return (status); } /* * expandp -- Expand control key sequences for output. */ expandp(srcstr, deststr, maxlength) char *srcstr; /* string to expand */ char *deststr; /* destination of expanded string */ int maxlength; /* maximum chars in destination */ { 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; } else if (c < 0x20 || c == 0x7f) /* control character */ { *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(curline, curoff, dir) LINE *curline; int curoff, dir; { register 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(pcurline, pcuroff, dir) LINE **pcurline; int *pcuroff; int dir; { register LINE *curline; register int curoff; register 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 */ } 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. */ int mcstr() { MC *mcptr, *rtpcm; char *patptr; int mj; int pchr; int status = TRUE; int does_closure = FALSE; /* If we had metacharacters in the MC 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 LATTICE movmem(--mcptr, rtpcm++, sizeof (MC)); #endif #if MWC86 | AZTEC | MSC | VMS | USG | BSD | V7 *rtpcm++ = *--mcptr; #endif } rtpcm->mc_type = MCNIL; } else { (--mcptr)->mc_type = MCNIL; mcclear(); } return(status); } /* * mcclear -- Free up any CCL bitmaps, and MCNIL the MC arrays. */ mcclear() { register MC *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; } /* * 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(bc, mt) int bc; MC *mt; { register int result; 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); } /* * 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(ppatptr, mcptr) char **ppatptr; MC *mcptr; { BITMAP clearbits(); BITMAP bmap; register char *patptr; register 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(bc, cclmap) int bc; BITMAP cclmap; { if (bc >= HICHAR) return FALSE; return( (*(cclmap + (bc >> 3)) & BIT(bc & 7))? TRUE: FALSE ); } /* * clearbits -- Allocate and zero out a CCL bitmap. */ static BITMAP clearbits() { char *malloc(); BITMAP cclstart, cclmap; register int j; if ((cclmap = cclstart = (BITMAP) malloc(HIBYTE)) != NULL) for (j = 0; j < HIBYTE; j++) *cclmap++ = 0; return (cclstart); } /* * setbit -- Set a bit (ON only) in the bitmap. */ static setbit(bc, cclmap) int bc; BITMAP cclmap; { if (bc < HICHAR) *(cclmap + (bc >> 3)) |= BIT(bc & 7); } #endif SHAR_EOF chmod +x 'search.c' fi exit 0 # End of shell archive