Path: utzoo!utgpu!cs.utexas.edu!uunet!zephyr.ens.tek.com!tektronix!nosun!qiclab!pdxgate!eecs.cs.pdx.edu!kirkenda From: kirkenda@eecs.cs.pdx.edu (Steve Kirkendall) Newsgroups: alt.sources Subject: Elvis 1.4, part 4 of 8 Message-ID: <828@pdxgate.UUCP> Date: 3 Dec 90 21:31:07 GMT Sender: news@pdxgate.UUCP Reply-To: kirkenda@eecs.cs.pdx.edu (Steve Kirkendall) Organization: Portland State University, Portland, OR Lines: 3791 # --------------------------- cut here ---------------------------- # This is a shar archive. To unpack it, save it to a file, and delete # anything above the "cut here" line. Then run sh on the file. # # -rw-r--r-- 1 kirkenda 9230 Dec 2 17:57 blk.c # -rw-r--r-- 1 kirkenda 23975 Dec 2 17:57 cmd1.c # -rw-r--r-- 1 kirkenda 16797 Dec 2 17:57 cmd2.c # -rw-r--r-- 1 kirkenda 7951 Dec 2 17:57 config.h # -rw-r--r-- 1 kirkenda 14196 Dec 2 17:57 curses.c # if test -f blk.c -a "$1" != -f then echo Will not overwrite blk.c else echo Extracting blk.c sed 's/^X//' >blk.c <<\eof X/* blk.c */ X X/* Author: X * Steve Kirkendall X * 14407 SW Teal Blvd. #C X * Beaverton, OR 97005 X * kirkenda@cs.pdx.edu X */ X X X/* This file contains the functions that get/put blocks from the temp file. X * It also contains the "do" and "undo" functions. X */ X X#include "config.h" X#include "vi.h" X X#ifndef NBUFS X# define NBUFS 5 /* must be at least 3 -- more is better */ X#endif X Xextern long lseek(); X X/*------------------------------------------------------------------------*/ X XBLK hdr; /* buffer for the header block */ X Xstatic int b4cnt; /* used to count context of beforedo/afterdo */ Xstatic struct _blkbuf X{ X BLK buf; /* contents of a text block */ X unsigned short logical; /* logical block number */ X int dirty; /* must the buffer be rewritten? */ X} X blk[NBUFS], /* buffers for text[?] blocks */ X *toonew, /* buffer which shouldn't be recycled yet */ X *newtoo, /* another buffer which should be recycled */ X *recycle = blk; /* next block to be recycled */ X X X X X X/* This function wipes out all buffers */ Xvoid blkinit() X{ X int i; X X for (i = 0; i < NBUFS; i++) X { X blk[i].logical = 0; X blk[i].dirty = FALSE; X } X for (i = 0; i < MAXBLKS; i++) X { X hdr.n[i] = 0; X } X} X X/* This function allocates a buffer and fills it with a given block's text */ XBLK *blkget(logical) X int logical; /* logical block number to fetch */ X{ X REG struct _blkbuf *this; /* used to step through blk[] */ X REG int i; X X /* if logical is 0, just return the hdr buffer */ X if (logical == 0) X { X return &hdr; X } X X /* see if we have that block in mem already */ X for (this = blk; this < &blk[NBUFS]; this++) X { X if (this->logical == logical) X { X newtoo = toonew; X toonew = this; X return &this->buf; X } X } X X /* choose a block to be recycled */ X do X { X this = recycle++; X if (recycle == &blk[NBUFS]) X { X recycle = blk; X } X } while (this == toonew || this == newtoo); X X /* if it contains a block, flush that block */ X blkflush(this); X X /* fill this buffer with the desired block */ X this->logical = logical; X if (hdr.n[logical]) X { X /* it has been used before - fill it from tmp file */ X lseek(tmpfd, (long)hdr.n[logical] * (long)BLKSIZE, 0); X if (read(tmpfd, this->buf.c, (unsigned)BLKSIZE) != BLKSIZE) X { X msg("Error reading back from tmp file!"); X } X } X else X { X /* it is new - zero it */ X for (i = 0; i < BLKSIZE; i++) X { X this->buf.c[i] = 0; X } X } X X /* This isn't really a change, but it does potentially invalidate X * the kinds of shortcuts that the "changes" variable is supposed X * to protect us from... so count it as a change. X */ X changes++; X X /* mark it as being "not dirty" */ X this->dirty = 0; X X /* return it */ X newtoo = toonew; X toonew = this; X return &this->buf; X} X X X X/* This function writes a block out to the temporary file */ Xvoid blkflush(this) X REG struct _blkbuf *this; /* the buffer to flush */ X{ X long seekpos; /* seek position of the new block */ X unsigned short physical; /* physical block number */ X X /* if its empty (an orphan blkadd() maybe?) then make it dirty */ X if (this->logical && !*this->buf.c) X { X blkdirty(&this->buf); X } X X /* if it's an empty buffer or a clean version is on disk, quit */ X if (!this->logical || hdr.n[this->logical] && !this->dirty) X { X return; X } X X /* find a free place in the file */ X#ifndef NO_RECYCLE X seekpos = allocate(); X lseek(tmpfd, seekpos, 0); X#else X seekpos = lseek(tmpfd, 0L, 2); X#endif X physical = seekpos / BLKSIZE; X X /* put the block there */ X if (write(tmpfd, this->buf.c, (unsigned)BLKSIZE) != BLKSIZE) X { X msg("Trouble writing to tmp file"); X } X this->dirty = FALSE; X X /* update the header so it knows we put it there */ X hdr.n[this->logical] = physical; X} X X X/* This function sets a block's "dirty" flag or deletes empty blocks */ Xvoid blkdirty(bp) X BLK *bp; /* buffer returned by blkget() */ X{ X REG int i, j; X REG char *scan; X REG int k; X X /* find the buffer */ X for (i = 0; i < NBUFS && bp != &blk[i].buf; i++) X { X } X#ifdef DEBUG X if (i >= NBUFS) X { X msg("blkdirty() called with unknown buffer at 0x%lx", bp); X return; X } X if (blk[i].logical == 0) X { X msg("blkdirty called with freed buffer"); X return; X } X#endif X X /* if this block ends with line# INFINITY, then it must have been X * allocated unnecessarily during tmpstart(). Forget it. X */ X if (lnum[blk[i].logical] == INFINITY) X { X#ifdef DEBUG X if (blk[i].buf.c[0]) X { X msg("bkldirty called with non-empty extra BLK"); X } X#endif X blk[i].logical = 0; X blk[i].dirty = FALSE; X return; X } X X /* count lines in this block */ X for (j = 0, scan = bp->c; *scan && scan < bp->c + BLKSIZE; scan++) X { X if (*scan == '\n') X { X j++; X } X } X X /* adjust lnum, if necessary */ X k = blk[i].logical; X j += (lnum[k - 1] - lnum[k]); X if (j != 0) X { X nlines += j; X while (k < MAXBLKS && lnum[k] != INFINITY) X { X lnum[k++] += j; X } X } X X /* if it still has text, mark it as dirty */ X if (*bp->c) X { X blk[i].dirty = TRUE; X } X else /* empty block, so delete it */ X { X /* adjust the cache */ X k = blk[i].logical; X for (j = 0; j < NBUFS; j++) X { X if (blk[j].logical >= k) X { X blk[j].logical--; X } X } X X /* delete it from hdr.n[] and lnum[] */ X blk[i].logical = 0; X blk[i].dirty = FALSE; X while (k < MAXBLKS - 1) X { X hdr.n[k] = hdr.n[k + 1]; X lnum[k] = lnum[k + 1]; X k++; X } X hdr.n[MAXBLKS - 1] = 0; X lnum[MAXBLKS - 1] = INFINITY; X } X} X X X/* insert a new block into hdr, and adjust the cache */ XBLK *blkadd(logical) X int logical; /* where to insert the new block */ X{ X REG int i; X X /* adjust hdr and lnum[] */ X for (i = MAXBLKS - 1; i > logical; i--) X { X hdr.n[i] = hdr.n[i - 1]; X lnum[i] = lnum[i - 1]; X } X hdr.n[logical] = 0; X lnum[logical] = lnum[logical - 1]; X X /* adjust the cache */ X for (i = 0; i < NBUFS; i++) X { X if (blk[i].logical >= logical) X { X blk[i].logical++; X } X } X X /* return the new block, via blkget() */ X return blkget(logical); X} X X X/* This function forces all dirty blocks out to disk */ Xvoid blksync() X{ X int i; X X for (i = 0; i < NBUFS; i++) X { X /* blk[i].dirty = TRUE; */ X blkflush(&blk[i]); X } X if (*o_sync) X { X sync(); X } X} X X/*------------------------------------------------------------------------*/ X Xstatic MARK undocurs; /* where the cursor should go if undone */ Xstatic long oldnlines; Xstatic long oldlnum[MAXBLKS]; X X X/* This function should be called before each command that changes the text. X * It defines the state that undo() will reset the file to. X */ Xvoid beforedo(forundo) X int forundo; /* boolean: is this for an undo? */ X{ X REG int i; X REG long l; X X /* if this is a nested call to beforedo, quit! Use larger context */ X if (b4cnt++ > 0) X { X return; X } X X /* force all block buffers to disk */ X blksync(); X X#ifndef NO_RECYCLE X /* perform garbage collection on blocks from tmp file */ X garbage(); X#endif X X /* force the header out to disk */ X lseek(tmpfd, 0L, 0); X if (write(tmpfd, hdr.c, (unsigned)BLKSIZE) != BLKSIZE) X { X msg("Trouble writing header to tmp file "); X } X X /* copy or swap oldnlines <--> nlines, oldlnum <--> lnum */ X if (forundo) X { X for (i = 0; i < MAXBLKS; i++) X { X l = lnum[i]; X lnum[i] = oldlnum[i]; X oldlnum[i] = l; X } X l = nlines; X nlines = oldnlines; X oldnlines = l; X } X else X { X for (i = 0; i < MAXBLKS; i++) X { X oldlnum[i] = lnum[i]; X } X oldnlines = nlines; X } X X /* save the cursor position */ X undocurs = cursor; X X /* upon return, the calling function continues and makes changes... */ X} X X/* This function marks the end of a (nested?) change to the file */ Xvoid afterdo() X{ X if (--b4cnt) X { X /* after abortdo(), b4cnt may decribe nested beforedo/afterdo X * pairs incorrectly. If it is decremented to often, then X * keep b4cnt sane but don't do anything else. X */ X if (b4cnt < 0) X b4cnt = 0; X X return; X } X X /* make sure the cursor wasn't left stranded in deleted text */ X if (markline(cursor) > nlines) X { X cursor = MARK_LAST; X } X /* NOTE: it is still possible that markidx(cursor) is after the X * end of a line, so the Vi mode will have to take care of that X * itself */ X X /* if a significant change has been made to this file, then set the X * MODIFIED flag. X */ X if (significant) X { X setflag(file, MODIFIED); X } X} X X/* This function cuts short the current set of changes. It is called after X * a SIGINT. X */ Xvoid abortdo() X{ X /* finish the operation immediately. */ X if (b4cnt > 0) X { X b4cnt = 1; X afterdo(); X } X X /* in visual mode, the screen is probably screwed up */ X if (mode == MODE_COLON) X { X mode = MODE_VI; X } X if (mode == MODE_VI) X { X redraw(MARK_UNSET, FALSE); X } X} X X/* This function discards all changes made since the last call to beforedo() */ Xint undo() X{ X BLK oldhdr; X X /* if beforedo() has never been run, fail */ X if (!tstflag(file, MODIFIED)) X { X msg("You haven't modified this file yet."); X return FALSE; X } X X /* read the old header form the tmp file */ X lseek(tmpfd, 0L, 0); X if (read(tmpfd, oldhdr.c, (unsigned)BLKSIZE) != BLKSIZE) X { X msg("Trouble rereading the old header from tmp file"); X } X X /* "do" the changed version, so we can undo the "undo" */ X cursor = undocurs; X beforedo(TRUE); X afterdo(); X X /* wipe out the block buffers - we can't assume they're correct */ X blkinit(); X X /* use the old header -- and therefore the old text blocks */ X hdr = oldhdr; X X /* This is a change */ X changes++; X X return TRUE; X} eof if test `wc -c cmd1.c <<\eof X/* cmd1.c */ X X/* Author: X * Steve Kirkendall X * 14407 SW Teal Blvd. #C X * Beaverton, OR 97005 X * kirkenda@cs.pdx.edu X */ X X X/* This file contains some of the EX commands - mostly ones that deal with X * files, options, etc. -- anything except text. X */ X X#include "config.h" X#include X#include "vi.h" X#include "regexp.h" X X#if MSDOS X#define DATE __DATE__ X#endif X X#ifdef DEBUG X/* print the selected lines with info on the blocks */ X/*ARGSUSED*/ Xvoid cmd_debug(frommark, tomark, cmd, bang, extra) X MARK frommark; X MARK tomark; X CMD cmd; X int bang; X char *extra; X{ X REG char *scan; X REG long l; X REG int i; X int len; X X /* scan lnum[] to determine which block its in */ X l = markline(frommark); X for (i = 1; l > lnum[i]; i++) X { X } X X do X { X /* fetch text of the block containing that line */ X scan = blkget(i)->c; X X /* calculate its length */ X if (scan[BLKSIZE - 1]) X { X len = BLKSIZE; X } X else X { X len = strlen(scan); X } X X /* print block stats */ X msg("##### hdr[%d]=%d, lnum[%d-1]=%ld, lnum[%d]=%ld (%ld lines)", X i, hdr.n[i], i, lnum[i-1], i, lnum[i], lnum[i] - lnum[i - 1]); X msg("##### len=%d, buf=0x%lx, %sdirty", X len, scan, ((int *)scan)[MAXBLKS + 1] ? "" : "not "); X if (bang) X { X while (--len >= 0) X { X addch(*scan); X scan++; X } X } X exrefresh(); X X /* next block */ X i++; X } while (i < MAXBLKS && lnum[i] && lnum[i - 1] < markline(tomark)); X} X X X/* This function checks a lot of conditions to make sure they aren't screwy */ X/*ARGSUSED*/ Xvoid cmd_validate(frommark, tomark, cmd, bang, extra) X MARK frommark; X MARK tomark; X CMD cmd; X int bang; X char *extra; X{ X char *scan; X int i; X int nlcnt; /* used to count newlines */ X int len; /* counts non-NUL characters */ X X /* check lnum[0] */ X if (lnum[0] != 0L) X { X msg("lnum[0] = %ld", lnum[0]); X } X X /* check each block */ X for (i = 1; lnum[i] <= nlines; i++) X { X scan = blkget(i)->c; X if (scan[BLKSIZE - 1]) X { X msg("block %d has no NUL at the end", i); X } X else X { X for (nlcnt = len = 0; *scan; scan++, len++) X { X if (*scan == '\n') X { X nlcnt++; X } X } X if (scan[-1] != '\n') X { X msg("block %d doesn't end with '\\n' (length %d)", i, len); X } X if (bang || nlcnt != lnum[i] - lnum[i - 1]) X { X msg("block %d (line %ld?) has %d lines, but should have %ld", X i, lnum[i - 1] + 1L, nlcnt, lnum[i] - lnum[i - 1]); X } X } X exrefresh(); X } X X /* check lnum again */ X if (lnum[i] != INFINITY) X { X msg("hdr.n[%d] = %d, but lnum[%d] = %ld", X i, hdr.n[i], i, lnum[i]); X } X X msg("# = \"%s\", %% = \"%s\"", prevorig, origname); X} X#endif /* DEBUG */ X X X/*ARGSUSED*/ Xvoid cmd_mark(frommark, tomark, cmd, bang, extra) X MARK frommark; X MARK tomark; X CMD cmd; X int bang; X char *extra; X{ X /* validate the name of the mark */ X if (!extra || *extra < 'a' || *extra > 'z' || extra[1]) X { X msg("Invalid mark name"); X return; X } X X mark[*extra - 'a'] = tomark; X} X X/*ARGSUSED*/ Xvoid cmd_write(frommark, tomark, cmd, bang, extra) X MARK frommark; X MARK tomark; X CMD cmd; X int bang; X char *extra; X{ X int fd; X int append; /* boolean: write in "append" mode? */ X REG long l; X REG char *scan; X REG int i; X X /* if all lines are to be written, use tmpsave() */ X if (frommark == MARK_FIRST && tomark == MARK_LAST) X { X tmpsave(extra, bang); X return; X } X X /* see if we're going to do this in append mode or not */ X append = FALSE; X if (extra[0] == '>' && extra[1] == '>') X { X extra += 2; X append = TRUE; X } X X /* either the file must not exist, or we must have a ! or be appending */ X if (access(extra, 0) == 0 && !bang && !append) X { X msg("File already exists - Use :w! to overwrite"); X return; X } X X /* else do it line-by-line, like cmd_print() */ X if (append) X { X#ifdef O_APPEND X fd = open(extra, O_WRONLY|O_APPEND); X#else X fd = open(extra, O_WRONLY); X if (fd >= 0) X { X lseek(fd, 0L, 2); X } X#endif X } X else X { X fd = -1; /* so we know the file isn't open yet */ X } X X if (fd < 0) X { X fd = creat(extra, FILEPERMS); X if (fd < 0) X { X msg("Can't write to \"%s\"", extra); X return; X } X } X for (l = markline(frommark); l <= markline(tomark); l++) X { X /* get the next line */ X scan = fetchline(l); X i = strlen(scan); X scan[i++] = '\n'; X X /* print the line */ X twrite(fd, scan, i); X } X close(fd); X} X X X/*ARGSUSED*/ Xvoid cmd_shell(frommark, tomark, cmd, bang, extra) X MARK frommark, tomark; X CMD cmd; X int bang; X char *extra; X{ X static char prevextra[80]; X X /* special case: ":sh" means ":!sh" */ X if (cmd == CMD_SHELL) X { X extra = o_shell; X frommark = tomark = 0L; X } X X /* if extra is "!", substute previous command */ X if (*extra == '!') X { X if (!*prevextra) X { X msg("No previous shell command to substitute for '!'"); X return; X } X extra = prevextra; X } X else if (cmd == CMD_BANG && strlen(extra) < sizeof(prevextra) - 1) X { X strcpy(prevextra, extra); X } X X /* if no lines were specified, just run the command */ X suspend_curses(); X if (frommark == 0L) X { X system(extra); X } X else /* pipe lines from the file through the command */ X { X filter(frommark, tomark, extra); X } X X /* resume curses quietly for MODE_EX, but noisily otherwise */ X resume_curses(mode == MODE_EX); X} X X X/*ARGSUSED*/ Xvoid cmd_global(frommark, tomark, cmd, bang, extra) X MARK frommark, tomark; X CMD cmd; X int bang; X char *extra; /* rest of the command line */ X{ X char *cmdptr; /* the command from the command line */ X char cmdln[100]; /* copy of the command from the command line */ X char *line; /* a line from the file */ X long l; /* used as a counter to move through lines */ X long lqty; /* quantity of lines to be scanned */ X long nchanged; /* number of lines changed */ X regexp *re; /* the compiled search expression */ X X /* can't nest global commands */ X if (doingglobal) X { X msg("Can't nest global commands."); X rptlines = -1L; X return; X } X X /* ":g! ..." is the same as ":v ..." */ X if (bang) X { X cmd = CMD_VGLOBAL; X } X X /* make sure we got a search pattern */ X if (*extra != '/' && *extra != '?') X { X msg("Usage: %c /regular expression/ command", cmd == CMD_GLOBAL ? 'g' : 'v'); X return; X } X X /* parse & compile the search pattern */ X cmdptr = parseptrn(extra); X if (!extra[1]) X { X msg("Can't use empty regular expression with '%c' command", cmd == CMD_GLOBAL ? 'g' : 'v'); X return; X } X re = regcomp(extra + 1); X if (!re) X { X /* regcomp found & described an error */ X return; X } X X /* for each line in the range */ X doingglobal = TRUE; X ChangeText X { X /* NOTE: we have to go through the lines in a forward order, X * otherwise "g/re/p" would look funny. *BUT* for "g/re/d" X * to work, simply adding 1 to the line# on each loop won't X * work. The solution: count lines relative to the end of X * the file. Think about it. X */ X for (l = nlines - markline(frommark), X lqty = markline(tomark) - markline(frommark) + 1L, X nchanged = 0L; X lqty > 0 && nlines - l >= 0 && nchanged >= 0L; X l--, lqty--) X { X /* fetch the line */ X line = fetchline(nlines - l); X X /* if it contains the search pattern... */ X if ((!regexec(re, line, 1)) == (cmd != CMD_GLOBAL)) X { X /* move the cursor to that line */ X cursor = MARK_AT_LINE(nlines - l); X X /* do the ex command (without mucking up X * the original copy of the command line) X */ X strcpy(cmdln, cmdptr); X rptlines = 0L; X doexcmd(cmdln); X nchanged += rptlines; X } X } X } X doingglobal = FALSE; X X /* free the regexp */ X free(re); X X /* Reporting...*/ X rptlines = nchanged; X} X X X/*ARGSUSED*/ Xvoid cmd_file(frommark, tomark, cmd, bang, extra) X MARK frommark, tomark; X CMD cmd; X int bang; X char *extra; X{ X#ifndef CRUNCH X /* if we're given a new filename, use it as this file's name */ X if (extra && *extra) X { X strcpy(origname, extra); X } X#endif X if (cmd == CMD_FILE) X { X msg("\"%s\" %s%s %ld lines, line %ld [%ld%%]", X *origname ? origname : "[NO FILE]", X tstflag(file, MODIFIED) ? "[MODIFIED]" : "", X tstflag(file, READONLY) ? "[READONLY]" : "", X nlines, X markline(frommark), X markline(frommark) * 100 / nlines); X } X else if (markline(frommark) == markline(tomark)) X { X msg("%ld", markline(frommark)); X } X else X { X msg("range \"%ld,%ld\" contains %ld lines", X markline(frommark), X markline(tomark), X markline(tomark) - markline(frommark) + 1L); X } X} X X X/*ARGSUSED*/ Xvoid cmd_edit(frommark, tomark, cmd, bang, extra) X MARK frommark, tomark; X CMD cmd; X int bang; X char *extra; X{ X long line = 1L; /* might be set to prevline */ X X /* Editing previous file? Then start at previous line */ X if (!strcmp(extra, prevorig)) X { X line = prevline; X } X X#ifndef CRUNCH X /* if we were given an explicit starting line, then start there */ X if (*extra == '+') X { X for (extra++, line = 0L; *extra >= '0' && *extra <= '9'; extra++) X { X line *= 10L; X line += (*extra - '0'); X } X while (isascii(*extra) && isspace(*extra)) X { X extra++; X } X } X#endif /* not CRUNCH */ X X /* switch files */ X if (tmpabort(bang)) X { X tmpstart(extra); X if (line <= nlines && line >= 1L) X { X cursor = MARK_AT_LINE(line); X } X } X else X { X msg("Use edit! to abort changes, or w to save changes"); X X /* so we can say ":e!#" next time... */ X strcpy(prevorig, extra); X prevline = 1L; X } X} X X/* This code is also used for rewind -- GB */ X X/*ARGSUSED*/ Xvoid cmd_next(frommark, tomark, cmd, bang, extra) X MARK frommark, tomark; X CMD cmd; X int bang; X char *extra; X{ X int i, j; X char *scan; X char *build; X X /* if extra stuff given, use ":args" to define a new args list */ X if (cmd == CMD_NEXT && extra && *extra) X { X cmd_args(frommark, tomark, cmd, bang, extra); X } X X /* move to the next arg */ X if (cmd == CMD_NEXT) X { X i = argno + 1; X } X else if (cmd == CMD_PREVIOUS) X { X i = argno - 1; X } X else /* cmd == CMD_REWIND */ X { X i = 0; X } X if (i < 0 || i >= nargs) X { X msg("No %sfiles to edit", cmd == CMD_REWIND ? "" : "more "); X return; X } X X /* find & isolate the name of the file to edit */ X for (j = i, scan = args; j > 0; j--) X { X while(!isascii(*scan) || !isspace(*scan)) X { X scan++; X } X while (isascii(*scan) && isspace(*scan)) X { X scan++; X } X } X for (build = tmpblk.c; *scan && (!isascii(*scan) || !isspace(*scan)); ) X { X *build++ = *scan++; X } X *build = '\0'; X X /* switch to the next file */ X if (tmpabort(bang)) X { X tmpstart(tmpblk.c); X argno = i; X } X else X { X msg("Use :%s! to abort changes, or w to save changes", X cmd == CMD_NEXT ? "next" : X cmd == CMD_PREVIOUS ? "previous" : X "rewind"); X } X} X X/* also called from :wq -- always writes back in this case */ X X/*ARGSUSED*/ Xvoid cmd_xit(frommark, tomark, cmd, bang, extra) X MARK frommark, tomark; X CMD cmd; X int bang; X char *extra; X{ X static long whenwarned; /* when the user was last warned of extra files */ X int oldflag; X X /* if there are more files to edit, then warn user */ X if (argno + 1 < nargs && whenwarned != changes && (!bang || cmd != CMD_QUIT)) X { X msg("More files to edit -- Use \":n\" to go to next file"); X whenwarned = changes; X return; X } X X if (cmd == CMD_QUIT) X { X if (tmpabort(bang)) X { X mode = MODE_QUIT; X } X else X { X msg("Use q! to abort changes, or wq to save changes"); X } X } X else X { X /* else try to save this file */ X oldflag = tstflag(file, MODIFIED); X if (cmd == CMD_WQUIT) X setflag(file, MODIFIED); X if (tmpend(bang)) X { X mode = MODE_QUIT; X } X else X { X msg("Could not save file -- use quit! to abort changes, or w filename"); X } X if (!oldflag) X clrflag(file, MODIFIED); X } X} X X X/*ARGSUSED*/ Xvoid cmd_args(frommark, tomark, cmd, bang, extra) X MARK frommark, tomark; X CMD cmd; X int bang; X char *extra; X{ X char *scan; X char *eow; X int col; X int arg; X int addcols; X int scrolled = 0; X X /* if no extra names given, or just current name, then report the args X * we have now. X */ X if (!extra || !*extra) X { X for (scan = args, col=arg=0; *scan; ) X { X while (*scan && isascii(*scan) && isspace(*scan)) X scan++; X eow = scan; X while (*eow && (!isascii(*++eow) || !isspace(*eow))) X ; X if (arg == argno) X addcols = 2; X else X addcols = 0; X if (col+addcols+(int)(eow-scan)+1>=COLS) X { X addch('\n'); X scrolled=1; X col=0; X } X else if (arg) X { qaddch(' '); X col++; X } X if (arg == argno) X qaddch('['); X while (scan < eow) X { qaddch(*scan++); X col++; X } X if (arg == argno) X qaddch(']'); X arg++; X col+=addcols; X } X /* write a trailing newline */ X if ((mode == MODE_EX || mode == MODE_COLON || scrolled) && col) X addch('\n'); X exrefresh(); X } X else /* new args list given */ X { X strcpy(args, extra); X argno = -1; /* before the first, so :next will go to first */ X X /* count the names */ X for (nargs = 0, scan = args; *scan; nargs++) X { X while (*scan && (!isascii(*scan) || !isspace(*scan))) X { X scan++; X } X while (isascii(*scan) && isspace(*scan)) X { X scan++; X } X } X msg("%d files to edit", nargs); X } X} X X X/*ARGSUSED*/ Xvoid cmd_cd(frommark, tomark, cmd, bang, extra) X MARK frommark, tomark; X CMD cmd; X int bang; X char *extra; X{ X char *getenv(); X X /* default directory name is $HOME */ X if (!*extra) X { X extra = getenv("HOME"); X if (!extra) X { X msg("environment variable $HOME not set"); X return; X } X } X X /* go to the directory */ X if (chdir(extra) < 0) X { X perror(extra); X } X} X X X/*ARGSUSED*/ Xvoid cmd_map(frommark, tomark, cmd, bang, extra) X MARK frommark, tomark; X CMD cmd; X int bang; X char *extra; X{ X char *mapto; X X /* "map" with no extra will dump the map table contents */ X if (!*extra) X { X dumpkey(bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD); X } X else X { X /* "extra" is key to map, followed my what it maps to */ X for (mapto = extra; *mapto && *mapto != ' ' && *mapto!= '\t'; mapto++) X { X } X while (*mapto == ' ' || *mapto == '\t') X { X *mapto++ = '\0'; X } X X mapkey(extra, mapto, bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD, (char *)0); X } X} X X X/*ARGSUSED*/ Xvoid cmd_set(frommark, tomark, cmd, bang, extra) X MARK frommark, tomark; X CMD cmd; X int bang; X char *extra; X{ X if (!*extra) X { X dumpopts(FALSE);/* "FALSE" means "don't dump all" - only set */ X } X else if (!strcmp(extra, "all")) X { X dumpopts(TRUE); /* "TRUE" means "dump all" - even unset vars */ X } X else X { X setopts(extra); X X /* That option may have affected the appearence of text */ X changes++; X } X} X X/*ARGSUSED*/ Xvoid cmd_tag(frommark, tomark, cmd, bang, extra) X MARK frommark, tomark; X CMD cmd; X int bang; X char *extra; X{ X char *scan; /* used to scan through the tmpblk.c */ X char *cmp; /* char of tag name we're comparing, or NULL */ X char *end; /* marks the end of chars in tmpblk.c */ X int fd; /* file descriptor used to read the file */ X#ifndef NO_MAGIC X char wasmagic; /* preserves the original state of o_magic */ X#endif X static char prevtag[30]; X X /* if no tag is given, use the previous tag */ X if (!extra || !*extra) X { X if (!*prevtag) X { X msg("No previous tag"); X return; X } X extra = prevtag; X } X else X { X strncpy(prevtag, extra, sizeof prevtag); X } X X /* open the tags file */ X fd = open(TAGS, O_RDONLY); X if (fd < 0) X { X msg("No tags file"); X return; X } X X /* Hmmm... this would have been a lot easier with */ X X /* find the line with our tag in it */ X for(scan = end = tmpblk.c, cmp = extra; ; scan++) X { X /* read a block, if necessary */ X if (scan >= end) X { X end = tmpblk.c + tread(fd, tmpblk.c, BLKSIZE); X scan = tmpblk.c; X if (scan >= end) X { X msg("tag \"%s\" not found", extra); X close(fd); X return; X } X } X X /* if we're comparing, compare... */ X if (cmp) X { X /* matched??? wow! */ X if (!*cmp && *scan == '\t') X { X break; X } X if (*cmp++ != *scan) X { X /* failed! skip to newline */ X cmp = (char *)0; X } X } X X /* if we're skipping to newline, do it fast! */ X if (!cmp) X { X while (scan < end && *scan != '\n') X { X scan++; X } X if (scan < end) X { X cmp = extra; X } X } X } X X /* found it! get the rest of the line into memory */ X for (cmp = tmpblk.c, scan++; scan < end && *scan != '\n'; ) X { X *cmp++ = *scan++; X } X if (scan == end) X { X tread(fd, cmp, BLKSIZE - (cmp - tmpblk.c)); X } X X /* we can close the tags file now */ X close(fd); X X /* extract the filename from the line, and edit the file */ X for (cmp = tmpblk.c; *cmp != '\t'; cmp++) X { X } X *cmp++ = '\0'; X if (strcmp(origname, tmpblk.c) != 0) X { X if (!tmpabort(bang)) X { X msg("Use :tag! to abort changes, or :w to save changes"); X return; X } X tmpstart(tmpblk.c); X } X X /* move to the desired line (or to line 1 if that fails) */ X#ifndef NO_MAGIC X wasmagic = *o_magic; X *o_magic = FALSE; X#endif X cursor = MARK_FIRST; X linespec(cmp, &cursor); X if (cursor == MARK_UNSET) X { X cursor = MARK_FIRST; X } X#ifndef NO_MAGIC X *o_magic = wasmagic; X#endif X} X X X X/*ARGSUSED*/ Xvoid cmd_visual(frommark, tomark, cmd, bang, extra) X MARK frommark, tomark; X CMD cmd; X int bang; X char *extra; X{ X mode = MODE_VI; X msg(""); X} X X X X X X/* describe this version of the program */ X/*ARGSUSED*/ Xvoid cmd_version(frommark, tomark, cmd, bang, extra) X MARK frommark; X MARK tomark; X CMD cmd; X int bang; X char *extra; X{ X#ifndef DATE X msg("%s", VERSION); X#else X msg("%s (%s)", VERSION, DATE); X#endif X#ifdef COMPILED_BY X msg("Compiled by %s", COMPILED_BY); X#endif X#ifdef CREDIT X msg("%s", CREDIT); X#endif X#ifdef COPYING X msg("%s", COPYING); X#endif X} X X X#ifndef NO_MKEXRC X/* make a .exrc file which describes the current configuration */ X/*ARGSUSED*/ Xvoid cmd_mkexrc(frommark, tomark, cmd, bang, extra) X MARK frommark; X MARK tomark; X CMD cmd; X int bang; X char *extra; X{ X int fd; X X /* the default name for the .exrc file EXRC */ X if (!*extra) X { X extra = EXRC; X } X X /* create the .exrc file */ X fd = creat(extra, FILEPERMS); X if (fd < 0) X { X msg("Couldn't create a new \"%s\" file", extra); X return; X } X X /* save stuff */ X savekeys(fd); X saveopts(fd); X#ifndef NO_DIGRAPH X savedigs(fd); X#endif X#ifndef NO_ABBR X saveabbr(fd); X#endif X X /* close the file */ X close(fd); X msg("Created a new \"%s\" file", extra); X} X#endif X X#ifndef NO_DIGRAPH X/*ARGSUSED*/ Xvoid cmd_digraph(frommark, tomark, cmd, bang, extra) X MARK frommark; X MARK tomark; X CMD cmd; X int bang; X char *extra; X{ X do_digraph(bang, extra); X} X#endif X X X#ifndef NO_ERRLIST Xstatic char errfile[256]; /* the name of a file containing an error */ Xstatic long errline; /* the line number for an error */ X X/* This static function tries to parse an error message. X * X * For most compilers, the first word is taken to be the name of the erroneous X * file, and the first number after that is taken to be the line number where X * the error was detected. The description of the error follows, possibly X * preceded by an "error ... :" or "warning ... :" label which is skipped. X * X * For Coherent, error messages look like "line#: filename: message". X * X * For non-error lines, or unparsable error lines, this function returns NULL. X * Normally, though, it alters errfile and errline, and returns a pointer to X * the description. X */ Xstatic char *parse_errmsg(text) X REG char *text; X{ X REG char *cpy; X long atol(); X# if COHERENT || TOS /* any Mark Williams compiler */ X /* Get the line number. If no line number, then ignore this line. */ X errline = atol(text); X if (errline == 0L) X return (char *)0; X X /* Skip to the start of the filename */ X while (*text && *text++ != ':') X { X } X if (!*text++) X return (char *)0; X X /* copy the filename to errfile */ X for (cpy = errfile; *text && (*cpy++ = *text++) != ':'; ) X { X } X if (!*text++) X return (char *)0; X cpy[-1] = '\0'; X X return text; X# else /* not a Mark Williams compiler */ X char *errmsg; X X /* the error message is the whole line, by default */ X errmsg = text; X X /* skip leading garbage */ X while (*text && !(isascii(*text) && isalnum(*text))) X { X text++; X } X X /* copy over the filename */ X cpy = errfile; X while(isascii(*text) && isalnum(*text) || *text == '.') X { X *cpy++ = *text++; X } X *cpy = '\0'; X X /* ignore the name "Error" and filenames that contain a '/' */ X if (*text == '/' || !strcmp(errfile + 1, "rror") || access(errfile, 0) < 0) X { X return (char *)0; X } X X /* skip garbage between filename and line number */ X while (*text && !(isascii(*text) && isdigit(*text))) X { X text++; X } X X /* if the number is part of a larger word, then ignore this line */ X if (*text && isascii(text[-1]) && isalpha(text[-1])) X { X return (char *)0; X } X X /* get the error line */ X errline = 0L; X while (isascii(*text) && isdigit(*text)) X { X errline *= 10; X errline += (*text - '0'); X text++; X } X X /* any line which lacks a filename or line number should be ignored */ X if (!errfile[0] || !errline) X { X return (char *)0; X } X X /* locate the beginning of the error description */ X while (*text && isascii(*text) && !isspace(*text)) X { X text++; X } X while (*text) X { X# ifndef CRUNCH X /* skip "error #:" and "warning #:" clauses */ X if (!strncmp(text + 1, "rror ", 5) X || !strncmp(text + 1, "arning ", 7) X || !strncmp(text + 1, "atal error", 10)) X { X do X { X text++; X } while (*text && *text != ':'); X continue; X } X# endif X X /* anything other than whitespace or a colon is important */ X if (!isascii(*text) || (!isspace(*text) && *text != ':')) X { X errmsg = text; X break; X } X X /* else keep looking... */ X text++; X } X X return errmsg; X# endif /* not COHERENT */ X} X X/*ARGSUSED*/ Xvoid cmd_errlist(frommark, tomark, cmd, bang, extra) X MARK frommark, tomark; X CMD cmd; X int bang; X char *extra; X{ X static long endline;/* original number of lines in this file */ X static long offset; /* offset of the next line in the errlist file */ X static int fd = -2;/* fd of the errlist file */ X int i; X char *errmsg; X X /* if a new errlist file is named, open it */ X if (extra && extra[0]) X { X /* close the old one */ X if (fd >= 0) X { X close(fd); X } X X fd = open(extra, O_RDONLY); X offset = 0L; X } X else if (fd < 0) X { X fd = open(ERRLIST, O_RDONLY); X offset = 0L; X } X X /* do we have an errlist file now? */ X if (fd < 0) X { X msg("There is no errlist file"); X beep(); X return; X } X X /* find the next error message in the file */ X do X { X /* read the next line from the errlist */ X lseek(fd, offset, 0); X if (tread(fd, tmpblk.c, (unsigned)BLKSIZE) <= 0) X { X msg("No more errors"); X beep(); X close(fd); X return; X } X for (i = 0; tmpblk.c[i] != '\n'; i++) X { X } X tmpblk.c[i++] = 0; X X /* look for an error message in the line */ X errmsg = parse_errmsg(tmpblk.c); X if (!errmsg) X { X offset += i; X } X X } while (!errmsg); X X /* switch to the file containing the error, if this isn't it */ X if (strcmp(origname, errfile)) X { X if (!tmpabort(bang)) X { X msg("Use :er! to abort changes, or :w to save changes"); X beep(); X return; X } X tmpstart(errfile); X endline = nlines; X } X else if (endline == 0L) X { X endline = nlines; X } X X /* go to the line where the error was detected */ X cursor = MARK_AT_LINE(errline + (nlines - endline)); X if (cursor > MARK_LAST) X { X cursor = MARK_LAST; X } X if (mode == MODE_VI) X { X redraw(cursor, FALSE); X } X X /* display the error message */ X if (nlines > endline) X { X msg("line %ld(+%ld): %.60s", errline, nlines - endline, errmsg); X } X else if (nlines < endline) X { X msg("line %ld(-%ld): %.60s", errline, endline - nlines, errmsg); X } X else X { X msg("line %ld: %.65s", errline, errmsg); X } X X /* remember where the NEXT error line will start */ X offset += i; X} X X X/*ARGSUSED*/ Xvoid cmd_make(frommark, tomark, cmd, bang, extra) X MARK frommark, tomark; X CMD cmd; X int bang; X char *extra; X{ X BLK buf; X X /* if the file hasn't been saved, then complain unless ! */ X if (tstflag(file, MODIFIED) && !bang) X { X msg("\"%s\" not saved yet", origname); X return; X } X X /* build the command */ X sprintf(buf.c, "%s %s %s%s", (cmd == CMD_CC ? o_cc : o_make), extra, REDIRECT, ERRLIST); X qaddstr(buf.c); X addch('\n'); X X /* run the command, with curses temporarily disabled */ X suspend_curses(); X system(buf.c); X resume_curses(mode == MODE_EX); X if (mode == MODE_COLON) X mode = MODE_VI; X X /* run the "errlist" command */ X cmd_errlist(MARK_UNSET, MARK_UNSET, cmd, bang, ERRLIST); X} X#endif X X X#ifndef NO_ABBR X/*ARGSUSED*/ Xvoid cmd_abbr(frommark, tomark, cmd, bang, extra) X MARK frommark, tomark; X CMD cmd; X int bang; X char *extra; X{ X do_abbr(extra); X} X#endif eof if test `wc -c cmd2.c <<\eof X/* cmd2.c */ X X/* Author: X * Steve Kirkendall X * 14407 SW Teal Blvd. #C X * Beaverton, OR 97005 X * kirkenda@cs.pdx.edu X */ X X X/* This file contains some of the commands - mostly ones that change text */ X X#include X#include "config.h" X#include "vi.h" X#include "regexp.h" X#if TOS X# include X#else X# if OSK X# include "osk.h" X# else X# include X# endif X#endif X X X/*ARGSUSED*/ Xvoid cmd_substitute(frommark, tomark, cmd, bang, extra) X MARK frommark; X MARK tomark; X CMD cmd; X int bang; X char *extra; /* rest of the command line */ X{ X char *line; /* a line from the file */ X regexp *re; /* the compiled search expression */ X char *subst; /* the substitution string */ X char *opt; /* substitution options */ X long l; /* a line number */ X char *s, *d; /* used during subtitutions */ X char *conf; /* used during confirmation */ X long chline; /* # of lines changed */ X long chsub; /* # of substitutions made */ X static optp; /* boolean option: print when done? */ X static optg; /* boolean option: substitute globally in line? */ X static optc; /* boolean option: confirm before subst? */ X X X /* for now, assume this will fail */ X rptlines = -1L; X X if (cmd == CMD_SUBAGAIN) X { X#ifndef NO_MAGIC X if (*o_magic) X subst = "~"; X else X#endif X subst = "\\~"; X re = regcomp(""); X X /* if visual "&", then turn off the "p" and "c" options */ X if (bang) X { X optp = optc = FALSE; X } X } X else X { X /* make sure we got a search pattern */ X if (*extra != '/' && *extra != '?') X { X msg("Usage: s/regular expression/new text/"); X return; X } X X /* parse & compile the search pattern */ X subst = parseptrn(extra); X re = regcomp(extra + 1); X } X X /* abort if RE error -- error message already given by regcomp() */ X if (!re) X { X return; X } X X if (cmd == CMD_SUBSTITUTE) X { X /* parse the substitution string & find the option string */ X for (opt = subst; *opt && *opt != *extra; opt++) X { X if (*opt == '\\' && opt[1]) X { X opt++; X } X } X if (*opt) X { X *opt++ = '\0'; X } X X /* analyse the option string */ X if (!*o_edcompatible) X { X optp = optg = optc = FALSE; X } X while (*opt) X { X switch (*opt++) X { X case 'p': optp = !optp; break; X case 'g': optg = !optg; break; X case 'c': optc = !optc; break; X case ' ': X case '\t': break; X default: X msg("Subst options are p, c, and g -- not %c", opt[-1]); X return; X } X } X } X X /* if "c" or "p" flag was given, and we're in visual mode, then NEWLINE */ X if ((optc || optp) && mode == MODE_VI) X { X addch('\n'); X exrefresh(); X } X X ChangeText X { X /* reset the change counters */ X chline = chsub = 0L; X X /* for each selected line */ X for (l = markline(frommark); l <= markline(tomark); l++) X { X /* fetch the line */ X line = fetchline(l); X X /* if it contains the search pattern... */ X if (regexec(re, line, TRUE)) X { X /* increment the line change counter */ X chline++; X X /* initialize the pointers */ X s = line; X d = tmpblk.c; X X /* do once or globally ... */ X do X { X#ifndef CRUNCH X /* confirm, if necessary */ X if (optc) X { X for (conf = line; conf < re->startp[0]; conf++) X addch(*conf); X standout(); X for ( ; conf < re->endp[0]; conf++) X addch(*conf); X standend(); X for (; *conf; conf++) X addch(*conf); X addch('\n'); X exrefresh(); X if (getkey(0) != 'y') X { X /* copy accross the original chars */ X while (s < re->endp[0]) X *d++ = *s++; X X /* skip to next match on this line, if any */ X continue; X } X } X#endif /* not CRUNCH */ X X /* increment the substitution change counter */ X chsub++; X X /* this may be the first line to redraw */ X redrawrange(l, l + 1L, l + 1L); X X /* copy stuff from before the match */ X while (s < re->startp[0]) X { X *d++ = *s++; X } X X /* substitute for the matched part */ X regsub(re, subst, d); X s = re->endp[0]; X d += strlen(d); X X } while (optg && regexec(re, s, FALSE)); X X /* copy stuff from after the match */ X while (*d++ = *s++) /* yes, ASSIGNMENT! */ X { X } X X /* replace the old version of the line with the new */ X d[-1] = '\n'; X d[0] = '\0'; X change(MARK_AT_LINE(l), MARK_AT_LINE(l + 1), tmpblk.c); X X /* if supposed to print it, do so */ X if (optp) X { X addstr(tmpblk.c); X exrefresh(); X } X X /* move the cursor to that line */ X cursor = MARK_AT_LINE(l); X } X } X } X X /* tweak for redrawing */ X mustredraw = TRUE; X X /* free the regexp */ X free(re); X X /* if done from within a ":g" command, then finish silently */ X if (doingglobal) X { X rptlines = chline; X rptlabel = "changed"; X return; X } X X /* Reporting */ X if (chsub == 0) X { X msg("Substitution failed"); X } X else if (chline >= *o_report) X { X msg("%ld substitutions on %ld lines", chsub, chline); X } X} X X X X X/*ARGSUSED*/ Xvoid cmd_delete(frommark, tomark, cmd, bang, extra) X MARK frommark; X MARK tomark; X CMD cmd; X int bang; X char *extra; X{ X MARK curs2; /* an altered form of the cursor */ X X /* choose your cut buffer */ X if (*extra == '"') X { X extra++; X } X if (*extra) X { X cutname(*extra); X } X X /* make sure we're talking about whole lines here */ X frommark = frommark & ~(BLKSIZE - 1); X tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE; X X /* yank the lines */ X cut(frommark, tomark); X X /* if CMD_DELETE then delete the lines */ X if (cmd != CMD_YANK) X { X curs2 = cursor; X ChangeText X { X /* delete the lines */ X delete(frommark, tomark); X } X if (curs2 > tomark) X { X cursor = curs2 - tomark + frommark; X } X else if (curs2 > frommark) X { X cursor = frommark; X } X } X} X X X/*ARGSUSED*/ Xvoid cmd_append(frommark, tomark, cmd, bang, extra) X MARK frommark; X MARK tomark; X CMD cmd; X int bang; X char *extra; X{ X long l; /* line counter */ X X ChangeText X { X /* if we're doing a change, delete the old version */ X if (cmd == CMD_CHANGE) X { X /* delete 'em */ X cmd_delete(frommark, tomark, cmd, bang, extra); X } X X /* new lines start at the frommark line, or after it */ X l = markline(frommark); X if (cmd == CMD_APPEND) X { X l++; X } X X /* get lines until no more lines, or "." line, and insert them */ X while (vgets('\0', tmpblk.c, BLKSIZE) >= 0) X { X addch('\n'); X if (!strcmp(tmpblk.c, ".")) X { X break; X } X X strcat(tmpblk.c, "\n"); X add(MARK_AT_LINE(l), tmpblk.c); X l++; X } X } X X /* on the odd chance that we're calling this from vi mode ... */ X redraw(MARK_UNSET, FALSE); X} X X X/*ARGSUSED*/ Xvoid cmd_put(frommark, tomark, cmd, bang, extra) X MARK frommark; X MARK tomark; X CMD cmd; X int bang; X char *extra; X{ X /* choose your cut buffer */ X if (*extra == '"') X { X extra++; X } X if (*extra) X { X cutname(*extra); X } X X /* paste it */ X ChangeText X { X cursor = paste(frommark, TRUE, FALSE); X } X} X X X/*ARGSUSED*/ Xvoid cmd_join(frommark, tomark, cmd, bang, extra) X MARK frommark; X MARK tomark; X CMD cmd; X int bang; X char *extra; X{ X long l; X char *scan; X int len; /* length of the new line */ X X /* if only one line is specified, assume the following one joins too */ X if (markline(frommark) == nlines) X { X msg("Nothing to join with this line"); X return; X } X if (markline(frommark) == markline(tomark)) X { X tomark += BLKSIZE; X } X X /* get the first line */ X l = markline(frommark); X strcpy(tmpblk.c, fetchline(l)); X len = strlen(tmpblk.c); X X /* build the longer line */ X while (++l <= markline(tomark)) X { X /* get the next line */ X scan = fetchline(l); X X /* remove any leading whitespace */ X while (*scan == '\t' || *scan == ' ') X { X scan++; X } X X /* see if the line will fit */ X if (strlen(scan) + len + 3 > BLKSIZE) X { X msg("Can't join -- the resulting line would be too long"); X return; X } X X /* catenate it, with a space (or two) in between */ X if (len >= 1 && X (tmpblk.c[len - 1] == '.' X || tmpblk.c[len - 1] == '?' X || tmpblk.c[len - 1] == '!')) X { X tmpblk.c[len++] = ' '; X } X tmpblk.c[len++] = ' '; X strcpy(tmpblk.c + len, scan); X len += strlen(scan); X } X tmpblk.c[len++] = '\n'; X tmpblk.c[len] = '\0'; X X /* make the change */ X ChangeText X { X frommark &= ~(BLKSIZE - 1); X tomark &= ~(BLKSIZE - 1); X tomark += BLKSIZE; X change(frommark, tomark, tmpblk.c); X } X X /* Reporting... */ X rptlines = markline(tomark) - markline(frommark) - 1L; X rptlabel = "joined"; X} X X X X/*ARGSUSED*/ Xvoid cmd_shift(frommark, tomark, cmd, bang, extra) X MARK frommark; X MARK tomark; X CMD cmd; X int bang; X char *extra; X{ X long l; /* line number counter */ X int oldidx; /* number of chars previously used for indent */ X int newidx; /* number of chars in the new indent string */ X int oldcol; /* previous indent amount */ X int newcol; /* new indent amount */ X char *text; /* pointer to the old line's text */ X X /* figure out how much of the screen we must redraw (for vi mode) */ X if (markline(frommark) != markline(tomark)) X { X mustredraw = TRUE; X redrawrange(markline(frommark), markline(tomark) + 1L, markline(tomark) + 1L); X } X X ChangeText X { X /* for each line to shift... */ X for (l = markline(frommark); l <= markline(tomark); l++) X { X /* get the line - ignore empty lines unless ! mode */ X text = fetchline(l); X if (!*text && !bang) X continue; X X /* calc oldidx and oldcol */ X for (oldidx = 0, oldcol = 0; X text[oldidx] == ' ' || text[oldidx] == '\t'; X oldidx++) X { X if (text[oldidx] == ' ') X { X oldcol += 1; X } X else X { X oldcol += *o_tabstop - (oldcol % *o_tabstop); X } X } X X /* calc newcol */ X if (cmd == CMD_SHIFTR) X { X newcol = oldcol + (*o_shiftwidth & 0xff); X } X else X { X newcol = oldcol - (*o_shiftwidth & 0xff); X if (newcol < 0) X newcol = 0; X } X X /* if no change, then skip to next line */ X if (oldcol == newcol) X continue; X X /* build a new indent string */ X newidx = 0; X while (newcol >= *o_tabstop) X { X tmpblk.c[newidx++] = '\t'; X newcol -= *o_tabstop; X } X while (newcol > 0) X { X tmpblk.c[newidx++] = ' '; X newcol--; X } X tmpblk.c[newidx] = '\0'; X X /* change the old indent string into the new */ X change(MARK_AT_LINE(l), MARK_AT_LINE(l) + oldidx, tmpblk.c); X } X } X X /* Reporting... */ X rptlines = markline(tomark) - markline(frommark) + 1L; X if (cmd == CMD_SHIFTR) X { X rptlabel = ">ed"; X } X else X { X rptlabel = " 0) X { X /* count newlines, convert NULs, etc. ... */ X for (lines = 0, scan = tmpblk.c; rc > 0; rc--, scan++) X { X /* break up long lines */ X if (*scan != '\n' && len + 2 > BLKSIZE) X { X *scan = '\n'; X addnl = TRUE; X } X X /* protect against NUL chars in file */ X if (!*scan) X { X *scan = 0x80; X hadnul = TRUE; X } X X /* starting a new line? */ X if (*scan == '\n') X { X /* reset length at newline */ X len = 0; X lines++; X } X else X { X len++; X } X } X X /* add the text */ X *scan = '\0'; X add(tomark, tmpblk.c); X tomark += MARK_AT_LINE(lines) + len - markidx(tomark); X } X X /* if partial last line, then retain that first newline */ X if (len > 0) X { X msg("Last line had no newline"); X tomark += BLKSIZE; /* <- for the rptlines calc */ X } X else /* delete that first newline */ X { X delete(tomark, (tomark | (BLKSIZE - 1L)) + 1L); X } X } X X /* close the file */ X close(fd); X X /* Reporting... */ X rptlines = markline(tomark) - markline(frommark); X rptlabel = "read"; X X if (addnl) X msg("Newlines were added to break up long lines"); X if (hadnul) X msg("NULs were converted to 0x80"); X} X X X X/*ARGSUSED*/ Xvoid cmd_undo(frommark, tomark, cmd, bang, extra) X MARK frommark; X MARK tomark; X CMD cmd; X int bang; X char *extra; X{ X undo(); X} X X X/* print the selected lines */ X/*ARGSUSED*/ Xvoid cmd_print(frommark, tomark, cmd, bang, extra) X MARK frommark; X MARK tomark; X CMD cmd; X int bang; X char *extra; X{ X REG char *scan; X REG long l; X REG int col; X X for (l = markline(frommark); l <= markline(tomark); l++) X { X /* display a line number, if CMD_NUMBER */ X if (cmd == CMD_NUMBER) X { X sprintf(tmpblk.c, "%6ld ", l); X qaddstr(tmpblk.c); X col = 8; X } X else X { X col = 0; X } X X /* get the next line & display it */ X for (scan = fetchline(l); *scan; scan++) X { X /* expand tabs to the proper width */ X if (*scan == '\t' && cmd != CMD_LIST) X { X do X { X qaddch(' '); X col++; X } while (col % *o_tabstop != 0); X } X else if (*scan >= 0 && *scan < ' ' || *scan == '\177') X { X qaddch('^'); X qaddch(*scan ^ 0x40); X col += 2; X } X else if ((*scan & 0x80) && cmd == CMD_LIST) X { X sprintf(tmpblk.c, "\\%03o", *scan); X qaddstr(tmpblk.c); X col += 4; X } X else X { X qaddch(*scan); X col++; X } X X /* wrap at the edge of the screen */ X if (!has_AM && col >= COLS) X { X addch('\n'); X col -= COLS; X } X } X if (cmd == CMD_LIST) X { X qaddch('$'); X } X addch('\n'); X exrefresh(); X } X} X X X/* move or copy selected lines */ X/*ARGSUSED*/ Xvoid cmd_move(frommark, tomark, cmd, bang, extra) X MARK frommark; X MARK tomark; X CMD cmd; X int bang; X char *extra; X{ X MARK destmark; X X /* parse the destination linespec. No defaults. Line 0 is okay */ X destmark = cursor; X if (!strcmp(extra, "0")) X { X destmark = 0L; X } X else if (linespec(extra, &destmark) == extra || !destmark) X { X msg("invalid destination address"); X return; X } X X /* flesh the marks out to encompass whole lines */ X frommark &= ~(BLKSIZE - 1); X tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE; X destmark = (destmark & ~(BLKSIZE - 1)) + BLKSIZE; X X /* make sure the destination is valid */ X if (cmd == CMD_MOVE && destmark >= frommark && destmark < tomark) X { X msg("invalid destination address"); X } X X /* Do it */ X ChangeText X { X /* save the text to a cut buffer */ X cutname('\0'); X cut(frommark, tomark); X X /* if we're not copying, delete the old text & adjust destmark */ X if (cmd != CMD_COPY) X { X delete(frommark, tomark); X if (destmark >= frommark) X { X destmark -= (tomark - frommark); X } X } X X /* add the new text */ X paste(destmark, FALSE, FALSE); X } X X /* move the cursor to the last line of the moved text */ X cursor = destmark + (tomark - frommark) - BLKSIZE; X if (cursor < MARK_FIRST || cursor >= MARK_LAST + BLKSIZE) X { X cursor = MARK_LAST; X } X X /* Reporting... */ X rptlabel = ( (cmd == CMD_COPY) ? "copied" : "moved" ); X} X X X X/* execute EX commands from a file */ X/*ARGSUSED*/ Xvoid cmd_source(frommark, tomark, cmd, bang, extra) X MARK frommark; X MARK tomark; X CMD cmd; X int bang; X char *extra; X{ X /* must have a filename */ X if (!*extra) X { X msg("\"source\" requires a filename"); X return; X } X X doexrc(extra); X} X X X#ifndef NO_AT X/*ARGSUSED*/ Xvoid cmd_at(frommark, tomark, cmd, bang, extra) X MARK frommark; X MARK tomark; X CMD cmd; X int bang; X char *extra; X{ X static nest = FALSE; X int result; X char buf[MAXRCLEN]; X X /* don't allow nested macros */ X if (nest) X { X msg("@ macros can't be nested"); X return; X } X nest = TRUE; X X /* require a buffer name */ X if (*extra == '"') X extra++; X if (!*extra || !isascii(*extra) ||!islower(*extra)) X { X msg("@ requires a cut buffer name (a-z)"); X } X X /* get the contents of the buffer */ X result = cb2str(*extra, buf, (unsigned)(sizeof buf)); X if (result <= 0) X { X msg("buffer \"%c is empty", *extra); X } X else if (result >= sizeof buf) X { X msg("buffer \"%c is too large to execute", *extra); X } X else X { X /* execute the contents of the buffer as ex commands */ X exstring(buf, result); X } X X nest = FALSE; X} X#endif eof if test `wc -c config.h <<\eof X/* X * vi configuration file X * We try to automatically configure to various compilers and operating X * systems. Extend the autoconf section as needed. X */ X X/*************************** autoconf section ************************/ X X/* standard unix V (?) */ X#ifdef M_SYSV X# define UNIXV 1 X#endif X X/* xelos system, University of Ulm */ X#ifdef xelos X# define UNIXV 1 X#endif X X/* BSD UNIX? */ X#ifdef bsd X# define BSD 1 X#endif X X/* Microsoft C: sorry, Watcom does the same thing */ X#ifdef M_I86 X# ifndef M_SYSV X# define MSDOS 1 X# define MICROSOFT 1 X# define COMPILED_BY "Microsoft C 5.10" X# endif X#endif X X/* Borlands Turbo C */ X#ifdef __TURBOC__ X# define MSDOS 1 X# define TURBOC 1 X# define COMPILED_BY "Turbo C 2.00" X#endif X X/* Tos Mark-Williams */ X#ifdef M68000 X# define TOS 1 X# define COMPILED_BY "Mark Williams C" X#endif X X/* OS9/68000 */ X#ifdef OSK X# define COMPILED_BY "Microware C V2.3 Edition 40" X#endif X X/*************************** end of autoconf section ************************/ X X/* All undefined symbols are defined to zero here, to allow for older */ X/* compilers which dont understand #if defined() or #if UNDEFINED_SYMBOL */ X X/*************************** operating systems *****************************/ X X#ifndef BSD X# define BSD 0 /* UNIX - Berkeley 4.x */ X#endif X X#ifndef UNIXV X# define UNIXV 0 /* UNIX - AT&T SYSV */ X#endif X X#ifndef UNIX7 X# define UNIX7 0 /* UNIX - version 7 */ X#endif X X#ifndef MSDOS X# define MSDOS 0 /* PC */ X#endif X X#ifndef TOS X# define TOS 0 /* Atari ST */ X#endif X X#ifndef AMIGA X# define AMIGA 0 /* Commodore Amiga */ X#endif X X#ifndef OSK X# define OSK 0 /* OS-9 / 68k */ X#endif X X#ifndef COHERENT X# define COHERENT 0 /* Coherent */ X#endif X X /* Minix has no predefines */ X#if !BSD && !UNIXV && !UNIX7 && !MSDOS && !TOS && !AMIGA && !OSK && !COHERENT X# define MINIX 1 X#else X# define MINIX 0 X#endif X X /* generic combination of Unices */ X#if UNIXV || UNIX7 || BSD || MINIX || COHERENT X# define ANY_UNIX 1 X#else X# define ANY_UNIX 0 X#endif X X/*************************** compilers **************************************/ X X#ifndef MICROSOFT X# define MICROSOFT 0 X#endif X X#ifndef TURBOC X# define TURBOC 0 X#endif X X/******************************* Credit ************************************/ X X#if MSDOS X# define CREDIT "Ported to MS-DOS by Guntram Blohm & Martin Patzel" X#endif X X#if TOS X# define CREDIT "Ported to Atari/TOS by Guntram Blohm & Martin Patzel" X#endif X X#if OSK X# define CREDIT "Ported to Microware OS9/68k by Peter Reinig" X#endif X X#if COHERENT X# define CREDIT "Ported to Coherent by Esa Ahola" X#endif X X/*************************** functions depending on OS *********************/ X X/* Only MSDOS, TOS, and OS9 need a special function for reading from the X * keyboard. All others just read from file descriptor 0. X */ X#if !MSDOS && !TOS && !OSK X# define ttyread(buf, len) read(0, buf, (unsigned)len) /* raw read */ X#endif X#if !TOS X# define ttywrite(buf, len) write(1, buf, (unsigned)(len)) /* raw write */ X#endif X X/* The strchr() function is an official standard now, so everybody has it X * except Unix version 7 (which is old) and BSD Unix (which is academic). X * Those guys use something called index() to do the same thing. X */ X#if BSD || UNIX7 || OSK X# define strchr index X#endif Xextern char *strchr(); X X/* BSD uses bcopy() instead of memcpy() */ X#if BSD X#define memcpy(dest, src, siz) bcopy(src, dest, siz) X#endif X X/* text versa binary mode for read/write */ X#if !TOS X#define tread(fd,buf,n) read(fd,buf,(unsigned)(n)) X#define twrite(fd,buf,n) write(fd,buf,(unsigned)(n)) X#endif X X/**************************** Compiler quirks *********************************/ X X/* the UNIX version 7 and (some) TOS compilers, don't allow "void" */ X#if UNIX7 || TOS X# define void int X#endif X X/* as far as I know, all compilers except version 7 support unsigned char */ X/* NEWFLASH: the Minix-ST compiler has subtle problems with unsigned char */ X#if UNIX7 || MINIX X# define UCHAR(c) ((c) & 0xff) X# define uchar char X#else X# define UCHAR(c) ((unsigned char)(c)) X# define uchar unsigned char X#endif X X/* Some compilers prefer to have malloc declared as returning a (void *) */ X#if BSD Xextern void *malloc(); X#else Xextern char *malloc(); X#endif X X/* Most compilers could benefit from using the "register" storage class */ X#if 1 X# define REG register X#endif X X/******************* Names of files and environment vars **********************/ X X#if ANY_UNIX X# ifndef TMPDIR X# if MINIX X# define TMPDIR "/usr/tmp" /* Keep elvis' temp files off RAM disk! */ X# else X# define TMPDIR "/tmp" /* directory where temp files live */ X# endif X# endif X# define TMPNAME "%s/elv%x%04x%03x" /* temp file */ X# define CUTNAME "%s/elv_%04x%03x" /* cut buffer's temp file */ X# ifndef EXRC X# define EXRC ".exrc" /* init file in current directory */ X# endif X# define SCRATCHOUT "%s/soXXXXXX" /* temp file used as input to filter */ X# ifndef EXINIT X# define EXINIT "EXINIT" X# endif X# ifndef SHELL X# define SHELL "/bin/sh" /* default shell */ X# endif X# if COHERENT X# ifndef REDIRECT X# define REDIRECT ">" /* Coherent CC writes errors to stdout */ X# endif X# endif X#endif X X#if MSDOS || TOS X/* do not change TMPNAME, CUTNAME and SCRATCH*: they MUST begin with '%s\\'! */ X# ifndef TMPDIR X# define TMPDIR "C:\\tmp" /* directory where temp files live */ X# endif X# define TMPNAME "%s\\elv%x%04x.%03x" /* temp file */ X# define CUTNAME "%s\\elv_%04x.%03x" /* cut buffer's temp file */ X# if MSDOS X# if MICROSOFT X# define CC_COMMAND "cl -c" /* C compiler */ X# else /* TURBO_C */ X# define CC_COMMAND "tc" /* C compiler */ X# endif X# endif X# define SCRATCHIN "%s\\siXXXXXX" /* DOS ONLY - output of filter program */ X# define SCRATCHOUT "%s\\soXXXXXX" /* temp file used as input to filter */ X# define SLASH '\\' X# ifndef SHELL X# if TOS X# define SHELL "shell.ttp" /* default shell */ X# else X# define SHELL "command.com" /* default shell */ X# endif X# endif X# define NEEDSYNC TRUE /* assume ":se sync" by default */ X# define REDIRECT ">" /* shell's redirection of stderr */ X# ifndef MAXMAPS X# define MAXMAPS 40 X# endif X# ifndef EXINIT X# define EXINIT "EXINIT" X# endif X#endif X X#if OSK X# ifndef TMPDIR X# define TMPDIR "/dd/tmp" /* directory where temp files live */ X# endif X# define TMPNAME "%s/elv%x%04x%03x" /* temp file */ X# define CUTNAME "%s/elv_%04x%03x" /* cut buffer's temp file */ X# ifndef CC_COMMAND X# define CC_COMMAND "cc -r" /* name of the compiler */ X# endif X# ifndef EXRC X# define EXRC ".exrc" /* init file in current directory */ X# endif X# define SCRATCHOUT "%s/soXXXXXX" /* temp file used as input to filter */ X# ifndef SHELL X# define SHELL "shell" /* default shell */ X# endif X# define FILEPERMS (S_IREAD|S_IWRITE) /* file permissions used for creat() */ X# define REDIRECT ">>-" /* shell's redirection of stderr */ X#endif X X#ifndef TAGS X# define TAGS "tags" /* tags file */ X#endif X X#ifndef TMPNAME X# define TMPNAME "%s/elv%x%04x.%03x" /* temp file */ X#endif X X#ifndef CUTNAME X# define CUTNAME "%s/elv_%04x.%03x" /* cut buffer's temp file */ X#endif X X#ifndef EXRC X# define EXRC "elvis.rc" X#endif X X#ifndef HMEXRC X# if !MSDOS && !TOS X# define HMEXRC EXRC X# endif X#endif X X#ifndef KEYWORDPRG X# define KEYWORDPRG "ref" X#endif X X#ifndef SCRATCHOUT X# define SCRATCHIN "%s/SIXXXXXX" X# define SCRATCHOUT "%s/SOXXXXXX" X#endif X X#ifndef ERRLIST X# define ERRLIST "errlist" X#endif X X#ifndef SLASH X# define SLASH '/' X#endif X X#ifndef SHELL X# define SHELL "shell" X#endif X X#ifndef REG X# define REG X#endif X X#ifndef NEEDSYNC X# define NEEDSYNC FALSE X#endif X X#ifndef FILEPERMS X# define FILEPERMS 0666 X#endif X X#ifndef CC_COMMAND X# define CC_COMMAND "cc -c" X#endif X X#ifndef MAKE_COMMAND X# define MAKE_COMMAND "make" X#endif X X#ifndef REDIRECT X# define REDIRECT "2>" X#endif X X#ifndef MAXMAPS X# define MAXMAPS 20 /* number of :map keys */ X#endif X#ifndef MAXDIGS X# define MAXDIGS 30 /* number of :digraph combos */ X#endif X#ifndef MAXABBR X# define MAXABBR 20 /* number of :abbr entries */ X#endif eof if test `wc -c curses.c <<\eof X/* curses.c */ X X/* Author: X * Steve Kirkendall X * 14407 SW Teal Blvd. #C X * Beaverton, OR 97005 X * kirkenda@cs.pdx.edu X */ X X X/* This file contains the functions & variables needed for a tiny subset of X * curses. The principle advantage of this version of curses is its X * extreme speed. Disadvantages are potentially larger code, few supported X * functions, limited compatibility with full curses, and only stdscr. X */ X X#include "config.h" X#include "vi.h" X X#if ANY_UNIX X# if UNIXV X# include X# undef TIOCWINSZ /* we can't handle it correctly yet */ X# else X# include X# endif X#endif X X#if TOS X# include X#endif X X#if OSK X# include X#endif X X#include X Xextern char *getenv(); Xstatic void starttcap(); X X/* variables, publicly available & used in the macros */ Xshort ospeed; /* speed of the tty, eg B2400 */ X#if OSK Xchar PC_; /* Pad char */ Xchar *BC; /* backspace character string */ X#else Xchar PC; /* Pad char */ X#endif XWINDOW *stdscr; /* pointer into kbuf[] */ XWINDOW kbuf[KBSIZ]; /* a very large output buffer */ Xint LINES; /* :li#: number of rows */ Xint COLS; /* :co#: number of columns */ Xint AM; /* :am: boolean: auto margins? */ Xint PT; /* :pt: boolean: physical tabs? */ Xchar *VB; /* :vb=: visible bell */ Xchar *UP; /* :up=: move cursor up */ Xchar *SO; /* :so=: standout start */ Xchar *SE; /* :se=: standout end */ Xchar *US = ""; /* :us=: underline start */ Xchar *UE = ""; /* :ue=: underline end */ Xchar *MD = ""; /* :md=: bold start */ Xchar *ME = ""; /* :me=: bold end */ Xchar *AS; /* :as=: alternate (italic) start */ Xchar *AE; /* :ae=: alternate (italic) end */ Xchar *CM; /* :cm=: cursor movement */ Xchar *CE; /* :ce=: clear to end of line */ Xchar *CD; /* :cd=: clear to end of screen */ Xchar *AL; /* :al=: add a line */ Xchar *DL; /* :dl=: delete a line */ X#if OSK Xchar *SR_; /* :sr=: scroll reverse */ X#else Xchar *SR; /* :sr=: scroll reverse */ X#endif Xchar *KS; /* :ks=: init string for cursor */ Xchar *KE; /* :ke=: restore string for cursor */ Xchar *KU; /* :ku=: key sequence sent by up arrow */ Xchar *KD; /* :kd=: key sequence sent by down arrow */ Xchar *KL; /* :kl=: key sequence sent by left arrow */ Xchar *KR; /* :kr=: key sequence sent by right arrow */ Xchar *HM; /* :HM=: key sequence sent by the key */ Xchar *EN; /* :EN=: key sequence sent by the key */ Xchar *PU; /* :PU=: key sequence sent by the key */ Xchar *PD; /* :PD=: key sequence sent by the key */ Xchar *IM; /* :im=: insert mode start */ Xchar *IC = ""; /* :ic=: insert the following character */ Xchar *EI; /* :ei=: insert mode end */ Xchar *DC; /* :dc=: delete a character */ Xchar *TI; /* :ti=: terminal init */ /* GB */ Xchar *TE; /* :te=: terminal exit */ /* GB */ X#ifndef NO_CURSORSHAPE Xchar *CQ = (char *)0;/* :cQ=: normal cursor */ Xchar *CX = (char *)1;/* :cX=: cursor used for EX command/entry */ Xchar *CV = (char *)2;/* :cV=: cursor used for VI command mode */ Xchar *CI = (char *)3;/* :cI=: cursor used for VI input mode */ Xchar *CR = (char *)4;/* :cR=: cursor used for VI replace mode */ X#endif Xchar *aend = ""; /* end an attribute -- either UE or ME */ Xchar ERASEKEY; /* backspace key taken from ioctl structure */ X X#if ANY_UNIX X# if UNIXV Xstatic struct termio oldtermio; /* original tty mode */ Xstatic struct termio newtermio; /* cbreak/noecho tty mode */ X# else Xstatic struct sgttyb oldsgttyb; /* original tty mode */ Xstatic struct sgttyb newsgttyb; /* cbreak/nl/noecho tty mode */ Xstatic int oldint; /* ^C or DEL, the "intr" character */ X# ifdef TIOCSLTC Xstatic int oldswitch; /* ^Z, the "suspend" character */ Xstatic int oldquote; /* ^V, the "quote next char" char */ X# endif X# endif X#endif X X#if OSK Xstatic struct sgbuf oldsgttyb; /* orginal tty mode */ Xstatic struct sgbuf newsgttyb; /* noecho tty mode */ X#endif X Xstatic char *capbuf; /* capability string buffer */ X X Xvoid initscr() X{ X /* make sure TERM variable is set */ X#if MSDOS X char *val; X if (! (val = getenv("TERM")) X || !strcmp(val, "pcbios")) X#else X if (!getenv("TERM")) X#endif X { X#if ANY_UNIX X write(2, "Environment variable TERM must be set\n", (unsigned)38); X exit(1); X#endif X#if OSK X writeln(2, "Environment variable TERM must be set\n", (unsigned)38); X exit(1); X#endif X#if MSDOS || TOS X getsize(0); X#endif X } X else X { X#if MSDOS X *o_pcbios=0; X#endif X /* start termcap stuff */ X starttcap(); X } X X /* create stdscr and curscr */ X stdscr = kbuf; X X /* change the terminal mode to cbreak/noecho */ X#if ANY_UNIX X# if UNIXV X ioctl(2, TCGETA, &oldtermio); X# else X ioctl(2, TIOCGETP, &oldsgttyb); X# endif X#endif X X#if OSK X _gs_opt(0, &oldsgttyb); X#endif X resume_curses(TRUE); X} X X Xvoid endwin() X{ X /* change the terminal mode back the way it was */ X suspend_curses(); X} X X Xstatic int curses_active = FALSE; X Xvoid suspend_curses() X{ X#if ANY_UNIX && !UNIXV X struct tchars tbuf; X# ifdef TIOCSLTC X struct ltchars ltbuf; X# endif X#endif X#ifndef NO_CURSORSHAPE X if (has_CQ) X { X do_CQ(); X } X#endif X if (has_TE) /* GB */ X { X do_TE(); X } X if (has_KE) X { X do_KE(); X } X refresh(); X X /* change the terminal mode back the way it was */ X#if ANY_UNIX X# if UNIXV X ioctl(2, TCSETAW, &oldtermio); X# else X ioctl(2, TIOCSETP, &oldsgttyb); X X ioctl(2, TIOCGETC, &tbuf); X tbuf.t_intrc = oldint; X ioctl(2, TIOCSETC, &tbuf); X X# ifdef TIOCSLTC X ioctl(2, TIOCGLTC, <buf); X ltbuf.t_suspc = oldswitch; X ltbuf.t_lnextc = oldquote; X ioctl(2, TIOCSLTC, <buf); X# endif X# endif X#endif X#if OSK X _ss_opt(0, &oldsgttyb); X#endif X curses_active = FALSE; X} X Xvoid resume_curses(quietly) X int quietly; X{ X if (!curses_active) X { X /* change the terminal mode to cbreak/noecho */ X#if ANY_UNIX X# if UNIXV X ospeed = (oldtermio.c_cflag & CBAUD); X ERASEKEY = oldtermio.c_cc[VERASE]; X newtermio = oldtermio; X newtermio.c_iflag &= (IXON|IXOFF|IXANY|ISTRIP|IGNBRK); X newtermio.c_oflag &= ~OPOST; X newtermio.c_lflag &= ISIG; X newtermio.c_cc[VINTR] = ctrl('C'); /* always use ^C for interrupts */ X newtermio.c_cc[VMIN] = 1; X newtermio.c_cc[VTIME] = 0; X# ifdef VSWTCH X newtermio.c_cc[VSWTCH] = 0; X# endif X ioctl(2, TCSETAW, &newtermio); X# else /* BSD or V7 or Coherent or Minix */ X struct tchars tbuf; X# ifdef TIOCSLTC X struct ltchars ltbuf; X# endif X X ospeed = oldsgttyb.sg_ospeed; X ERASEKEY = oldsgttyb.sg_erase; X newsgttyb = oldsgttyb; X newsgttyb.sg_flags |= CBREAK; X newsgttyb.sg_flags &= ~(CRMOD|ECHO|XTABS); X ioctl(2, TIOCSETP, &newsgttyb); X X ioctl(2, TIOCGETC, &tbuf); X oldint = tbuf.t_intrc; X tbuf.t_intrc = ctrl('C'); /* always use ^C for interrupts */ X ioctl(2, TIOCSETC, &tbuf); X X# ifdef TIOCSLTC X ioctl(2, TIOCGLTC, <buf); X oldswitch = ltbuf.t_suspc; X ltbuf.t_suspc = 0; /* disable ^Z for elvis */ X oldquote = ltbuf.t_lnextc; X ltbuf.t_lnextc = 0; /* disable ^V for elvis */ X ioctl(2, TIOCSLTC, <buf); X# endif X X# endif X#endif X#if OSK X newsgttyb = oldsgttyb; X newsgttyb.sg_echo = 0; X newsgttyb.sg_eofch = 0; X newsgttyb.sg_kbach = 0; X newsgttyb.sg_kbich = ctrl('C'); X _ss_opt(0, &newsgttyb); X ospeed = oldsgttyb.sg_baud; X ERASEKEY = oldsgttyb.sg_bspch; X#endif X X if (has_TI) /* GB */ X { X do_TI(); X } X if (has_KS) X { X do_KS(); X } X X curses_active = TRUE; X } X X /* If we're supposed to quit quietly, then we're done */ X if (quietly) X { X return; X } X X signal(SIGINT, SIG_IGN); X X move(LINES - 1, 0); X do_SO(); X qaddstr("[Press to continue]"); X do_SE(); X refresh(); X ttyread(kbuf, 20); /* in RAW mode, so <20 is very likely */ X if (kbuf[0] == ':') X { X mode = MODE_COLON; X addch('\n'); X refresh(); X } X else X { X mode = MODE_VI; X redraw(MARK_UNSET, FALSE); X } X exwrote = FALSE; X X#if TURBOC X signal(SIGINT, (void(*)()) trapint); X#else X signal(SIGINT, trapint); X#endif X} X Xstatic void lacking(s) X char *s; X{ X write(2, "This termcap entry lacks the :", (unsigned)30); X write(2, s, (unsigned)2); X write(2, "=: capability\n", (unsigned)14); X#if OSK X write(2, "\l", 1); X#endif X exit(1); X} X Xstatic void starttcap() X{ X char *str; X static char cbmem[800]; X#define MUSTHAVE(T,s) if (!(T = tgetstr(s, &capbuf))) lacking(s) X#define MAYHAVE(T,s) if (str = tgetstr(s, &capbuf)) T = str X#define PAIR(T,U,sT,sU) T=tgetstr(sT,&capbuf);U=tgetstr(sU,&capbuf);if (!T||!U)T=U="" X X /* allocate memory for capbuf */ X capbuf = cbmem; X X /* get the termcap entry */ X switch (tgetent(kbuf, getenv("TERM"))) X { X case -1: X write(2, "Can't read /etc/termcap\n", (unsigned)24); X#if OSK X write(2, "\l", 1); X#endif X exit(2); X X case 0: X write(2, "Unrecognized TERM type\n", (unsigned)23); X#if OSK X write(2, "\l", 1); X#endif X exit(3); X } X X /* get strings */ X MUSTHAVE(UP, "up"); X MAYHAVE(VB, "vb"); X MUSTHAVE(CM, "cm"); X PAIR(SO, SE, "so", "se"); X PAIR(TI, TE, "ti", "te"); X if (tgetnum("ug") <= 0) X { X PAIR(US, UE, "us", "ue"); X PAIR(MD, ME, "md", "me"); X X /* get italics, or have it default to underline */ X PAIR(AS, AE, "as", "ae"); X if (!*AS) X { X AS = US; X AE = UE; X } X } X MAYHAVE(AL, "al"); X MAYHAVE(DL, "dl"); X MUSTHAVE(CE, "ce"); X MAYHAVE(CD, "cd"); X#if OSK X MAYHAVE(SR_, "sr"); X#else X MAYHAVE(SR, "sr"); X#endif X PAIR(IM, EI, "im", "ei"); X MAYHAVE(IC, "ic"); X MAYHAVE(DC, "dc"); X X /* other termcap stuff */ X AM = tgetflag("am"); X PT = tgetflag("pt"); X getsize(0); X X /* Key sequences */ X PAIR(KS, KE, "ks", "ke"); X MAYHAVE(KU, "ku"); /* up */ X MAYHAVE(KD, "kd"); /* down */ X MAYHAVE(KL, "kl"); /* left */ X MAYHAVE(KR, "kr"); /* right */ X MAYHAVE(PU, "kP"); /* PgUp */ X MAYHAVE(PD, "kN"); /* PgDn */ X MAYHAVE(HM, "kh"); /* Home */ X MAYHAVE(EN, "kH"); /* End */ X#ifndef CRUNCH X if (!PU) MAYHAVE(PU, "K2"); /* "3x3 pad" names for PgUp, etc. */ X if (!PD) MAYHAVE(PD, "K5"); X if (!HM) MAYHAVE(HM, "K1"); X if (!EN) MAYHAVE(EN, "K4"); X X MAYHAVE(PU, "PU"); /* old XENIX names for PgUp, etc. */ X MAYHAVE(PD, "PD"); /* (overrides others, if used.) */ X MAYHAVE(HM, "HM"); X MAYHAVE(EN, "EN"); X#endif X X#ifndef NO_CURSORSHAPE X /* cursor shapes */ X CQ = tgetstr("cQ", &capbuf); X if (has_CQ) X { X CX = tgetstr("cX", &capbuf); X if (!CX) CX = CQ; X CV = tgetstr("cV", &capbuf); X if (!CV) CV = CQ; X CI = tgetstr("cI", &capbuf); X if (!CI) CI = CQ; X CR = tgetstr("cR", &capbuf); X if (!CR) CR = CQ; X } X# ifndef CRUNCH X else X { X PAIR(CQ, CV, "ve", "vs"); X CX = CI = CR = CQ; X } X# endif /* !CRUNCH */ X#endif /* !NO_CURSORSHAPE */ X X#undef MUSTHAVE X#undef MAYHAVE X#undef PAIR X} X X X/* This function gets the window size. It uses the TIOCGWINSZ ioctl call if X * your system has it, or tgetnum("li") and tgetnum("co") if it doesn't. X * This function is called once during initialization, and thereafter it is X * called whenever the SIGWINCH signal is sent to this process. X */ Xint getsize(signo) X int signo; X{ X int lines; X int cols; X#ifdef TIOCGWINSZ X struct winsize size; X#endif X X#ifdef SIGWINCH X /* reset the signal vector */ X signal(SIGWINCH, getsize); X#endif X X /* get the window size, one way or another. */ X lines = cols = 0; X#ifdef TIOCGWINSZ X if (ioctl(2, TIOCGWINSZ, &size) >= 0) X { X lines = size.ws_row; X cols = size.ws_col; X } X#endif X if ((lines == 0 || cols == 0) && signo == 0) X { X LINES = CHECKBIOS(v_rows(), tgetnum("li")); X COLS = CHECKBIOS(v_cols(), tgetnum("co")); X } X if (lines >= 2 && cols >= 30) X { X LINES = lines; X COLS = cols; X } X X /* Make sure we got values that we can live with */ X if (LINES < 2 || COLS < 30) X { X write(2, "Screen too small\n", (unsigned)17); X#if OSK X write(2, "\l", 1); X#endif X endwin(); X exit(2); X } X X /* !!! copy the new values into Elvis' options */ X { X extern char o_columns[], o_lines[]; X X *o_columns = COLS; X *o_lines = LINES; X } X X return 0; X} X X X/* This is a function version of addch() -- it is used by tputs() */ Xint faddch(ch) X int ch; X{ X addch(ch); X X return 0; X} X X/* These functions are equivelent to the macros of the same names... */ X Xvoid qaddstr(str) X char *str; X{ X REG char *s_, *d_; X X#if MSDOS X if (o_pcbios[0]) X { X while (*str) X qaddch(*str++); X return; X } X#endif X for (s_=(str), d_=stdscr; *d_++ = *s_++; ) X { X } X stdscr = d_ - 1; X} X Xvoid attrset(a) X int a; X{ X do_aend(); X if (a == A_BOLD) X { X do_MD(); X aend = ME; X } X else if (a == A_UNDERLINE) X { X do_US(); X aend = UE; X } X else if (a == A_ALTCHARSET) X { X do_AS(); X aend = AE; X } X else X { X aend = ""; X } X} X X Xvoid insch(ch) X int ch; X{ X if (has_IM) X do_IM(); X do_IC(); X qaddch(ch); X if (has_EI) X do_EI(); X} X X#if MSDOS X Xstatic int alarmtime; X X/* raw read - #defined to read (0, ...) on non-MSDOS. X * With MSDOS, am maximum of 1 byte is read. X * If more bytes should be read, just change the loop. X * The following code uses the IBM-PC-System-Timer, so probably wont't work X * on non-compatibles. X */ X/*ARGSUSED*/ Xttyread(buf, len) X char *buf; X int len; X{ X volatile char far *biostimer; X char oldtime; X int nticks = 0; X int pos = 0; X X biostimer = (char far *)0x0040006cl; X oldtime = *biostimer; X X while (!pos && (!alarmtime || nticks>16; X } X } X Supexec(gettime); X } X return pos; X} X Xalarm(time) X int time; X{ X alarmtime = 50 * time; /* ticks are 1/200 sec. */ X} X Xttywrite(buf, len) X char *buf; X int len; X{ X while (len--) X Bconout(2, *buf++); X} X#endif X X#if OSK Xttyread(buf, len) X char *buf; X int len; X{ X REG int i; X if ((i = _gs_rdy(0)) > 0) X return read(0, buf, i < len ? i : len); X else X return read(0, buf, 1); X} X Xalarm(time) X int time; X{ X#define TIME(secs) ((secs << 8) | 0x80000000) X static int alrmid; X X if (time) X alrmid = alm_set(SIGQUIT, TIME(time)); X else X alm_delete(alrmid); X} X#endif /* OSK */ eof if test `wc -c