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 3 of 9 Message-ID: <1991Jan11.015719.27142@wlbr.imsd.contel.com> Date: 11 Jan 91 01:57:19 GMT Sender: news@wlbr.imsd.contel.com (news) Distribution: comp Organization: Contel FSD, Westlake Village, CA Lines: 4650 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: # alias.c # atari.c # blk.c # cmd1.c # cmd2.c # ctags.c # curses.c # cut.c # This archive created: Thu Jan 10 17:21:07 1991 export PATH; PATH=/bin:$PATH if test -f 'alias.c' then echo shar: will not over-write existing file "'alias.c'" else cat << \SHAR_EOF > 'alias.c' /* alias.c */ /* Author: * Peter Reinig * Universitaet Kaiserslautern * Postfach 3049 * 7650 Kaiserslautern * W. Germany * reinig@physik.uni-kl.de */ /* This tiny program executes elvis with the flags that are appropriate * for a given command name. This program is used only on systems that * don't allow UNIX-style file links. * * The benefit of this program is: instead of having 5 copies of elvis * on your disk, you only need one copy of elvis and 4 copies of this * little program. */ #include #include "config.h" #if OSK #define ELVIS "/dd/usr/cmds/elvis" #else #define ELVIS "elvis" #endif extern char **environ; extern int errno; extern char *malloc(); main(argc, argv) int argc; char *argv[]; { int pid, i, j; int letter; char **argblk; #if OSK extern int chainc(); #endif /* allocate enough space for a copy of the argument list, plus a * terminating NULL, plus maybe an added flag. */ argblk = (char **) malloc((argc + 2) * sizeof(char *)); if (!argblk) { #if OSK _errmsg(errno, "Can't get enough memory\n"); #else perror(argv[0]); #endif exit(2); } /* find the last letter in the invocation name of this program */ i = strlen(argv[0]); #if MSDOS || TOS /* we almost certainly must bypass ".EXE" or ".TTP" from argv[0] */ if (i > 4 && argv[0][i - 4] == '.') i -= 4; #endif letter = argv[0][i - 1]; /* copy argv to argblk, possibly inserting a flag such as "-R" */ argblk[0] = ELVIS; i = j = 1; switch (letter) { case 'w': /* "view" */ case 'W': argblk[i++] = "-R"; break; #if !OSK case 'x': /* "ex" */ case 'X': argblk[i++] = "-e"; break; #endif case 't': /* "input" */ case 'T': argblk[i++] = "-i"; break; } while (j < argc) { argblk[i++] = argv[j++]; } argblk[i] = (char *)0; /* execute the real ELVIS program */ #if OSK pid = os9exec(chainc, argblk[0], argblk, environ, 0, 0, 3); fprintf(stderr, "%s: cannot execute\n", argblk[0]); #else (void)execvp(argblk[0], argblk); perror(ELVIS); #endif } SHAR_EOF fi # end of overwriting check if test -f 'atari.c' then echo shar: will not over-write existing file "'atari.c'" else cat << \SHAR_EOF > 'atari.c' /* atari.c */ /* Author: * Guntram Blohm * Buchenstrasse 19 * 7904 Erbach, West Germany * Tel. ++49-7305-6997 * sorry - no regular network connection */ /* * This file contains the 'standard' functions which are not supported * by Atari/Mark Williams, and some other TOS-only requirements. */ #include "config.h" #include "vi.h" #if TOS #include /* vi uses mode==0 only ... */ int access(file, mode) char *file; { int fd=Fopen(file, 0); if (fd<0) return -1; Fclose(fd); return 0; } char *mktemp(template) char *template; { return template; } /* read -- text mode, compress \r\n to \n * warning: might fail when maxlen==1 and at eol */ int tread(fd, buf, maxlen) int fd; char *buf; int maxlen; { int i, j, nread=read(fd, buf, (unsigned)maxlen); if (nread && buf[nread-1]=='\r') { nread--; lseek(fd, -1l, 1); } for (i=j=0; j 'blk.c' /* blk.c */ /* Author: * Steve Kirkendall * 14407 SW Teal Blvd. #C * Beaverton, OR 97005 * kirkenda@cs.pdx.edu */ /* This file contains the functions that get/put blocks from the temp file. * It also contains the "do" and "undo" functions. */ #include "config.h" #include "vi.h" #ifndef NBUFS # define NBUFS 5 /* must be at least 3 -- more is better */ #endif extern long lseek(); /*------------------------------------------------------------------------*/ BLK hdr; /* buffer for the header block */ static int b4cnt; /* used to count context of beforedo/afterdo */ static struct _blkbuf { BLK buf; /* contents of a text block */ unsigned short logical; /* logical block number */ int dirty; /* must the buffer be rewritten? */ } blk[NBUFS], /* buffers for text[?] blocks */ *toonew, /* buffer which shouldn't be recycled yet */ *newtoo, /* another buffer which should be recycled */ *recycle = blk; /* next block to be recycled */ /* This function wipes out all buffers */ void blkinit() { int i; for (i = 0; i < NBUFS; i++) { blk[i].logical = 0; blk[i].dirty = FALSE; } for (i = 0; i < MAXBLKS; i++) { hdr.n[i] = 0; } } /* This function allocates a buffer and fills it with a given block's text */ BLK *blkget(logical) int logical; /* logical block number to fetch */ { REG struct _blkbuf *this; /* used to step through blk[] */ REG int i; /* if logical is 0, just return the hdr buffer */ if (logical == 0) { return &hdr; } /* see if we have that block in mem already */ for (this = blk; this < &blk[NBUFS]; this++) { if (this->logical == logical) { newtoo = toonew; toonew = this; return &this->buf; } } /* choose a block to be recycled */ do { this = recycle++; if (recycle == &blk[NBUFS]) { recycle = blk; } } while (this == toonew || this == newtoo); /* if it contains a block, flush that block */ blkflush(this); /* fill this buffer with the desired block */ this->logical = logical; if (hdr.n[logical]) { /* it has been used before - fill it from tmp file */ lseek(tmpfd, (long)hdr.n[logical] * (long)BLKSIZE, 0); if (read(tmpfd, this->buf.c, (unsigned)BLKSIZE) != BLKSIZE) { msg("Error reading back from tmp file!"); } } else { /* it is new - zero it */ for (i = 0; i < BLKSIZE; i++) { this->buf.c[i] = 0; } } /* This isn't really a change, but it does potentially invalidate * the kinds of shortcuts that the "changes" variable is supposed * to protect us from... so count it as a change. */ changes++; /* mark it as being "not dirty" */ this->dirty = 0; /* return it */ newtoo = toonew; toonew = this; return &this->buf; } /* This function writes a block out to the temporary file */ void blkflush(this) REG struct _blkbuf *this; /* the buffer to flush */ { long seekpos; /* seek position of the new block */ unsigned short physical; /* physical block number */ /* if its empty (an orphan blkadd() maybe?) then make it dirty */ if (this->logical && !*this->buf.c) { blkdirty(&this->buf); } /* if it's an empty buffer or a clean version is on disk, quit */ if (!this->logical || hdr.n[this->logical] && !this->dirty) { return; } /* find a free place in the file */ #ifndef NO_RECYCLE seekpos = allocate(); lseek(tmpfd, seekpos, 0); #else seekpos = lseek(tmpfd, 0L, 2); #endif physical = seekpos / BLKSIZE; /* put the block there */ if (write(tmpfd, this->buf.c, (unsigned)BLKSIZE) != BLKSIZE) { msg("Trouble writing to tmp file"); } this->dirty = FALSE; /* update the header so it knows we put it there */ hdr.n[this->logical] = physical; } /* This function sets a block's "dirty" flag or deletes empty blocks */ void blkdirty(bp) BLK *bp; /* buffer returned by blkget() */ { REG int i, j; REG char *scan; REG int k; /* find the buffer */ for (i = 0; i < NBUFS && bp != &blk[i].buf; i++) { } #ifdef DEBUG if (i >= NBUFS) { msg("blkdirty() called with unknown buffer at 0x%lx", bp); return; } if (blk[i].logical == 0) { msg("blkdirty called with freed buffer"); return; } #endif /* if this block ends with line# INFINITY, then it must have been * allocated unnecessarily during tmpstart(). Forget it. */ if (lnum[blk[i].logical] == INFINITY) { #ifdef DEBUG if (blk[i].buf.c[0]) { msg("bkldirty called with non-empty extra BLK"); } #endif blk[i].logical = 0; blk[i].dirty = FALSE; return; } /* count lines in this block */ for (j = 0, scan = bp->c; *scan && scan < bp->c + BLKSIZE; scan++) { if (*scan == '\n') { j++; } } /* adjust lnum, if necessary */ k = blk[i].logical; j += (lnum[k - 1] - lnum[k]); if (j != 0) { nlines += j; while (k < MAXBLKS && lnum[k] != INFINITY) { lnum[k++] += j; } } /* if it still has text, mark it as dirty */ if (*bp->c) { blk[i].dirty = TRUE; } else /* empty block, so delete it */ { /* adjust the cache */ k = blk[i].logical; for (j = 0; j < NBUFS; j++) { if (blk[j].logical >= k) { blk[j].logical--; } } /* delete it from hdr.n[] and lnum[] */ blk[i].logical = 0; blk[i].dirty = FALSE; while (k < MAXBLKS - 1) { hdr.n[k] = hdr.n[k + 1]; lnum[k] = lnum[k + 1]; k++; } hdr.n[MAXBLKS - 1] = 0; lnum[MAXBLKS - 1] = INFINITY; } } /* insert a new block into hdr, and adjust the cache */ BLK *blkadd(logical) int logical; /* where to insert the new block */ { REG int i; /* adjust hdr and lnum[] */ for (i = MAXBLKS - 1; i > logical; i--) { hdr.n[i] = hdr.n[i - 1]; lnum[i] = lnum[i - 1]; } hdr.n[logical] = 0; lnum[logical] = lnum[logical - 1]; /* adjust the cache */ for (i = 0; i < NBUFS; i++) { if (blk[i].logical >= logical) { blk[i].logical++; } } /* return the new block, via blkget() */ return blkget(logical); } /* This function forces all dirty blocks out to disk */ void blksync() { int i; for (i = 0; i < NBUFS; i++) { /* blk[i].dirty = TRUE; */ blkflush(&blk[i]); } if (*o_sync) { sync(); } } /*------------------------------------------------------------------------*/ static MARK undocurs; /* where the cursor should go if undone */ static long oldnlines; static long oldlnum[MAXBLKS]; /* This function should be called before each command that changes the text. * It defines the state that undo() will reset the file to. */ void beforedo(forundo) int forundo; /* boolean: is this for an undo? */ { REG int i; REG long l; /* if this is a nested call to beforedo, quit! Use larger context */ if (b4cnt++ > 0) { return; } /* force all block buffers to disk */ blksync(); #ifndef NO_RECYCLE /* perform garbage collection on blocks from tmp file */ garbage(); #endif /* force the header out to disk */ lseek(tmpfd, 0L, 0); if (write(tmpfd, hdr.c, (unsigned)BLKSIZE) != BLKSIZE) { msg("Trouble writing header to tmp file "); } /* copy or swap oldnlines <--> nlines, oldlnum <--> lnum */ if (forundo) { for (i = 0; i < MAXBLKS; i++) { l = lnum[i]; lnum[i] = oldlnum[i]; oldlnum[i] = l; } l = nlines; nlines = oldnlines; oldnlines = l; } else { for (i = 0; i < MAXBLKS; i++) { oldlnum[i] = lnum[i]; } oldnlines = nlines; } /* save the cursor position */ undocurs = cursor; /* upon return, the calling function continues and makes changes... */ } /* This function marks the end of a (nested?) change to the file */ void afterdo() { if (--b4cnt) { /* after abortdo(), b4cnt may decribe nested beforedo/afterdo * pairs incorrectly. If it is decremented to often, then * keep b4cnt sane but don't do anything else. */ if (b4cnt < 0) b4cnt = 0; return; } /* make sure the cursor wasn't left stranded in deleted text */ if (markline(cursor) > nlines) { cursor = MARK_LAST; } /* NOTE: it is still possible that markidx(cursor) is after the * end of a line, so the Vi mode will have to take care of that * itself */ /* if a significant change has been made to this file, then set the * MODIFIED flag. */ if (significant) { setflag(file, MODIFIED); } } /* This function cuts short the current set of changes. It is called after * a SIGINT. */ void abortdo() { /* finish the operation immediately. */ if (b4cnt > 0) { b4cnt = 1; afterdo(); } /* in visual mode, the screen is probably screwed up */ if (mode == MODE_COLON) { mode = MODE_VI; } if (mode == MODE_VI) { redraw(MARK_UNSET, FALSE); } } /* This function discards all changes made since the last call to beforedo() */ int undo() { BLK oldhdr; /* if beforedo() has never been run, fail */ if (!tstflag(file, MODIFIED)) { msg("You haven't modified this file yet."); return FALSE; } /* read the old header form the tmp file */ lseek(tmpfd, 0L, 0); if (read(tmpfd, oldhdr.c, (unsigned)BLKSIZE) != BLKSIZE) { msg("Trouble rereading the old header from tmp file"); } /* "do" the changed version, so we can undo the "undo" */ cursor = undocurs; beforedo(TRUE); afterdo(); /* wipe out the block buffers - we can't assume they're correct */ blkinit(); /* use the old header -- and therefore the old text blocks */ hdr = oldhdr; /* This is a change */ changes++; return TRUE; } SHAR_EOF fi # end of overwriting check if test -f 'cmd1.c' then echo shar: will not over-write existing file "'cmd1.c'" else cat << \SHAR_EOF > 'cmd1.c' /* cmd1.c */ /* Author: * Steve Kirkendall * 14407 SW Teal Blvd. #C * Beaverton, OR 97005 * kirkenda@cs.pdx.edu */ /* This file contains some of the EX commands - mostly ones that deal with * files, options, etc. -- anything except text. */ #include "config.h" #include #include "vi.h" #include "regexp.h" #if MSDOS #define DATE __DATE__ #endif #ifdef DEBUG /* print the selected lines with info on the blocks */ /*ARGSUSED*/ void cmd_debug(frommark, tomark, cmd, bang, extra) MARK frommark; MARK tomark; CMD cmd; int bang; char *extra; { REG char *scan; REG long l; REG int i; int len; /* scan lnum[] to determine which block its in */ l = markline(frommark); for (i = 1; l > lnum[i]; i++) { } do { /* fetch text of the block containing that line */ scan = blkget(i)->c; /* calculate its length */ if (scan[BLKSIZE - 1]) { len = BLKSIZE; } else { len = strlen(scan); } /* print block stats */ msg("##### hdr[%d]=%d, lnum[%d-1]=%ld, lnum[%d]=%ld (%ld lines)", i, hdr.n[i], i, lnum[i-1], i, lnum[i], lnum[i] - lnum[i - 1]); msg("##### len=%d, buf=0x%lx, %sdirty", len, scan, ((int *)scan)[MAXBLKS + 1] ? "" : "not "); if (bang) { while (--len >= 0) { addch(*scan); scan++; } } exrefresh(); /* next block */ i++; } while (i < MAXBLKS && lnum[i] && lnum[i - 1] < markline(tomark)); } /* This function checks a lot of conditions to make sure they aren't screwy */ /*ARGSUSED*/ void cmd_validate(frommark, tomark, cmd, bang, extra) MARK frommark; MARK tomark; CMD cmd; int bang; char *extra; { char *scan; int i; int nlcnt; /* used to count newlines */ int len; /* counts non-NUL characters */ /* check lnum[0] */ if (lnum[0] != 0L) { msg("lnum[0] = %ld", lnum[0]); } /* check each block */ for (i = 1; lnum[i] <= nlines; i++) { scan = blkget(i)->c; if (scan[BLKSIZE - 1]) { msg("block %d has no NUL at the end", i); } else { for (nlcnt = len = 0; *scan; scan++, len++) { if (*scan == '\n') { nlcnt++; } } if (scan[-1] != '\n') { msg("block %d doesn't end with '\\n' (length %d)", i, len); } if (bang || nlcnt != lnum[i] - lnum[i - 1]) { msg("block %d (line %ld?) has %d lines, but should have %ld", i, lnum[i - 1] + 1L, nlcnt, lnum[i] - lnum[i - 1]); } } exrefresh(); } /* check lnum again */ if (lnum[i] != INFINITY) { msg("hdr.n[%d] = %d, but lnum[%d] = %ld", i, hdr.n[i], i, lnum[i]); } msg("# = \"%s\", %% = \"%s\"", prevorig, origname); } #endif /* DEBUG */ /*ARGSUSED*/ void cmd_mark(frommark, tomark, cmd, bang, extra) MARK frommark; MARK tomark; CMD cmd; int bang; char *extra; { /* validate the name of the mark */ if (!extra || *extra < 'a' || *extra > 'z' || extra[1]) { msg("Invalid mark name"); return; } mark[*extra - 'a'] = tomark; } /*ARGSUSED*/ void cmd_write(frommark, tomark, cmd, bang, extra) MARK frommark; MARK tomark; CMD cmd; int bang; char *extra; { int fd; int append; /* boolean: write in "append" mode? */ REG long l; REG char *scan; REG int i; /* if all lines are to be written, use tmpsave() */ if (frommark == MARK_FIRST && tomark == MARK_LAST) { tmpsave(extra, bang); return; } /* see if we're going to do this in append mode or not */ append = FALSE; if (extra[0] == '>' && extra[1] == '>') { extra += 2; append = TRUE; } /* either the file must not exist, or we must have a ! or be appending */ if (access(extra, 0) == 0 && !bang && !append) { msg("File already exists - Use :w! to overwrite"); return; } /* else do it line-by-line, like cmd_print() */ if (append) { #ifdef O_APPEND fd = open(extra, O_WRONLY|O_APPEND); #else fd = open(extra, O_WRONLY); if (fd >= 0) { lseek(fd, 0L, 2); } #endif } else { fd = -1; /* so we know the file isn't open yet */ } if (fd < 0) { fd = creat(extra, FILEPERMS); if (fd < 0) { msg("Can't write to \"%s\"", extra); return; } } for (l = markline(frommark); l <= markline(tomark); l++) { /* get the next line */ scan = fetchline(l); i = strlen(scan); scan[i++] = '\n'; /* print the line */ twrite(fd, scan, i); } close(fd); } /*ARGSUSED*/ void cmd_shell(frommark, tomark, cmd, bang, extra) MARK frommark, tomark; CMD cmd; int bang; char *extra; { static char prevextra[80]; /* special case: ":sh" means ":!sh" */ if (cmd == CMD_SHELL) { extra = o_shell; frommark = tomark = 0L; } /* if extra is "!", substute previous command */ if (*extra == '!') { if (!*prevextra) { msg("No previous shell command to substitute for '!'"); return; } extra = prevextra; } else if (cmd == CMD_BANG && strlen(extra) < sizeof(prevextra) - 1) { strcpy(prevextra, extra); } /* if no lines were specified, just run the command */ Suspend_curses(); if (frommark == 0L) { system(extra); } else /* pipe lines from the file through the command */ { filter(frommark, tomark, extra); } /* resume curses quietly for MODE_EX, but noisily otherwise */ Resume_curses(mode == MODE_EX); } /*ARGSUSED*/ void cmd_global(frommark, tomark, cmd, bang, extra) MARK frommark, tomark; CMD cmd; int bang; char *extra; /* rest of the command line */ { char *cmdptr; /* the command from the command line */ char cmdln[100]; /* copy of the command from the command line */ char *line; /* a line from the file */ long l; /* used as a counter to move through lines */ long lqty; /* quantity of lines to be scanned */ long nchanged; /* number of lines changed */ regexp *re; /* the compiled search expression */ /* can't nest global commands */ if (doingglobal) { msg("Can't nest global commands."); rptlines = -1L; return; } /* ":g! ..." is the same as ":v ..." */ if (bang) { cmd = CMD_VGLOBAL; } /* make sure we got a search pattern */ if (*extra != '/' && *extra != '?') { msg("Usage: %c /regular expression/ command", cmd == CMD_GLOBAL ? 'g' : 'v'); return; } /* parse & compile the search pattern */ cmdptr = parseptrn(extra); if (!extra[1]) { msg("Can't use empty regular expression with '%c' command", cmd == CMD_GLOBAL ? 'g' : 'v'); return; } re = regcomp(extra + 1); if (!re) { /* regcomp found & described an error */ return; } /* for each line in the range */ doingglobal = TRUE; ChangeText { /* NOTE: we have to go through the lines in a forward order, * otherwise "g/re/p" would look funny. *BUT* for "g/re/d" * to work, simply adding 1 to the line# on each loop won't * work. The solution: count lines relative to the end of * the file. Think about it. */ for (l = nlines - markline(frommark), lqty = markline(tomark) - markline(frommark) + 1L, nchanged = 0L; lqty > 0 && nlines - l >= 0 && nchanged >= 0L; l--, lqty--) { /* fetch the line */ line = fetchline(nlines - l); /* if it contains the search pattern... */ if ((!regexec(re, line, 1)) == (cmd != CMD_GLOBAL)) { /* move the cursor to that line */ cursor = MARK_AT_LINE(nlines - l); /* do the ex command (without mucking up * the original copy of the command line) */ strcpy(cmdln, cmdptr); rptlines = 0L; doexcmd(cmdln); nchanged += rptlines; } } } doingglobal = FALSE; /* free the regexp */ free(re); /* Reporting...*/ rptlines = nchanged; } /*ARGSUSED*/ void cmd_file(frommark, tomark, cmd, bang, extra) MARK frommark, tomark; CMD cmd; int bang; char *extra; { #ifndef CRUNCH /* if we're given a new filename, use it as this file's name */ if (extra && *extra) { strcpy(origname, extra); } #endif if (cmd == CMD_FILE) { msg("\"%s\" %s%s %ld lines, line %ld [%ld%%]", *origname ? origname : "[NO FILE]", tstflag(file, MODIFIED) ? "[MODIFIED]" : "", tstflag(file, READONLY) ? "[READONLY]" : "", nlines, markline(frommark), markline(frommark) * 100 / nlines); } else if (markline(frommark) == markline(tomark)) { msg("%ld", markline(frommark)); } else { msg("range \"%ld,%ld\" contains %ld lines", markline(frommark), markline(tomark), markline(tomark) - markline(frommark) + 1L); } } /*ARGSUSED*/ void cmd_edit(frommark, tomark, cmd, bang, extra) MARK frommark, tomark; CMD cmd; int bang; char *extra; { long line = 1L; /* might be set to prevline */ /* Editing previous file? Then start at previous line */ if (!strcmp(extra, prevorig)) { line = prevline; } #ifndef CRUNCH /* if we were given an explicit starting line, then start there */ if (*extra == '+') { for (extra++, line = 0L; *extra >= '0' && *extra <= '9'; extra++) { line *= 10L; line += (*extra - '0'); } while (isascii(*extra) && isspace(*extra)) { extra++; } } #endif /* not CRUNCH */ /* switch files */ if (tmpabort(bang)) { Tmpstart(extra); if (line <= nlines && line >= 1L) { cursor = MARK_AT_LINE(line); } } else { msg("Use edit! to abort changes, or w to save changes"); /* so we can say ":e!#" next time... */ strcpy(prevorig, extra); prevline = 1L; } } /* This code is also used for rewind -- GB */ /*ARGSUSED*/ void cmd_next(frommark, tomark, cmd, bang, extra) MARK frommark, tomark; CMD cmd; int bang; char *extra; { int i, j; char *scan; char *build; /* if extra stuff given, use ":args" to define a new args list */ if (cmd == CMD_NEXT && extra && *extra) { cmd_args(frommark, tomark, cmd, bang, extra); } /* move to the next arg */ if (cmd == CMD_NEXT) { i = argno + 1; } else if (cmd == CMD_PREVIOUS) { i = argno - 1; } else /* cmd == CMD_REWIND */ { i = 0; } if (i < 0 || i >= nargs) { msg("No %sfiles to edit", cmd == CMD_REWIND ? "" : "more "); return; } /* find & isolate the name of the file to edit */ for (j = i, scan = args; j > 0; j--) { while(!isascii(*scan) || !isspace(*scan)) { scan++; } while (isascii(*scan) && isspace(*scan)) { scan++; } } for (build = tmpblk.c; *scan && (!isascii(*scan) || !isspace(*scan)); ) { *build++ = *scan++; } *build = '\0'; /* switch to the next file */ if (tmpabort(bang)) { Tmpstart(tmpblk.c); argno = i; } else { msg("Use :%s! to abort changes, or w to save changes", cmd == CMD_NEXT ? "next" : cmd == CMD_PREVIOUS ? "previous" : "rewind"); } } /* also called from :wq -- always writes back in this case */ /*ARGSUSED*/ void cmd_xit(frommark, tomark, cmd, bang, extra) MARK frommark, tomark; CMD cmd; int bang; char *extra; { static long whenwarned; /* when the user was last warned of extra files */ int oldflag; /* if there are more files to edit, then warn user */ if (argno + 1 < nargs && whenwarned != changes && (!bang || cmd != CMD_QUIT)) { msg("More files to edit -- Use \":n\" to go to next file"); whenwarned = changes; return; } if (cmd == CMD_QUIT) { if (tmpabort(bang)) { mode = MODE_QUIT; } else { msg("Use q! to abort changes, or wq to save changes"); } } else { /* else try to save this file */ oldflag = tstflag(file, MODIFIED); if (cmd == CMD_WQUIT) setflag(file, MODIFIED); if (tmpend(bang)) { mode = MODE_QUIT; } else { msg("Could not save file -- use quit! to abort changes, or w filename"); } if (!oldflag) clrflag(file, MODIFIED); } } /*ARGSUSED*/ void cmd_args(frommark, tomark, cmd, bang, extra) MARK frommark, tomark; CMD cmd; int bang; char *extra; { char *scan; char *eow; int col; int arg; int addcols; int scrolled = 0; /* if no extra names given, or just current name, then report the args * we have now. */ if (!extra || !*extra) { for (scan = args, col=arg=0; *scan; ) { while (*scan && isascii(*scan) && isspace(*scan)) scan++; eow = scan; while (*eow && (!isascii(*++eow) || !isspace(*eow))) ; if (arg == argno) addcols = 2; else addcols = 0; if (col+addcols+(int)(eow-scan)+1>=COLS) { addch('\n'); scrolled=1; col=0; } else if (arg) { qaddch(' '); col++; } if (arg == argno) qaddch('['); while (scan < eow) { qaddch(*scan++); col++; } if (arg == argno) qaddch(']'); arg++; col+=addcols; } /* write a trailing newline */ if ((mode == MODE_EX || mode == MODE_COLON || scrolled) && col) addch('\n'); exrefresh(); } else /* new args list given */ { strcpy(args, extra); argno = -1; /* before the first, so :next will go to first */ /* count the names */ for (nargs = 0, scan = args; *scan; nargs++) { while (*scan && (!isascii(*scan) || !isspace(*scan))) { scan++; } while (isascii(*scan) && isspace(*scan)) { scan++; } } msg("%d files to edit", nargs); } } /*ARGSUSED*/ void cmd_cd(frommark, tomark, cmd, bang, extra) MARK frommark, tomark; CMD cmd; int bang; char *extra; { char *getenv(); /* default directory name is $HOME */ if (!*extra) { extra = getenv("HOME"); if (!extra) { msg("environment variable $HOME not set"); return; } } /* go to the directory */ if (chdir(extra) < 0) { perror(extra); } } /*ARGSUSED*/ void cmd_map(frommark, tomark, cmd, bang, extra) MARK frommark, tomark; CMD cmd; int bang; char *extra; { char *mapto; /* "map" with no extra will dump the map table contents */ if (!*extra) { dumpkey(bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD); } else { /* "extra" is key to map, followed my what it maps to */ for (mapto = extra; *mapto && *mapto != ' ' && *mapto!= '\t'; mapto++) { } while (*mapto == ' ' || *mapto == '\t') { *mapto++ = '\0'; } mapkey(extra, mapto, bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD, (char *)0); } } /*ARGSUSED*/ void cmd_set(frommark, tomark, cmd, bang, extra) MARK frommark, tomark; CMD cmd; int bang; char *extra; { if (!*extra) { dumpopts(FALSE);/* "FALSE" means "don't dump all" - only set */ } else if (!strcmp(extra, "all")) { dumpopts(TRUE); /* "TRUE" means "dump all" - even unset vars */ } else { setopts(extra); /* That option may have affected the appearence of text */ changes++; } } /*ARGSUSED*/ void cmd_tag(frommark, tomark, cmd, bang, extra) MARK frommark, tomark; CMD cmd; int bang; char *extra; { char *scan; /* used to scan through the tmpblk.c */ char *cmp; /* char of tag name we're comparing, or NULL */ char *end; /* marks the end of chars in tmpblk.c */ int fd; /* file descriptor used to read the file */ #ifndef NO_MAGIC char wasmagic; /* preserves the original state of o_magic */ #endif static char prevtag[30]; /* if no tag is given, use the previous tag */ if (!extra || !*extra) { if (!*prevtag) { msg("No previous tag"); return; } extra = prevtag; } else { strncpy(prevtag, extra, sizeof prevtag); } /* open the tags file */ fd = open(TAGS, O_RDONLY); if (fd < 0) { msg("No tags file"); return; } /* Hmmm... this would have been a lot easier with */ /* find the line with our tag in it */ for(scan = end = tmpblk.c, cmp = extra; ; scan++) { /* read a block, if necessary */ if (scan >= end) { end = tmpblk.c + tread(fd, tmpblk.c, BLKSIZE); scan = tmpblk.c; if (scan >= end) { msg("tag \"%s\" not found", extra); close(fd); return; } } /* if we're comparing, compare... */ if (cmp) { /* matched??? wow! */ if (!*cmp && *scan == '\t') { break; } if (*cmp++ != *scan) { /* failed! skip to newline */ cmp = (char *)0; } } /* if we're skipping to newline, do it fast! */ if (!cmp) { while (scan < end && *scan != '\n') { scan++; } if (scan < end) { cmp = extra; } } } /* found it! get the rest of the line into memory */ for (cmp = tmpblk.c, scan++; scan < end && *scan != '\n'; ) { *cmp++ = *scan++; } if (scan == end) { tread(fd, cmp, BLKSIZE - (cmp - tmpblk.c)); } /* we can close the tags file now */ close(fd); /* extract the filename from the line, and edit the file */ for (cmp = tmpblk.c; *cmp != '\t'; cmp++) { } *cmp++ = '\0'; if (strcmp(origname, tmpblk.c) != 0) { if (!tmpabort(bang)) { msg("Use :tag! to abort changes, or :w to save changes"); return; } Tmpstart(tmpblk.c); } /* move to the desired line (or to line 1 if that fails) */ #ifndef NO_MAGIC wasmagic = *o_magic; *o_magic = FALSE; #endif cursor = MARK_FIRST; linespec(cmp, &cursor); if (cursor == MARK_UNSET) { cursor = MARK_FIRST; } #ifndef NO_MAGIC *o_magic = wasmagic; #endif } /*ARGSUSED*/ void cmd_visual(frommark, tomark, cmd, bang, extra) MARK frommark, tomark; CMD cmd; int bang; char *extra; { mode = MODE_VI; msg(""); } /* describe this version of the program */ /*ARGSUSED*/ void cmd_version(frommark, tomark, cmd, bang, extra) MARK frommark; MARK tomark; CMD cmd; int bang; char *extra; { #ifndef DATE msg("%s", VERSION); #else msg("%s (%s)", VERSION, DATE); #endif #ifdef COMPILED_BY msg("Compiled by %s", COMPILED_BY); #endif #ifdef CREDIT msg("%s", CREDIT); #endif #ifdef COPYING msg("%s", COPYING); #endif } #ifndef NO_MKEXRC /* make a .exrc file which describes the current configuration */ /*ARGSUSED*/ void cmd_mkexrc(frommark, tomark, cmd, bang, extra) MARK frommark; MARK tomark; CMD cmd; int bang; char *extra; { int fd; /* the default name for the .exrc file EXRC */ if (!*extra) { extra = EXRC; } /* create the .exrc file */ fd = creat(extra, FILEPERMS); if (fd < 0) { msg("Couldn't create a new \"%s\" file", extra); return; } /* save stuff */ savekeys(fd); saveopts(fd); #ifndef NO_DIGRAPH savedigs(fd); #endif #ifndef NO_ABBR saveabbr(fd); #endif /* close the file */ close(fd); msg("Created a new \"%s\" file", extra); } #endif #ifndef NO_DIGRAPH /*ARGSUSED*/ void cmd_digraph(frommark, tomark, cmd, bang, extra) MARK frommark; MARK tomark; CMD cmd; int bang; char *extra; { do_digraph(bang, extra); } #endif #ifndef NO_ERRLIST static char errfile[256]; /* the name of a file containing an error */ static long errline; /* the line number for an error */ /* This static function tries to parse an error message. * * For most compilers, the first word is taken to be the name of the erroneous * file, and the first number after that is taken to be the line number where * the error was detected. The description of the error follows, possibly * preceded by an "error ... :" or "warning ... :" label which is skipped. * * For Coherent, error messages look like "line#: filename: message". * * For non-error lines, or unparsable error lines, this function returns NULL. * Normally, though, it alters errfile and errline, and returns a pointer to * the description. */ static char *parse_errmsg(text) REG char *text; { REG char *cpy; long atol(); # if COHERENT || TOS /* any Mark Williams compiler */ /* Get the line number. If no line number, then ignore this line. */ errline = atol(text); if (errline == 0L) return (char *)0; /* Skip to the start of the filename */ while (*text && *text++ != ':') { } if (!*text++) return (char *)0; /* copy the filename to errfile */ for (cpy = errfile; *text && (*cpy++ = *text++) != ':'; ) { } if (!*text++) return (char *)0; cpy[-1] = '\0'; return text; # else /* not a Mark Williams compiler */ char *errmsg; /* the error message is the whole line, by default */ errmsg = text; /* skip leading garbage */ while (*text && !(isascii(*text) && isalnum(*text))) { text++; } /* copy over the filename */ cpy = errfile; while(isascii(*text) && isalnum(*text) || *text == '.') { *cpy++ = *text++; } *cpy = '\0'; /* ignore the name "Error" and filenames that contain a '/' */ if (*text == '/' || !strcmp(errfile + 1, "rror") || access(errfile, 0) < 0) { return (char *)0; } /* skip garbage between filename and line number */ while (*text && !(isascii(*text) && isdigit(*text))) { text++; } /* if the number is part of a larger word, then ignore this line */ if (*text && isascii(text[-1]) && isalpha(text[-1])) { return (char *)0; } /* get the error line */ errline = 0L; while (isascii(*text) && isdigit(*text)) { errline *= 10; errline += (*text - '0'); text++; } /* any line which lacks a filename or line number should be ignored */ if (!errfile[0] || !errline) { return (char *)0; } /* locate the beginning of the error description */ while (*text && isascii(*text) && !isspace(*text)) { text++; } while (*text) { # ifndef CRUNCH /* skip "error #:" and "warning #:" clauses */ if (!strncmp(text + 1, "rror ", 5) || !strncmp(text + 1, "arning ", 7) || !strncmp(text + 1, "atal error", 10)) { do { text++; } while (*text && *text != ':'); continue; } # endif /* anything other than whitespace or a colon is important */ if (!isascii(*text) || (!isspace(*text) && *text != ':')) { errmsg = text; break; } /* else keep looking... */ text++; } return errmsg; # endif /* not COHERENT */ } /*ARGSUSED*/ void cmd_errlist(frommark, tomark, cmd, bang, extra) MARK frommark, tomark; CMD cmd; int bang; char *extra; { static long endline;/* original number of lines in this file */ static long offset; /* offset of the next line in the errlist file */ static int fd = -2;/* fd of the errlist file */ int i; char *errmsg; /* if a new errlist file is named, open it */ if (extra && extra[0]) { /* close the old one */ if (fd >= 0) { close(fd); } fd = open(extra, O_RDONLY); offset = 0L; } else if (fd < 0) { fd = open(ERRLIST, O_RDONLY); offset = 0L; } /* do we have an errlist file now? */ if (fd < 0) { msg("There is no errlist file"); beep(); return; } /* find the next error message in the file */ do { /* read the next line from the errlist */ lseek(fd, offset, 0); if (tread(fd, tmpblk.c, (unsigned)BLKSIZE) <= 0) { msg("No more errors"); beep(); close(fd); return; } for (i = 0; tmpblk.c[i] != '\n'; i++) { } tmpblk.c[i++] = 0; /* look for an error message in the line */ errmsg = parse_errmsg(tmpblk.c); if (!errmsg) { offset += i; } } while (!errmsg); /* switch to the file containing the error, if this isn't it */ if (strcmp(origname, errfile)) { if (!tmpabort(bang)) { msg("Use :er! to abort changes, or :w to save changes"); beep(); return; } Tmpstart(errfile); endline = nlines; } else if (endline == 0L) { endline = nlines; } /* go to the line where the error was detected */ cursor = MARK_AT_LINE(errline + (nlines - endline)); if (cursor > MARK_LAST) { cursor = MARK_LAST; } if (mode == MODE_VI) { redraw(cursor, FALSE); } /* display the error message */ if (nlines > endline) { msg("line %ld(+%ld): %.60s", errline, nlines - endline, errmsg); } else if (nlines < endline) { msg("line %ld(-%ld): %.60s", errline, endline - nlines, errmsg); } else { msg("line %ld: %.65s", errline, errmsg); } /* remember where the NEXT error line will start */ offset += i; } /*ARGSUSED*/ void cmd_make(frommark, tomark, cmd, bang, extra) MARK frommark, tomark; CMD cmd; int bang; char *extra; { BLK buf; /* if the file hasn't been saved, then complain unless ! */ if (tstflag(file, MODIFIED) && !bang) { msg("\"%s\" not saved yet", origname); return; } /* build the command */ sprintf(buf.c, "%s %s %s%s", (cmd == CMD_CC ? o_cc : o_make), extra, REDIRECT, ERRLIST); qaddstr(buf.c); addch('\n'); /* run the command, with curses temporarily disabled */ Suspend_curses(); system(buf.c); Resume_curses(mode == MODE_EX); if (mode == MODE_COLON) mode = MODE_VI; /* run the "errlist" command */ cmd_errlist(MARK_UNSET, MARK_UNSET, cmd, bang, ERRLIST); } #endif #ifndef NO_ABBR /*ARGSUSED*/ void cmd_abbr(frommark, tomark, cmd, bang, extra) MARK frommark, tomark; CMD cmd; int bang; char *extra; { do_abbr(extra); } #endif SHAR_EOF fi # end of overwriting check if test -f 'cmd2.c' then echo shar: will not over-write existing file "'cmd2.c'" else cat << \SHAR_EOF > 'cmd2.c' /* cmd2.c */ /* Author: * Steve Kirkendall * 14407 SW Teal Blvd. #C * Beaverton, OR 97005 * kirkenda@cs.pdx.edu */ /* This file contains some of the commands - mostly ones that change text */ #include #include "config.h" #include "vi.h" #include "regexp.h" #if TOS # include #else # if OSK # include "osk.h" # else # include # endif #endif /*ARGSUSED*/ void cmd_substitute(frommark, tomark, cmd, bang, extra) MARK frommark; MARK tomark; CMD cmd; int bang; char *extra; /* rest of the command line */ { char *line; /* a line from the file */ regexp *re; /* the compiled search expression */ char *subst; /* the substitution string */ char *opt; /* substitution options */ long l; /* a line number */ char *s, *d; /* used during subtitutions */ char *conf; /* used during confirmation */ long chline; /* # of lines changed */ long chsub; /* # of substitutions made */ static optp; /* boolean option: print when done? */ static optg; /* boolean option: substitute globally in line? */ static optc; /* boolean option: confirm before subst? */ /* for now, assume this will fail */ rptlines = -1L; if (cmd == CMD_SUBAGAIN) { #ifndef NO_MAGIC if (*o_magic) subst = "~"; else #endif subst = "\\~"; re = regcomp(""); /* if visual "&", then turn off the "p" and "c" options */ if (bang) { optp = optc = FALSE; } } else { /* make sure we got a search pattern */ if (*extra != '/' && *extra != '?') { msg("Usage: s/regular expression/new text/"); return; } /* parse & compile the search pattern */ subst = parseptrn(extra); re = regcomp(extra + 1); } /* abort if RE error -- error message already given by regcomp() */ if (!re) { return; } if (cmd == CMD_SUBSTITUTE) { /* parse the substitution string & find the option string */ for (opt = subst; *opt && *opt != *extra; opt++) { if (*opt == '\\' && opt[1]) { opt++; } } if (*opt) { *opt++ = '\0'; } /* analyse the option string */ if (!*o_edcompatible) { optp = optg = optc = FALSE; } while (*opt) { switch (*opt++) { case 'p': optp = !optp; break; case 'g': optg = !optg; break; case 'c': optc = !optc; break; case ' ': case '\t': break; default: msg("Subst options are p, c, and g -- not %c", opt[-1]); return; } } } /* if "c" or "p" flag was given, and we're in visual mode, then NEWLINE */ if ((optc || optp) && mode == MODE_VI) { addch('\n'); exrefresh(); } ChangeText { /* reset the change counters */ chline = chsub = 0L; /* for each selected line */ for (l = markline(frommark); l <= markline(tomark); l++) { /* fetch the line */ line = fetchline(l); /* if it contains the search pattern... */ if (regexec(re, line, TRUE)) { /* increment the line change counter */ chline++; /* initialize the pointers */ s = line; d = tmpblk.c; /* do once or globally ... */ do { #ifndef CRUNCH /* confirm, if necessary */ if (optc) { for (conf = line; conf < re->startp[0]; conf++) addch(*conf); standout(); for ( ; conf < re->endp[0]; conf++) addch(*conf); standend(); for (; *conf; conf++) addch(*conf); addch('\n'); exrefresh(); if (getkey(0) != 'y') { /* copy accross the original chars */ while (s < re->endp[0]) *d++ = *s++; /* skip to next match on this line, if any */ continue; } } #endif /* not CRUNCH */ /* increment the substitution change counter */ chsub++; /* this may be the first line to redraw */ redrawrange(l, l + 1L, l + 1L); /* copy stuff from before the match */ while (s < re->startp[0]) { *d++ = *s++; } /* substitute for the matched part */ regsub(re, subst, d); s = re->endp[0]; d += strlen(d); } while (optg && regexec(re, s, FALSE)); /* copy stuff from after the match */ while (*d++ = *s++) /* yes, ASSIGNMENT! */ { } /* replace the old version of the line with the new */ d[-1] = '\n'; d[0] = '\0'; change(MARK_AT_LINE(l), MARK_AT_LINE(l + 1), tmpblk.c); /* if supposed to print it, do so */ if (optp) { addstr(tmpblk.c); exrefresh(); } /* move the cursor to that line */ cursor = MARK_AT_LINE(l); } } } /* tweak for redrawing */ mustredraw = TRUE; /* free the regexp */ free(re); /* if done from within a ":g" command, then finish silently */ if (doingglobal) { rptlines = chline; rptlabel = "changed"; return; } /* Reporting */ if (chsub == 0) { msg("Substitution failed"); } else if (chline >= *o_report) { msg("%ld substitutions on %ld lines", chsub, chline); } } /*ARGSUSED*/ void cmd_delete(frommark, tomark, cmd, bang, extra) MARK frommark; MARK tomark; CMD cmd; int bang; char *extra; { MARK curs2; /* an altered form of the cursor */ /* choose your cut buffer */ if (*extra == '"') { extra++; } if (*extra) { cutname(*extra); } /* make sure we're talking about whole lines here */ frommark = frommark & ~(BLKSIZE - 1); tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE; /* yank the lines */ cut(frommark, tomark); /* if CMD_DELETE then delete the lines */ if (cmd != CMD_YANK) { curs2 = cursor; ChangeText { /* delete the lines */ delete(frommark, tomark); } if (curs2 > tomark) { cursor = curs2 - tomark + frommark; } else if (curs2 > frommark) { cursor = frommark; } } } /*ARGSUSED*/ void cmd_append(frommark, tomark, cmd, bang, extra) MARK frommark; MARK tomark; CMD cmd; int bang; char *extra; { long l; /* line counter */ ChangeText { /* if we're doing a change, delete the old version */ if (cmd == CMD_CHANGE) { /* delete 'em */ cmd_delete(frommark, tomark, cmd, bang, extra); } /* new lines start at the frommark line, or after it */ l = markline(frommark); if (cmd == CMD_APPEND) { l++; } /* get lines until no more lines, or "." line, and insert them */ while (vgets('\0', tmpblk.c, BLKSIZE) >= 0) { addch('\n'); if (!strcmp(tmpblk.c, ".")) { break; } strcat(tmpblk.c, "\n"); add(MARK_AT_LINE(l), tmpblk.c); l++; } } /* on the odd chance that we're calling this from vi mode ... */ redraw(MARK_UNSET, FALSE); } /*ARGSUSED*/ void cmd_put(frommark, tomark, cmd, bang, extra) MARK frommark; MARK tomark; CMD cmd; int bang; char *extra; { /* choose your cut buffer */ if (*extra == '"') { extra++; } if (*extra) { cutname(*extra); } /* paste it */ ChangeText { cursor = paste(frommark, TRUE, FALSE); } } /*ARGSUSED*/ void cmd_join(frommark, tomark, cmd, bang, extra) MARK frommark; MARK tomark; CMD cmd; int bang; char *extra; { long l; char *scan; int len; /* length of the new line */ /* if only one line is specified, assume the following one joins too */ if (markline(frommark) == nlines) { msg("Nothing to join with this line"); return; } if (markline(frommark) == markline(tomark)) { tomark += BLKSIZE; } /* get the first line */ l = markline(frommark); strcpy(tmpblk.c, fetchline(l)); len = strlen(tmpblk.c); /* build the longer line */ while (++l <= markline(tomark)) { /* get the next line */ scan = fetchline(l); /* remove any leading whitespace */ while (*scan == '\t' || *scan == ' ') { scan++; } /* see if the line will fit */ if (strlen(scan) + len + 3 > BLKSIZE) { msg("Can't join -- the resulting line would be too long"); return; } /* catenate it, with a space (or two) in between */ if (len >= 1 && (tmpblk.c[len - 1] == '.' || tmpblk.c[len - 1] == '?' || tmpblk.c[len - 1] == '!')) { tmpblk.c[len++] = ' '; } tmpblk.c[len++] = ' '; strcpy(tmpblk.c + len, scan); len += strlen(scan); } tmpblk.c[len++] = '\n'; tmpblk.c[len] = '\0'; /* make the change */ ChangeText { frommark &= ~(BLKSIZE - 1); tomark &= ~(BLKSIZE - 1); tomark += BLKSIZE; change(frommark, tomark, tmpblk.c); } /* Reporting... */ rptlines = markline(tomark) - markline(frommark) - 1L; rptlabel = "joined"; } /*ARGSUSED*/ void cmd_shift(frommark, tomark, cmd, bang, extra) MARK frommark; MARK tomark; CMD cmd; int bang; char *extra; { long l; /* line number counter */ int oldidx; /* number of chars previously used for indent */ int newidx; /* number of chars in the new indent string */ int oldcol; /* previous indent amount */ int newcol; /* new indent amount */ char *text; /* pointer to the old line's text */ /* figure out how much of the screen we must redraw (for vi mode) */ if (markline(frommark) != markline(tomark)) { mustredraw = TRUE; redrawrange(markline(frommark), markline(tomark) + 1L, markline(tomark) + 1L); } ChangeText { /* for each line to shift... */ for (l = markline(frommark); l <= markline(tomark); l++) { /* get the line - ignore empty lines unless ! mode */ text = fetchline(l); if (!*text && !bang) continue; /* calc oldidx and oldcol */ for (oldidx = 0, oldcol = 0; text[oldidx] == ' ' || text[oldidx] == '\t'; oldidx++) { if (text[oldidx] == ' ') { oldcol += 1; } else { oldcol += *o_tabstop - (oldcol % *o_tabstop); } } /* calc newcol */ if (cmd == CMD_SHIFTR) { newcol = oldcol + (*o_shiftwidth & 0xff); } else { newcol = oldcol - (*o_shiftwidth & 0xff); if (newcol < 0) newcol = 0; } /* if no change, then skip to next line */ if (oldcol == newcol) continue; /* build a new indent string */ newidx = 0; while (newcol >= *o_tabstop) { tmpblk.c[newidx++] = '\t'; newcol -= *o_tabstop; } while (newcol > 0) { tmpblk.c[newidx++] = ' '; newcol--; } tmpblk.c[newidx] = '\0'; /* change the old indent string into the new */ change(MARK_AT_LINE(l), MARK_AT_LINE(l) + oldidx, tmpblk.c); } } /* Reporting... */ rptlines = markline(tomark) - markline(frommark) + 1L; if (cmd == CMD_SHIFTR) { rptlabel = ">ed"; } else { rptlabel = " 0) { /* count newlines, convert NULs, etc. ... */ for (lines = 0, scan = tmpblk.c; rc > 0; rc--, scan++) { /* break up long lines */ if (*scan != '\n' && len + 2 > BLKSIZE) { *scan = '\n'; addnl = TRUE; } /* protect against NUL chars in file */ if (!*scan) { *scan = 0x80; hadnul = TRUE; } /* starting a new line? */ if (*scan == '\n') { /* reset length at newline */ len = 0; lines++; } else { len++; } } /* add the text */ *scan = '\0'; add(tomark, tmpblk.c); tomark += MARK_AT_LINE(lines) + len - markidx(tomark); } /* if partial last line, then retain that first newline */ if (len > 0) { msg("Last line had no newline"); tomark += BLKSIZE; /* <- for the rptlines calc */ } else /* delete that first newline */ { delete(tomark, (tomark | (BLKSIZE - 1L)) + 1L); } } /* close the file */ close(fd); /* Reporting... */ rptlines = markline(tomark) - markline(frommark); rptlabel = "read"; if (addnl) msg("Newlines were added to break up long lines"); if (hadnul) msg("NULs were converted to 0x80"); } /*ARGSUSED*/ void cmd_undo(frommark, tomark, cmd, bang, extra) MARK frommark; MARK tomark; CMD cmd; int bang; char *extra; { undo(); } /* print the selected lines */ /*ARGSUSED*/ void cmd_print(frommark, tomark, cmd, bang, extra) MARK frommark; MARK tomark; CMD cmd; int bang; char *extra; { REG char *scan; REG long l; REG int col; for (l = markline(frommark); l <= markline(tomark); l++) { /* display a line number, if CMD_NUMBER */ if (cmd == CMD_NUMBER) { sprintf(tmpblk.c, "%6ld ", l); qaddstr(tmpblk.c); col = 8; } else { col = 0; } /* get the next line & display it */ for (scan = fetchline(l); *scan; scan++) { /* expand tabs to the proper width */ if (*scan == '\t' && cmd != CMD_LIST) { do { qaddch(' '); col++; } while (col % *o_tabstop != 0); } else if (*scan >= 0 && *scan < ' ' || *scan == '\177') { qaddch('^'); qaddch(*scan ^ 0x40); col += 2; } else if ((*scan & 0x80) && cmd == CMD_LIST) { sprintf(tmpblk.c, "\\%03o", *scan); qaddstr(tmpblk.c); col += 4; } else { qaddch(*scan); col++; } /* wrap at the edge of the screen */ if (!has_AM && col >= COLS) { addch('\n'); col -= COLS; } } if (cmd == CMD_LIST) { qaddch('$'); } addch('\n'); exrefresh(); } } /* move or copy selected lines */ /*ARGSUSED*/ void cmd_move(frommark, tomark, cmd, bang, extra) MARK frommark; MARK tomark; CMD cmd; int bang; char *extra; { MARK destmark; /* parse the destination linespec. No defaults. Line 0 is okay */ destmark = cursor; if (!strcmp(extra, "0")) { destmark = 0L; } else if (linespec(extra, &destmark) == extra || !destmark) { msg("invalid destination address"); return; } /* flesh the marks out to encompass whole lines */ frommark &= ~(BLKSIZE - 1); tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE; destmark = (destmark & ~(BLKSIZE - 1)) + BLKSIZE; /* make sure the destination is valid */ if (cmd == CMD_MOVE && destmark >= frommark && destmark < tomark) { msg("invalid destination address"); } /* Do it */ ChangeText { /* save the text to a cut buffer */ cutname('\0'); cut(frommark, tomark); /* if we're not copying, delete the old text & adjust destmark */ if (cmd != CMD_COPY) { delete(frommark, tomark); if (destmark >= frommark) { destmark -= (tomark - frommark); } } /* add the new text */ paste(destmark, FALSE, FALSE); } /* move the cursor to the last line of the moved text */ cursor = destmark + (tomark - frommark) - BLKSIZE; if (cursor < MARK_FIRST || cursor >= MARK_LAST + BLKSIZE) { cursor = MARK_LAST; } /* Reporting... */ rptlabel = ( (cmd == CMD_COPY) ? "copied" : "moved" ); } /* execute EX commands from a file */ /*ARGSUSED*/ void cmd_source(frommark, tomark, cmd, bang, extra) MARK frommark; MARK tomark; CMD cmd; int bang; char *extra; { /* must have a filename */ if (!*extra) { msg("\"source\" requires a filename"); return; } doexrc(extra); } #ifndef NO_AT /*ARGSUSED*/ void cmd_at(frommark, tomark, cmd, bang, extra) MARK frommark; MARK tomark; CMD cmd; int bang; char *extra; { static nest = FALSE; int result; char buf[MAXRCLEN]; /* don't allow nested macros */ if (nest) { msg("@ macros can't be nested"); return; } nest = TRUE; /* require a buffer name */ if (*extra == '"') extra++; if (!*extra || !isascii(*extra) ||!islower(*extra)) { msg("@ requires a cut buffer name (a-z)"); } /* get the contents of the buffer */ result = cb2str(*extra, buf, (unsigned)(sizeof buf)); if (result <= 0) { msg("buffer \"%c is empty", *extra); } else if (result >= sizeof buf) { msg("buffer \"%c is too large to execute", *extra); } else { /* execute the contents of the buffer as ex commands */ exstring(buf, result); } nest = FALSE; } #endif SHAR_EOF fi # end of overwriting check if test -f 'ctags.c' then echo shar: will not over-write existing file "'ctags.c'" else cat << \SHAR_EOF > 'ctags.c' /* ctags.c */ /* Author: * Steve Kirkendall * 14407 SW Teal Blvd. #C * Beaverton, OR 97005 * kirkenda@cs.pdx.edu */ /* This file contains the complete source to the ctags program. */ /* Special abilities: * Can also make a "refs" file for use by the "ref" program. */ /* Limitations: * This version of ctags always writes its output to the file "tags". * It assumes that every command-line argument (but "-r") is a C source file. * It does not sort the list of tags, unless CFLAGS=-DSORT. * It does not recognize duplicate definitions. * It does not try to handle "static" functions in a clever way. * It probably won't scan ANSI-C source code very well. */ /* Implementation: * Lines are scanned one-at-a-time. * The context of lines is tracked via a finite state machine. * Contexts are: * EXPECTFN - we're looking for a function name. * ARGS - between function name and its opening { * BODY - we found a function name, skip to end of body. * * Function tags are referenced by a search string, so that lines may be * inserted or deleted without mucking up the tag search. * * Macro tags are referenced by their line number, because 1) they usually * occur near the top of a file, so their line# won't change much; 2) They * often contain characters that are hard to search for; and 3) Their #define * line is likely to be altered. * * Each line of the resulting "tags" file describes one tag. Lines begin with * the tag name, then a tab, then the file name, then a tab, and then either * a line number or a slash-delimited search string. */ #include #include #include "config.h" #define REFS "refs" #if OSK #define NUMFMT "%%.%ds\t%%s\t%%ld\n" #define SRCHFMT "%%.%ds\t%%s\t/^%%s$/\n" #define MAINFMT "M%%.%ds\t%%s\t/^%%s$/\n" static char fmt[256]; #else #define NUMFMT "%.*s\t%s\t%ld\n" #define SRCHFMT "%.*s\t%s\t/^%s$/\n" #define MAINFMT "M%.*s\t%s\t/^%s$/\n" #endif #ifdef VERBOSE # define SAY(x) fprintf(stderr, "%s\n", x); #else # define SAY(x) #endif #define EXPECTFN 1 #define ARGS 2 #define BODY 3 extern char *fgets(); char *progname; /* argv[0], used for diagnostic output */ main(argc, argv) int argc; char **argv; { FILE *fp; int i; FILE *refs; /* used to write to the refs file */ #if MSDOS || TOS char **wildexpand(); argv=wildexpand(&argc, argv); #endif /* notice the program name */ progname = argv[0]; /* create the "refs" file if first arg is "-r" */ if (argc > 1 && !strcmp(argv[1], "-r")) { /* delete the "-r" flag from the args list */ argc--; argv++; /* open the "refs" file for writing */ refs = fopen(REFS, "w"); if (!refs) { fprintf(stderr, "%s: could not create \"%s\"\n", progname, REFS); exit(2); } } else { refs = (FILE *)0; } /* process each file named on the command line, or complain if none */ if (argc > 1) { /* redirect stdout to go to the "tags" file */ if (!freopen("tags", "w", stdout)) { fprintf(stderr, "%s: could not create \"%s\"\n", progname, TAGS); exit(2); } for (i = 1; i < argc; i++) { /* process this named file */ fp = fopen(argv[i], "r"); if (!fp) { fprintf(stderr, "%s: could not read \"%s\"\n", progname, argv[i]); continue; } ctags(fp, argv[i], refs); fclose(fp); } #ifdef SORT /* This is a hack which will sort the tags list. It should * on UNIX and Minix. You may have trouble with csh. Note * that the tags list only has to be sorted if you intend to * use it with the real vi; elvis permits unsorted tags. */ fflush(stdout); #if OSK fclose(stdout); system("qsort tags >-_tags; -nx; del tags; rename _tags tags"); #else system("sort tags >_tags$$; mv _tags$$ tags"); #endif #endif exit(0); } else { fprintf(stderr, "usage: %s *.[ch]\n", progname); exit(2); } } /* this function finds all tags in a given file */ ctags(fp, name, refs) FILE *fp; /* stream of the file to scan */ char *name; /* name of the file being scanned */ FILE *refs; /* NULL, or where to write refs lines */ { int context; /* context - either EXPECTFN, ARGS, or BODY */ long lnum; /* line number */ char text[1000]; /* a line of text from the file */ char *scan; /* used for searching through text */ int len; /* length of the line */ /* for each line of the file... */ for (context = EXPECTFN, lnum = 1; fgets(text, sizeof text, fp); lnum++) { #ifdef VERBOSE switch(context) { case EXPECTFN: scan = "EXPECTFN"; break; case ARGS: scan = "ARGS "; break; case BODY: scan = "BODY "; break; default: scan = "context?"; } fprintf(stderr, "%s:%s", scan, text); #endif /* start of body? */ if (text[0] == '{') { context = BODY; SAY("Start of BODY"); continue; } /* argument line, to be written to "refs" ? */ if (refs && context == ARGS) { if (text[0] != '\t') { putc('\t', refs); } fputs(text, refs); SAY("Argument line"); continue; } /* ignore empty or indented lines */ if (text[0] <= ' ') { SAY("Empty or indented"); continue; } /* end of body? */ if (text[0] == '}') { context = EXPECTFN; SAY("End of BODY"); continue; } /* ignore lines in the body of a function */ if (context != EXPECTFN) { SAY("BODY or ARGS"); continue; } /* strip the newline */ len = strlen(text); text[--len] = '\0'; /* a preprocessor line? */ if (text[0] == '#') { /* find the preprocessor directive */ for (scan = &text[1]; isspace(*scan); scan++) { } /* if it's a #define, make a tag out of it */ if (!strncmp(scan, "define", 6)) { /* find the start of the symbol name */ for (scan += 6; isspace(*scan); scan++) { } /* find the length of the symbol name */ for (len = 1; isalnum(scan[len]) || scan[len] == '_'; len++) { } #if OSK sprintf(fmt, NUMFMT, len); printf(fmt, scan, name, lnum); #else printf(NUMFMT, len, scan, name, lnum); #endif } SAY("Preprocessor line"); continue; } /* an extern or static declaration? */ if (text[len - 1] == ';' || !strncmp(text, "extern", 6) || !strncmp(text, "EXTERN", 6) || !strncmp(text, "static", 6) || !strncmp(text, "PRIVATE", 7)) { SAY("Extern or static"); continue; } /* if we get here & the first punctuation other than "*" is * a "(" which is immediately preceded by a name, then * assume the name is that of a function. */ for (scan = text; *scan; scan++) { if (ispunct(*scan) && !isspace(*scan) /* in BSD, spaces are punctuation?*/ && *scan != '*' && *scan != '_' && *scan != '(') { SAY("Funny punctuation"); goto ContinueContinue; } if (*scan == '(') { /* permit 0 or 1 spaces between name & '(' */ if (scan > text && scan[-1] == ' ') { scan--; } /* find the start & length of the name */ for (len = 0, scan--; scan >= text && (isalnum(*scan) || *scan == '_'); scan--, len++) { } scan++; /* did we find a function? */ if (len > 0) { /* found a function! */ if (len == 4 && !strncmp(scan, "main", 4)) { #if OSK sprintf(fmt, MAINFMT, strlen(name) - 2); printf(fmt, name, name, text); #else printf(MAINFMT, strlen(name) - 2, name, name, text); #endif } #if OSK sprintf(fmt, SRCHFMT, len); printf(fmt, scan, name, text); #else printf(SRCHFMT, len, scan, name, text); #endif context = ARGS; /* add a line to refs, if needed */ if (refs) { fputs(text, refs); putc('\n', refs); } goto ContinueContinue; } } else { SAY("No parenthesis"); } } SAY("No punctuation"); ContinueContinue:; } } #if MSDOS || TOS #define WILDCARD_NO_MAIN #include "wildcard.c" #endif SHAR_EOF fi # end of overwriting check if test -f 'curses.c' then echo shar: will not over-write existing file "'curses.c'" else cat << \SHAR_EOF > 'curses.c' /* curses.c */ /* Author: * Steve Kirkendall * 14407 SW Teal Blvd. #C * Beaverton, OR 97005 * kirkenda@cs.pdx.edu */ /* This file contains the functions & variables needed for a tiny subset of * curses. The principle advantage of this version of curses is its * extreme speed. Disadvantages are potentially larger code, few supported * functions, limited compatibility with full curses, and only stdscr. */ #include "config.h" #include "vi.h" #if ANY_UNIX # if UNIXV # include # undef TIOCWINSZ /* we can't handle it correctly yet */ # else # include # endif #endif #if TOS # include #endif #if OSK # include #endif #include extern char *getenv(); void starttcap(); /* variables, publicly available & used in the macros */ short ospeed; /* speed of the tty, eg B2400 */ #if OSK char PC_; /* Pad char */ #else char PC; /* Pad char */ #endif WINDOW *stdscr; /* pointer into kbuf[] */ WINDOW kbuf[KBSIZ]; /* a very large output buffer */ int LINES; /* :li#: number of rows */ int COLS; /* :co#: number of columns */ int AM; /* :am: boolean: auto margins? */ int PT; /* :pt: boolean: physical tabs? */ char *BC = "\b"; /* :bc backspace character string */ char *VB; /* :vb=: visible bell */ char *UP; /* :up=: move cursor up */ char *SO; /* :so=: standout start */ char *SE; /* :se=: standout end */ char *US = ""; /* :us=: underline start */ char *UE = ""; /* :ue=: underline end */ char *MD = ""; /* :md=: bold start */ char *ME = ""; /* :me=: bold end */ char *AS; /* :as=: alternate (italic) start */ char *AE; /* :ae=: alternate (italic) end */ char *CM; /* :cm=: cursor movement */ char *CE; /* :ce=: clear to end of line */ char *CD; /* :cd=: clear to end of screen */ char *AL; /* :al=: add a line */ char *DL; /* :dl=: delete a line */ #if OSK char *SR_; /* :sr=: scroll reverse */ #else char *SR; /* :sr=: scroll reverse */ #endif char *KS; /* :ks=: init string for cursor */ char *KE; /* :ke=: restore string for cursor */ char *KU; /* :ku=: key sequence sent by up arrow */ char *KD; /* :kd=: key sequence sent by down arrow */ char *KL; /* :kl=: key sequence sent by left arrow */ char *KR; /* :kr=: key sequence sent by right arrow */ char *HM; /* :HM=: key sequence sent by the key */ char *EN; /* :EN=: key sequence sent by the key */ char *PU; /* :PU=: key sequence sent by the key */ char *PD; /* :PD=: key sequence sent by the key */ char *IM; /* :im=: insert mode start */ char *IC = ""; /* :ic=: insert the following character */ char *EI; /* :ei=: insert mode end */ char *DC; /* :dc=: delete a character */ char *TI; /* :ti=: terminal init */ /* GB */ char *TE; /* :te=: terminal exit */ /* GB */ #ifndef NO_CURSORSHAPE char *CQ = (char *)0;/* :cQ=: normal cursor */ char *CX = (char *)1;/* :cX=: cursor used for EX command/entry */ char *CV = (char *)2;/* :cV=: cursor used for VI command mode */ char *CI = (char *)3;/* :cI=: cursor used for VI input mode */ char *CR = (char *)4;/* :cR=: cursor used for VI replace mode */ #endif char *aend = ""; /* end an attribute -- either UE or ME */ char ERASEKEY; /* backspace key taken from ioctl structure */ #if ANY_UNIX # if UNIXV static struct termio oldtermio; /* original tty mode */ static struct termio newtermio; /* cbreak/noecho tty mode */ # else static struct sgttyb oldsgttyb; /* original tty mode */ static struct sgttyb newsgttyb; /* cbreak/nl/noecho tty mode */ static int oldint; /* ^C or DEL, the "intr" character */ # ifdef TIOCSLTC static int oldswitch; /* ^Z, the "suspend" character */ static int oldquote; /* ^V, the "quote next char" char */ # endif # endif #endif #if OSK static struct sgbuf oldsgttyb; /* orginal tty mode */ static struct sgbuf newsgttyb; /* noecho tty mode */ #endif static char *capbuf; /* capability string buffer */ void initscr() { /* make sure TERM variable is set */ #if MSDOS char *val; if (! (val = getenv("TERM")) || !strcmp(val, "pcbios")) #else if (!getenv("TERM")) #endif { #if ANY_UNIX write(2, "Environment variable TERM must be set\n", (unsigned)38); exit(1); #endif #if OSK writeln(2, "Environment variable TERM must be set\n", (unsigned)38); exit(1); #endif #if MSDOS || TOS Getsize(0); #endif } else { #if MSDOS *o_pcbios=0; #endif /* start termcap stuff */ starttcap(); } /* create stdscr and curscr */ stdscr = kbuf; /* change the terminal mode to cbreak/noecho */ #if ANY_UNIX # if UNIXV ioctl(2, TCGETA, &oldtermio); # else ioctl(2, TIOCGETP, &oldsgttyb); # endif #endif #if OSK _gs_opt(0, &oldsgttyb); #endif Resume_curses(TRUE); } void endwin() { /* change the terminal mode back the way it was */ Suspend_curses(); } static int curses_active = FALSE; void suspend_curses() { #if ANY_UNIX && !UNIXV struct tchars tbuf; # ifdef TIOCSLTC struct ltchars ltbuf; # endif #endif #ifndef NO_CURSORSHAPE if (has_CQ) { do_CQ(); } #endif if (has_TE) /* GB */ { do_TE(); } if (has_KE) { do_KE(); } refresh(); /* change the terminal mode back the way it was */ #if ANY_UNIX # if UNIXV ioctl(2, TCSETAW, &oldtermio); # else ioctl(2, TIOCSETP, &oldsgttyb); ioctl(2, TIOCGETC, &tbuf); tbuf.t_intrc = oldint; ioctl(2, TIOCSETC, &tbuf); # ifdef TIOCSLTC ioctl(2, TIOCGLTC, <buf); ltbuf.t_suspc = oldswitch; ltbuf.t_lnextc = oldquote; ioctl(2, TIOCSLTC, <buf); # endif # endif #endif #if OSK _ss_opt(0, &oldsgttyb); #endif curses_active = FALSE; } void resume_curses(quietly) int quietly; { if (!curses_active) { /* change the terminal mode to cbreak/noecho */ #if ANY_UNIX # if UNIXV ospeed = (oldtermio.c_cflag & CBAUD); ERASEKEY = oldtermio.c_cc[VERASE]; newtermio = oldtermio; newtermio.c_iflag &= (IXON|IXOFF|IXANY|ISTRIP|IGNBRK); newtermio.c_oflag &= ~OPOST; newtermio.c_lflag &= ISIG; newtermio.c_cc[VINTR] = ctrl('C'); /* always use ^C for interrupts */ newtermio.c_cc[VMIN] = 1; newtermio.c_cc[VTIME] = 0; # ifdef VSWTCH newtermio.c_cc[VSWTCH] = 0; # endif ioctl(2, TCSETAW, &newtermio); # else /* BSD or V7 or Coherent or Minix */ struct tchars tbuf; # ifdef TIOCSLTC struct ltchars ltbuf; # endif ospeed = oldsgttyb.sg_ospeed; ERASEKEY = oldsgttyb.sg_erase; newsgttyb = oldsgttyb; newsgttyb.sg_flags |= CBREAK; newsgttyb.sg_flags &= ~(CRMOD|ECHO|XTABS); ioctl(2, TIOCSETP, &newsgttyb); ioctl(2, TIOCGETC, &tbuf); oldint = tbuf.t_intrc; tbuf.t_intrc = ctrl('C'); /* always use ^C for interrupts */ ioctl(2, TIOCSETC, &tbuf); # ifdef TIOCSLTC ioctl(2, TIOCGLTC, <buf); oldswitch = ltbuf.t_suspc; ltbuf.t_suspc = 0; /* disable ^Z for elvis */ oldquote = ltbuf.t_lnextc; ltbuf.t_lnextc = 0; /* disable ^V for elvis */ ioctl(2, TIOCSLTC, <buf); # endif # endif #endif #if OSK newsgttyb = oldsgttyb; newsgttyb.sg_echo = 0; newsgttyb.sg_eofch = 0; newsgttyb.sg_kbach = 0; newsgttyb.sg_kbich = ctrl('C'); _ss_opt(0, &newsgttyb); ospeed = oldsgttyb.sg_baud; ERASEKEY = oldsgttyb.sg_bspch; #endif if (has_TI) /* GB */ { do_TI(); } if (has_KS) { do_KS(); } curses_active = TRUE; } /* If we're supposed to quit quietly, then we're done */ if (quietly) { return; } signal(SIGINT, SIG_IGN); move(LINES - 1, 0); do_SO(); qaddstr("[Press to continue]"); do_SE(); refresh(); ttyread(kbuf, 20); /* in RAW mode, so <20 is very likely */ if (kbuf[0] == ':') { mode = MODE_COLON; addch('\n'); refresh(); } else { mode = MODE_VI; redraw(MARK_UNSET, FALSE); } exwrote = FALSE; #if TURBOC signal(SIGINT, (void(*)()) trapint); #else signal(SIGINT, trapint); #endif } static void lacking(s) char *s; { write(2, "This termcap entry lacks the :", (unsigned)30); write(2, s, (unsigned)2); write(2, "=: capability\n", (unsigned)14); #if OSK write(2, "\l", 1); #endif exit(1); } void starttcap() { char *str; static char cbmem[800]; #define MUSTHAVE(T,s) if (!(T = Tgetstr(s, &capbuf))) lacking(s) #define MAYHAVE(T,s) if (str = Tgetstr(s, &capbuf)) T = str #define PAIR(T,U,sT,sU) T=Tgetstr(sT,&capbuf);U=Tgetstr(sU,&capbuf);if (!T||!U)T=U="" /* allocate memory for capbuf */ capbuf = cbmem; /* get the termcap entry */ switch (Tgetent(kbuf, getenv("TERM"))) { case -1: write(2, "Can't read /etc/termcap\n", (unsigned)24); #if OSK write(2, "\l", 1); #endif exit(2); case 0: write(2, "Unrecognized TERM type\n", (unsigned)23); #if OSK write(2, "\l", 1); #endif exit(3); } /* get strings */ MUSTHAVE(UP, "up"); MAYHAVE(BC, "bc"); MAYHAVE(VB, "vb"); MUSTHAVE(CM, "cm"); PAIR(SO, SE, "so", "se"); PAIR(TI, TE, "ti", "te"); if (Tgetnum("ug") <= 0) { PAIR(US, UE, "us", "ue"); PAIR(MD, ME, "md", "me"); /* get italics, or have it default to underline */ PAIR(AS, AE, "as", "ae"); if (!*AS) { AS = US; AE = UE; } } MAYHAVE(AL, "al"); MAYHAVE(DL, "dl"); MUSTHAVE(CE, "ce"); MAYHAVE(CD, "cd"); #if OSK MAYHAVE(SR_, "sr"); #else MAYHAVE(SR, "sr"); #endif PAIR(IM, EI, "im", "ei"); MAYHAVE(IC, "ic"); MAYHAVE(DC, "dc"); /* other termcap stuff */ AM = Tgetflag("am"); PT = Tgetflag("pt"); Getsize(0); /* Key sequences */ PAIR(KS, KE, "ks", "ke"); MAYHAVE(KU, "ku"); /* up */ MAYHAVE(KD, "kd"); /* down */ MAYHAVE(KL, "kl"); /* left */ MAYHAVE(KR, "kr"); /* right */ MAYHAVE(PU, "kP"); /* PgUp */ MAYHAVE(PD, "kN"); /* PgDn */ MAYHAVE(HM, "kh"); /* Home */ MAYHAVE(EN, "kH"); /* End */ #ifndef CRUNCH if (!PU) MAYHAVE(PU, "K2"); /* "3x3 pad" names for PgUp, etc. */ if (!PD) MAYHAVE(PD, "K5"); if (!HM) MAYHAVE(HM, "K1"); if (!EN) MAYHAVE(EN, "K4"); MAYHAVE(PU, "PU"); /* old XENIX names for PgUp, etc. */ MAYHAVE(PD, "PD"); /* (overrides others, if used.) */ MAYHAVE(HM, "HM"); MAYHAVE(EN, "EN"); #endif #ifndef NO_CURSORSHAPE /* cursor shapes */ CQ = Tgetstr("cQ", &capbuf); if (has_CQ) { CX = Tgetstr("cX", &capbuf); if (!CX) CX = CQ; CV = Tgetstr("cV", &capbuf); if (!CV) CV = CQ; CI = Tgetstr("cI", &capbuf); if (!CI) CI = CQ; CR = Tgetstr("cR", &capbuf); if (!CR) CR = CQ; } # ifndef CRUNCH else { PAIR(CQ, CV, "ve", "vs"); CX = CI = CR = CQ; } # endif /* !CRUNCH */ #endif /* !NO_CURSORSHAPE */ #undef MUSTHAVE #undef MAYHAVE #undef PAIR } /* This function gets the window size. It uses the TIOCGWINSZ ioctl call if * your system has it, or Tgetnum("li") and Tgetnum("co") if it doesn't. * This function is called once during initialization, and thereafter it is * called whenever the SIGWINCH signal is sent to this process. */ int getsize(signo) int signo; { int lines; int cols; #ifdef TIOCGWINSZ struct winsize size; #endif #ifdef SIGWINCH /* reset the signal vector */ signal(SIGWINCH, getsize); #endif /* get the window size, one way or another. */ lines = cols = 0; #ifdef TIOCGWINSZ if (ioctl(2, TIOCGWINSZ, &size) >= 0) { lines = size.ws_row; cols = size.ws_col; } #endif if ((lines == 0 || cols == 0) && signo == 0) { LINES = CHECKBIOS(v_rows(), Tgetnum("li")); COLS = CHECKBIOS(v_cols(), Tgetnum("co")); } if (lines >= 2 && cols >= 30) { LINES = lines; COLS = cols; } /* Make sure we got values that we can live with */ if (LINES < 2 || COLS < 30) { write(2, "Screen too small\n", (unsigned)17); #if OSK write(2, "\l", 1); #endif endwin(); exit(2); } /* !!! copy the new values into Elvis' options */ { extern char o_columns[], o_lines[]; *o_columns = COLS; *o_lines = LINES; } return 0; } /* This is a function version of addch() -- it is used by tputs() */ int faddch(ch) int ch; { addch(ch); return 0; } /* These functions are equivelent to the macros of the same names... */ void qaddstr(str) char *str; { REG char *s_, *d_; #if MSDOS if (o_pcbios[0]) { while (*str) qaddch(*str++); return; } #endif for (s_=(str), d_=stdscr; *d_++ = *s_++; ) { } stdscr = d_ - 1; } void attrset(a) int a; { do_aend(); if (a == A_BOLD) { do_MD(); aend = ME; } else if (a == A_UNDERLINE) { do_US(); aend = UE; } else if (a == A_ALTCHARSET) { do_AS(); aend = AE; } else { aend = ""; } } void insch(ch) int ch; { if (has_IM) do_IM(); do_IC(); qaddch(ch); if (has_EI) do_EI(); } #if MSDOS static int alarmtime; /* raw read - #defined to read (0, ...) on non-MSDOS. * With MSDOS, am maximum of 1 byte is read. * If more bytes should be read, just change the loop. * The following code uses the IBM-PC-System-Timer, so probably wont't work * on non-compatibles. */ /*ARGSUSED*/ ttyread(buf, len) char *buf; int len; { volatile char far *biostimer; char oldtime; int nticks = 0; int pos = 0; biostimer = (char far *)0x0040006cl; oldtime = *biostimer; while (!pos && (!alarmtime || nticks>16; } } Supexec(gettime); } return pos; } alarm(time) int time; { alarmtime = 50 * time; /* ticks are 1/200 sec. */ } ttywrite(buf, len) char *buf; int len; { while (len--) Bconout(2, *buf++); } #endif #if OSK ttyread(buf, len) char *buf; int len; { REG int i; if ((i = _gs_rdy(0)) > 0) return read(0, buf, i < len ? i : len); else return read(0, buf, 1); } alarm(time) int time; { #define TIME(secs) ((secs << 8) | 0x80000000) static int alrmid; if (time) alrmid = alm_set(SIGQUIT, TIME(time)); else alm_delete(alrmid); } #endif /* OSK */ SHAR_EOF fi # end of overwriting check if test -f 'cut.c' then echo shar: will not over-write existing file "'cut.c'" else cat << \SHAR_EOF > 'cut.c' /* cut.c */ /* Author: * Steve Kirkendall * 14407 SW Teal Blvd. #C * Beaverton, OR 97005 * kirkenda@cs.pdx.edu */ /* This file contains function which manipulate the cut buffers. */ #include "config.h" #include "vi.h" #if TURBOC #include /* needed for getpid */ #endif #if TOS #include #define rename(a,b) Frename(0,a,b) #endif # define NANNONS 9 /* number of annonymous buffers */ static struct cutbuf { short *phys; /* pointer to an array of #s of BLKs containing text */ int nblks; /* number of blocks in phys[] array */ int start; /* offset into first block of start of cut */ int end; /* offset into last block of end of cut */ int fd; /* fd of tmp file, or -1 to use tmpfd */ char lnmode; /* boolean: line-mode cut? (as opposed to char-mode) */ } named[27], /* cut buffers "a through "z and ". */ annon[NANNONS]; /* annonymous cut buffers */ static char cbname; /* name chosen for next cut/paste operation */ #ifndef NO_RECYCLE /* This function builds a list of all blocks needed in the current tmp file * for the contents of cut buffers. * !!! WARNING: if you have more than ~450000 bytes of text in all of the * cut buffers, then this will fail disastrously, because buffer overflow * is *not* allowed for. */ int cutneeds(need) BLK *need; /* this is where we deposit the list */ { struct cutbuf *cb; /* used to count through cut buffers */ int i; /* used to count through blocks of a cut buffer */ int n; /* total number of blocks in list */ n = 0; /* first the named buffers... */ for (cb = named; cb < &named[27]; cb++) { if (cb->fd > 0) continue; for (i = cb->nblks; i-- > 0; ) { need->n[n++] = cb->phys[i]; } } /* then the anonymous buffers */ for (cb = annon; cb < &annon[NANNONS]; cb++) { if (cb->fd > 0) continue; for (i = cb->nblks; i-- > 0; ) { need->n[n++] = cb->phys[i]; } } return n; } #endif /* This function frees a cut buffer */ static void cutfree(buf) struct cutbuf *buf; { char cutfname[50]; int i; /* return immediately if the buffer is already empty */ if (buf->nblks <= 0) { return; } /* else free up stuff */ buf->nblks = 0; #ifdef DEBUG if (!buf->phys) msg("cutfree() tried to free an NULL buf->phys pointer."); #endif free((char *)buf->phys); /* see if anybody else needs this tmp file */ if (buf->fd >= 0) { for (i = 0; i < 27; i++) { if (named[i].nblks > 0 && named[i].fd == buf->fd) { break; } } } /* if nobody else needs it, then discard the tmp file */ if (buf->fd >= 0 && i == 27) { close(buf->fd); #if MSDOS || TOS strcpy(cutfname, o_directory); if ((i = strlen(cutfname)) && !strchr(":/\\", cutfname[i-1])) cutfname[i++]=SLASH; sprintf(cutfname+i, CUTNAME+3, getpid(), buf->fd); #else sprintf(cutfname, CUTNAME, o_directory, getpid(), buf->fd); #endif unlink(cutfname); } } /* This function is called when we are about to abort a tmp file. If any * cut buffers still need the file, then a copy of the file should be * created for use by the cut buffers. * * To minimize the number of extra files lying around, only named cut buffers * are preserved in a file switch; the annonymous buffers just go away. */ void cutswitch(tmpname) char *tmpname; /* name of the tmp file */ { char cutfname[50]; /* used to build a new name for the tmp file */ int fd; /* a new fd for the current tmp file */ int i; #if MSDOS || TOS int j; #endif /* discard all annonymous cut buffers */ for (i = 0; i < NANNONS; i++) { cutfree(&annon[i]); } /* find the first named buffer that uses this tmp file */ for (i = 0; i < 27; i++) { if (named[i].nblks > 0 && named[i].fd < 0) { break; } } /* if none of them use this tmp file, then we're done */ if (i == 27) { return; } /* else we'll need this file and an fd a little longer */ #if MSDOS || TOS strcpy(cutfname, o_directory); if ((j = strlen(cutfname)) && !strchr(":/\\", cutfname[j-1])) cutfname[j++]=SLASH; close(tmpfd); fd = open(tmpname, O_RDONLY|O_BINARY); close(fd); sprintf(cutfname+j, CUTNAME+3, getpid(), fd); rename(tmpname, cutfname); fd = open(cutfname, O_RDONLY|O_BINARY); tmpfd = -1; /* we'll try to close this in tmp.c, but who cares? */ #else fd = dup(tmpfd); # if OSK sprintf(cutfname, CUTNAME, "", getpid(), fd); if (!link(tmpname, &cutfname[1])) /* skip slash */ unlink(tmpname); # else sprintf(cutfname, CUTNAME, o_directory, getpid(), fd); link(tmpname, cutfname) || unlink(tmpname); # endif #endif /* have all cut buffers use the new fd instead */ for (; i < 27; i++) { if (named[i].nblks > 0 && named[i].fd < 0) { named[i].fd = fd; } } } /* This function should be called just before termination of vi */ void cutend() { int i; /* free all named cut buffers, since they might be forcing an older * tmp file to be retained. */ for (i = 0; i < 27; i++) { cutfree(&named[i]); } } /* This function is used to select the cut buffer to be used next */ void cutname(name) int name; /* a single character */ { cbname = name; } /* This function copies a selected segment of text to a cut buffer */ void cut(from, to) MARK from; /* start of text to cut */ MARK to; /* end of text to cut */ { int first; /* logical number of first block in cut */ int last; /* logical number of last block used in cut */ long line; /* a line number */ int lnmode; /* boolean: will this be a line-mode cut? */ MARK delthru;/* end of text temporarily inserted for apnd */ REG struct cutbuf *cb; REG long l; REG int i; REG char *scan; char *blkc; /* detect whether this must be a line-mode cut or char-mode cut */ if (markidx(from) == 0 && markidx(to) == 0) lnmode = TRUE; else lnmode = FALSE; /* by default, we don't "delthru" anything */ delthru = MARK_UNSET; /* decide which cut buffer to use */ if (!cbname) { /* free up the last annonymous cut buffer */ cutfree(&annon[NANNONS - 1]); /* shift the annonymous cut buffers */ for (i = NANNONS - 1; i > 0; i--) { annon[i] = annon[i - 1]; } /* use the first annonymous cut buffer */ cb = annon; cb->nblks = 0; } else if (cbname >= 'a' && cbname <= 'z') { cb = &named[cbname - 'a']; cutfree(cb); } #ifndef CRUNCH else if (cbname >= 'A' && cbname <= 'Z') { cb = &named[cbname - 'A']; if (cb->nblks > 0) { /* resolve linemode/charmode differences */ if (!lnmode && cb->lnmode) { from &= ~(BLKSIZE - 1); if (markidx(to) != 0 || to == from) { to = to + BLKSIZE - markidx(to); } lnmode = TRUE; } /* insert the old cut-buffer before the new text */ mark[28] = to; delthru = paste(from, FALSE, TRUE); if (delthru == MARK_UNSET) { return; } delthru++; to = mark[28]; } cutfree(cb); } #endif /* not CRUNCH */ else if (cbname == '.') { cb = &named[26]; cutfree(cb); } else { msg("Invalid cut buffer name: \"%c", cbname); cbname = '\0'; return; } cbname = '\0'; cb->fd = -1; /* detect whether we're doing a line mode cut */ cb->lnmode = lnmode; /* ---------- */ /* Reporting... */ if (markidx(from) == 0 && markidx(to) == 0) { rptlines = markline(to) - markline(from); rptlabel = "yanked"; } /* ---------- */ /* make sure each block has a physical disk address */ blksync(); /* find the first block in the cut */ line = markline(from); for (first = 1; line > lnum[first]; first++) { } /* fetch text of the block containing that line */ blkc = scan = blkget(first)->c; /* find the mark in the block */ for (l = lnum[first - 1]; ++l < line; ) { while (*scan++ != '\n') { } } scan += markidx(from); /* remember the offset of the start */ cb->start = scan - blkc; /* ---------- */ /* find the last block in the cut */ line = markline(to); for (last = first; line > lnum[last]; last++) { } /* fetch text of the block containing that line */ if (last != first) { blkc = scan = blkget(last)->c; } else { scan = blkc; } /* find the mark in the block */ for (l = lnum[last - 1]; ++l < line; ) { while (*scan++ != '\n') { } } if (markline(to) <= nlines) { scan += markidx(to); } /* remember the offset of the end */ cb->end = scan - blkc; /* ------- */ /* remember the physical block numbers of all included blocks */ cb->nblks = last - first; if (cb->end > 0) { cb->nblks++; } #ifdef lint cb->phys = (short *)0; #else cb->phys = (short *)malloc((unsigned)(cb->nblks * sizeof(short))); #endif for (i = 0; i < cb->nblks; i++) { cb->phys[i] = hdr.n[first++]; } #ifndef CRUNCH /* if we temporarily inserted text for appending, then delete that * text now -- before the user sees it. */ if (delthru) { line = rptlines; delete(from, delthru); rptlines = line; rptlabel = "yanked"; } #endif /* not CRUNCH */ } static void readcutblk(cb, blkno) struct cutbuf *cb; int blkno; { int fd; /* either tmpfd or cb->fd */ /* decide which fd to use */ if (cb->fd >= 0) { fd = cb->fd; } else { fd = tmpfd; } /* get the block */ lseek(fd, (long)cb->phys[blkno] * (long)BLKSIZE, 0); if (read(fd, tmpblk.c, (unsigned)BLKSIZE) != BLKSIZE) { msg("Error reading back from tmp file for pasting!"); } } /* This function inserts text from a cut buffer, and returns the MARK where * insertion ended. Return MARK_UNSET on errors. */ MARK paste(at, after, retend) MARK at; /* where to insert the text */ int after; /* boolean: insert after mark? (rather than before) */ int retend; /* boolean: return end of text? (rather than start) */ { REG struct cutbuf *cb; REG int i; /* decide which cut buffer to use */ if (cbname >= 'A' && cbname <= 'Z') { cb = &named[cbname - 'A']; } else if (cbname >= 'a' && cbname <= 'z') { cb = &named[cbname - 'a']; } else if (cbname >= '1' && cbname <= '9') { cb = &annon[cbname - '1']; } else if (cbname == '.') { cb = &named[26]; } else if (!cbname) { cb = annon; } else { msg("Invalid cut buffer name: \"%c", cbname); cbname = '\0'; return MARK_UNSET; } /* make sure it isn't empty */ if (cb->nblks == 0) { if (cbname) msg("Cut buffer \"%c is empty", cbname); else msg("Cut buffer is empty"); cbname = '\0'; return MARK_UNSET; } cbname = '\0'; /* adjust the insertion MARK for "after" and line-mode cuts */ if (cb->lnmode) { at &= ~(BLKSIZE - 1); if (after) { at += BLKSIZE; } } else if (after) { /* careful! if markidx(at) == 0 we might be pasting into an * empty line -- so we can't blindly increment "at". */ if (markidx(at) == 0) { pfetch(markline(at)); if (plen != 0) { at++; } } else { at++; } } /* put a copy of the "at" mark in the mark[] array, so it stays in * sync with changes made via add(). */ mark[27] = at; /* simple one-block paste? */ if (cb->nblks == 1) { /* get the block */ readcutblk(cb, 0); /* isolate the text we need within it */ if (cb->end) { tmpblk.c[cb->end] = '\0'; } /* insert it */ ChangeText { add(at, &tmpblk.c[cb->start]); } } else { /* multi-block paste */ ChangeText { i = cb->nblks - 1; /* add text from the last block first */ if (cb->end > 0) { readcutblk(cb, i); tmpblk.c[cb->end] = '\0'; add(at, tmpblk.c); i--; } /* add intervening blocks */ while (i > 0) { readcutblk(cb, i); add(at, tmpblk.c); i--; } /* add text from the first cut block */ readcutblk(cb, 0); add(at, &tmpblk.c[cb->start]); } } /* Reporting... */ rptlines = markline(mark[27]) - markline(at); rptlabel = "pasted"; /* return the mark at the beginning/end of inserted text */ if (retend) { return mark[27] - 1L; } return at; } #ifndef NO_AT /* This function copies characters from a cut buffer into a string. * It returns the number of characters in the cut buffer. If the cut * buffer is too large to fit in the string (i.e. if cb2str() returns * a number >= size) then the characters will not have been copied. * It returns 0 if the cut buffer is empty, and -1 for invalid cut buffers. */ int cb2str(name, buf, size) int name; /* the name of a cut-buffer to get: a-z only! */ char *buf; /* where to put the string */ unsigned size; /* size of buf */ { REG struct cutbuf *cb; REG char *src; REG char *dest; /* decide which cut buffer to use */ if (name >= 'a' && name <= 'z') { cb = &named[name - 'a']; } else { return -1; } /* if the buffer is empty, return 0 */ if (cb->nblks == 0) { return 0; } /* !!! if not a single-block cut, then fail */ if (cb->nblks != 1) { return size; } /* if too big, return the size now, without doing anything */ if (cb->end - cb->start >= size) { return cb->end - cb->start; } /* get the block */ readcutblk(cb, 0); /* isolate the string within that blk */ if (cb->start == 0) { tmpblk.c[cb->end] = '\0'; } else { for (dest = tmpblk.c, src = dest + cb->start; src < tmpblk.c + cb->end; ) { *dest++ = *src++; } *dest = '\0'; } /* copy the string into the buffer */ if (buf != tmpblk.c) { strcpy(buf, tmpblk.c); } /* return the length */ return cb->end - cb->start; } #endif SHAR_EOF fi # end of overwriting check # End of shell archive exit 0