Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!usc!bbn.com!orc!inews!iwarp.intel.com!psueea!eecs.cs.pdx.edu!kirkenda From: kirkenda@eecs.cs.pdx.edu (Steve Kirkendall) Newsgroups: alt.sources Subject: elvis 1.3 - a clone of vi/ex, part 6 of 6 Message-ID: <3281@psueea.UUCP> Date: 24 Aug 90 18:00:44 GMT Sender: news@psueea.UUCP Reply-To: kirkenda@eecs.cs.pdx.edu (Steve Kirkendall) Organization: Portland State University, Portland, OR Lines: 3133 Archive-name: elvis1.3/part6 #! /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: # vars.c # vcmd.c # vi.c # vi.h # ctags.c # ref.c # virec.c # wildcard.c # shell.c # This archive created: Fri Aug 24 10:30:00 1990 export PATH; PATH=/bin:/usr/bin:$PATH if test -f 'vars.c' then echo shar: "will not over-write existing file 'vars.c'" else cat << \SHAR_EOF > 'vars.c' /* vars.c */ /* Author: * Steve Kirkendall * 16820 SW Tallac Way * Beaverton, OR 97006 * kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda */ /* This file contains variables which weren't happy anyplace else */ #include "config.h" #include "vi.h" /*------------------------------------------------------------------------*/ /* used to remember whether the file has been modified */ struct _viflags viflags; /* used to access the tmp file */ long lnum[MAXBLKS]; long nlines; int tmpfd = -1; /* used to keep track of the current file & alternate file */ long origtime; char origname[256]; char prevorig[256]; long prevline = 1; /* used to track various places in the text */ MARK mark[NMARKS]; /* marks 'a through 'z, plus mark '' */ MARK cursor; /* the cursor position within the file */ /* which mode of the editor we're in */ int mode; /* vi mode? ex mode? quitting? */ /* used to manage the args list */ char args[BLKSIZE]; /* list of filenames to edit */ int argno; /* index of current file in args list */ int nargs; /* number of filenames in args[] */ /* dummy var, never explicitly referenced */ int bavar; /* used only in BeforeAfter macros */ /* have we made a multi-line change? */ int mustredraw; /* must we redraw the whole screen? */ long redrawafter; /* line# of first line that must be redrawn */ long preredraw; /* line# of last line changed, before change */ long postredraw; /* line# of last line changed, after change */ /* (postredraw - preredraw) = #lines added */ /* used to detect changes that invalidate cached text/blocks */ long changes; /* incremented when file is changed */ /* used to support the pfetch() macro */ int plen; /* length of the line */ long pline; /* line number that len refers to */ long pchgs; /* "changes" level that len refers to */ char *ptext; /* text of previous line, if valid */ /* misc temporary storage - mostly for strings */ BLK tmpblk; /* a block used to accumulate changes */ /* screen oriented stuff */ long topline; /* file line number of top line */ int leftcol; /* column number of left col */ int physcol; /* physical column number that cursor is on */ int physrow; /* physical row number that cursor is on */ /* used to help minimize that "[Hit a key to continue]" message */ int exwrote; /* Boolean: was the last ex command wordy? */ /* This variable affects the behaviour of certain functions -- most importantly * the input function. */ int doingdot; /* boolean: are we doing the "." command? */ /* These are used for reporting multi-line changes to the user */ long rptlines; /* number of lines affected by a command */ char *rptlabel; /* description of how lines were affected */ /* These store info that pertains to the shift-U command */ long U_line; /* line# of the undoable line, or 0l for none */ char U_text[BLKSIZE]; /* contents of the undoable line */ /* Bigger stack req'ed for TOS */ #if TOS long _stksize = 16384; #endif SHAR_EOF fi if test -f 'vcmd.c' then echo shar: "will not over-write existing file 'vcmd.c'" else cat << \SHAR_EOF > 'vcmd.c' /* vcmd.c */ /* Author: * Steve Kirkendall * 16820 SW Tallac Way * Beaverton, OR 97006 * kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda */ /* This file contains the functions that handle VI commands */ #include "config.h" #include "vi.h" #if MSDOS #include #include #endif #if TOS #include #include #endif /* This function puts the editor in EX mode */ MARK v_quit() { mode = MODE_EX; return cursor; } /* This function causes the screen to be redrawn */ MARK v_redraw() { redraw(MARK_UNSET, FALSE); return cursor; } /* This function executes a single EX command, and waits for a user keystroke * before returning to the VI screen. If that keystroke is another ':', then * another EX command is read and executed. */ /*ARGSUSED*/ MARK v_1ex(m, text) MARK m; /* the current line */ char *text; /* the first command to execute */ { /* scroll up, so we don't overwrite the command */ if (mode == MODE_COLON) { addch('\n'); refresh(); } /* run the command. be careful about modes & output */ exwrote = (mode == MODE_COLON); doexcmd(text); exrefresh(); /* if mode is no longer MODE_VI, then we should quit right away! */ if (mode != MODE_VI && mode != MODE_COLON) return cursor; /* The command did some output. Wait for a keystoke. */ if (exwrote) { mode = MODE_VI; msg("[Hit any key to continue]"); if (getkey(0) == ':') { mode = MODE_COLON; addch('\n'); } else redraw(MARK_UNSET, FALSE); } return cursor; } /* This function undoes the last change */ /*ARGSUSED*/ MARK v_undo(m) MARK m; /* (ignored) */ { undo(); redraw(MARK_UNSET, FALSE); return cursor; } /* This function deletes the character(s) that the cursor is on */ MARK v_xchar(m, cnt) MARK m; /* where to start deletions */ long cnt; /* number of chars to delete */ { DEFAULT(1); pfetch(markline(m)); if (markidx(m + cnt) > plen) { cnt = plen - markidx(m); } if (cnt == 0L) { return MARK_UNSET; } ChangeText { cut(m, m + cnt); delete(m, m + cnt); } return m; } /* This function deletes character to the left of the cursor */ MARK v_Xchar(m, cnt) MARK m; /* where deletions end */ long cnt; /* number of chars to delete */ { DEFAULT(1); /* if we're at the first char of the line, error! */ if (markidx(m) == 0) { return MARK_UNSET; } /* make sure we don't try to delete more chars than there are */ if (cnt > markidx(m)) { cnt = markidx(m); } /* delete 'em */ ChangeText { cut(m - cnt, m); delete(m - cnt, m); } return m - cnt; } /* This function defines a mark */ /*ARGSUSED*/ MARK v_mark(m, count, key) MARK m; /* where the mark will be */ long count; /* (ignored) */ int key; /* the ASCII label of the mark */ { if (key < 'a' || key > 'z') { msg("Marks must be from a to z"); } else { mark[key - 'a'] = m; } return m; } /* This function toggles upper & lower case letters */ MARK v_ulcase(m) MARK m; /* where to make the change */ { char new[2]; #if MSDOS || TOS char *pos; char *lower="\207\201\202\204\206\221\224\244"; char *upper="\200\232\220\216\217\222\231\245"; #endif /* extract the char that's there now */ pfetch(markline(m)); new[0] = ptext[markidx(m)]; new[1] = '\0'; /* change it if necessary */ if (new[0] >= 'a' && new[0] <= 'z' || new[0] >= 'A' && new[0] <= 'Z') { new[0] ^= ('A' ^ 'a'); ChangeText { change(m, m + 1, new); } } #if MSDOS || TOS if ((pos=strchr(lower, new[0]))!=0) new[0]=upper[(int)(pos-lower)]; else if ((pos=strchr(upper, new[0]))!=0) new[0]=lower[(int)(pos-upper)]; else goto nochange; /* Urghh - GB */ ChangeText { change(m, m + 1, new); } nochange: #endif if (new[0] && ptext[markidx(m) + 1]) { m++; } return m; } MARK v_replace(m, cnt, key) MARK m; /* first char to be replaced */ long cnt; /* number of chars to replace */ int key; /* what to replace them with */ { register char *text; register int i; static int samekey; DEFAULT(1); /* map ^M to '\n' */ if (key == '\r') { key = '\n'; } else if (key == ctrl('V')) { if (doingdot) key = samekey; else key = samekey = getkey(0); if (key == 0) return MARK_UNSET; } else if (!doingdot && key == ctrl('[')) { samekey = 0; return MARK_UNSET; } /* make sure the resulting line isn't too long */ if (cnt > BLKSIZE - 2 - markidx(m)) { cnt = BLKSIZE - 2 - markidx(m); } /* build a string of the desired character with the desired length */ for (text = tmpblk.c, i = cnt; i > 0; i--) { *text++ = key; } *text = '\0'; /* make sure cnt doesn't extend past EOL */ pfetch(markline(m)); key = markidx(m); if (key + cnt > plen) { cnt = plen - key; } /* do the replacement */ ChangeText { change(m, m + cnt, tmpblk.c); } if (*tmpblk.c == '\n') { return (m & ~(BLKSIZE - 1)) + cnt * BLKSIZE; } else { return m + cnt - 1; } } MARK v_overtype(m) MARK m; /* where to start overtyping */ { MARK end; /* end of a substitution */ static long width; /* width of a single-line replace */ /* the "doingdot" version of replace is really a substitution */ if (doingdot) { /* was the last one really repeatable? */ if (width < 0) { msg("Can't repeat a multi-line overtype command"); return MARK_UNSET; } /* replacing nothing by nothing? Don't bother */ if (width == 0) { return m; } /* replace some chars by repeated text */ return v_subst(m, width); } /* Normally, we input starting here, in replace mode */ ChangeText { end = input(m, m, WHEN_VIREP); } /* if we ended on the same line we started on, then this * overtype is repeatable via the dot key. */ if (markline(end) == markline(m) && end >= m - 1L) { width = end - m + 1L; } else /* it isn't repeatable */ { width = -1L; } return end; } /* This function selects which cut buffer to use */ /*ARGSUSED*/ MARK v_selcut(m, cnt, key) MARK m; long cnt; int key; { cutname(key); return m; } /* This function pastes text from a cut buffer */ /*ARGSUSED*/ MARK v_paste(m, cnt, cmd) MARK m; /* where to paste the text */ long cnt; /* (ignored) */ int cmd; /* either 'p' or 'P' */ { ChangeText { m = paste(m, cmd == 'p', FALSE); } return m; } /* This function yanks text into a cut buffer */ MARK v_yank(m, n) MARK m, n; /* range of text to yank */ { cut(m, n); return m; } /* This function deletes a range of text */ MARK v_delete(m, n) MARK m, n; /* range of text to delete */ { /* illegal to try and delete nothing */ if (n <= m) { return MARK_UNSET; } /* Do it */ ChangeText { cut(m, n); delete(m, n); } return m; } /* This starts input mode without deleting anything */ MARK v_insert(m, cnt, key) MARK m; /* where to start (sort of) */ long cnt; /* repeat how many times? */ int key; /* what command is this for? {a,A,i,I,o,O} */ { int wasdot; long reps; DEFAULT(1); ChangeText { /* tweak the insertion point, based on command key */ switch (key) { case 'i': break; case 'a': pfetch(markline(m)); if (plen > 0) { m++; } break; case 'I': m = m_front(m, 1L); break; case 'A': pfetch(markline(m)); m = (m & ~(BLKSIZE - 1)) + plen; break; case 'O': m &= ~(BLKSIZE - 1); add(m, "\n"); break; case 'o': m = (m & ~(BLKSIZE - 1)) + BLKSIZE; add(m, "\n"); break; } /* insert the same text once or more */ for (reps = cnt, wasdot = doingdot; reps > 0; reps--, doingdot = TRUE) { m = input(m, m, WHEN_VIINP); } /* compensate for inaccurate redraw clues from input() */ if ((key == 'O' || key == 'o') && wasdot) { redraw(MARK_UNSET); } doingdot = FALSE; } return m; } /* This starts input mode with some text deleted */ MARK v_change(m, n) MARK m, n; /* the range of text to change */ { int lnmode; /* is this a line-mode change? */ /* swap them if they're in reverse order */ if (m > n) { MARK tmp; tmp = m; m = n; n = tmp; } /* for line mode, retain the last newline char */ lnmode = (markidx(m) == 0 && markidx(n) == 0 && m != n); if (lnmode) { n -= BLKSIZE; pfetch(markline(n)); n = (n & ~(BLKSIZE - 1)) + plen; } ChangeText { cut(m, n); m = input(m, n, WHEN_VIINP); } /* compensate for inaccurate redraw clues from paste() */ if (doingdot) { preredraw = markline(n); if (lnmode) { preredraw++; postredraw++; } } return m; } /* This function replaces a given number of characters with input */ MARK v_subst(m, cnt) MARK m; /* where substitutions start */ long cnt; /* number of chars to replace */ { DEFAULT(1); /* make sure we don't try replacing past EOL */ pfetch(markline(m)); if (markidx(m) + cnt > plen) { cnt = plen - markidx(m); } /* Go for it! */ ChangeText { cut(m, m + cnt); m = input(m, m + cnt, WHEN_VIINP); } return m; } /* This calls the ex "join" command to join some lines together */ MARK v_join(m, cnt) MARK m; /* the first line to be joined */ long cnt; /* number of other lines to join */ { MARK joint; /* where the lines were joined */ DEFAULT(1); /* figure out where the joint will be */ pfetch(markline(m)); joint = (m & ~(BLKSIZE - 1)) + plen; /* join the lines */ cmd_join(m, m + MARK_AT_LINE(cnt), CMD_JOIN, 0, ""); mustredraw = TRUE; /* the cursor should be left at the joint */ return joint; } /* This calls the ex shifter command to shift some lines */ static MARK shift_help(m, n, excmd) MARK m, n; /* range of lines to shift */ CMD excmd; /* which way do we shift? */ { /* make sure our endpoints aren't in reverse order */ if (m > n) { MARK tmp; tmp = n; n = m; m = tmp; } /* linemode? adjust for inclusive endmarks in ex */ if (markidx(m) == 0 && markidx(n) == 0) { n -= BLKSIZE; } cmd_shift(m, n, excmd, 0, ""); return m; } /* This calls the ex "<" command to shift some lines left */ MARK v_lshift(m, n) MARK m, n; /* range of lines to shift */ { return shift_help(m, n, CMD_SHIFTL); } /* This calls the ex ">" command to shift some lines right */ MARK v_rshift(m, n) MARK m, n; /* range of lines to shift */ { return shift_help(m, n, CMD_SHIFTR); } /* This runs some lines through a filter program */ MARK v_filter(m, n) MARK m, n; /* range of lines to shift */ { char cmdln[100]; /* a shell command line */ /* linemode? adjust for inclusive endmarks in ex */ if (markidx(m) == 0 && markidx(n) == 0) { n -= BLKSIZE; } if (vgets('!', cmdln, sizeof(cmdln)) > 0) { filter(m, n, cmdln); } redraw(MARK_UNSET, FALSE); return m; } /* This function runs the ex "file" command to show the file's status */ MARK v_status() { cmd_file(cursor, cursor, CMD_FILE, 0, ""); return cursor; } /* This function switches to the previous file, if possible */ MARK v_switch() { if (!*prevorig) msg("No previous file"); else { strcpy(tmpblk.c, prevorig); cmd_edit(cursor, cursor, CMD_EDIT, 0, tmpblk.c); } return cursor; } /* This function does a tag search on a keyword */ /*ARGSUSED*/ MARK v_tag(keyword, m, cnt) char *keyword; MARK m; long cnt; { /* remember the initial change level */ cnt = changes; /* move the cursor to the start of the tag name, where m is */ cursor = m; /* perform the tag search */ cmd_tag(cursor, cursor, CMD_TAG, 0, keyword); return cursor; } #ifndef NO_EXTENSIONS /* This function looks up a keyword by calling the helpprog program */ /*ARGSUSED*/ MARK v_keyword(keyword, m, cnt) char *keyword; MARK m; long cnt; { int status; #if TOS char cmdline[130]; #endif move(LINES - 1, 0); addstr("---------------------------------------------------------\n"); clrtoeol(); refresh(); suspend_curses(); #if ANY_UNIX switch (fork()) { case -1: /* error */ break; case 0: /* child */ execl(o_keywordprg, o_keywordprg, keyword, (char *)0); exit(2); /* if we get here, the exec failed */ default: /* parent */ wait(&status); if (status > 0) { write(2, "<<< failed >>>\n", 15); } } #endif #if MSDOS if ((status=spawnlp(P_WAIT, o_keywordprg, o_keywordprg, keyword, (char *)0))==-1) write(2, "<<< failed >>>\n", 15); #endif #if TOS strcpy(cmdline+1, keyword); cmdline[0]=strlen(keyword); if ((status=Pexec(0, o_keywordprg, cmdline, "\0"))<0) write(2, "<<< failed >>>\n", 15); #endif resume_curses(FALSE); /* "resume, but not quietly" */ redraw(MARK_UNSET, FALSE); return m; } MARK v_increment(keyword, m, cnt) char *keyword; MARK m; long cnt; { static sign; char newval[12]; long atol(); DEFAULT(1); /* get one more keystroke, unless doingdot */ if (!doingdot) { sign = getkey(0); } /* adjust the number, based on that second keystroke */ switch (sign) { case '+': case '#': cnt = atol(keyword) + cnt; break; case '-': cnt = atol(keyword) - cnt; break; case '=': break; default: return MARK_UNSET; } sprintf(newval, "%ld", cnt); ChangeText { change(m, m + strlen(keyword), newval); } return m; } #endif /* This function acts like the EX command "xit" */ /*ARGSUSED*/ MARK v_xit(m, cnt, key) MARK m; /* ignored */ long cnt; /* ignored */ int key; /* must be a second 'Z' */ { /* if second char wasn't 'Z', fail */ if (key != 'Z') { return MARK_UNSET; } /* move the physical cursor to the end of the screen */ move(LINES - 1, 0); clrtoeol(); refresh(); /* do the xit command */ cmd_xit(m, m, CMD_XIT, FALSE, ""); /* if we're really going to quit, then scroll the screen up 1 line */ if (mode == MODE_QUIT) { addch('\n'); refresh(); } /* regardless of whether we succeeded or failed, return the cursor */ return m; } /* This function undoes changes to a single line, if possible */ MARK v_undoline(m) MARK m; /* where we hope to undo the change */ { if (markline(m) != U_line) { return MARK_UNSET; } ChangeText { changeline(U_line, U_text); } return m & ~(BLKSIZE - 1); } SHAR_EOF fi if test -f 'vi.c' then echo shar: "will not over-write existing file 'vi.c'" else cat << \SHAR_EOF > 'vi.c' /* vi.c */ /* Author: * Steve Kirkendall * 16820 SW Tallac Way * Beaverton, OR 97006 * kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda */ #include "config.h" #include #include "vi.h" /* This array describes what each key does */ #define NO_FUNC (MARK (*)())0 #define NO_ARGS 0 #define CURSOR_COUNT 1 #define CURSOR 2 #define CURSOR_CNT_KEY 3 #define CURSOR_MOVED 4 #define CURSOR_EOL 5 #define ZERO 6 #define DIGIT 7 #define CURSOR_TEXT 8 #define CURSOR_CNT_CMD 9 #define KEYWORD 10 #define NO_FLAGS 0x00 #define MVMT 0x01 /* this is a movement command */ #define PTMV 0x02 /* this can be *part* of a movement command */ #define FRNT 0x04 /* after move, go to front of line */ #define INCL 0x08 /* include last char when used with c/d/y */ #define LNMD 0x10 /* use line mode of c/d/y */ #define NCOL 0x20 /* this command can't change the column# */ #define NREL 0x40 /* this is "non-relative" -- set the '' mark */ #define SDOT 0x80 /* set the "dot" variables, for the "." cmd */ static struct keystru { MARK (*func)(); /* the function to run */ char args; /* description of the args needed */ char flags; /* other stuff */ } vikeys[] = { /* NUL not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* ^A not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* ^B page backward */ {m_scroll, CURSOR_CNT_CMD, FRNT}, /* ^C not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* ^D scroll dn 1/2page*/ {m_scroll, CURSOR_CNT_CMD, NCOL}, /* ^E scroll up */ {m_scroll, CURSOR_CNT_CMD, NCOL}, /* ^F page forward */ {m_scroll, CURSOR_CNT_CMD, FRNT}, /* ^G show file status */ {v_status, NO_ARGS, NO_FLAGS}, /* ^H move left, like h*/ {m_left, CURSOR_COUNT, MVMT}, /* ^I not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* ^J move down */ {m_down, CURSOR_COUNT, MVMT|LNMD}, /* ^K not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* ^L redraw screen */ {v_redraw, NO_ARGS, NO_FLAGS}, /* ^M mv front next ln */ {m_down, CURSOR_COUNT, MVMT|FRNT|LNMD}, /* ^N move down */ {m_down, CURSOR_COUNT, MVMT|LNMD}, /* ^O not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* ^P not defined */ {m_up, CURSOR_COUNT, MVMT|LNMD}, /* ^Q not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* ^R redraw screen */ {v_redraw, NO_ARGS, NO_FLAGS}, /* ^S not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* ^T not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* ^U scroll up 1/2page*/ {m_scroll, CURSOR_CNT_CMD, NCOL}, /* ^V not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* ^W not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* ^X not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* ^Y scroll down */ {m_scroll, CURSOR_CNT_CMD, NCOL}, /* ^Z not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* ESC not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* ^\ not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* ^] keyword is tag */ {v_tag, KEYWORD, NO_FLAGS}, /* ^^ previous file */ {v_switch, CURSOR, NO_FLAGS}, /* ^_ not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* SPC move right,like l*/ {m_right, CURSOR_COUNT, MVMT}, /* ! run thru filter */ {v_filter, CURSOR_MOVED, NO_FLAGS}, /* " select cut buffer*/ {v_selcut, CURSOR_CNT_KEY, PTMV}, #ifndef NO_EXTENSIONS /* # increment number */ {v_increment, KEYWORD, SDOT}, #else /* # not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, #endif /* $ move to rear */ {m_rear, CURSOR, MVMT|INCL}, /* % move to match */ {m_match, CURSOR, MVMT|INCL}, /* & not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* ' move to a mark */ {m_tomark, CURSOR_CNT_KEY, MVMT|FRNT|NREL|LNMD}, #ifndef NO_SENTENCE /* ( mv back sentence */ {m_bsentence, CURSOR_COUNT, MVMT}, /* ) mv fwd sentence */ {m_fsentence, CURSOR_COUNT, MVMT}, #else /* ( not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* ) not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, #endif /* * not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* + mv front next ln */ {m_down, CURSOR_COUNT, MVMT|FRNT|LNMD}, #ifndef NO_CHARSEARCH /* , reverse [fFtT] cmd*/ {m__ch, CURSOR_CNT_CMD, MVMT|INCL}, #else /* , not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, #endif /* - mv front prev ln */ {m_up, CURSOR_COUNT, MVMT|FRNT|LNMD}, /* . special... */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* / forward search */ {m_fsrch, CURSOR_TEXT, MVMT|NREL}, /* 0 part of count? */ {NO_FUNC, ZERO, MVMT|PTMV}, /* 1 part of count */ {NO_FUNC, DIGIT, PTMV}, /* 2 part of count */ {NO_FUNC, DIGIT, PTMV}, /* 3 part of count */ {NO_FUNC, DIGIT, PTMV}, /* 4 part of count */ {NO_FUNC, DIGIT, PTMV}, /* 5 part of count */ {NO_FUNC, DIGIT, PTMV}, /* 6 part of count */ {NO_FUNC, DIGIT, PTMV}, /* 7 part of count */ {NO_FUNC, DIGIT, PTMV}, /* 8 part of count */ {NO_FUNC, DIGIT, PTMV}, /* 9 part of count */ {NO_FUNC, DIGIT, PTMV}, /* : run single EX cmd*/ {v_1ex, CURSOR_TEXT, NO_FLAGS}, #ifndef NO_CHARSEARCH /* ; repeat [fFtT] cmd*/ {m__ch, CURSOR_CNT_CMD, MVMT|INCL}, #else /* ; not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, #endif /* < shift text left */ {v_lshift, CURSOR_MOVED, SDOT|FRNT}, /* = not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* > shift text right */ {v_rshift, CURSOR_MOVED, SDOT|FRNT}, /* ? backward search */ {m_bsrch, CURSOR_TEXT, MVMT|NREL}, /* @ not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* A append at EOL */ {v_insert, CURSOR_CNT_CMD, SDOT}, /* B move back Word */ {m_bWord, CURSOR_COUNT, MVMT}, /* C change to EOL */ {v_change, CURSOR_EOL, SDOT}, /* D delete to EOL */ {v_delete, CURSOR_EOL, SDOT}, /* E move end of Word */ {m_eWord, CURSOR_COUNT, MVMT|INCL}, #ifndef NO_CHARSEARCH /* F move bk to char */ {m_Fch, CURSOR_CNT_KEY, MVMT|INCL}, #else /* F not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, #endif /* G move to line # */ {m_toline, CURSOR_COUNT, MVMT|NREL|LNMD}, /* H move to row */ {m_row, CURSOR_CNT_CMD, MVMT|FRNT}, /* I insert at front */ {v_insert, CURSOR_CNT_CMD, SDOT}, /* J join lines */ {v_join, CURSOR_COUNT, SDOT}, #ifndef NO_EXTENSIONS /* K look up keyword */ {v_keyword, KEYWORD, NO_FLAGS}, #else /* K not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, #endif /* L move to last row */ {m_row, CURSOR_CNT_CMD, MVMT|FRNT}, /* M move to mid row */ {m_row, CURSOR_CNT_CMD, MVMT|FRNT}, /* N reverse prev srch*/ {m_Nsrch, CURSOR, MVMT}, /* O insert above line*/ {v_insert, CURSOR_CNT_CMD, SDOT}, /* P paste before */ {v_paste, CURSOR_CNT_CMD, NO_FLAGS}, /* Q quit to EX mode */ {v_quit, NO_ARGS, NO_FLAGS}, /* R overtype */ {v_overtype, CURSOR, SDOT}, /* S change line */ {v_change, CURSOR_MOVED, SDOT}, #ifndef NO_CHARSEARCH /* T move bk to char */ {m_Tch, CURSOR_CNT_KEY, MVMT|INCL}, #else /* T not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, #endif /* U undo whole line */ {v_undoline, CURSOR, FRNT}, /* V not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* W move forward Word*/ {m_fWord, CURSOR_COUNT, MVMT}, /* X delete to left */ {v_Xchar, CURSOR_COUNT, SDOT}, /* Y yank text */ {v_yank, CURSOR_MOVED, NO_FLAGS}, /* Z save file & exit */ {v_xit, CURSOR_CNT_KEY, NO_FLAGS}, /* [ move back section*/ {m_bsection, CURSOR_CNT_KEY, MVMT|LNMD|NREL}, /* \ not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* ] move fwd section */ {m_fsection, CURSOR_CNT_KEY, MVMT|LNMD|NREL}, /* ^ move to front */ {m_front, CURSOR, MVMT}, /* _ not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* ` move to mark */ {m_tomark, CURSOR_CNT_KEY, MVMT|NREL}, /* a append at cursor */ {v_insert, CURSOR_CNT_CMD, SDOT}, /* b move back word */ {m_bword, CURSOR_COUNT, MVMT}, /* c change text */ {v_change, CURSOR_MOVED, SDOT}, /* d delete op */ {v_delete, CURSOR_MOVED, SDOT}, /* e move end word */ {m_eword, CURSOR_COUNT, MVMT|INCL}, #ifndef NO_CHARSEARCH /* f move fwd for char*/ {m_fch, CURSOR_CNT_KEY, MVMT|INCL}, #else /* f not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, #endif /* g not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* h move left */ {m_left, CURSOR_COUNT, MVMT}, /* i insert at cursor */ {v_insert, CURSOR_CNT_CMD, SDOT}, /* j move down */ {m_down, CURSOR_COUNT, MVMT|NCOL|LNMD}, /* k move up */ {m_up, CURSOR_COUNT, MVMT|NCOL|LNMD}, /* l move right */ {m_right, CURSOR_COUNT, MVMT}, /* m define a mark */ {v_mark, CURSOR_CNT_KEY, NO_FLAGS}, /* n repeat prev srch */ {m_nsrch, CURSOR, MVMT}, /* o insert below line*/ {v_insert, CURSOR_CNT_CMD, SDOT}, /* p paste after */ {v_paste, CURSOR_CNT_CMD, NO_FLAGS}, /* q not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* r replace chars */ {v_replace, CURSOR_CNT_KEY, SDOT}, /* s subst N chars */ {v_subst, CURSOR_COUNT, SDOT}, #ifndef NO_CHARSEARCH /* t move fwd to char */ {m_tch, CURSOR_CNT_KEY, MVMT|INCL}, #else /* t not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, #endif /* u undo */ {v_undo, CURSOR, NO_FLAGS}, /* v not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* w move fwd word */ {m_fword, CURSOR_COUNT, MVMT}, /* x delete character */ {v_xchar, CURSOR_COUNT, SDOT}, /* y yank text */ {v_yank, CURSOR_MOVED, NO_FLAGS}, /* z adjust scrn row */ {m_z, CURSOR_CNT_KEY, NCOL}, /* { back paragraph */ {m_bparagraph, CURSOR_COUNT, MVMT|LNMD}, /* | move to column */ {m_tocol, CURSOR_COUNT, NREL}, /* } fwd paragraph */ {m_fparagraph, CURSOR_COUNT, MVMT|LNMD}, /* ~ upper/lowercase */ {v_ulcase, CURSOR, SDOT}, /* DEL not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, }; vi() { register int key; /* keystroke from user */ long count; /* numeric argument to some functions */ register struct keystru *keyptr;/* pointer to vikeys[] element */ MARK tcurs; /* temporary cursor */ int prevkey;/* previous key, if d/c/y//! */ MARK range; /* start of range for d/c/y//! */ char text[100]; int dotkey; /* last "key" of a change */ int dotpkey;/* last "prevkey" of a change */ int dotkey2;/* last extra "getkey()" of a change */ int dotcnt; /* last "count" of a change */ int firstkey; register int i; /* tell the redraw() function to start from scratch */ redraw(MARK_UNSET, FALSE); msg((char *)0); #ifdef lint /* lint says that "range" might be used before it is set. This * can't really happen due to the way "range" and "prevkey" are used, * but lint doesn't know that. This line is here ONLY to keep lint * happy. */ range = 0L; #endif /* safeguard against '.' with no previous command */ dotkey = 0; #ifndef NO_EXTENSIONS /* go immediately into insert mode, if ":set inputmode" */ firstkey = 0; if (*o_inputmode) { firstkey = 'i'; } #endif /* Repeatedly handle VI commands */ for (count = 0, prevkey = '\0'; mode == MODE_VI; ) { /* if we've moved off the undoable line, then we can't undo it at all */ if (markline(cursor) != U_line) { U_line = 0L; } /* report any changes from the previous command */ if (rptlines >= *o_report) { redraw(cursor, FALSE); msg("%ld lines %s", rptlines, rptlabel); } rptlines = 0L; /* get the next command key. It must be ASCII */ if (firstkey) { key = firstkey; firstkey = 0; } else { do { key = getkey(WHEN_VICMD); } while (key < 0 || key > 127); } /* change cw and cW commands to ce and cE, respectively */ /* (Why? because the real vi does it that way!) */ if (prevkey == 'c') { if (key == 'w') key = 'e'; else if (key == 'W') key = 'E'; /* wouldn't work right at the end of a word unless we * backspace one character before doing the move. This * will fix most cases. */ if (markidx(cursor) > 0 && (key == 'e' || key == 'E')) { cursor--; } } /* look up the structure describing this command */ keyptr = &vikeys[key]; /* if we're in the middle of a d/c/y//! command, reject * anything but movement or a doubled version like "dd". */ if (prevkey && key != prevkey && !(keyptr->flags & (MVMT|PTMV))) { beep(); prevkey = 0; count = 0; continue; } /* set the "dot" variables, if we're supposed to */ if ((keyptr->flags & SDOT) || (prevkey && vikeys[prevkey].flags & SDOT)) { dotkey = key; dotpkey = prevkey; dotkey2 = '\0'; dotcnt = count; /* remember the line before any changes are made */ if (U_line != markline(cursor)) { U_line = markline(cursor); strcpy(U_text, fetchline(U_line)); } } /* if this is "." then set other vars from the "dot" vars */ if (key == '.') { key = dotkey; keyptr = &vikeys[key]; prevkey = dotpkey; if (prevkey) { range = cursor; } if (count == 0) { count = dotcnt; } doingdot = TRUE; /* remember the line before any changes are made */ if (U_line != markline(cursor)) { U_line = markline(cursor); strcpy(U_text, fetchline(U_line)); } } else { doingdot = FALSE; } /* process the key as a command */ tcurs = cursor; switch (keyptr->args) { case ZERO: if (count == 0) { tcurs = cursor & ~(BLKSIZE - 1); break; } /* else fall through & treat like other digits... */ case DIGIT: count = count * 10 + key - '0'; break; case KEYWORD: /* if not on a keyword, fail */ pfetch(markline(cursor)); key = markidx(cursor); if (isascii(ptext[key]) && !isalnum(ptext[key]) && ptext[key] != '_') { tcurs = MARK_UNSET; break; } /* find the start of the keyword */ while (key > 0 && (!isascii(ptext[key-1]) || isalnum(ptext[key - 1]) || ptext[key - 1] == '_')) { key--; } tcurs = (cursor & ~(BLKSIZE - 1)) + key; /* copy it into a buffer, and NUL-terminate it */ i = 0; do { text[i++] = ptext[key++]; } while (!isascii(ptext[key]) || isalnum(ptext[key]) || ptext[key] == '_'); text[i] = '\0'; /* call the function */ tcurs = (*keyptr->func)(text, tcurs, count); count = 0L; break; case NO_ARGS: if (keyptr->func) { (*keyptr->func)(); } else { beep(); } count = 0L; break; case CURSOR_COUNT: tcurs = (*keyptr->func)(cursor, count); count = 0L; break; case CURSOR: tcurs = (*keyptr->func)(cursor); count = 0L; break; case CURSOR_CNT_KEY: if (doingdot) { tcurs = (*keyptr->func)(cursor, count, dotkey2); } else if (keyptr->flags & SDOT || (prevkey && vikeys[prevkey].flags & SDOT)) { dotkey2 = getkey(0); tcurs = (*keyptr->func)(cursor, count, dotkey2); } else { tcurs = (*keyptr->func)(cursor, count, getkey(0)); } count = 0L; break; case CURSOR_MOVED: /* uppercase keys always act like doubled */ if (isascii(key) && isupper(key)) { prevkey = key; range = cursor; } if (prevkey) { /* doubling up a command, use complete lines */ range &= ~(BLKSIZE - 1); if (count) { tcurs = range + MARK_AT_LINE(count); count = 0; } else { tcurs = range + BLKSIZE; } } else { prevkey = key; range = cursor; key = -1; /* so we don't think we doubled yet */ } break; case CURSOR_EOL: prevkey = key; /* a zero-length line needs special treatment */ pfetch(markline(cursor)); if (plen == 0) { /* act on a zero-length section of text */ range = tcurs = cursor; key = ' '; } else { /* act like CURSOR_MOVED with '$' movement */ range = cursor; tcurs = m_rear(cursor, 1L); key = '$'; } count = 0L; keyptr = &vikeys[key]; break; case CURSOR_TEXT: do { if (vgets(key, text, sizeof text) >= 0) { /* reassure user that was hit */ qaddch('\r'); refresh(); /* call the function with the text */ tcurs = (*keyptr->func)(cursor, text); } else mode = MODE_VI; } while (mode == MODE_COLON); count = 0L; break; case CURSOR_CNT_CMD: tcurs = (*keyptr->func)(cursor, count, key); count = 0L; break; } /* if that command took us out of vi mode, then exit the loop * NOW, without tweaking the cursor or anything. This is very * important when mode == MODE_QUIT. */ if (mode != MODE_VI) { break; } /* now move the cursor, as appropriate */ if (prevkey && markline(tcurs) > nlines) { /* destination for operator may be nlines + 1 */ cursor = MARK_AT_LINE(nlines + 1); } else if (keyptr->args == CURSOR_MOVED) { /* the < and > keys have FRNT, * but it shouldn't be applied yet */ cursor = adjmove(cursor, tcurs, 0); } else { cursor = adjmove(cursor, tcurs, keyptr->flags); } /* was that the end of a d/c/y//! command? */ if (prevkey && (prevkey == key || (keyptr->flags & MVMT))) { /* if the movement command failed, cancel operation */ if (tcurs == MARK_UNSET) { prevkey = 0; count = 0; continue; } /* make sure range=front and tcurs=rear */ if (cursor < range) { tcurs = range; range = cursor; } else { tcurs = cursor; } /* adjust for line mode */ if (keyptr->flags & LNMD) { range &= ~(BLKSIZE - 1); tcurs &= ~(BLKSIZE - 1); tcurs += BLKSIZE; } /* adjust for inclusion of last char */ if (keyptr->flags & INCL) { tcurs++; } /* temporarily move the cursor to "range" so that * beforedo() remembers the cursor's real location. * This is important if the user later does undo() */ cursor = range; /* run the function */ tcurs = (*vikeys[prevkey].func)(range, tcurs); cursor = adjmove(cursor, tcurs, vikeys[prevkey].flags); /* cleanup */ prevkey = 0; } } } /* This function adjusts the MARK value that they return; here we make sure * it isn't past the end of the line, and that the column hasn't been * *accidentally* changed. */ MARK adjmove(old, new, flags) MARK old; /* the cursor position before the command */ register MARK new; /* the cursor position after the command */ int flags; /* various flags regarding cursor mvmt */ { static int colno; /* the column number that we want */ register char *text; /* used to scan through the line's text */ register int i; /* if the command failed, bag it! */ if (new == MARK_UNSET) { beep(); return old; } /* if this is a non-relative movement, set the '' mark */ if (flags & NREL) { mark[26] = old; } /* make sure it isn't past the end of the file */ if (markline(new) < 1) { new = MARK_FIRST; } else if (markline(new) > nlines) { new = MARK_LAST; } /* fetch the new line */ pfetch(markline(new)); /* move to the front, if we're supposed to */ if (flags & FRNT) { new = m_front(new, 1L); } /* change the column#, or change the mark to suit the column# */ if (!(flags & NCOL)) { /* change the column# */ i = markidx(new); if (i == BLKSIZE - 1) { new &= ~(BLKSIZE - 1); if (plen > 0) { new += plen - 1; } colno = BLKSIZE * 8; /* one heck of a big colno */ } else if (plen > 0) { if (i >= plen) { new = (new & ~(BLKSIZE - 1)) + plen - 1; } colno = idx2col(new, ptext, FALSE); } else { new &= ~(BLKSIZE - 1); colno = 0; } } else { /* adjust the mark to get as close as possible to column# */ for (i = 0, text = ptext; i <= colno && *text; text++) { if (*text == '\t' && !*o_list) { i += *o_tabstop - (i % *o_tabstop); } else if (UCHAR(*text) < ' ' || *text == 127) { i += 2; } #ifndef NO_CHARATTR else if (*o_charattr && text[0] == '\\' && text[1] == 'f' && text[2]) { text += 2; /* plus one more in "for()" stmt */ } #endif else { i++; } } if (text > ptext) { text--; } new = (new & ~(BLKSIZE - 1)) + (int)(text - ptext); } return new; } SHAR_EOF fi if test -f 'vi.h' then echo shar: "will not over-write existing file 'vi.h'" else cat << \SHAR_EOF > 'vi.h' /* vi.h */ /* Author: * Steve Kirkendall * 16820 SW Tallac Way * Beaverton, OR 97006 * kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda */ /* This is the header file for my version of vi. */ #define VERSION "ELVIS 1.3, by Steve Kirkendall" #define COPYING "This version of ELVIS is freely redistributable." #include extern int errno; #if TOS #define ENOENT (-AEFILNF) #endif #if TOS # include # define O_RDONLY 0 # define O_WRONLY 1 # define O_RDWR 2 #else # include # include #endif #ifndef O_BINARY # define O_BINARY 0 #endif #include "curses.h" /*------------------------------------------------------------------------*/ /* Miscellaneous constants. */ #define INFINITY 2000000001L /* a very large integer */ #if MSDOS # define MAXMAPS 50 /* then we have lots of specials */ #else # define MAXMAPS 20 /* number of :map keys */ #endif #define LONGKEY 10 /* longest possible raw :map key */ #define MAXDIGS 30 /* number of :digraph combos */ #define MAXRCLEN 1000 /* longest possible .exrc file */ /*------------------------------------------------------------------------*/ /* These describe how temporary files are divided into blocks */ #define BLKSIZE 1024 /* size of blocks */ #define MAXBLKS (BLKSIZE / sizeof(unsigned short)) typedef union { char c[BLKSIZE]; /* for text blocks */ unsigned short n[MAXBLKS]; /* for the header block */ } BLK; /*------------------------------------------------------------------------*/ /* These are used manipulate BLK buffers. */ extern BLK hdr; /* buffer for the header block */ extern BLK blkbuf[2]; /* buffers for text blocks */ extern BLK *blkget(); /* given index into hdr.c[], reads block */ extern BLK *blkadd(); /* inserts a new block into hdr.c[] */ /*------------------------------------------------------------------------*/ /* These are used to keep track of various flags */ extern struct _viflags { short file; /* file flags */ } viflags; /* file flags */ #define NEWFILE 0x0001 /* the file was just created */ #define READONLY 0x0002 /* the file is read-only */ #define HADNUL 0x0004 /* the file contained NUL characters */ #define MODIFIED 0x0008 /* the file has been modified */ #define NOFILE 0x0010 /* no name is known for the current text */ #define ADDEDNL 0x0020 /* newlines were added to the file */ /* macros used to set/clear/test flags */ #define setflag(x,y) viflags.x |= y #define clrflag(x,y) viflags.x &= ~y #define tstflag(x,y) (viflags.x & y) #define initflags() viflags.file = 0; /* The options */ extern char o_autoindent[1]; extern char o_autowrite[1]; #ifndef NO_CHARATTR extern char o_charattr[1]; #endif extern char o_columns[3]; extern char o_directory[30]; extern char o_errorbells[1]; extern char o_exrefresh[1]; #ifndef NO_SENTENCE extern char o_hideformat[1]; #endif extern char o_ignorecase[1]; #ifndef NO_EXTENSIONS extern char o_inputmode[1]; #endif extern char o_keytime[3]; extern char o_keywordprg[80]; extern char o_lines[3]; extern char o_list[1]; #ifndef NO_MAGIC extern char o_magic[1]; #endif #ifndef NO_SENTENCE extern char o_paragraphs[30]; #endif #if MSDOS extern char o_pcbios[1]; #endif extern char o_readonly[1]; extern char o_report[3]; extern char o_scroll[3]; #ifndef NO_SENTENCE extern char o_sections[30]; #endif extern char o_shell[60]; extern char o_showmode[1]; extern char o_shiftwidth[3]; extern char o_sidescroll[3]; extern char o_sync[1]; extern char o_tabstop[3]; extern char o_term[30]; extern char o_vbell[1]; extern char o_warn[1]; extern char o_wrapmargin[3]; extern char o_wrapscan[1]; /*------------------------------------------------------------------------*/ /* These help support the single-line multi-change "undo" -- shift-U */ extern char U_text[BLKSIZE]; extern long U_line; /*------------------------------------------------------------------------*/ /* These are used to refer to places in the text */ typedef long MARK; #define markline(x) (long)((x) / BLKSIZE) #define markidx(x) (int)((x) & (BLKSIZE - 1)) #define MARK_UNSET ((MARK)0) #define MARK_FIRST ((MARK)BLKSIZE) #define MARK_LAST ((MARK)(nlines * BLKSIZE)) #define MARK_AT_LINE(x) ((MARK)((x) * BLKSIZE)) #define NMARKS 28 extern MARK mark[NMARKS]; /* marks 'a through 'z, plus mark '' */ extern MARK cursor; /* mark where line is */ /*------------------------------------------------------------------------*/ /* These are used to keep track of the current & previous files. */ extern long origtime; /* modification date&time of the current file */ extern char origname[256]; /* name of the current file */ extern char prevorig[256]; /* name of the preceding file */ extern long prevline; /* line number from preceding file */ /*------------------------------------------------------------------------*/ /* misc housekeeping variables & functions */ extern int tmpfd; /* fd used to access the tmp file */ extern long lnum[MAXBLKS]; /* last line# of each block */ extern long nlines; /* number of lines in the file */ extern char args[BLKSIZE]; /* file names given on the command line */ extern int argno; /* the current element of args[] */ extern int nargs; /* number of filenames in args */ extern long changes; /* counts changes, to prohibit short-cuts */ extern int mustredraw; /* boolean: force total redraw of screen? */ extern long redrawafter; /* line# of first line to redraw */ extern long preredraw; /* line# of last line changed, before change */ extern long postredraw; /* line# of last line changed, after change */ extern BLK tmpblk; /* a block used to accumulate changes */ extern long topline; /* file line number of top line */ extern int leftcol; /* column number of left col */ #define botline (topline + LINES - 2) #define rightcol (leftcol + COLS - 1) extern int physcol; /* physical column number that cursor is on */ extern int physrow; /* physical row number that cursor is on */ extern int exwrote; /* used to detect verbose ex commands */ extern int doingdot; /* boolean: are we doing the "." command? */ extern long rptlines; /* number of lines affected by a command */ extern char *rptlabel; /* description of how lines were affected */ extern char *fetchline(); /* read a given line from tmp file */ extern char *parseptrn(); /* isolate a regexp in a line */ extern MARK paste(); /* paste from cut buffer to a given point */ extern char *wildcard(); /* expand wildcards in filenames */ extern MARK input(); /* inserts characters from keyboard */ extern char *linespec(); /* finds the end of a /regexp/ string */ #define ctrl(ch) ((ch)&037) #ifndef NO_RECYCLE extern long allocate(); /* allocate a free block of the tmp file */ #endif extern int trapint(); /* trap handler for SIGINT */ /*------------------------------------------------------------------------*/ /* macros that are used as control structures */ #define BeforeAfter(before, after) for((before),bavar=1;bavar;(after),bavar=0) #define ChangeText BeforeAfter(beforedo(FALSE),afterdo()) extern int bavar; /* used only in BeforeAfter macros */ /*------------------------------------------------------------------------*/ /* These are the movement commands. Each accepts a mark for the starting */ /* location & number and returns a mark for the destination. */ extern MARK m_up(); /* k */ extern MARK m_down(); /* j */ extern MARK m_right(); /* h */ extern MARK m_left(); /* l */ extern MARK m_toline(); /* G */ extern MARK m_tocol(); /* | */ extern MARK m_front(); /* ^ */ extern MARK m_rear(); /* $ */ extern MARK m_fword(); /* w */ extern MARK m_bword(); /* b */ extern MARK m_eword(); /* e */ extern MARK m_fWord(); /* W */ extern MARK m_bWord(); /* B */ extern MARK m_eWord(); /* E */ extern MARK m_fparagraph(); /* } */ extern MARK m_bparagraph(); /* { */ extern MARK m_fsection(); /* ]] */ extern MARK m_bsection(); /* [[ */ extern MARK m_match(); /* % */ #ifndef NO_SENTENCE extern MARK m_fsentence(); /* ) */ extern MARK m_bsentence(); /* ( */ #endif extern MARK m_tomark(); /* 'm */ extern MARK m_nsrch(); /* n */ extern MARK m_Nsrch(); /* N */ extern MARK m_fsrch(); /* /regexp */ extern MARK m_bsrch(); /* ?regexp */ extern MARK m__ch(); /* ; , */ extern MARK m_fch(); /* f */ extern MARK m_tch(); /* t */ extern MARK m_Fch(); /* F */ extern MARK m_Tch(); /* T */ extern MARK m_row(); /* H L M */ extern MARK m_z(); /* z */ extern MARK m_scroll(); /* ^B ^F ^E ^Y ^U ^D */ /* Some stuff that is used by movement functions... */ extern MARK adjmove(); /* a helper fn, used by move fns */ /* This macro is used to set the default value of cnt */ #define DEFAULT(val) if (cnt < 1) cnt = (val) /* These are used to minimize calls to fetchline() */ extern int plen; /* length of the line */ extern long pline; /* line number that len refers to */ extern long pchgs; /* "changes" level that len refers to */ extern char *ptext; /* text of previous line, if valid */ extern void pfetch(); /* This is used to build a MARK that corresponds to a specific point in the * line that was most recently pfetch'ed. */ #define buildmark(text) (MARK)(BLKSIZE * pline + (int)((text) - ptext)) /*------------------------------------------------------------------------*/ /* These are used to handle EX commands. */ #define CMD_NULL 0 /* NOT A VALID COMMAND */ #define CMD_ABBR 1 /* "define an abbreviation" */ #define CMD_ARGS 2 /* "show me the args" */ #define CMD_APPEND 3 /* "insert lines after this line" */ #define CMD_BANG 4 /* "run a single shell command" */ #define CMD_COPY 5 /* "copy the selected text to a given place" */ #define CMD_CD 6 /* "change directories" */ #define CMD_CHANGE 7 /* "change some lines" */ #define CMD_DELETE 8 /* "delete the selected text" */ #define CMD_DIGRAPH 9 /* "add a digraph, or display them all" */ #define CMD_EDIT 10 /* "switch to a different file" */ #define CMD_FILE 11 /* "show the file's status" */ #define CMD_GLOBAL 12 /* "globally search & do a command" */ #define CMD_INSERT 13 /* "insert lines before the current line" */ #define CMD_JOIN 14 /* "join the selected line & the one after" */ #define CMD_LIST 15 /* "print lines, making control chars visible" */ #define CMD_MAP 16 /* "adjust the keyboard map" */ #define CMD_MARK 17 /* "mark this line" */ #define CMD_MKEXRC 18 /* "make a .exrc file" */ #define CMD_MOVE 19 /* "move the selected text to a given place" */ #define CMD_NEXT 20 /* "switch to next file in args" */ #define CMD_PRESERVE 21 /* "act as though vi crashed" */ #define CMD_PREVIOUS 22 /* "switch to the previous file in args" */ #define CMD_PRINT 23 /* "print the selected text" */ #define CMD_PUT 24 /* "insert any cut lines before this line" */ #define CMD_QUIT 25 /* "quit without writing the file" */ #define CMD_READ 26 /* "append the given file after this line */ #define CMD_RECOVER 27 /* "recover file after vi crashes" - USE -r FLAG */ #define CMD_REWIND 28 /* "rewind to first file" */ #define CMD_SET 29 /* "set a variable's value" */ #define CMD_SHELL 30 /* "run some lines through a command" */ #define CMD_SHIFTL 31 /* "shift lines left" */ #define CMD_SHIFTR 32 /* "shift lines right" */ #define CMD_SOURCE 33 /* "interpret a file's contents as ex commands" */ #define CMD_STOP 34 /* same as CMD_SUSPEND */ #define CMD_SUSPEND 35 /* "suspend the vi session" */ #define CMD_SUBSTITUTE 36 /* "substitute text in this line" */ #define CMD_TR 37 /* "transliterate chars in the selected lines" */ #define CMD_TAG 38 /* "go to a particular tag" */ #define CMD_UNABBR 39 /* "remove an abbreviation definition" */ #define CMD_UNDO 40 /* "undo the previous command" */ #define CMD_UNMAP 41 /* "remove a key sequence map */ #define CMD_VERSION 42 /* "describe which version this is" */ #define CMD_VGLOBAL 43 /* "apply a cmd to lines NOT containing an RE" */ #define CMD_VISUAL 44 /* "go into visual mode" */ #define CMD_WQUIT 45 /* "write this file out (any case) & quit" */ #define CMD_WRITE 46 /* "write the selected(?) text to a given file" */ #define CMD_XIT 47 /* "write this file out (if modified) & quit" */ #define CMD_YANK 48 /* "copy the selected text into the cut buffer" */ #ifdef DEBUG # define CMD_DEBUG 49 /* access to internal data structures */ # define CMD_VALIDATE 50 /* check for internal consistency */ #endif typedef int CMD; extern ex(); extern vi(); extern doexcmd(); extern void cmd_append(); extern void cmd_args(); extern void cmd_cd(); extern void cmd_delete(); #ifndef NO_DIGRAPH extern void cmd_digraph(); #endif extern void cmd_edit(); extern void cmd_file(); extern void cmd_global(); extern void cmd_join(); extern void cmd_mark(); extern void cmd_list(); extern void cmd_map(); #ifndef NO_EXTENSIONS extern void cmd_mkexrc(); #endif extern void cmd_next(); extern void cmd_print(); extern void cmd_put(); extern void cmd_quit(); extern void cmd_read(); extern void cmd_rewind(); extern void cmd_set(); extern void cmd_shell(); extern void cmd_shift(); extern void cmd_source(); extern void cmd_substitute(); extern void cmd_tag(); extern void cmd_undo(); extern void cmd_version(); extern void cmd_visual(); extern void cmd_write(); extern void cmd_xit(); extern void cmd_move(); #ifdef DEBUG extern void cmd_debug(); extern void cmd_validate(); #endif /*----------------------------------------------------------------------*/ /* These are used to handle VI commands */ extern MARK v_1ex(); /* : */ extern MARK v_mark(); /* m */ extern MARK v_quit(); /* Q */ extern MARK v_redraw(); /* ^L ^R */ extern MARK v_ulcase(); /* ~ */ extern MARK v_undo(); /* u */ extern MARK v_xchar(); /* x */ extern MARK v_Xchar(); /* X */ extern MARK v_replace(); /* r */ extern MARK v_overtype(); /* R */ extern MARK v_selcut(); /* " */ extern MARK v_paste(); /* p P */ extern MARK v_yank(); /* y Y */ extern MARK v_delete(); /* d D */ extern MARK v_join(); /* J */ extern MARK v_insert(); /* a A i I o O */ extern MARK v_change(); /* c C */ extern MARK v_subst(); /* s */ extern MARK v_lshift(); /* < */ extern MARK v_rshift(); /* > */ extern MARK v_filter(); /* ! */ extern MARK v_status(); /* ^G */ extern MARK v_switch(); /* ^^ */ extern MARK v_tag(); /* ^] */ #ifndef NO_EXTENSIONS extern MARK v_keyword(); /* ^K */ extern MARK v_increment(); /* * */ #endif extern MARK v_xit(); /* ZZ */ extern MARK v_undoline(); /* U */ /*----------------------------------------------------------------------*/ /* These describe what mode we're in */ #define MODE_EX 1 /* executing ex commands */ #define MODE_VI 2 /* executing vi commands */ #define MODE_COLON 3 /* executing an ex command from vi mode */ #define MODE_QUIT 4 extern int mode; #define WHEN_VICMD 1 /* getkey: we're reading a VI command */ #define WHEN_VIINP 2 /* getkey: we're in VI's INPUT mode */ #define WHEN_VIREP 4 /* getkey: we're in VI's REPLACE mode */ #define WHEN_EX 8 /* getkey: we're in EX mode */ #define WHEN_INMV 256 /* in input mode, interpret the key in VICMD mode */ SHAR_EOF fi 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 * 16820 SW Tallac Way * Beaverton, OR 97006 * kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda */ /* 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. * 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" #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" #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); system("sort tags >_tags$$; mv _tags$$ tags"); #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++) { } printf(NUMFMT, len, scan, name, lnum); } 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)) { printf(MAINFMT, strlen(name) - 2, name, name, text); } printf(SRCHFMT, len, scan, name, text); 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 if test -f 'ref.c' then echo shar: "will not over-write existing file 'ref.c'" else cat << \SHAR_EOF > 'ref.c' /* ref.c */ /* Author: * Steve Kirkendall * 16820 SW Tallac Way * Beaverton, OR 97006 * kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda */ /* This program looks up the declarations of library functions. */ #include /* This is the list of files that are searched. */ char *refslist[] = { "refs", "/usr/src/lib/refs", "../lib/refs", "/usr/local/lib/refs", }; #define NREFS (sizeof refslist / sizeof(char *)) main(argc, argv) int argc; char **argv; { int i; /* used to step through the refslist */ /* make sure our arguments are OK */ if (argc != 2) { fprintf(stderr, "usage: %s function_name\n", *argv); exit(2); } /* check for the function in each database */ for (i = 0; i < NREFS; i++) { if (lookinfor(refslist[i], argv[1])) { exit(0); } } fprintf(stderr, "%s: don't know about %s\n", argv[0], argv[1]); exit(2); } /* This function checks a single file for the function. Returns 1 if found */ int lookinfor(filename, func) char *filename; /* name of file to look in */ char *func; /* name of function to look for */ { FILE *fp; /* stream used to access the database */ char linebuf[300]; /* NOTE: in actual use, the first character of linebuf is */ /* set to ' ' and then we use all EXCEPT the 1st character */ /* everywhere in this function. This is because the func */ /* which examines the linebuf could, in some circumstances */ /* examine the character before the used part of linebuf; */ /* we need to control what happens then. */ /* open the database file */ fp = fopen(filename, "r"); if (!fp) { return 0; } /* find the desired entry */ *linebuf = ' '; do { if (!fgets(linebuf + 1, (sizeof linebuf) - 1, fp)) { fclose(fp); return 0; } } while (!desired(linebuf + 1, func)); /* print it */ do { fputs(linebuf + 1, stdout); } while (fgets(linebuf + 1, sizeof linebuf, fp) && linebuf[1] == '\t'); /* cleanup & exit */ fclose(fp); return 1; } /* This function checks to see if a given line is the first line of the */ /* entry the user wants to see. If it is, return non-0 else return 0 */ desired(line, word) char *line; /* the line buffer */ char *word; /* the string it should contain */ { static wlen = -1;/* length of the "word" variable's value */ register char *scan; /* if this line starts with a tab, it couldn't be desired */ if (*line == '\t') { return 0; } /* if we haven't found word's length yet, do so */ if (wlen < 0) { wlen = strlen(word); } /* search for the word in the line */ for (scan = line; *scan != '('; scan++) { } while (*--scan == ' ') { } scan -= wlen; if (scan < line - 1 || *scan != ' ' && *scan != '\t' && *scan != '*') { return 0; } scan++; return !strncmp(scan, word, wlen); } SHAR_EOF fi if test -f 'virec.c' then echo shar: "will not over-write existing file 'virec.c'" else cat << \SHAR_EOF > 'virec.c' /* virec.c */ /* This file contains the file recovery program */ /* Author: * Steve Kirkendall * 16820 SW Tallac Way * Beaverton, OR 97006 * kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda */ #include #include "config.h" #include "vi.h" #if TOS #include #else #include #endif extern char *getenv(); struct stat stbuf; BLK hdr; BLK text; /* the name of the directory where tmp files are stored. */ char o_directory[30] = TMPDIR; char *progname; main(argc, argv) int argc; char **argv; { char *tmp; #if MSDOS || TOS char **wildexpand(); argv = wildexpand(&argc, argv); #endif progname = argv[0]; /* set the o_directory variable */ if ((tmp = getenv("TMP")) /* yes, ASSIGNMENT! */ || (tmp = getenv("TEMP"))) /* yes, ASSIGNMENT! */ { strcpy(o_directory, tmp); } if (argc >= 3 && !strcmp(argv[1], "-d")) { strcpy(o_directory, argv[2]); argc -= 2; argv += 2; } /* process the arguments */ if (argc < 2) { /* maybe stdin comes from a file? */ if (fstat(0, &stbuf) < 0 || (S_IFMT & stbuf.st_mode) != S_IFREG) { fprintf(stderr, "usage: %s [-d tmpdir] lostfile...\n", progname); } else if (read(0, &hdr, BLKSIZE) != BLKSIZE) { fprintf(stderr, "couldn't get header\n"); } else { copytext(0, stdout); } } else { while (--argc > 0) { recover(*++argv); } } exit(0); } /* This function recovers a single file */ recover(filename) char *filename; { char tmpname[100]; int tmpfd; FILE *fp; long mtime; int i, j; /* get the file's status info */ if (stat(filename, &stbuf) < 0) { /* if serious error, give up on this file */ if (errno != ENOENT) { perror(filename); return; } /* else fake it for a new file */ stat(".", &stbuf); stbuf.st_mode = S_IFREG; stbuf.st_mtime = 0L; } /* find the tmp file */ sprintf(tmpname, TMPNAME, o_directory, stbuf.st_ino, stbuf.st_dev); tmpfd = open(tmpname, O_RDONLY); if (tmpfd < 0) { perror(tmpname); return; } /* make sure the file hasn't been modified more recently */ mtime = stbuf.st_mtime; fstat(tmpfd, &stbuf); if (stbuf.st_mtime < mtime) { printf("\"%s\" has been modified more recently than its recoverable version\n", filename); puts("Do you still want to recover it?\n"); puts("\ty - Yes, discard the current version and recover it.\n"); puts("\tn - No, discard the recoverable version and keep the current version\n"); puts("\tq - Quit without doing anything for this file.\n"); puts("Enter y, n, or q --> "); fflush(stdout); for (;;) { switch (getchar()) { case 'y': case 'Y': goto BreakBreak; case 'n': case 'N': close(tmpfd); unlink(tmpname); return; case 'q': case 'Q': close(tmpfd); return; } } BreakBreak:; } /* make sure this tmp file is intact */ if (read(tmpfd, &hdr, BLKSIZE) != BLKSIZE) { fprintf(stderr, "%s: bad header in tmp file\n", filename); close(tmpfd); unlink(tmpname); return; } for (i = j = 1; i < MAXBLKS && hdr.n[i]; i++) { if (hdr.n[i] > j) { j = hdr.n[i]; } } lseek(tmpfd, (long)j * (long)BLKSIZE, 0); if (read(tmpfd, &text, BLKSIZE) != BLKSIZE) { fprintf(stderr, "%s: bad data block in tmp file\n", filename); close(tmpfd); unlink(tmpname); return; } /* open the normal text file for writing */ fp = fopen(filename, "w"); if (!fp) { perror(filename); close(tmpfd); return; } /* copy the text */ copytext(tmpfd, fp); /* cleanup */ close(tmpfd); fclose(fp); unlink(tmpname); } /* This function moves text from the tmp file to the normal file */ copytext(tmpfd, fp) int tmpfd; /* fd of the tmp file */ FILE *fp; /* the stream to write it to */ { int i; /* write the data blocks to the normal text file */ for (i = 1; i < MAXBLKS && hdr.n[i]; i++) { lseek(tmpfd, (long)hdr.n[i] * (long)BLKSIZE, 0); read(tmpfd, &text, BLKSIZE); fputs(text.c, fp); } } #if MSDOS || TOS #define WILDCARD_NO_MAIN #include "wildcard.c" #endif SHAR_EOF fi if test -f 'wildcard.c' then echo shar: "will not over-write existing file 'wildcard.c'" else cat << \SHAR_EOF > 'wildcard.c' /* wildcard.c */ /* Author: * Guntram Blohm * Buchenstrasse 19 * 7904 Erbach, West Germany * Tel. ++49-7305-6997 * sorry - no regular network connection */ /* this program implements wildcard expansion for elvis/dos. It works * like UNIX echo, but uses the dos wildcard conventions * (*.* matches all files, * matches files without extension only, * filespecs may contain drive letters, wildcards not allowed in directory * names). * * It is also #included into ctags.c, ref.c, ...; in this case, * we don't want a main function here. */ #include #include #ifdef __TURBOC__ #include #endif #ifdef M_I86 #define findfirst(a,b,c) _dos_findfirst(a,c,b) #define findnext _dos_findnext #define ffblk find_t #define ff_name name #include #endif #ifdef M68000 #include #include #define findfirst(a,b,c) (Fsetdta(b), (Fsfirst(a,c))) #define findnext(x) (Fsnext()) #define ff_name d_fname #endif #define MAXFILES 1000 int pstrcmp(); extern char *calloc(); char *files[MAXFILES]; int nfiles; #ifndef WILDCARD_NO_MAIN main(argc, argv) char **argv; { int i; for (i=1; i=buf) { if (*filespec=='?' || *filespec=='*') wildcard=1; if (*filespec=='/' || *filespec=='\\' || *filespec==':') break; } if (!wildcard) addfile(buf); else { lastn=nfiles; filespec++; if ((err=findfirst(buf, &findbuf, 0))!=0) addfile(buf); while (!err) { strcpy(filespec, findbuf.ff_name); addfile(buf); err=findnext(&findbuf); } if (lastn!=nfiles) qsort(files+lastn, nfiles-lastn, sizeof(char *), pstrcmp); } } addfile(buf) char *buf; { char *p; for (p=buf; *p; p++) *p=tolower(*p); if (nfiles 'shell.c' /* shell.c */ /* Author: * Guntram Blohm * Buchenstrasse 19 * 7904 Erbach, West Germany * Tel. ++49-7305-6997 * sorry - no regular network connection */ /* * This file contains a minimal version of a shell for TOS. It allows the * setting of an environment, calling programs, and exiting. * If you don't have another one, this might be sufficient, but you should * prefer *any* other shell. * You may, however, want to set your SHELL environment variable to this * shell: it implements the -c switch, which is required by Elvis, and * not supported by most other atari shells. */ #include #include #include extern char *getenv(), *malloc(); extern char **environ; long _stksize=16384; #define MAXENV 50 struct { char *name; char *value; } myenv[MAXENV]; int cmd_set(), cmd_exit(); struct buildins { char *name; int (*func)(); } buildins[]= { "exit", cmd_exit, "set", cmd_set, 0, }; main(argc, argv) int argc; char **argv; { char buf[128]; int i; for (i=0; environ[i] && strncmp(environ[i],"ARGV=",5); i++) cmd_set(environ[i]); script("profile.sh"); if (argc>1 && !strcmp(argv[1], "-c")) { buf[0]='\0'; for (i=2; i2) strcat(buf, " "); strcat(buf, argv[i]); } execute(buf); } else while (fputs("$ ", stdout), gets(buf)) execute(buf); } execute(buf) char *buf; { char *scan=buf; char cmd[80]; char line[128]; char env[4096], *ep=env; int i; while (*scan==' ') scan++; if (!*scan) return; while (*scan && *scan!=' ') scan++; if (*scan) *scan++='\0'; for (i=0; buildins[i].name; i++) if (!strcmp(buf, buildins[i].name)) return (*buildins[i].func)(scan); if (!searchpath(buf, cmd)) { printf("%s: not found\n", buf); return -1; } strcpy(line+1, scan); line[0]=strlen(scan); for (i=0; i