Path: utzoo!utgpu!news-server.csri.toronto.edu!rutgers!sun-barr!lll-winken!ncis.tis.llnl.gov!blackbird!lonex.radc.af.mil!wlbr!roger.imsd.contel.com!mh From: mh@roger.imsd.contel.com (Mike H.) Newsgroups: comp.windows.news Subject: NeWS version of elvis (the vi clone) part 7 of 9 Message-ID: <1991Jan11.021329.27685@wlbr.imsd.contel.com> Date: 11 Jan 91 02:13:29 GMT References: Sender: news@wlbr.imsd.contel.com (news) Distribution: comp Organization: Contel FSD, Westlake Village, CA Lines: 3120 Nntp-Posting-Host: roger.imsd.contel.com #! /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 the files: # pc.c # recycle.c # redraw.c # ref.c # refont.c # regexp.c # regsub.c # This archive created: Thu Jan 10 17:21:09 1991 export PATH; PATH=/bin:$PATH if test -f 'pc.c' then echo shar: will not over-write existing file "'pc.c'" else cat << \SHAR_EOF > 'pc.c' /* pc.c */ /* Author: * Guntram Blohm * Buchenstrasse 19 * 7904 Erbach, West Germany * Tel. ++49-7305-6997 * sorry - no regular network connection */ /* This file implements the ibm pc bios interface. See IBM documentation * for details. * If TERM is set upon invocation of elvis, this code is ignored completely, * and the standard termcap functions are used, thus, even not-so-close * compatibles can run elvis. For close compatibles however, bios output * is much faster (and permits reverse scrolling, adding and deleting lines, * and much more ansi.sys isn't capable of). GB. */ #include "config.h" #include "vi.h" #if MSDOS #include static void video(); /* vmode contains the screen attribute index and is set by attrset.*/ int vmode; /* The following array contains attribute definitions for * color/monochrome attributes. Screen selects one of the sets. * Maybe i'll put them into elvis options one day. */ static int screen; static char attr[2][5] = { /* :se: :so: :VB: :ul: :as: */ { 0x1f, 0x1d, 0x1e, 0x1a, 0x1c, }, /* color */ { 0x07, 0x70, 0x0f, 0x01, 0x0f, }, /* monochrome */ }; /* * bios interface functions for elvis - pc version */ /* cursor up: determine current position, decrement row, set position */ void v_up() { int dx; video(0x300,(int *)0,&dx); dx-=0x100; video(0x200,(int *)0,&dx); } #ifndef NO_CURSORSHAPE /* cursor big: set begin scan to end scan - 4 */ void v_cb() { int cx; video(0x300, &cx, (int *)0); cx=((cx&0xff)|(((cx&0xff)-4)<<8)); video(0x100, &cx, (int *)0); } /* cursor small: set begin scan to end scan - 1 */ void v_cs() { int cx; video(0x300, &cx, (int *)0); cx=((cx&0xff)|(((cx&0xff)-1)<<8)); video(0x100, &cx, (int *)0); } #endif /* clear to end: get cursor position and emit the aproppriate number * of spaces, without moving cursor. */ void v_ce() { int cx, dx; video(0x300,(int *)0,&dx); cx=COLS-(dx&0xff); video(0x920,&cx,(int *)0); } /* clear screen: clear all and set cursor home */ void v_cl() { int cx=0, dx=((LINES-1)<<8)+COLS-1; video(0x0600,&cx,&dx); dx=0; video(0x0200,&cx,&dx); } /* clear to bottom: get position, clear to eol, clear next line to end */ void v_cd() { int cx, dx, dxtmp; video(0x0300,(int *)0,&dx); dxtmp=(dx&0xff00)|(COLS-1); cx=dx; video(0x0600,&cx,&dxtmp); cx=(dx&0xff00)+0x100; dx=((LINES-1)<<8)+COLS-1; video(0x600,&cx,&dx); } /* add line: scroll rest of screen down */ void v_al() { int cx,dx; video(0x0300,(int *)0,&dx); cx=(dx&0xff00); dx=((LINES-1)<<8)+COLS-1; video(0x701,&cx,&dx); } /* delete line: scroll rest up */ void v_dl() { int cx,dx; video(0x0300,(int *)0,&dx); cx=(dx&0xff00)/*+0x100*/; dx=((LINES-1)<<8)+COLS-1; video(0x601,&cx,&dx); } /* scroll reverse: scroll whole screen */ void v_sr() { int cx=0, dx=((LINES-1)<<8)+COLS-1; video(0x0701,&cx,&dx); } /* set cursor */ void v_move(x,y) int x, y; { int dx=(y<<8)+x; video(0x200,(int *)0,&dx); } /* put character: set attribute first, then execute char. * Also remember if current line has changed. */ int v_put(ch) int ch; { int cx=1; ch&=0xff; if (ch>=' ') video(0x900|ch,&cx,(int *)0); video(0xe00|ch,(int *)0, (int *)0); if (ch=='\n') { exwrote = TRUE; video(0xe0d, (int *)0, (int *)0); } return ch; } /* determine number of screen columns. Also set attrset according * to monochrome/color screen. */ int v_cols() { union REGS regs; regs.h.ah=0x0f; int86(0x10, ®s, ®s); if (regs.h.al==7) /* monochrome mode ? */ screen=1; else screen=0; return regs.h.ah; } /* Getting the number of rows is hard. Most screens support 25 only, * EGA/VGA also support 43/50 lines, and some OEM's even more. * Unfortunately, there is no standard bios variable for the number * of lines, and the bios screen memory size is always rounded up * to 0x1000. So, we'll really have to cheat. * When using the screen memory size, keep in mind that each character * byte has an associated attribute byte. * * uses: word at 40:4c contains memory size * byte at 40:84 # of rows-1 (sometimes) * byte at 40:4a # of columns */ int v_rows() { int line, oldline; /* screen size less then 4K? then we have 25 lines only */ if (*(int far *)(0x0040004cl)<=4096) return 25; /* VEGA vga uses the bios byte at 0x40:0x84 for # of rows. * Use that byte, if it makes sense together with memory size. */ if ((((*(unsigned char far *)(0x0040004aL)*2* (*(unsigned char far *)(0x00400084L)+1))+0xfff)&(~0xfff))== *(unsigned int far *)(0x0040004cL)) return *(unsigned char far *)(0x00400084L)+1; /* uh oh. Emit '\n's until screen starts scrolling. */ v_move(oldline=0, 0); for (;;) { video(0xe0a,(int *)0,(int *)0); video(0x300,(int *)0,&line); line>>=8; if (oldline==line) return line+1; oldline=line; } } /* the REAL bios interface -- used internally only. */ static void video(ax, cx, dx) int ax, *cx, *dx; { union REGS regs; regs.x.ax=ax; if ((ax&0xff00)==0x600 || (ax&0xff00)==0x700) regs.h.bh=attr[screen][vmode]; else { regs.h.bh=0; regs.h.bl=attr[screen][vmode]; } if (cx) regs.x.cx=*cx; if (dx) regs.x.dx=*dx; int86(0x10, ®s, ®s); if (dx) *dx=regs.x.dx; if (cx) *cx=regs.x.cx; } /* The following function determines which character is used for * commandline-options by command.com. This system call is undocumented * and valid for versions < 4.00 only. */ int switchar() { union REGS regs; regs.x.ax=0x3700; int86(0x21, ®s, ®s); return regs.h.dl; } #endif SHAR_EOF fi # end of overwriting check if test -f 'recycle.c' then echo shar: will not over-write existing file "'recycle.c'" else cat << \SHAR_EOF > 'recycle.c' /* recycle.c */ /* Author: * Steve Kirkendall * 14407 SW Teal Blvd. #C * Beaverton, OR 97005 * kirkenda@cs.pdx.edu */ /* This file contains the functions perform garbage collection and allocate * reusable blocks. */ #include "config.h" #include "vi.h" #ifndef NO_RECYCLE /* this whole file would have be skipped if NO_RECYCLE is defined */ extern long lseek(); #define BTST(bitno, byte) ((byte) & (1 << (bitno))) #define BSET(bitno, byte) ((byte) |= (1 << (bitno))) #define BCLR(bitno, byte) ((byte) &= ~(1 << (bitno))) #define TST(blkno) ((blkno) < MAXBIT ? BTST((blkno) & 7, bitmap[(blkno) >> 3]) : 1) #define SET(blkno) if ((blkno) < MAXBIT) BSET((blkno) & 7, bitmap[(blkno) >> 3]) #define CLR(blkno) if ((blkno) < MAXBIT) BCLR((blkno) & 7, bitmap[(blkno) >> 3]) /* bitmap of free blocks in first 4096k of tmp file */ static unsigned char bitmap[512]; #define MAXBIT (sizeof bitmap << 3) /* this function locates all free blocks in the current tmp file */ void garbage() { int i; BLK oldhdr; /* start by assuming every block is free */ for (i = 0; i < sizeof bitmap; i++) { bitmap[i] = 255; } /* header block isn't free */ #ifndef lint CLR(0); #endif /* blocks needed for current hdr aren't free */ for (i = 1; i < MAXBLKS; i++) { CLR(hdr.n[i]); } /* blocks needed for undo version aren't free */ lseek(tmpfd, 0L, 0); if (read(tmpfd, &oldhdr, (unsigned)sizeof oldhdr) != sizeof oldhdr) { msg("garbage() failed to read oldhdr??"); for (i = 0; i < sizeof bitmap; i++) { bitmap[i] = 0; } return; } for (i = 1; i < MAXBLKS; i++) { CLR(oldhdr.n[i]); } /* blocks needed for cut buffers aren't free */ for (i = cutneeds(&oldhdr) - 1; i >= 0; i--) { CLR(oldhdr.n[i]); } } /* This function allocates the first available block in the tmp file */ long allocate() { int i; long offset; /* search for the first byte with a free bit set */ for (i = 0; i < sizeof bitmap && bitmap[i] == 0; i++) { } /* if we hit the end of the bitmap, return the end of the file */ if (i == sizeof bitmap) { offset = lseek(tmpfd, 0L, 2); } else /* compute the offset for the free block */ { for (i <<= 3; TST(i) == 0; i++) { } offset = (long)i * (long)BLKSIZE; /* mark the block as "allocated" */ CLR(i); } return offset; } #endif SHAR_EOF fi # end of overwriting check if test -f 'redraw.c' then echo shar: will not over-write existing file "'redraw.c'" else cat << \SHAR_EOF > 'redraw.c' /* redraw.c */ /* Author: * Steve Kirkendall * 14407 SW Teal Blvd. #C * Beaverton, OR 97005 * kirkenda@cs.pdx.edu */ /* This file contains functions that draw text on the screen. The major entry * points are: * redrawrange() - called from modify.c to give hints about what parts * of the screen need to be redrawn. * redraw() - redraws the screen (or part of it) and positions * the cursor where it belongs. * idx2col() - converts a markidx() value to a logical column number. */ #include "config.h" #include "vi.h" /* This variable contains the line number that smartdrawtext() knows best */ static long smartlno; /* This function remebers where changes were made, so that the screen can be * redraw in a more efficient manner. */ static long redrawafter; /* line# of first line that must be redrawn */ static long preredraw; /* line# of last line changed, before change */ static long postredraw; /* line# of last line changed, after change */ void redrawrange(after, pre, post) long after; /* lower bound of redrawafter */ long pre; /* upper bound of preredraw */ long post; /* upper bound of postredraw */ { if (after == redrawafter) { /* multiple insertions/deletions at the same place -- combine * them */ preredraw -= (post - pre); if (postredraw < post) { preredraw += (post - postredraw); postredraw = post; } if (redrawafter > preredraw) { redrawafter = preredraw; } if (redrawafter < 1L) { redrawafter = 0L; preredraw = postredraw = INFINITY; } } else if (postredraw > 0L) { /* multiple changes in different places -- redraw everything * after "after". */ postredraw = preredraw = INFINITY; if (after < redrawafter) redrawafter = after; } else { /* first change */ redrawafter = after; preredraw = pre; postredraw = post; } } #ifndef NO_CHARATTR /* see if a given line uses character attribute strings */ static int hasattr(lno, text) long lno; /* the line# of the cursor */ REG char *text; /* the text of the line, from fetchline */ { static long plno; /* previous line number */ static long chgs; /* previous value of changes counter */ static int panswer;/* previous answer */ char *scan; /* if charattr is off, then the answer is "no, it doesn't" */ if (!*o_charattr) { chgs = 0; /* <- forces us to check if charattr is later set */ return FALSE; } /* if we already know the answer, return it... */ if (lno == plno && chgs == changes) { return panswer; } /* get the line & look for "\fX" */ if (!text[0] || !text[1] || !text[2]) { panswer = FALSE; } else { for (scan = text; scan[2] && !(scan[0] == '\\' && scan[1] == 'f'); scan++) { } panswer = (scan[2] != '\0'); } /* save the results */ plno = lno; chgs = changes; /* return the results */ return panswer; } #endif /* This function converts a MARK to a column number. It doesn't automatically * adjust for leftcol; that must be done by the calling function */ int idx2col(curs, text, inputting) MARK curs; /* the line# & index# of the cursor */ REG char *text; /* the text of the line, from fetchline */ int inputting; /* boolean: called from input() ? */ { static MARK pcursor;/* previous cursor, for possible shortcut */ static MARK pcol; /* column number for pcol */ static long chgs; /* previous value of changes counter */ REG int col; /* used to count column numbers */ REG int idx; /* used to count down the index */ REG int i; /* for now, assume we have to start counting at the left edge */ col = 0; idx = markidx(curs); /* if the file hasn't changed & line number is the same & it has no * embedded character attribute strings, can we do shortcuts? */ if (chgs == changes && !((curs ^ pcursor) & ~(BLKSIZE - 1)) #ifndef NO_CHARATTR && !hasattr(markline(curs), text) #endif ) { /* no movement? */ if (curs == pcursor) { /* return the column of the char; for tabs, return its last column */ if (text[idx] == '\t' && !inputting && !*o_list) { return pcol + *o_tabstop - (pcol % *o_tabstop) - 1; } else { return pcol; } } /* movement to right? */ if (curs > pcursor) { /* start counting from previous place */ col = pcol; idx = markidx(curs) - markidx(pcursor); text += markidx(pcursor); } } /* count over to the char after the idx position */ while (idx > 0 && (i = *text)) /* yes, ASSIGNMENT! */ { if (i == '\t' && !*o_list) { col += *o_tabstop; col -= col % *o_tabstop; } else if (i >= '\0' && i < ' ' || i == '\177') { col += 2; } #ifndef NO_CHARATTR else if (i == '\\' && text[1] == 'f' && text[2] && *o_charattr) { text += 2; /* plus one more at bottom of loop */ idx -= 2; } #endif else { col++; } text++; idx--; } /* save stuff to speed next call */ pcursor = curs; pcol = col; chgs = changes; /* return the column of the char; for tabs, return its last column */ if (*text == '\t' && !inputting && !*o_list) { return col + *o_tabstop - (col % *o_tabstop) - 1; } else { return col; } } /* This function is similar to idx2col except that it takes care of sideways * scrolling - for the given line, at least. */ int mark2phys(m, text, inputting) MARK m; /* a mark to convert */ char *text; /* the line that m refers to */ int inputting; /* boolean: caled from input() ? */ { int i; i = idx2col(m, text, inputting); while (i < leftcol) { leftcol -= *o_sidescroll; mustredraw = TRUE; redrawrange(1L, INFINITY, INFINITY); qaddch('\r'); /* drawtext(text); */ } while (i > rightcol) { leftcol += *o_sidescroll; mustredraw = TRUE; redrawrange(1L, INFINITY, INFINITY); qaddch('\r'); /* drawtext(text); */ } physcol = i - leftcol; physrow = markline(m) - topline; return physcol; } /* This function draws a single line of text on the screen. The screen's * cursor is assumed to be located at the leftmost column of the appropriate * row. */ void drawtext(text, clr) REG char *text; /* the text to draw */ int clr; /* boolean: do a clrtoeol? */ { REG int col; /* column number */ REG int i; REG int tabstop; /* *o_tabstop */ REG int limitcol; /* leftcol or leftcol + COLS */ int abnormal; /* boolean: charattr != A_NORMAL? */ #ifndef NO_SENTENCE /* if we're hiding format lines, and this is one of them, then hide it */ if (*o_hideformat && *text == '.') { clrtoeol(); #if OSK qaddch('\l'); #else qaddch('\n'); #endif return; } #endif /* move some things into registers... */ limitcol = leftcol; tabstop = *o_tabstop; abnormal = FALSE; #ifndef CRUNCH if (clr) clrtoeol(); #endif /* skip stuff that was scrolled off left edge */ for (col = 0; (i = *text) && col < limitcol; /* yes, ASSIGNMENT! */ text++) { if (i == '\t' && !*o_list) { col = col + tabstop - (col % tabstop); } else if (i >= 0 && i < ' ' || i == '\177') { col += 2; } #ifndef NO_CHARATTR else if (i == '\\' && text[1] == 'f' && text[2] && *o_charattr) { text += 2; /* plus one more as part of "for" loop */ /* since this attribute might carry over, we need it */ switch (*text) { case 'R': case 'P': attrset(A_NORMAL); abnormal = FALSE; break; case 'B': attrset(A_BOLD); abnormal = TRUE; break; case 'U': attrset(A_UNDERLINE); abnormal = TRUE; break; case 'I': attrset(A_ALTCHARSET); abnormal = TRUE; break; } } #endif else { col++; } } /* adjust for control char that was partially visible */ while (col > limitcol) { qaddch(' '); limitcol++; } /* now for the visible characters */ for (limitcol = leftcol + COLS; (i = *text) && col < limitcol; text++) { if (i == '\t' && !*o_list) { i = col + tabstop - (col % tabstop); if (i < limitcol) { #ifdef CRUNCH if (!clr && has_PT && !((i - leftcol) & 7)) #else if (has_PT && !((i - leftcol) & 7)) #endif { do { qaddch('\t'); col += 8; /* not exact! */ } while (col < i); col = i; /* NOW it is exact */ } else { do { qaddch(' '); col++; } while (col < i); } } else /* tab ending after screen? next line! */ { col = limitcol; if (has_AM) { addch('\n'); /* GB */ } } } else if (i >= 0 && i < ' ' || i == '\177') { col += 2; qaddch('^'); if (col <= limitcol) { qaddch(i ^ '@'); } } #ifndef NO_CHARATTR else if (i == '\\' && text[1] == 'f' && text[2] && *o_charattr) { text += 2; /* plus one more as part of "for" loop */ switch (*text) { case 'R': case 'P': attrset(A_NORMAL); abnormal = FALSE; break; case 'B': attrset(A_BOLD); abnormal = TRUE; break; case 'U': attrset(A_UNDERLINE); abnormal = TRUE; break; case 'I': attrset(A_ALTCHARSET); abnormal = TRUE; break; } } #endif else { col++; qaddch(i); } } /* get ready for the next line */ #ifndef NO_CHARATTR if (abnormal) { attrset(A_NORMAL); } #endif if (*o_list && col < limitcol) { qaddch('$'); col++; } #ifdef CRUNCH if (clr && col < limitcol) { clrtoeol(); } #endif if (!has_AM || col < limitcol) { addch('\n'); } } #if OPENWINDOWS # define NUDGE_COST (Ow.tty ? 1 : 5) /* move is always cheaper in NeWS */ #else # define NUDGE_COST 5 #endif #ifndef CRUNCH static void nudgecursor(same, scan, new, lno) int same; /* number of chars to be skipped over */ char *scan; /* where the same chars end */ char *new; /* where the visible part of the line starts */ long lno; /* line number of this line */ { if (same > 0) { if (same < NUDGE_COST) { /* move the cursor by overwriting */ while (same > 0) { qaddch(scan[-same]); same--; } } else { /* move the cursor by calling move() */ move((int)(lno - topline), (int)(scan - new)); } } } #endif /* not CRUNCH */ /* This function draws a single line of text on the screen, possibly with * some cursor optimization. The cursor is repositioned before drawing * begins, so its position before doesn't really matter. */ static void smartdrawtext(text, lno) REG char *text; /* the text to draw */ long lno; /* line number of the text */ { #ifdef CRUNCH move((int)(lno - topline), 0); drawtext(text, TRUE); #else /* not CRUNCH */ static char old[256]; /* how the line looked last time */ char new[256]; /* how it looks now */ char *build; /* used to put chars into new[] */ char *scan; /* used for moving thru new[] or old[] */ char *end; /* last non-blank changed char */ char *shift; /* used to insert/delete chars */ int same; /* length of a run of unchanged chars */ int limitcol; int col; int i; # ifndef NO_CHARATTR /* if this line has attributes, do it the dumb way instead */ if (hasattr(lno, text)) { move((int)(lno - topline), 0); drawtext(text, TRUE); return; } # endif # ifndef NO_SENTENCE /* if this line is a format line, & we're hiding format lines, then * let the dumb drawtext() function handle it */ if (*o_hideformat && *text == '.') { move((int)(lno - topline), 0); drawtext(text, TRUE); return; } # endif /* skip stuff that was scrolled off left edge */ limitcol = leftcol; for (col = 0; (i = *text) && col < limitcol; /* yes, ASSIGNMENT! */ text++) { if (i == '\t' && !*o_list) { col = col + *o_tabstop - (col % *o_tabstop); } else if (i >= 0 && i < ' ' || i == '\177') { col += 2; } else { col++; } } /* adjust for control char that was partially visible */ build = new; while (col > limitcol) { *build++ = ' '; limitcol++; } /* now for the visible characters */ for (limitcol = leftcol + COLS; (i = *text) && col < limitcol; text++) { if (i == '\t' && !*o_list) { i = col + *o_tabstop - (col % *o_tabstop); while (col < i && col < limitcol) { *build++ = ' '; col++; } } else if (i >= 0 && i < ' ' || i == '\177') { col += 2; *build++ = '^'; if (col <= limitcol) { *build++ = (i ^ '@'); } } else { col++; *build++ = i; } } if (col < limitcol && *o_list) { *build++ = '$'; col++; } end = build; while (col < limitcol) { *build++ = ' '; col++; } /* locate the last non-blank character */ while (end > new && end[-1] == ' ') { end--; } /* can we optimize the displaying of this line? */ if (lno != smartlno) { /* nope, can't optimize - different line */ move((int)(lno - topline), 0); for (scan = new, build = old; scan < end; ) { qaddch(*scan); *build++ = *scan++; } if (end < new + COLS) { clrtoeol(); while (build < old + COLS) { *build++ = ' '; } } smartlno = lno; return; } /* skip any initial unchanged characters */ for (scan = new, build = old; scan < end && *scan == *build; scan++, build++) { } move((int)(lno - topline), (int)(scan - new)); /* The in-between characters must be changed */ same = 0; while (scan < end) { /* is this character a match? */ if (scan[0] == build[0]) { same++; } else /* do we want to insert? */ if (scan < end - 1 && scan[1] == build[0] && (has_IC || has_IM)) { nudgecursor(same, scan, new, lno); same = 0; insch(*scan); for (shift = old + COLS; --shift > build; ) { shift[0] = shift[-1]; } *build = *scan; } else /* do we want to delete? */ if (build < old + COLS - 1 && scan[0] == build[1] && has_DC) { nudgecursor(same, scan, new, lno); same = 0; delch(); same++; for (shift = build; shift < old + COLS - 1; shift++) { shift[0] = shift[1]; } *shift = ' '; } else /* we must overwrite */ { nudgecursor(same, scan, new, lno); same = 0; addch(*scan); *build = *scan; } build++; scan++; } /* maybe clear to EOL */ while (build < old + COLS && *build == ' ') { build++; } if (build < old + COLS) { nudgecursor(same, scan, new, lno); same = 0; clrtoeol(); while (build < old + COLS) { *build++ = ' '; } } #endif /* not CRUNCH */ } /* This function is used in visual mode for drawing the screen (or just parts * of the screen, if that's all thats needed). It also takes care of * scrolling. */ void redraw(curs, inputting) MARK curs; /* where to leave the screen's cursor */ int inputting; /* boolean: being called from input() ? */ { char *text; /* a line of text to display */ static long chgs; /* previous changes level */ long l; int i; /* if curs == MARK_UNSET, then we should reset internal vars */ if (curs == MARK_UNSET) { if (topline < 1 || topline > nlines) { topline = 1L; } else { move(LINES - 1, 0); clrtoeol(); } leftcol = 0; mustredraw = TRUE; redrawafter = INFINITY; preredraw = 0L; postredraw = 0L; chgs = 0; smartlno = 0L; return; } /* figure out which column the cursor will be in */ l = markline(curs); text = fetchline(l); mark2phys(curs, text, inputting); /* adjust topline, if necessary, to get the cursor on the screen */ if (l >= topline && l <= botline) { /* it is on the screen already */ /* if the file was changed but !mustredraw, then redraw line */ if (chgs != changes && !mustredraw) { smartdrawtext(text, l); } } else if (l < topline && l > topline - LINES && (has_SR || has_AL)) { /* near top - scroll down */ if (!mustredraw) { Scrolldown(l, 0, 0); } else { topline = l; redrawafter = INFINITY; preredraw = 0L; postredraw = 0L; } } else if (l > topline && l < botline + LINES) { /* near bottom -- scroll up */ if (!mustredraw #if 1 || redrawafter == preredraw && preredraw == botline && postredraw == l #endif ) { Scrollup(l, LINES-1, 0); mustredraw = FALSE; } else { topline = l - (LINES - 2); redrawafter = INFINITY; preredraw = 0L; postredraw = 0L; } } else { /* distant line - center it & force a redraw */ topline = l - (LINES / 2) - 1; if (topline < 1) { topline = 1; } mustredraw = TRUE; redrawafter = INFINITY; preredraw = 0L; postredraw = 0L; } /* Now... do we really have to redraw? */ if (mustredraw) { /* If redrawfter (and friends) aren't set, assume we should * redraw everything. */ if (redrawafter == INFINITY) { redrawafter = 0L; preredraw = postredraw = INFINITY; } /* adjust smartlno to correspond with inserted/deleted lines */ if (smartlno >= redrawafter) { if (smartlno < preredraw) { smartlno = 0L; } else { smartlno += (postredraw - preredraw); } } /* should we insert some lines into the screen? */ if (preredraw < postredraw && preredraw <= botline) { /* lines were inserted into the file */ /* decide where insertion should start */ if (preredraw < topline) { l = topline; } else { l = preredraw; } /* insert the lines... maybe */ if (l + postredraw - preredraw > botline || !has_AL) { /* Whoa! a whole screen full - just redraw */ preredraw = postredraw = INFINITY; } else { /* really insert 'em */ move((int)(l - topline), 0); for (i = postredraw - preredraw; i > 0; i--) { insertln(); } /* NOTE: the contents of those lines will be * drawn as part of the regular redraw loop. */ /* clear the last line */ move(LINES - 1, 0); clrtoeol(); } } /* do we want to delete some lines from the screen? */ if (preredraw > postredraw && postredraw <= botline) { if (preredraw > botline || !has_DL) { postredraw = preredraw = INFINITY; } else /* we'd best delete some lines from the screen */ { /* clear the last line, so it doesn't look * ugly as it gets pulled up into the screen */ move(LINES - 1, 0); clrtoeol(); /* delete the lines */ move((int)(postredraw - topline), 0); for (l = postredraw; l < preredraw && l <= botline; l++) { deleteln(); } /* draw the lines that are now newly visible * at the bottom of the screen */ i = LINES - 1 + (postredraw - preredraw); move(i, 0); for (l = topline + i; l <= botline; l++) { /* clear this line */ clrtoeol(); /* draw the line, or ~ for non-lines */ if (l <= nlines) { text = fetchline(l); drawtext(text, FALSE); } else { addstr("~\n"); } } } } /* redraw the current line */ l = markline(curs); pfetch(l); smartdrawtext(ptext, l); /* decide where we should start redrawing from */ if (redrawafter < topline) { l = topline; } else { l = redrawafter; } move((int)(l - topline), 0); /* draw the other lines */ for (; l <= botline && l < postredraw; l++) { /* we already drew the current line, so skip it now */ /* smartlno is not always adjusted right. when it is fixed take out the stuff around the HACK comments */ if (l == smartlno /* HACK */ && 0 /* HACK */) { #if OSK qaddch('\l'); #else qaddch('\n'); #endif continue; } /* draw the line, or ~ for non-lines */ if (l <= nlines) { text = fetchline(l); drawtext(text, TRUE); } else { qaddch('~'); clrtoeol(); addch('\n'); } } mustredraw = FALSE; } /* force total (non-partial) redraw next time if not set */ redrawafter = INFINITY; preredraw = 0L; postredraw = 0L; /* move the cursor to where it belongs */ move((int)(markline(curs) - topline), physcol); wqrefresh(stdscr); chgs = changes; } int scrolldown(l,y,x) long l; int y, x; { char *text; move(y,x); while (l < topline) { topline--; if (has_SR) { do_SR(); } else { insertln(); } text = fetchline(topline); drawtext(text, FALSE); do_UP(); } /* blank out the last line */ move(LINES - 1, 0); clrtoeol(); return 0; } int scrollup(l, y, x) long l; int y,x; { char *text; move(y,x); clrtoeol(); while (l > botline) { topline++; /* <-- also adjusts botline */ text = fetchline(botline); drawtext(text, FALSE); } return 0; } SHAR_EOF fi # end of overwriting check if test -f 'ref.c' then echo shar: will not over-write existing file "'ref.c'" else cat << \SHAR_EOF > 'ref.c' /* ref.c */ /* Author: * Steve Kirkendall * 14407 SW Teal Blvd. #C * Beaverton, OR 97005 * kirkenda@cs.pdx.edu */ /* This program looks up the declarations of library functions. */ #include /* This is the list of files that are searched. */ #ifdef OSK char *refslist[] = { "refs", "/dd/usr/src/lib/refs", "../lib/refs", "/dd/usr/local/lib/refs", }; #else char *refslist[] = { "refs", "/usr/src/lib/refs", "../lib/refs", "/usr/local/lib/refs" }; #endif #define NREFS (sizeof refslist / sizeof(char *)) main(argc, argv) int argc; char **argv; { int i; /* used to step through the refslist */ /* make sure our arguments are OK */ if (argc != 2) { fprintf(stderr, "usage: %s function_name\n", *argv); exit(2); } /* check for the function in each database */ for (i = 0; i < NREFS; i++) { if (lookinfor(refslist[i], argv[1])) { exit(0); } } fprintf(stderr, "%s: don't know about %s\n", argv[0], argv[1]); exit(2); } /* This function checks a single file for the function. Returns 1 if found */ int lookinfor(filename, func) char *filename; /* name of file to look in */ char *func; /* name of function to look for */ { FILE *fp; /* stream used to access the database */ char linebuf[300]; /* NOTE: in actual use, the first character of linebuf is */ /* set to ' ' and then we use all EXCEPT the 1st character */ /* everywhere in this function. This is because the func */ /* which examines the linebuf could, in some circumstances */ /* examine the character before the used part of linebuf; */ /* we need to control what happens then. */ /* open the database file */ fp = fopen(filename, "r"); if (!fp) { return 0; } /* find the desired entry */ *linebuf = ' '; do { if (!fgets(linebuf + 1, (sizeof linebuf) - 1, fp)) { fclose(fp); return 0; } } while (!desired(linebuf + 1, func)); /* print it */ do { fputs(linebuf + 1, stdout); } while (fgets(linebuf + 1, sizeof linebuf, fp) && linebuf[1] == '\t'); /* cleanup & exit */ fclose(fp); return 1; } /* This function checks to see if a given line is the first line of the */ /* entry the user wants to see. If it is, return non-0 else return 0 */ desired(line, word) char *line; /* the line buffer */ char *word; /* the string it should contain */ { static wlen = -1;/* length of the "word" variable's value */ register char *scan; /* if this line starts with a tab, it couldn't be desired */ if (*line == '\t') { return 0; } /* if we haven't found word's length yet, do so */ if (wlen < 0) { wlen = strlen(word); } /* search for the word in the line */ for (scan = line; *scan != '('; scan++) { } while (*--scan == ' ') { } scan -= wlen; if (scan < line - 1 || *scan != ' ' && *scan != '\t' && *scan != '*') { return 0; } scan++; return !strncmp(scan, word, wlen); } SHAR_EOF fi # end of overwriting check if test -f 'refont.c' then echo shar: will not over-write existing file "'refont.c'" else cat << \SHAR_EOF > 'refont.c' /* refont.c */ /* Author: * Steve Kirkendall * 14407 SW Teal Blvd. #C * Beaverton, OR 97005 * kirkenda@cs.pdx.edu */ /* This file contains the complete source code for the refont program */ /* The refont program converts font designations to the format of your choice. * Known font formats are: * -b overtype notation, using backspaces * -c overtype notation, using carriage returns * -d the "dot" command notation used by nroff (doesn't work well) * -e Epson-compatible line printer codes * -f the "\f" notation * -x none (strip out the font codes) * * Other flags are: * -I recognize \f and dot notations in input * -F output a formfeed character between files */ #include #include "config.h" /* the program name, for diagnostics */ char *progname; /* remembers which output format to use */ int outfmt = 'f'; /* do we allow "dot" and "backslash-f" on input? */ int infmt = 0; /* do we insert formfeeds between input files? */ int add_form_feed = 0; main(argc, argv) int argc; /* number of command-line args */ char **argv; /* values of the command line args */ { FILE *fp; int i, j; int retcode; progname = argv[0]; /* parse the flags */ i = 1; if (i < argc && argv[i][0] == '-' && argv[i][1]) { for (j = 1; argv[i][j]; j++) { switch (argv[i][j]) { case 'b': #if !OSK case 'c': #endif case 'd': case 'e': case 'f': case 'x': outfmt = argv[i][j]; break; case 'I': infmt = 'I'; break; case 'F': add_form_feed = 1; break; default: usage(); } } i++; } retcode = 0; if (i == argc) { /* probably shouldn't read from keyboard */ if (isatty(fileno(stdin))) { usage(); } /* no files named, so use stdin */ refont(stdin); } else { for (; i < argc; i++) { fp = fopen(argv[i], "r"); if (!fp) { perror(argv[i]); retcode = 1; } else { refont(fp); if (i < argc - 1 && add_form_feed) { putchar('\f'); } fclose(fp); } } } exit(retcode); } usage() { fputs("usage: ", stderr); fputs(progname, stderr); fputs(" [-bcdefxFI] [filename]...\n", stderr); exit(2); } /* This function does the refont thing to a single file */ /* I apologize for the length of this function. It is gross. */ refont(fp) FILE *fp; { char textbuf[1025]; /* chars of a line of text */ char fontbuf[1025]; /* fonts of chars in fontbuf */ int col; /* where we are in the line */ int font; /* remembered font */ int more; /* more characters to be output? */ int ch; /* reset some stuff */ for (col = sizeof fontbuf; --col >= 0; ) { fontbuf[col] = 'R'; textbuf[col] = '\0'; } col = 0; font = 'R'; /* get the first char - quit if eof */ while ((ch = getc(fp)) != EOF) { /* if "dot" command, read the rest of the command */ if (infmt == 'I' && ch == '.' && col == 0) { textbuf[col++] = '.'; textbuf[col++] = getc(fp); textbuf[col++] = getc(fp); textbuf[col++] = ch = getc(fp); /* is it a font line? */ font = 0; if (textbuf[1] == 'u' && textbuf[2] == 'l') { font = 'U'; } else if (textbuf[1] == 'b' && textbuf[2] == 'o') { font = 'B'; } else if (textbuf[1] == 'i' && textbuf[2] == 't') { font = 'I'; } /* if it is, discard the stuff so far but remember font */ if (font) { while (col > 0) { textbuf[--col] = '\0'; } } else /* some other format line - write it literally */ { while (ch != '\n') { textbuf[col++] = ch = getc(fp); } fputs(textbuf, fp); while (col > 0) { textbuf[--col] = '\0'; } } continue; } /* is it a "\f" formatted font? */ if (infmt == 'I' && ch == '\\') { ch = getc(fp); if (ch == 'f') { font = getc(fp); } else { textbuf[col++] = '\\'; textbuf[col++] = ch; } continue; } /* is it an Epson font? */ if (ch == '\033') { ch = getc(fp); switch (ch) { case '4': font = 'I'; break; case 'E': case 'G': font = 'B'; break; case '5': case 'F': case 'H': font = 'R'; break; case '-': font = (getc(fp) & 1) ? 'U' : 'R'; break; } continue; } /* control characters? */ if (ch == '\b') { if (col > 0) col--; continue; } else if (ch == '\t') { do { if (textbuf[col] == '\0') { textbuf[col] = ' '; } col++; } while (col & 7); continue; } #if !OSK else if (ch == '\r') { col = 0; continue; } #endif else if (ch == ' ') { if (textbuf[col] == '\0') { textbuf[col] = ' '; fontbuf[col] = font; col++; } continue; } /* newline? */ if (ch == '\n') { more = 0; for (col = 0, font = 'R'; textbuf[col]; col++) { if (fontbuf[col] != font) { switch (outfmt) { case 'd': putchar('\n'); switch (fontbuf[col]) { case 'B': fputs(".bo\n", stdout); break; case 'I': fputs(".it\n", stdout); break; case 'U': fputs(".ul\n", stdout); break; } while (textbuf[col] == ' ') { col++; } break; case 'e': switch (fontbuf[col]) { case 'B': fputs("\033E", stdout); break; case 'I': fputs("\0334", stdout); break; case 'U': fputs("\033-1", stdout); break; default: switch (font) { case 'B': fputs("\033F", stdout); break; case 'I': fputs("\0335", stdout); break; case 'U': fputs("\033-0", stdout); break; } } break; case 'f': putchar('\\'); putchar('f'); putchar(fontbuf[col]); break; } font = fontbuf[col]; } if (fontbuf[col] != 'R' && textbuf[col] != ' ') { switch (outfmt) { case 'b': if (fontbuf[col] == 'B') { putchar(textbuf[col]); } else { putchar('_'); } putchar('\b'); break; #if !OSK case 'c': more = col + 1; break; #endif } } putchar(textbuf[col]); } #if !OSK /* another pass? */ if (more > 0) { putchar('\r'); for (col = 0; col < more; col++) { switch (fontbuf[col]) { case 'I': case 'U': putchar('_'); break; case 'B': putchar(textbuf[col]); break; default: putchar(' '); } } } #endif /* not OSK */ /* newline */ if (font != 'R') { switch (outfmt) { case 'f': putchar('\\'); putchar('f'); putchar('R'); break; case 'e': switch (font) { case 'B': fputs("\033F", stdout); break; case 'I': fputs("\0335", stdout); break; case 'U': fputs("\033-0", stdout); break; } } } putchar('\n'); /* reset some stuff */ for (col = sizeof fontbuf; --col >= 0; ) { fontbuf[col] = 'R'; textbuf[col] = '\0'; } col = 0; font = 'R'; continue; } /* normal character */ if (font != 'R') { textbuf[col] = ch; fontbuf[col] = font; } else if (textbuf[col] == '_') { textbuf[col] = ch; fontbuf[col] = 'U'; } else if (textbuf[col] && textbuf[col] != ' ' && ch == '_') { fontbuf[col] = 'U'; } else if (textbuf[col] == ch) { fontbuf[col] = 'B'; } else { textbuf[col] = ch; } col++; } } SHAR_EOF fi # end of overwriting check if test -f 'regexp.c' then echo shar: will not over-write existing file "'regexp.c'" else cat << \SHAR_EOF > 'regexp.c' /* regexp.c */ /* This file contains the code that compiles regular expressions and executes * them. It supports the same syntax and features as vi's regular expression * code. Specifically, the meta characters are: * ^ matches the beginning of a line * $ matches the end of a line * \< matches the beginning of a word * \> matches the end of a word * . matches any single character * [] matches any character in a character class * \( delimits the start of a subexpression * \) delimits the end of a subexpression * * repeats the preceding 0 or more times * NOTE: You cannot follow a \) with a *. * * The physical structure of a compiled RE is as follows: * - First, there is a one-byte value that says how many character classes * are used in this regular expression * - Next, each character class is stored as a bitmap that is 256 bits * (32 bytes) long. * - A mixture of literal characters and compiled meta characters follows. * This begins with M_BEGIN(0) and ends with M_END(0). All meta chars * are stored as a \n followed by a one-byte code, so they take up two * bytes apiece. Literal characters take up one byte apiece. \n can't * be used as a literal character. * * If NO_MAGIC is defined, then a different set of functions is used instead. * That right, this file contains TWO versions of the code. */ #include #include #include "config.h" #include "vi.h" #include "regexp.h" static char *previous; /* the previous regexp, used when null regexp is given */ #ifndef NO_MAGIC /* THE REAL REGEXP PACKAGE IS USED UNLESS "NO_MAGIC" IS DEFINED */ /* These are used to classify or recognize meta-characters */ #define META '\0' #define BASE_META(m) ((m) - 256) #define INT_META(c) ((c) + 256) #define IS_META(m) ((m) >= 256) #define IS_CLASS(m) ((m) >= M_CLASS(0) && (m) <= M_CLASS(9)) #define IS_START(m) ((m) >= M_START(0) && (m) <= M_START(9)) #define IS_END(m) ((m) >= M_END(0) && (m) <= M_END(9)) #define IS_CLOSURE(m) ((m) >= M_SPLAT && (m) <= M_QMARK) #define ADD_META(s,m) (*(s)++ = META, *(s)++ = BASE_META(m)) #define GET_META(s) (*(s) == META ? INT_META(*++(s)) : *s) /* These are the internal codes used for each type of meta-character */ #define M_BEGLINE 256 /* internal code for ^ */ #define M_ENDLINE 257 /* internal code for $ */ #define M_BEGWORD 258 /* internal code for \< */ #define M_ENDWORD 259 /* internal code for \> */ #define M_ANY 260 /* internal code for . */ #define M_SPLAT 261 /* internal code for * */ #define M_PLUS 262 /* internal code for \+ */ #define M_QMARK 263 /* internal code for \? */ #define M_CLASS(n) (264+(n)) /* internal code for [] */ #define M_START(n) (274+(n)) /* internal code for \( */ #define M_END(n) (284+(n)) /* internal code for \) */ /* These are used during compilation */ static int class_cnt; /* used to assign class IDs */ static int start_cnt; /* used to assign start IDs */ static int end_stk[NSUBEXP];/* used to assign end IDs */ static int end_sp; static char *retext; /* points to the text being compiled */ /* error-handling stuff */ jmp_buf errorhandler; #define FAIL(why) regerror(why); longjmp(errorhandler, 1) /* This function builds a bitmap for a particular class */ static char *makeclass(text, bmap) REG char *text; /* start of the class */ REG char *bmap; /* the bitmap */ { REG int i; int complement = 0; # if TRACE printf("makeclass(\"%s\", 0x%lx)\n", text, (long)bmap); # endif /* zero the bitmap */ for (i = 0; bmap && i < 32; i++) { bmap[i] = 0; } /* see if we're going to complement this class */ if (*text == '^') { text++; complement = 1; } /* add in the characters */ while (*text && *text != ']') { /* is this a span of characters? */ if (text[1] == '-' && text[2]) { /* spans can't be backwards */ if (text[0] > text[2]) { FAIL("Backwards span in []"); } /* add each character in the span to the bitmap */ for (i = text[0]; bmap && i <= text[2]; i++) { bmap[i >> 3] |= (1 << (i & 7)); } /* move past this span */ text += 3; } else { /* add this single character to the span */ i = *text++; if (bmap) { bmap[i >> 3] |= (1 << (i & 7)); } } } /* make sure the closing ] is missing */ if (*text++ != ']') { FAIL("] missing"); } /* if we're supposed to complement this class, then do so */ if (complement && bmap) { for (i = 0; i < 32; i++) { bmap[i] = ~bmap[i]; } } return text; } /* This function gets the next character or meta character from a string. * The pointer is incremented by 1, or by 2 for \-quoted characters. For [], * a bitmap is generated via makeclass() (if re is given), and the * character-class text is skipped. */ static int gettoken(sptr, re) char **sptr; regexp *re; { int c; c = **sptr; ++*sptr; if (c == '\\') { c = **sptr; ++*sptr; switch (c) { case '<': return M_BEGWORD; case '>': return M_ENDWORD; case '(': if (start_cnt >= NSUBEXP) { FAIL("Too many \\(s"); } end_stk[end_sp++] = start_cnt; return M_START(start_cnt++); case ')': if (end_sp <= 0) { FAIL("Mismatched \\)"); } return M_END(end_stk[--end_sp]); case '*': return (*o_magic ? c : M_SPLAT); case '.': return (*o_magic ? c : M_ANY); case '+': return M_PLUS; case '?': return M_QMARK; default: return c; } } else if (*o_magic) { switch (c) { case '^': if (*sptr == retext + 1) { return M_BEGLINE; } return c; case '$': if (!**sptr) { return M_ENDLINE; } return c; case '.': return M_ANY; case '*': return M_SPLAT; case '[': /* make sure we don't have too many classes */ if (class_cnt >= 10) { FAIL("Too many []s"); } /* process the character list for this class */ if (re) { /* generate the bitmap for this class */ *sptr = makeclass(*sptr, re->program + 1 + 32 * class_cnt); } else { /* skip to end of the class */ *sptr = makeclass(*sptr, (char *)0); } return M_CLASS(class_cnt++); default: return c; } } else /* unquoted nomagic */ { switch (c) { case '^': if (*sptr == retext + 1) { return M_BEGLINE; } return c; case '$': if (!**sptr) { return M_ENDLINE; } return c; default: return c; } } /*NOTREACHED*/ } /* This function calculates the number of bytes that will be needed for a * compiled RE. Its argument is the uncompiled version. It is not clever * about catching syntax errors; that is done in a later pass. */ static unsigned calcsize(text) char *text; { unsigned size; int token; retext = text; class_cnt = 0; start_cnt = 1; end_sp = 0; size = 5; while ((token = gettoken(&text, (regexp *)0)) != 0) { if (IS_CLASS(token)) { size += 34; } else if (IS_META(token)) { size += 2; } else { size++; } } return size; } /* This function compiles a regexp. */ regexp *regcomp(text) char *text; { int needfirst; unsigned size; int token; int peek; char *build; regexp *re; /* prepare for error handling */ re = (regexp *)0; if (setjmp(errorhandler)) { if (re) { free(re); } return (regexp *)0; } /* if an empty regexp string was given, use the previous one */ if (*text == 0) { if (!previous) { FAIL("No previous RE"); } text = previous; } else /* non-empty regexp given, so remember it */ { if (previous) free(previous); previous = (char *)malloc((unsigned)(strlen(text) + 1)); if (previous) strcpy(previous, text); } /* allocate memory */ class_cnt = 0; start_cnt = 1; end_sp = 0; retext = text; size = calcsize(text) + sizeof(regexp); #ifdef lint re = ((regexp *)0) + size; #else re = (regexp *)malloc((unsigned)size); #endif if (!re) { FAIL("Not enough memory for this RE"); } /* compile it */ build = &re->program[1 + 32 * class_cnt]; re->program[0] = class_cnt; for (token = 0; token < NSUBEXP; token++) { re->startp[token] = re->endp[token] = (char *)0; } re->first = 0; re->bol = 0; re->minlen = 0; needfirst = 1; class_cnt = 0; start_cnt = 1; end_sp = 0; retext = text; for (token = M_START(0), peek = gettoken(&text, re); token; token = peek, peek = gettoken(&text, re)) { /* special processing for the closure operator */ if (IS_CLOSURE(peek)) { /* detect misuse of closure operator */ if (IS_START(token)) { FAIL("* or \\+ or \\? follows nothing"); } else if (IS_META(token) && token != M_ANY && !IS_CLASS(token)) { FAIL("* or \\+ or \\? can only follow a normal character or . or []"); } /* it is okay -- make it prefix instead of postfix */ ADD_META(build, peek); /* take care of "needfirst" - is this the first char? */ if (needfirst && peek == M_PLUS && !IS_META(token)) { re->first = token; } needfirst = 0; /* we used "peek" -- need to refill it */ peek = gettoken(&text, re); if (IS_CLOSURE(peek)) { FAIL("* or \\+ or \\? doubled up"); } } else if (!IS_META(token)) { /* normal char is NOT argument of closure */ if (needfirst) { re->first = token; needfirst = 0; } re->minlen++; } else if (token == M_ANY || IS_CLASS(token)) { /* . or [] is NOT argument of closure */ needfirst = 0; re->minlen++; } /* the "token" character is not closure -- process it normally */ if (token == M_BEGLINE) { /* set the BOL flag instead of storing M_BEGLINE */ re->bol = 1; } else if (IS_META(token)) { ADD_META(build, token); } else { *build++ = token; } } /* end it with a \) which MUST MATCH the opening \( */ ADD_META(build, M_END(0)); if (end_sp > 0) { FAIL("Not enough \\)s"); } return re; } /*---------------------------------------------------------------------------*/ /* This function checks for a match between a character and a token which is * known to represent a single character. It returns 0 if they match, or * 1 if they don't. */ int match1(re, ch, token) regexp *re; REG char ch; REG int token; { if (!ch) { /* the end of a line can't match any RE of width 1 */ return 1; } if (token == M_ANY) { return 0; } else if (IS_CLASS(token)) { if (re->program[1 + 32 * (token - M_CLASS(0)) + (ch >> 3)] & (1 << (ch & 7))) return 0; } else if (ch == token || (*o_ignorecase && isupper(ch) && tolower(ch) == token)) { return 0; } return 1; } /* This function checks characters up to and including the next closure, at * which point it does a recursive call to check the rest of it. This function * returns 0 if everything matches, or 1 if something doesn't match. */ int match(re, str, prog, here) regexp *re; /* the regular expression */ char *str; /* the string */ REG char *prog; /* a portion of re->program, an compiled RE */ REG char *here; /* a portion of str, the string to compare it to */ { REG int token; REG int nmatched; REG int closure; for (token = GET_META(prog); !IS_CLOSURE(token); prog++, token = GET_META(prog)) { switch (token) { /*case M_BEGLINE: can't happen; re->bol is used instead */ case M_ENDLINE: if (*here) return 1; break; case M_BEGWORD: if (here != str && (here[-1] == '_' || isascii(here[-1]) && isalnum(here[-1]))) return 1; break; case M_ENDWORD: if (here[0] == '_' || isascii(here[0]) && isalnum(here[0])) return 1; break; case M_START(0): case M_START(1): case M_START(2): case M_START(3): case M_START(4): case M_START(5): case M_START(6): case M_START(7): case M_START(8): case M_START(9): re->startp[token - M_START(0)] = (char *)here; break; case M_END(0): case M_END(1): case M_END(2): case M_END(3): case M_END(4): case M_END(5): case M_END(6): case M_END(7): case M_END(8): case M_END(9): re->endp[token - M_END(0)] = (char *)here; if (token == M_END(0)) { return 0; } break; default: /* literal, M_CLASS(n), or M_ANY */ if (match1(re, *here, token) != 0) return 1; here++; } } /* C L O S U R E */ /* step 1: see what we have to match against, and move "prog" to point * the the remainder of the compiled RE. */ closure = token; prog++, token = GET_META(prog); prog++; /* step 2: see how many times we can match that token against the string */ for (nmatched = 0; (closure != M_QMARK || nmatched < 1) && *here && match1(re, *here, token) == 0; nmatched++, here++) { } /* step 3: try to match the remainder, and back off if it doesn't */ while (nmatched >= 0 && match(re, str, prog, here) != 0) { nmatched--; here--; } /* so how did it work out? */ if (nmatched >= ((closure == M_PLUS) ? 1 : 0)) return 0; return 1; } /* This function searches through a string for text that matches an RE. */ int regexec(re, str, bol) regexp *re; /* the compiled regexp to search for */ char *str; /* the string to search through */ int bol; /* boolean: does str start at the beginning of a line? */ { char *prog; /* the entry point of re->program */ int len; /* length of the string */ REG char *here; /* if must start at the beginning of a line, and this isn't, then fail */ if (re->bol && !bol) { return 0; } len = strlen(str); prog = re->program + 1 + 32 * re->program[0]; /* search for the RE in the string */ if (re->bol) { /* must occur at BOL */ if ((re->first && match1(re, *(char *)str, re->first))/* wrong first letter? */ || len < re->minlen /* not long enough? */ || match(re, (char *)str, prog, str)) /* doesn't match? */ return 0; /* THEN FAIL! */ } #ifndef CRUNCH else if (!*o_ignorecase) { /* can occur anywhere in the line, noignorecase */ for (here = (char *)str; (re->first && re->first != *here) || match(re, (char *)str, prog, here); here++, len--) { if (len < re->minlen) return 0; } } #endif else { /* can occur anywhere in the line, ignorecase */ for (here = (char *)str; (re->first && match1(re, *here, (int)re->first)) || match(re, (char *)str, prog, here); here++, len--) { if (len < re->minlen) return 0; } } /* if we didn't fail, then we must have succeeded */ return 1; } #else /* NO_MAGIC */ regexp *regcomp(exp) char *exp; { char *src; char *dest; regexp *re; int i; /* allocate a big enough regexp structure */ #ifdef lint re = (regexp *)0; #else re = (regexp *)malloc((unsigned)(strlen(exp) + 1 + sizeof(struct regexp))); #endif if (!re) { regerror("Could not malloc a regexp structure"); return (regexp *)0; } /* initialize all fields of the structure */ for (i = 0; i < NSUBEXP; i++) { re->startp[i] = re->endp[i] = (char *)0; } re->minlen = 0; re->first = 0; re->bol = 0; /* copy the string into it, translating ^ and $ as needed */ for (src = exp, dest = re->program + 1; *src; src++) { switch (*src) { case '^': if (src == exp) { re->bol += 1; } else { *dest++ = '^'; re->minlen++; } break; case '$': if (!src[1]) { re->bol += 2; } else { *dest++ = '$'; re->minlen++; } break; case '\\': if (src[1]) { *dest++ = *++src; re->minlen++; } else { regerror("extra \\ at end of regular expression"); } break; default: *dest++ = *src; re->minlen++; } } *dest = '\0'; return re; } /* This "helper" function checks for a match at a given location. It returns * 1 if it matches, 0 if it doesn't match here but might match later on in the * string, or -1 if it could not possibly match */ static int reghelp(prog, string, bolflag) struct regexp *prog; char *string; int bolflag; { char *scan; char *str; /* if ^, then require bolflag */ if ((prog->bol & 1) && !bolflag) { return -1; } /* if it matches, then it will start here */ prog->startp[0] = string; /* compare, possibly ignoring case */ if (*o_ignorecase) { for (scan = &prog->program[1]; *scan; scan++, string++) if (tolower(*scan) != tolower(*string)) return *string ? 0 : -1; } else { for (scan = &prog->program[1]; *scan; scan++, string++) if (*scan != *string) return *string ? 0 : -1; } /* if $, then require string to end here, too */ if ((prog->bol & 2) && *string) { return 0; } /* if we get to here, it matches */ prog->endp[0] = string; return 1; } int regexec(prog, string, bolflag) struct regexp *prog; char *string; int bolflag; { int rc; /* keep trying to match it */ for (rc = reghelp(prog, string, bolflag); rc == 0; rc = reghelp(prog, string, 0)) { string++; } /* did we match? */ return rc == 1; } #endif SHAR_EOF fi # end of overwriting check if test -f 'regsub.c' then echo shar: will not over-write existing file "'regsub.c'" else cat << \SHAR_EOF > 'regsub.c' /* regsub.c */ /* This file contains the regsub() function, which performs substitutions * after a regexp match has been found. */ #include #include "config.h" #include "vi.h" #include "regexp.h" static char *previous; /* a copy of the text from the previous substitution */ /* perform substitutions after a regexp match */ void regsub(re, src, dst) regexp *re; REG char *src; REG char *dst; { REG char *cpy; REG char *end; REG char c; char *start; #ifndef CRUNCH int mod; mod = 0; #endif start = src; while ((c = *src++) != '\0') { #ifndef NO_MAGIC /* recognize any meta characters */ if (c == '&' && *o_magic) { cpy = re->startp[0]; end = re->endp[0]; } else if (c == '~' && *o_magic) { cpy = previous; if (cpy) end = cpy + strlen(cpy); } else #endif /* not NO_MAGIC */ if (c == '\\') { c = *src++; switch (c) { #ifndef NO_MAGIC case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': /* \0 thru \9 mean "copy subexpression" */ c -= '0'; cpy = re->startp[c]; end = re->endp[c]; break; # ifndef CRUNCH case 'U': case 'u': case 'L': case 'l': /* \U and \L mean "convert to upper/lowercase" */ mod = c; continue; case 'E': case 'e': /* \E ends the \U or \L */ mod = 0; continue; # endif /* not CRUNCH */ case '&': /* "\&" means "original text" */ if (*o_magic) { *dst++ = c; continue; } cpy = re->startp[0]; end = re->endp[0]; break; case '~': /* "\~" means "previous text, if any" */ if (*o_magic) { *dst++ = c; continue; } cpy = previous; if (cpy) end = cpy + strlen(cpy); break; #else /* NO_MAGIC */ case '&': /* "\&" means "original text" */ cpy = re->startp[0]; end = re->endp[0]; break; case '~': /* "\~" means "previous text, if any" */ cpy = previous; if (cpy) end = cpy + strlen(cpy); break; #endif /* NO_MAGIC */ default: /* ordinary char preceded by backslash */ *dst++ = c; continue; } } else { /* ordinary character, so just copy it */ *dst++ = c; continue; } /* Note: to reach this point in the code, we must have evaded * all "continue" statements. To do that, we must have hit * a metacharacter that involves copying. */ /* if there is nothing to copy, loop */ if (!cpy) continue; /* copy over a portion of the original */ while (cpy < end) { #ifndef NO_MAGIC # ifndef CRUNCH switch (mod) { case 'U': case 'u': /* convert to uppercase */ if (isascii(*cpy) && islower(*cpy)) { *dst++ = toupper(*cpy); cpy++; } else { *dst++ = *cpy++; } break; case 'L': case 'l': /* convert to lowercase */ if (isascii(*cpy) && isupper(*cpy)) { *dst++ = tolower(*cpy); cpy++; } else { *dst++ = *cpy++; } break; default: /* copy without any conversion */ *dst++ = *cpy++; } /* \u and \l end automatically after the first char */ if (mod && (mod == 'u' || mod == 'l')) { mod = 0; } # else /* CRUNCH */ *dst++ = *cpy++; # endif /* CRUNCH */ #else /* NO_MAGIC */ *dst++ = *cpy++; #endif /* NO_MAGIC */ } } *dst = '\0'; /* remember what text we inserted this time */ if (previous) free(previous); previous = (char *)malloc((unsigned)(strlen(start) + 1)); if (previous) strcpy(previous, start); } SHAR_EOF fi # end of overwriting check # End of shell archive exit 0