Path: utzoo!attcan!uunet!cs.utexas.edu!rutgers!apple!oliveb!sun!swap!page From: page%swap@Sun.COM (Bob Page) Newsgroups: comp.sources.amiga Subject: v89i134: stevie - vi editor clone v3.6, Part05/06 Message-ID: <104421@sun.Eng.Sun.COM> Date: 12 May 89 03:07:23 GMT Sender: news@sun.Eng.Sun.COM Lines: 2069 Approved: page@sun.com Submitted-by: grwalter@watmath.waterloo.edu (Fred Walter) Posting-number: Volume 89, Issue 134 Archive-name: editors/stevie36.5 # This is a shell archive. # Remove anything above and including the cut line. # Then run the rest of the file through 'sh'. # Unpacked files will be owned by you and have default permissions. #----cut here-----cut here-----cut here-----cut here----# #!/bin/sh # shar: SHell ARchive # Run the following text through 'sh' to create: # regexp.cat.uu # regexp.h # regmagic.h # regsub.c # s_io.c # screen.c # search.c # This is archive 5 of a 6-part kit. # This archive created: Thu May 11 19:41:28 1989 echo "extracting regexp.cat.uu" sed 's/^X//' << \SHAR_EOF > regexp.cat.uu X Xbegin 644 regexp.cat XM"@H*"E)%1T584"@S*2`@("`@("`@("`@("`@("!,:6)R87)Y($9U;F-T:6]NR XM&5C+"!R96=S=6(L(')E9V5R'!R97-S:6]N"B`@("`@:&%N9&QE<@H*4PA360A93@A.3PA/4`A04PA3& XM20A)4PA3"B`@("`@(P@C:0AI;@AN8PAC;`AL=0AU9`AD90AE(#P(/'(('`(<"X(+F@(:#X(/@H*("`@("!R"')E"&5G"&=E"&5X"'APO XM"'`@*@@J<@AR90AE9PAG8PAC;PAO;0AM<`AP*`@H90AE>`AX<`AP*0@I"B`@T XM("`@8PAC:`AH80AA<@AR("H(*F4(97@(>'`(<#L(.PH*("`@("!I"&EN"&YT. XM"'0@<@AR90AE9PAG90AE>`AX90AE8PAC*`@H<`AP<@AR;PAO9PAG+`@L(',(] XM'`(<"`JW XM""IP"'!R"')O"&]G"&<["#L*("`@("!C"&-H"&AA"&%R"'(@*@@J`AX<`AP("H(*G`(<'(('!R97-S:6]N(&EN=&\@82!S=')U8W1U%\(<"P*("`@("!A;F0@%\(95\(8PH@("`@(&UA=&-H97,@82!.54PM=&5R;6EN871E) XM9"!?"'-?"'1?"')?"&E?"&Y?"&<@86=A:6YS="!T:&4@8V]M<&EL960@'!R97-S:6]N"B`@("`@:6X@7PAP7PAR7PAO7PAG+@H@("`@($ETN XM(')E='5R;G,@,2!F;W(@2!&=6YC=&EO;G,@("`@K XM("`@("`@("`@("!214=%6%`H,RD*"@H*("`@("!/;F-E(&$@F5D(&5X<')E'!RD XM97-S:6]N XM;R!T:&4*("`@("!M;W-T(')E8V5N="!?"')?"&5?"&=?"&5?"'A?"&5?"&,@2 XM<&5R9F]R;65D('5S:6YG(%\(<%\(2!?"'-?"'1?"&%?# XM"')?"'1?"'!;7P@P72!A;F0*("`@("!?"&5?"&Y?"&1?"'!;7P@P72X*("`@B XM("!%86-H(&EN"!I="!W:71H(&!%\(:5\(="@R*2X*("`@("!?"%)?"&5?"&=?"&5?"')?"')?$ XM"&]?"'(*("`@("!C86X@8F4@2!F;VQL;W=E9"!B>2!@*B2!@/R<@;6%T8VAE)RDN"B`@("`@5&\@:6YC;'5D92!A(&QI=&5R86P@8"TG+"!MJ XM86ME(&ET('1H92!F:7)S="!O'!R97-S^ XM:6]N(&-O=6QD(&UA=&-H('1W;R!D:69F97)E;G0@<&%R=',@;V8@=&AE(&EN2 XM<'5T('-T XM8F5G:6YS(&5A2!S<&]K96X@9F]R+`H@("`@('1H92!@8BHG(&UUB'!R*#$I"@H*"@H*1F]R;6%T=&5D(#@X+S$R+S,P("`@("`@("`@% XM("`@;&]C86P@("`@("`@("`@("`@("`@("`@("`@("`@("`@(#,*#`H*"@I2. XM14=%6%`H,RD@("`@("`@("`@("`@("`@3&EB2!&=6YC=&EO;G,@("`@K XM("`@("`@("`@("!214=%6%`H,RD*"@H*1`A$20A)00A!1PA'3@A.3PA/4PA39 XM5`A420A)0PA#4PA3"B`@("`@7PA27PAE7PAG7PAC7PAO7PAM7PAP(')E='5R@ XM;G,@3E5,3"!F;W(@82!F86EL=7)E"B`@("`@*%\(6EN9R!@*R<@;W(@8"HG('1O(&$@<&]S2!A%\(<"@S*2P*("`@> XM("!B=70@87)E(&YO="!D97)I=F5D(&9R;VT@0F5L;"!C;V1E+@H*0@A"50A5% XM1PA'4PA3"B`@("`@16UP='D@8G)A;F-H97,@86YD(&5M<'1Y(')E9W5L87(@W XM97AP6EN9R!@*B<@;W(@8"LG/ XM('1O(&$@<&]S2!F87-T+@H@("`@($ET(&1O97,@9VEV92!SD XM<&5C:6%L(&%T=&5N=&EO;B!T;R!H86YD;&EN9R!S:6UP;&4@8V%S97,@<75I4 XM8VML>2X*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@I&;W)M871T960@3 XM.#@O,3(O,S`@("`@("`@("`@("!L;V-A;"`@("`@("`@("`@("`@("`@("`@? X+("`@("`@("`@-`H@^ X`` Xend Xsize 8651 SHAR_EOF echo "extracting regexp.h" sed 's/^X//' << \SHAR_EOF > regexp.h X/* X * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE X * X * This is NOT the original regular expression code as written by X * Henry Spencer. This code has been modified specifically for use X * with the STEVIE editor, and should not be used apart from compiling X * STEVIE. If you want a good regular expression library, get the X * original code. The copyright notice that follows is from the X * original. X * X * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE X * X * Definitions etc. for regexp(3) routines. X * X * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof], X * not the System V one. X */ X#define NSUBEXP 10 Xtypedef struct regexp { X char *startp[NSUBEXP]; X char *endp[NSUBEXP]; X char regstart; /* Internal use only. */ X char reganch; /* Internal use only. */ X char *regmust; /* Internal use only. */ X int regmlen; /* Internal use only. */ X char program[1]; /* Unwarranted chumminess with compiler. */ X} regexp; X Xextern regexp *regcomp(); Xextern int regexec(); Xextern void regsub(); Xextern void regerror(); X X#ifndef ORIGINAL Xextern int reg_ic; /* set non-zero to ignore case in searches */ X#endif SHAR_EOF echo "extracting regmagic.h" sed 's/^X//' << \SHAR_EOF > regmagic.h X/* X * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE X * X * This is NOT the original regular expression code as written by X * Henry Spencer. This code has been modified specifically for use X * with the STEVIE editor, and should not be used apart from compiling X * STEVIE. If you want a good regular expression library, get the X * original code. The copyright notice that follows is from the X * original. X * X * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE X * X * The first byte of the regexp internal "program" is actually this magic X * number; the start node begins in the second byte. X */ X#define MAGIC 0234 SHAR_EOF echo "extracting regsub.c" sed 's/^X//' << \SHAR_EOF > regsub.c X/* X * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE X * X * This is NOT the original regular expression code as written by X * Henry Spencer. This code has been modified specifically for use X * with the STEVIE editor, and should not be used apart from compiling X * STEVIE. If you want a good regular expression library, get the X * original code. The copyright notice that follows is from the X * original. X * X * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE X * X * regsub X * X * Copyright (c) 1986 by University of Toronto. X * Written by Henry Spencer. Not derived from licensed software. X * X * Permission is granted to anyone to use this software for any X * purpose on any computer system, and to redistribute it freely, X * subject to the following restrictions: X * X * 1. The author is not responsible for the consequences of use of X * this software, no matter how awful, even if they arise X * from defects in it. X * X * 2. The origin of this software must not be misrepresented, either X * by explicit claim or by omission. X * X * 3. Altered versions must be plainly marked as such, and must not X * be misrepresented as being the original software. X * X * $Log: regsub.c,v $ X * Revision 1.2 88/04/28 08:11:25 tony X * First modification of the regexp library. Added an external variable X * 'reg_ic' which can be set to indicate that case should be ignored. X * Added a new parameter to regexec() to indicate that the given string X * comes from the beginning of a line and is thus eligible to match X * 'beginning-of-line'. X * X */ X X#include "env.h" X X#ifdef MEGAMAX Xoverlay "regexp" X#endif X X#include X#include "regexp.h" X#include "regmagic.h" X X#ifndef CHARBITS X#define UCHARAT(p) ((int)*(unsigned char *)(p)) X#else X#define UCHARAT(p) ((int)*(p)&CHARBITS) X#endif X X/* X - regsub - perform substitutions after a regexp match X */ Xvoid Xregsub(prog, source, dest) X regexp *prog; X char *source; X char *dest; X{ X register char *src; X register char *dst; X register char c; X register int no; X register int len; X extern char *strncpy(); X X if (prog == NULL || source == NULL || dest == NULL) { X regerror("NULL parm to regsub"); X return; X } X if (UCHARAT(prog->program) != MAGIC) { X regerror("damaged regexp fed to regsub"); X return; X } X src = source; X dst = dest; X while ((c = *src++) != '\0') { X if (c == '&') X no = 0; X else if (c == '\\' && '0' <= *src && *src <= '9') X no = *src++ - '0'; X else X no = -1; X if (no < 0) { /* Ordinary character. */ X if (c == '\\' && (*src == '\\' || *src == '&')) X c = *src++; X *dst++ = c; X } else if (prog->startp[no] != NULL && prog->endp[no] != NULL) { X len = prog->endp[no] - prog->startp[no]; X (void) strncpy(dst, prog->startp[no], len); X dst += len; X if (len != 0 && *(dst - 1) == '\0') { /* strncpy hit NUL. */ X regerror("damaged match string"); X return; X } X } X } X *dst = '\0'; X} SHAR_EOF echo "extracting s_io.c" sed 's/^X//' << \SHAR_EOF > s_io.c X/* X * s_io() - routines that do screen I/O or effect what we think is X * on the screen. X * X * By G. R. (Fred) Walter watmath!watcgl!grwalter X */ X X#include "stevie.h" X X/* X * screen_ins(row, nlines, total_rows) - insert 'nlines' lines at 'row' X * X * NOTE: this routine assumes it is called with valid arguments. X */ X Xstatic void Xscreen_ins(row, nlines, total_rows) X int row; X int nlines; X int total_rows; X{ X if (nlines < 1 || (row + nlines) > total_rows) X return; X X#ifndef T_IL_B X { X int i; X X for (i = 0; i < nlines; i++) { X windgoto(row, 0); X outstr(T_IL); X } X } X#else X windgoto(row, 0); X outstr(T_IL); X if (nlines >= 10) X outchar((char) (nlines / 10 + '0')); X outchar((char) (nlines % 10 + '0')); X outstr(T_IL_B); X#endif X X /* delete any garbage that may have been shifted to the status line */ X windgoto(total_rows - 1, 0); X outstr(T_EL); X} X X/* X * screen_del(row, nlines, total_rows) - delete 'nlines' lines at 'row' X * X * NOTE: this routine assumes it is called with valid arguments. X */ X Xstatic void Xscreen_del(row, nlines, total_rows) X int row; X int nlines; X int total_rows; X{ X if (nlines < 1 || (row + nlines) > total_rows) X return; X X /* delete any garbage that may have been on the status line */ X windgoto(total_rows - 1, 0); X outstr(T_EL); X X#ifndef T_DL_B X { X int i; X X for (i = 0; i < nlines; i++) { X windgoto(row, 0); X outstr(T_DL); /* delete a line */ X } X } X#else X windgoto(row, 0); X outstr(T_DL); X if (nlines >= 10) X outchar((char) (nlines / 10 + '0')); X outchar((char) (nlines % 10 + '0')); X outstr(T_DL_B); X#endif X} X X/* X * screen_refresh() X * X * Based on the current value of Topchar, refresh the contents of the screen X * and update Botchar. X */ X Xstatic void Xscreen_refresh(type) X int type; X{ X char *ptr; X int total_rows; X int row; X int col; X LINE *memp; X LPtr start; X bool_t off_top; X bool_t done; /* if TRUE, we hit the end of the file */ X bool_t didline; /* if TRUE, we finished the last line */ X int lno; /* number of the line we're doing */ X int coff; X int idx; X int i; X int j; X X if (NumLineSizes <= 0) X type = NOT_VALID; X X if (!RedrawingDisabled) X outstr(T_CI); X X off_top = FALSE; X idx = 0; X row = 0; X total_rows = Rows - 1; X memp = Topchar->linep; X X if ((type == VALID) || (type == VALID_TO_CURSCHAR)) { X j = -1; X for (i = 0; i < NumLineSizes; i++) { X if (LinePointers[i] == memp) { X j = i; X break; X } X row += LineSizes[i]; X } X if (j == -1) { X /* Are we off the top of the screen by one line ? */ X if (memp->next == LinePointers[0]) { X i = plines(Topchar->linep->s); X if (i < (total_rows)) { X off_top = TRUE; X for (idx = NumLineSizes; idx > 0; idx--) { X LinePointers[idx] = LinePointers[idx - 1]; X LineSizes[idx] = LineSizes[idx - 1]; X } X LineSizes[idx] = (char) i; X if (!RedrawingDisabled) X screen_ins(0, i, Rows); X } X } X row = 0; X } else if (j == 0 && type == VALID) { X if (!RedrawingDisabled) X outstr(T_CV); X return; X } else { X if (!RedrawingDisabled) X screen_del(0, row, Rows); X row = 0; X for (;;) { X LineSizes[idx] = LineSizes[j]; X LinePointers[idx] = LinePointers[j]; X X if (type == VALID_TO_CURSCHAR) { X if (LinePointers[idx] == Curschar->linep) { X memp = LinePointers[idx]; X break; X } X } X j++; X if (j >= NumLineSizes) { X memp = LinePointers[idx]; X if (memp->next != Fileend->linep) { X row += LineSizes[idx]; X idx++; X memp = memp->next; X } X break; X } X row += LineSizes[idx]; X idx++; X } X } X } X coff = 0; X if (P(P_NU)) { X coff = 8; X start.linep = memp; X lno = cntllines(Filemem, &start); X } X didline = TRUE; X done = FALSE; X X for (;;) { X ptr = format_line(memp->s, &col); X i = 1 + ((col - 1) / Columns); X if ((row + i) <= total_rows) { X LinePointers[idx] = memp; X LineSizes[idx++] = (char) i; X if (!RedrawingDisabled) { X windgoto(row, 0); X X if (P(P_NU)) X outstr(mkline(lno++)); X outstr(ptr); X X j = col + coff; X col = j % Columns; X if ((col != 0) || (j == 0)) { X#ifdef T_END_L X windgoto(row + i - 1, col); X outstr(T_END_L); X#else X for (; col < Columns; col++) X outchar(' '); X#endif X } X } X row += i; X if (memp->next != Fileend->linep) { X memp = memp->next; X } else { X done = TRUE; X break; X } X if (off_top) X break; X } else { X didline = FALSE; X break; X } X } X X /* Do we have to do off the top of the screen processing ? */ X if (off_top && !done) { X row = 0; X for (idx = 0; idx <= NumLineSizes && row < total_rows; idx++) { X row += LineSizes[idx]; X } X X idx--; X X if (row < total_rows) { X done = TRUE; X idx++; X } else if (row > total_rows) { X row -= LineSizes[idx]; X didline = FALSE; X memp = LinePointers[idx]; X } else { X didline = TRUE; X memp = LinePointers[idx]->next; X idx++; X } X } X NumLineSizes = idx; X X /* X * If we didn't hit the end of the file, and we didn't finish the last X * line we were working on, then the line didn't fit. X */ X if (!done && !didline) { X if (!RedrawingDisabled) { X /* Clear the rest of the screen. */ X#ifdef T_END_D X windgoto(row, 0); X outstr(T_END_D); X#else X screen_del(row, total_rows - row, Rows); X windgoto(row, 0); X#endif X /* put '@'s on rows that are part of a line that's too long */ X for (; row < total_rows; row++) X outstr("@\n\r"); X } X Botchar->linep = memp; X } else { X if (!RedrawingDisabled && !off_top) { X /* Clear the rest of the screen. */ X#ifdef T_END_D X windgoto(row, 0); X outstr(T_END_D); X#else X screen_del(row, total_rows - row, Rows); X windgoto(row, 0); X#endif X /* put '~'s on rows that aren't part of the file. */ X for (; row < total_rows; row++) X outstr("~\n\r"); X } X if (done) /* we hit the end of the file */ X *Botchar = *Fileend; X else X Botchar->linep = memp; /* FIX - prev? */ X } X X if (!RedrawingDisabled) X outstr(T_CV); X} X X/* X * s_refresh() X * X * Based on the current value of Curschar, (if necessary) update Topchar and X * Botchar and refresh the screen contensts. X */ X Xvoid Xs_refresh(type) X int type; X{ X LPtr *p; X LPtr *pp; X int i; X int nlines; X int refreshed; X X refreshed = FALSE; X X if (bufempty()) { /* special case - file is empty */ X *Topchar = *Filemem; X *Curschar = *Filemem; X screen_refresh(NOT_VALID); X return; X } X if (NumLineSizes < 0) { X type = NOT_VALID; X } X if (type != VALID) { X screen_refresh(type); X refreshed = TRUE; X type = VALID; X } X if (LINEOF(Curschar) < LINEOF(Topchar)) { X nlines = cntllines(Curschar, Topchar); X /* X * if the cursor is above the top of the screen, put it at the top of X * the screen.. X */ X *Topchar = *Curschar; X Topchar->index = 0; X /* X * ... and, if we weren't very close to begin with, we scroll so that X * the line is close to the middle. X */ X if (nlines > Rows / 3) { X p = Topchar; X for (i = 0; i < Rows / 3; i += plines(p->linep->s)) { X pp = prevline(p); X if (pp == NULL) X break; X p = pp; X } X *Topchar = *p; X } X screen_refresh(VALID); X } else if (LINEOF(Curschar) >= LINEOF(Botchar)) { X nlines = cntllines(Botchar, Curschar); X /* X * If the cursor is off the bottom of the screen, put it at the top X * of the screen.. ... and back up X */ X if (nlines > Rows / 3) { X p = Curschar; X for (i = 0; i < (2 * Rows) / 3; i += plines(p->linep->s)) { X pp = prevline(p); X if (pp == NULL) X break; X p = pp; X } X *Topchar = *p; X } else { X scrollup(nlines); X } X screen_refresh(VALID); X } else if (refreshed == FALSE) { X screen_refresh(type); X } X /* Check if we are below Botchar (this can occur). */ X if (LINEOF(Curschar) == LINEOF(Botchar)) { X pp = nextline(Topchar); X if (pp != NULL) { X Topchar->linep = pp->linep; X screen_refresh(VALID); X } X } else if (LINEOF(Curschar) > LINEOF(Botchar)) { X nlines = cntllines(Botchar, Curschar); X /* X * If the cursor is off the bottom of the screen, put it at the top X * of the screen.. ... and back up X */ X if (nlines > Rows / 3) { X p = Curschar; X for (i = 0; i < (2 * Rows) / 3; i += plines(p->linep->s)) { X pp = prevline(p); X if (pp == NULL) X break; X p = pp; X } X *Topchar = *p; X } else { X scrollup(nlines); X } X screen_refresh(VALID); X } X} X X/* X * s_clear() - clear the screen and mark the stored information as invalid. X */ Xvoid Xs_clear() X{ X outstr(T_ED); /* clear the display */ X S_NOT_VALID; X} X X/* X * Update_Botchar() X * X * Based on the current value of Topchar update Botchar. X */ X Xvoid XUpdate_Botchar() X{ X int row; X LINE *memp; X int total_rows; X int i; X X row = 0; X total_rows = Rows - 1; X memp = Topchar->linep; X X for (;;) { X i = plines(memp->s); X if ((row + i) <= total_rows) { X row += i; X memp = memp->next; X if (memp == Fileend->linep) X break; X } else { X break; X } X } X Botchar->linep = memp; X X MustUpdateBotchar = FALSE; X} X X#ifdef DONTINCLUDEANYMORE X/* X * NotValidFromCurschar() X * X * Mark the lines in NumLinePointers and NumLineSizes from Curschar on as X * not valid. X */ X Xvoid XNotValidFromCurschar() X{ X register int idx; X register unsigned long num; X X S_VALID_TO_CURSCHAR; X X num = LINEOF(Curschar); X for (idx = 0; idx < NumLineSizes; idx++) { X if (LinePointers[idx]->num >= num) X break; X } X NumLineSizes = idx; X} X#endif SHAR_EOF echo "extracting screen.c" sed 's/^X//' << \SHAR_EOF > screen.c X/* X * STEVIE - Simply Try this Editor for VI Enthusiasts X * X * Code Contributions By : Tim Thompson twitch!tjt X * Tony Andrews onecom!wldrdg!tony X * G. R. (Fred) Walter watmath!watcgl!grwalter X */ X X#include "stevie.h" X X/* X * The following variable is set (in cursupdate) to the number of physical X * lines taken by the line the cursor is on. We use this to avoid extra calls X * to plines(). The optimized routine updateline() makes sure that the size of X * the cursor line hasn't changed. If so, lines below the cursor will move up X * or down and we need to call the routine s_refresh() to examine the X * entire screen. X */ Xstatic int Cline_size; /* size (in rows) of the cursor line */ Xstatic int Cline_row; /* starting row of the cursor line */ X X/* X * updateline() - like s_refresh() but only for cursor line X * X * This determines whether or not we need to call s_refresh() to examine X * the entire screen for changes. This occurs if the size of the cursor line X * (in rows) has changed. X */ Xstatic void Xupdateline() X{ X char *ptr; X int col; X int size; X int j; X X if (RedrawingDisabled) /* Is this the correct action ? */ X return; X X ptr = format_line(Curschar->linep->s, &col); X X size = 1 + ((col - 1) / Columns); X if (Cline_size == size) { X outstr(T_CI); X windgoto(Cline_row, 0); X if (P(P_NU)) { X /* X * This should be done more efficiently. X */ X outstr(mkline(cntllines(Filemem, Curschar))); X } X outstr(ptr); X X j = col + (P(P_NU) ? 8 : 0); X col = j % Columns; X if ((col != 0) || (j == 0)) { X#ifdef T_END_L X windgoto(Cline_row + size - 1, col); X outstr(T_END_L); X#else X for (; col < Columns; col++) X outchar(' '); X#endif X } X outstr(T_CV); X } else { X s_refresh(VALID_TO_CURSCHAR); X } X} X Xvoid Xcursupdate(type) X int type; X{ X register char c; X register int incr; X register int i; X register int didincr; X X if (MustUpdateBotchar == TRUE) X Update_Botchar(); X X if (NumLineSizes < 0) { X s_refresh(NOT_VALID); X } else { X if (LineNotValid == TRUE) X updateline(); X X if (type != UPDATE_CURSOR) { X s_refresh(type); X } else if (ValidToCurschar == TRUE) { X s_refresh(VALID_TO_CURSCHAR); X } else if (CheckTopcharAndBotchar == TRUE) { X s_refresh(VALID); X } X } X X CheckTopcharAndBotchar = FALSE; X MustUpdateBotchar = FALSE; X ValidToCurschar = FALSE; X LineNotValid = FALSE; X X Cursrow = Curscol = Cursvcol = i = 0; X for (i = 0; i < NumLineSizes; i++) { X if (LinePointers[i] == Curschar->linep) X break; X Cursrow += LineSizes[i]; X } X X if (P(P_NU)) X Curscol = 8; X X Cline_row = Cursrow; X Cline_size = LineSizes[i]; X X for (i = 0; i <= Curschar->index; i++) { X c = Curschar->linep->s[i]; X /* A tab gets expanded, depending on the current column */ X if (c == TAB && !P(P_LS)) X incr = P(P_TS) - (Cursvcol % P(P_TS)); X else X incr = chars[c].ch_size; X Curscol += incr; X Cursvcol += incr; X if (Curscol >= Columns) { X Curscol -= Columns; X Cursrow++; X didincr = TRUE; X } else X didincr = FALSE; X } X if (didincr) X Cursrow--; X X if (c == TAB && State == NORMAL && !P(P_LS)) { X Curscol--; X Cursvcol--; X } else { X Curscol -= incr; X Cursvcol -= incr; X } X if (Curscol < 0) X Curscol += Columns; X X if (set_want_col) { X Curswant = Cursvcol; X set_want_col = FALSE; X } X} SHAR_EOF echo "extracting search.c" sed 's/^X//' << \SHAR_EOF > search.c X/* X * STEVIE - Simply Try this Editor for VI Enthusiasts X * X * Code Contributions By : Tim Thompson twitch!tjt X * Tony Andrews onecom!wldrdg!tony X * G. R. (Fred) Walter watmath!watcgl!grwalter X */ X X#include "stevie.h" X/* modified Henry Spencer's regular expression routines */ X#include "regexp.h" X X#ifdef MEGAMAX Xoverlay "search" X#endif X X/* X * This file contains various searching-related routines. These fall into X * three groups: string searches (for /, ?, n, and N), character searches X * within a single line (for f, F, t, T, etc), and "other" kinds of searches X * like the '%' command, and 'word' searches. X */ X X/* X * String searches X * X * The actual searches are done using Henry Spencer's regular expression X * library. X */ X X#define BEGWORD "([^a-zA-Z0-9_]|^)" /* replaces "\<" in search strings */ X#define ENDWORD "([^a-zA-Z0-9_]|$)" /* likewise replaces "\>" */ X Xbool_t begword; /* does the search include a 'begin word' X * match */ X X/* X * mapstring(s) - map special backslash sequences X */ Xstatic char * Xmapstring(s) X register char *s; X{ X static char ns[MAX_COLUMNS + 1]; X register char *p; X X begword = FALSE; X X for (p = ns; *s; s++) { X if ((*s == '(') || (*s == ')')) { X *p++ = '\\'; X *p++ = *s; X continue; X } X if (*s != '\\') { /* not an escape */ X *p++ = *s; X continue; X } X switch (*++s) { X case '/': X *p++ = '/'; X break; X X case '<': X strcpy(p, BEGWORD); X p += strlen(BEGWORD); X begword = TRUE; X break; X X case '>': X strcpy(p, ENDWORD); X p += strlen(ENDWORD); X break; X X default: X *p++ = '\\'; X *p++ = *s; X break; X } X } X *p = NUL; X X return ns; X} X Xstatic LPtr * Xbcksearch(str) X char *str; X{ X static LPtr infile; X register LPtr *p; X regexp *prog; X register char *s; X register int i; X bool_t want_start = (*str == '^'); /* looking for start of line? */ X register char *match; X X /* make sure str isn't empty */ X if (str == NULL || *str == NUL) X return NULL; X X prog = regcomp(str); X if (prog == NULL) { X emsg("Invalid search string"); X return NULL; X } X p = Curschar; X dec(p); X X if (begword) /* so we don't get stuck on one match */ X dec(p); X X i = (want_start) ? 0 : p->index; X X do { X s = p->linep->s; X X if (regexec(prog, s, TRUE)) { /* match somewhere on line */ X X if (want_start) { /* could only have been one */ X infile.linep = p->linep; X infile.index = (int) (prog->startp[0] - s); X free((char *) prog); X return (&infile); X } X /* X * Now, if there are multiple matches on this line, we have to X * get the last one. Or the last one before the cursor, if we're X * on that line. X */ X X match = prog->startp[0]; X X while (regexec(prog, prog->endp[0], FALSE)) { X if ((i >= 0) && ((prog->startp[0] - s) > i)) X break; X match = prog->startp[0]; X } X X if ((i >= 0) && ((match - s) > i)) { X i = -1; X continue; X } X infile.linep = p->linep; X infile.index = (int) (match - s); X free((char *) prog); X return (&infile); X } X i = -1; X X } while ((p = prevline(p)) != NULL); X X /* X * If wrapscan isn't set, bag the search now X */ X if (!P(P_WS)) { X free((char *) prog); X return NULL; X } X /* search backward from the end of the file */ X p = prevline(Fileend); X do { X s = p->linep->s; X X if (regexec(prog, s, TRUE)) { /* match somewhere on line */ X X if (want_start) { /* could only have been one */ X infile.linep = p->linep; X infile.index = (int) (prog->startp[0] - s); X free((char *) prog); X return (&infile); X } X /* X * Now, if there are multiple matches on this line, we have to X * get the last one. X */ X X match = prog->startp[0]; X X while (regexec(prog, prog->endp[0], FALSE)) X match = prog->startp[0]; X X infile.linep = p->linep; X infile.index = (int) (match - s); X free((char *) prog); X return (&infile); X } X if (p->linep == Curschar->linep) X break; X X } while ((p = prevline(p)) != NULL); X X free((char *) prog); X return NULL; X} X Xstatic LPtr * Xfwdsearch(str) X char *str; X{ X static LPtr infile; X LPtr *p; X regexp *prog; X bool_t want_start = (*str == '^'); /* looking for start of line? */ X X char *s; X int i; X X prog = regcomp(str); X if (prog == NULL) { X emsg("Invalid search string"); X return NULL; X } X p = Curschar; X i = Curschar->index + 1; X do { X s = p->linep->s + i; X i = 0; X X if (regexec(prog, s, i == 0)) { /* got a match */ X /* X * If we wanted the start of a line and we aren't really there, X * then a match doesn't count. X */ X if (want_start && (s != p->linep->s)) X continue; X X infile.linep = p->linep; X infile.index = (int) (prog->startp[0] - p->linep->s); X free((char *) prog); X return (&infile); X } X } while ((p = nextline(p)) != NULL); X X /* X * If wrapscan isn't set, then don't scan from the beginning of the file. X * Just return failure here. X */ X if (!P(P_WS)) { X free((char *) prog); X return NULL; X } X /* search from the beginning of the file to Curschar */ X for (p = Filemem; p != NULL; p = nextline(p)) { X s = p->linep->s; X X if (regexec(prog, s, TRUE)) { /* got a match */ X infile.linep = p->linep; X infile.index = (int) (prog->startp[0] - s); X free((char *) prog); X return (&infile); X } X if (p->linep == Curschar->linep) X break; X } X X free((char *) prog); X return (NULL); X} X Xstatic char *laststr = NULL; Xstatic int lastsdir; X Xstatic LPtr * Xssearch(dir, str) X int dir; /* FORWARD or BACKWARD */ X char *str; X{ X LPtr *pos; X X reg_ic = P(P_IC); /* tell the regexp routines how to search */ X X if (laststr != str) { X if (laststr != NULL) X free(laststr); X laststr = strsave(str); X } X lastsdir = dir; X X if (dir == BACKWARD) X pos = bcksearch(mapstring(str)); X else X pos = fwdsearch(mapstring(str)); X X /* X * This is kind of a kludge, but its needed to make 'beginning of word' X * searches land on the right place. X */ X if (pos != NULL && begword) { X if (pos->index != 0) X pos->index += 1; X } X return pos; X} X Xbool_t Xdosearch(dir, str) X int dir; X char *str; X{ X LPtr *p; X X S_CHECK_TOPCHAR_AND_BOTCHAR; X X if ((p = ssearch(dir, str)) == NULL) { X msg("Pattern not found"); X return (FALSE); X } else { X LPtr savep; X X /* if we're backing up, we make sure the line we're on */ X /* is on the screen. */ X setpcmark(); X *Curschar = savep = *p; X X return (TRUE); X } X} X Xvoid Xsearchagain(dir) X int dir; X{ X if (laststr == NULL) X beep(); X else X dosearch(dir, laststr); X X lastsdir = dir; X} X X#define OTHERDIR(x) (((x) == FORWARD) ? BACKWARD : FORWARD) X Xbool_t Xrepsearch(flag) X bool_t flag; X{ X int dir = lastsdir; X bool_t found; X X if (laststr == NULL) { X beep(); X return FALSE; X } X found = dosearch(flag ? OTHERDIR(lastsdir) : lastsdir, laststr); X X /* X * We have to save and restore 'lastsdir' because it gets munged by X * ssearch() and winds up saving the wrong direction from here if 'flag' X * is true. X */ X lastsdir = dir; X X return (found); X} X X/* X * regerror - called by regexp routines when errors are detected. X */ Xvoid Xregerror(s) X char *s; X{ X emsg(s); X} X X/* X * dosub(lp, up, cmd) X * X * Perform a substitution from line 'lp' to line 'up' using the X * command pointed to by 'cmd' which should be of the form: X * X * /pattern/substitution/g X * X * The trailing 'g' is optional and, if present, indicates that multiple X * substitutions should be performed on each line, if applicable. X * The usual escapes are supported as described in the regexp docs. X */ X Xvoid Xdosub(lp, up, cmd) X LPtr *lp, *up; X char *cmd; X{ X LINE *cp; X char *pat, *sub; X regexp *prog; X int nsubs; X bool_t do_all; /* do multiple substitutions per line */ X int n; X X /* X * If no range was given, do the current line. If only one line was X * given, just do that one. X */ X if (lp->linep == NULL) X *up = *lp = *Curschar; X else { X if (up->linep == NULL) X *up = *lp; X } X X pat = ++cmd; /* skip the initial '/' */ X X while (*cmd) { X if (cmd[0] == '/' && cmd[-1] != '\\') { X *cmd++ = NUL; X break; X } X cmd++; X } X X if (*pat == NUL) { X emsg("NULL pattern specified"); X return; X } X sub = cmd; X X do_all = FALSE; X X while (*cmd) { X if (cmd[0] == '/' && cmd[-1] != '\\') { X do_all = (cmd[1] == 'g'); X *cmd = NUL; X break; X } X cmd++; X } X X reg_ic = P(P_IC); /* set "ignore case" flag appropriately */ X X prog = regcomp(pat); X if (prog == NULL) { X emsg("Invalid search string"); X return; X } X nsubs = 0; X X ResetBuffers(); X n = RowNumber(lp); X X cp = lp->linep; X for (; cp != Fileend->linep && cp != NULL; cp = cp->next, n++) { X if (regexec(prog, cp->s, TRUE)) { /* a match on this line */ X char *ns, *sns, *p; X X /* X * Save the line that was last changed for the final cursor X * position (just like the real vi). X */ X Curschar->linep = cp; X X /* X * Get some space for a temporary buffer to do the substitution X * into. X */ X sns = ns = alloc(2048); X if (ns == NULL) X break; X X *sns = NUL; X X p = cp->s; X X do { X for (ns = sns; *ns; ns++); X /* X * copy up to the part that matched X */ X while (p < prog->startp[0]) X *ns++ = *p++; X X regsub(prog, sub, ns); X X /* X * continue searching after the match X */ X p = prog->endp[0]; X X } while (regexec(prog, p, FALSE) && do_all); X X for (ns = sns; *ns; ns++); X X /* X * copy the rest of the line, that didn't match X */ X while (*p) X *ns++ = *p++; X X *ns = NUL; X X AppendPositionToUndoUndobuff(0, n); X AppendPositionToUndobuff(0, n); X AppendToUndoUndobuff("c$"); X AppendToUndobuff("c$"); X AppendToUndoUndobuff(sns); X AppendToUndobuff(cp->s); X AppendToUndoUndobuff(ESC_STR); X AppendToUndobuff(ESC_STR); X X free(cp->s); /* free the original line */ X cp->s = strsave(sns); /* and save the modified str */ X cp->size = strlen(cp->s) + 1; X free(sns); /* free the temp buffer */ X nsubs++; X } X if (cp == up->linep) X break; X } X X if (nsubs) { X CHANGED; X S_NOT_VALID; X AppendPositionToUndoUndobuff(0, 1); X AppendPositionToUndobuff(0, 1); X beginline(TRUE); X if (nsubs >= P(P_RP)) X smsg("%d substitution%c", nsubs, (nsubs > 1) ? 's' : ' '); X } else X msg("No match"); X X free((char *) prog); X} X X/* X * doglob(cmd) X * X * Execute a global command of the form: X * X * g/pattern/X X * X * where 'x' is a command character, currently one of the following: X * X * d Delete all matching lines X * p Print all matching lines X * X * The command character (as well as the trailing slash) is optional, and X * is assumed to be 'p' if missing. X */ X Xvoid Xdoglob(lp, up, cmd) X LPtr *lp, *up; X char *cmd; X{ X LINE *cp; X X char *pat; X regexp *prog; X int ndone; X char cmdchar = NUL; /* what to do with matching lines */ X int nu; X int nuu = 0; X X /* X * If no range was given, do every line. If only one line was given, just X * do that one. X */ X if (lp->linep == NULL) { X *lp = *Filemem; X *up = *Fileend; X } else { X if (up->linep == NULL) X *up = *lp; X } X X pat = ++cmd; /* skip the initial '/' */ X X while (*cmd) { X if (cmd[0] == '/' && cmd[-1] != '\\') { X cmdchar = cmd[1]; X *cmd = NUL; X break; X } X cmd++; X } X if (cmdchar == NUL) X cmdchar = 'p'; X X reg_ic = P(P_IC); /* set "ignore case" flag appropriately */ X X if (cmdchar != 'd' && cmdchar != 'p') { X emsg("Invalid command character"); X return; X } X prog = regcomp(pat); X if (prog == NULL) { X emsg("Invalid search string"); X return; X } X msg(""); X ndone = 0; X X nu = RowNumber(lp); X if (cmdchar == 'd') { X ResetBuffers(); X nuu = nu; X } X cp = lp->linep; X for (; cp != Fileend->linep && cp != NULL; cp = cp->next, nu++) { X if (regexec(prog, cp->s, TRUE)) { /* a match on this line */ X Curschar->linep = cp; X Curschar->index = 0; X X switch (cmdchar) { X X case 'd': /* delete the line */ X AppendPositionToUndoUndobuff(0, nuu); X AppendToUndoUndobuff("dd"); X if (buf1line() && (ndone == 0)) { X AppendToUndobuff("a"); X } else if (buf1line()) { X AppendToUndobuff("j"); X AppendToUndobuff("I"); X } else if (cp->next == Fileend->linep) { X AppendPositionToUndobuff(0, nu); X AppendToUndobuff("o"); X } else { X AppendPositionToUndobuff(0, nu); X AppendToUndobuff("O"); X } X AppendToUndobuff(cp->s); X AppendToUndobuff(ESC_STR); X X delline(1); X break; X X case 'p': /* print the line */ X if (P(P_NU)) { X outstr(mkline(nu)); X } X outstr(T_CV); X outstr(format_line(cp->s, (int *) NULL)); X outstr(T_CI); X outstr("\r\n"); X break; X } X ndone++; X } else if (cmdchar == 'd') { X nuu++; X } X if (cp == up->linep) X break; X } X X if (ndone) { X switch (cmdchar) { X X case 'd': X S_NOT_VALID; X AppendPositionToUndobuff(0, 1); X if (ndone >= P(P_RP)) X smsg("%d fewer line%c", ndone, X (ndone > 1) ? 's' : ' '); X break; X X case 'p': X wait_return(); X break; X } X stuffReadbuff("^"); X } else X msg("No match"); X X free((char *) prog); X} X X/* X * Character Searches X */ X Xstatic char lastc = NUL; /* last character searched for */ Xstatic int lastcdir; /* last direction of character search */ Xstatic int lastctype; /* last type of search ("find" or "to") */ X X/* X * searchc(c, dir, type) X * X * Search for character 'c', in direction 'dir'. If type is 0, move to the X * position of the character, otherwise move to just before the char. X */ Xbool_t Xsearchc(c, dir, type) X char c; X int dir; X int type; X{ X LPtr save; X X save = *Curschar; /* save position in case we fail */ X lastc = c; X lastcdir = dir; X lastctype = type; X X /* X * On 'to' searches, skip one to start with so we can repeat searches in X * the same direction and have it work right. X */ X if (type) X (dir == FORWARD) ? oneright() : oneleft(); X X while ((dir == FORWARD) ? oneright() : oneleft()) { X if (gchar(Curschar) == c) { X if (type) X (dir == FORWARD) ? oneleft() : oneright(); X return TRUE; X } X } X *Curschar = save; X return FALSE; X} X Xbool_t Xcrepsearch(flag) X int flag; X{ X int dir = lastcdir; X int rval; X X if (lastc == NUL) X return FALSE; X X rval = searchc(lastc, flag ? OTHERDIR(lastcdir) : lastcdir, lastctype); X X lastcdir = dir; /* restore dir., since it may have changed */ X X return rval; X} X X/* X * "Other" Searches X */ X X/* X * showmatch - move the cursor to the matching paren or brace X */ XLPtr * Xshowmatch() X{ X static LPtr pos; X int (*move) (), inc(), dec(); X char initc = gchar(Curschar); /* initial char */ X char findc; /* terminating char */ X char c; X int count = 0; X X pos = *Curschar; /* set starting point */ X X switch (initc) { X X case '(': X findc = ')'; X move = inc; X break; X case ')': X findc = '('; X move = dec; X break; X case '{': X findc = '}'; X move = inc; X break; X case '}': X findc = '{'; X move = dec; X break; X case '[': X findc = ']'; X move = inc; X break; X case ']': X findc = '['; X move = dec; X break; X default: X return (LPtr *) NULL; X } X X while ((*move) (&pos) != -1) { /* until end of file */ X c = gchar(&pos); X if (c == initc) X count++; X else if (c == findc) { X if (count == 0) X return &pos; X count--; X } X } X return (LPtr *) NULL; /* never found it */ X} X X/* X * findfunc(dir) - Find the next function in direction 'dir' X * X * Return TRUE if a function was found. X */ Xbool_t Xfindfunc(dir) X int dir; X{ X LPtr *curr; X X S_CHECK_TOPCHAR_AND_BOTCHAR; X X curr = Curschar; X X do { X curr = (dir == FORWARD) ? nextline(curr) : prevline(curr); X X if (curr != NULL && curr->linep->s[0] == '{') { X setpcmark(); X *Curschar = *curr; X return TRUE; X } X } while (curr != NULL); X X return FALSE; X} X X/* X * The following routines do the word searches performed by the 'w', 'W', X * 'b', 'B', 'e', and 'E' commands. X */ X X/* X * To perform these searches, characters are placed into one of three X * classes, and transitions between classes determine word boundaries. X * X * The classes are: X * X * 0 - white space 1 - letters, digits, and underscore 2 - everything else X */ X Xstatic int stype; /* type of the word motion being performed */ X X#define C0(c) (((c) == ' ') || ((c) == '\t') || ((c) == NUL)) X#define C1(c) (isalpha(c) || isdigit(c) || ((c) == '_')) X X/* X * cls(c) - returns the class of character 'c' X * X * The 'type' of the current search modifies the classes of characters if a 'W', X * 'B', or 'E' motion is being done. In this case, chars. from class 2 are X * reported as class 1 since only white space boundaries are of interest. X */ Xstatic int Xcls(c) X char c; X{ X if (C0(c)) X return 0; X X if (C1(c)) X return 1; X X /* X * If stype is non-zero, report these as class 1. X */ X return (stype == 0) ? 2 : 1; X} X X X/* X * fwd_word(pos, type) - move forward one word X * X * Returns the resulting position, or NULL if EOF was reached. X */ XLPtr * Xfwd_word(p, type) X LPtr *p; X int type; X{ X static LPtr pos; X int sclass = cls(gchar(p)); /* starting class */ X X S_CHECK_TOPCHAR_AND_BOTCHAR; X X pos = *p; X X stype = type; X X /* X * We always move at least one character. X */ X if (inc(&pos) == -1) X return NULL; X X if (sclass != 0) { X while (cls(gchar(&pos)) == sclass) { X if (inc(&pos) == -1) X return NULL; X } X /* X * If we went from 1 -> 2 or 2 -> 1, return here. X */ X if (cls(gchar(&pos)) != 0) X return &pos; X } X /* We're in white space; go to next non-white */ X X while (cls(gchar(&pos)) == 0) { X /* X * We'll stop if we land on a blank line X */ X if (pos.index == 0 && pos.linep->s[0] == NUL) X break; X X if (inc(&pos) == -1) X return NULL; X } X X return &pos; X} X X/* X * bck_word(pos, type) - move backward one word X * X * Returns the resulting position, or NULL if top-of-file was reached. X */ XLPtr * Xbck_word(p, type) X LPtr *p; X int type; X{ X static LPtr pos; X int sclass = cls(gchar(p)); /* starting class */ X X S_CHECK_TOPCHAR_AND_BOTCHAR; X X pos = *p; X X stype = type; X X if (dec(&pos) == -1) X return NULL; X X /* X * If we're in the middle of a word, we just have to back up to the start X * of it. X */ X if (cls(gchar(&pos)) == sclass && sclass != 0) { X /* X * Move backward to start of the current word X */ X while (cls(gchar(&pos)) == sclass) { X if (dec(&pos) == -1) X return NULL; X } X inc(&pos); /* overshot - forward one */ X return &pos; X } X /* X * We were at the start of a word. Go back to the start of the prior X * word. X */ X X while (cls(gchar(&pos)) == 0) { /* skip any white space */ X /* X * We'll stop if we land on a blank line X */ X if (pos.index == 0 && pos.linep->s[0] == NUL) X return &pos; X X if (dec(&pos) == -1) X return NULL; X } X X sclass = cls(gchar(&pos)); X X /* X * Move backward to start of this word. X */ X while (cls(gchar(&pos)) == sclass) { X if (dec(&pos) == -1) X return NULL; X } X inc(&pos); /* overshot - forward one */ X X return &pos; X} X X/* X * end_word(pos, type) - move to the end of the word X * X * There is an apparent bug in the 'e' motion of the real vi. At least on the X * System V Release 3 version for the 80386. Unlike 'b' and 'w', the 'e' X * motion crosses blank lines. When the real vi crosses a blank line in an X * 'e' motion, the cursor is placed on the FIRST character of the next X * non-blank line. The 'E' command, however, works correctly. Since this X * appears to be a bug, I have not duplicated it here. X * X * Returns the resulting position, or NULL if EOF was reached. X */ XLPtr * Xend_word(p, type) X LPtr *p; X int type; X{ X static LPtr pos; X int sclass = cls(gchar(p)); /* starting class */ X X S_CHECK_TOPCHAR_AND_BOTCHAR; X X pos = *p; X X stype = type; X X if (inc(&pos) == -1) X return NULL; X X /* X * If we're in the middle of a word, we just have to move to the end of X * it. X */ X if (cls(gchar(&pos)) == sclass && sclass != 0) { X /* X * Move forward to end of the current word X */ X while (cls(gchar(&pos)) == sclass) { X if (inc(&pos) == -1) X return NULL; X } X dec(&pos); /* overshot - forward one */ X return &pos; X } X /* X * We were at the end of a word. Go to the end of the next word. X */ X X while (cls(gchar(&pos)) == 0) { /* skip any white space */ X if (inc(&pos) == -1) X return NULL; X } X X sclass = cls(gchar(&pos)); X X /* X * Move forward to end of this word. X */ X while (cls(gchar(&pos)) == sclass) { X if (inc(&pos) == -1) X return NULL; X } X dec(&pos); /* overshot - forward one */ X X return &pos; X} SHAR_EOF echo "End of archive 5 (of 6)" # if you want to concatenate archives, remove anything after this line exit