/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* Copyright (c) 1981 Regents of the University of California */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include "ex.h" #include "ex_argv.h" #include "ex_temp.h" #include "ex_tty.h" #include "ex_vis.h" bool pflag, nflag; int poffset; #define nochng() lchng = chng /* * Main loop for command mode command decoding. * A few commands are executed here, but main function * is to strip command addresses, do a little address oriented * processing and call command routines to do the real work. */ extern unsigned char *Version; commands(noprompt, exitoneof) bool noprompt, exitoneof; { line *addr; int c; int lchng; int given; int seensemi; int cnt; bool hadpr; bool gotfile; #ifdef XPG4 int d; #endif /* XPG4 */ unsigned char *vgetpass(); resetflav(); nochng(); for (;;) { if (!firstpat) laste = 0; /* * If dot at last command * ended up at zero, advance to one if there is a such. */ if (dot <= zero) { dot = zero; if (dol > zero) dot = one; } shudclob = 0; /* * If autoprint or trailing print flags, * print the line at the specified offset * before the next command. */ if ((pflag || lchng != chng && value(vi_AUTOPRINT) && !inglobal && !inopen && endline) || poffset != 0) { pflag = 0; nochng(); if (dol != zero) { addr1 = addr2 = dot + poffset; poffset = 0; if (addr1 < one || addr1 > dol) error(value(vi_TERSE) ? gettext("Offset out-of-bounds") : gettext("Offset after command " "too large")); dot = addr1; setdot1(); goto print; } } nochng(); /* * Print prompt if appropriate. * If not in global flush output first to prevent * going into pfast mode unreasonably. */ if (inglobal == 0) { flush(); if (!hush && value(vi_PROMPT) && !globp && !noprompt && endline) { putchar(':'); hadpr = 1; } TSYNC(); } /* * Gobble up the address. * Degenerate addresses yield ".". */ addr2 = 0; given = seensemi = 0; do { addr1 = addr2; addr = address(0); c = getcd(); if (addr == 0) { if (c == ',' || c == ';') addr = dot; else if (addr1 != 0) { addr2 = dot; break; } else break; } addr2 = addr; given++; if (c == ';') { c = ','; dot = addr; seensemi = 1; } } while (c == ','); if (c == '%') { /* %: same as 1,$ */ addr1 = one; addr2 = dol; given = 2; c = getchar(); } if (addr1 == 0) addr1 = addr2; /* * eat multiple colons */ while (c == ':') c = getchar(); /* * Set command name for special character commands. */ tailspec(c); /* * If called via : escape from open or visual, limit * the set of available commands here to save work below. */ if (inopen) { if (c == '\n' || c == '\r' || c == CTRL('d') || c == EOF) { if (addr2) dot = addr2; if (c == EOF) return; continue; } if (any(c, "o")) notinvis: tailprim(Command, 1, 1); } switch (c) { case 'a': switch (peekchar()) { case 'b': /* abbreviate */ tail("abbreviate"); setnoaddr(); mapcmd(0, 1); anyabbrs = 1; continue; case 'r': /* args */ tail("args"); setnoaddr(); eol(); pargs(); continue; } /* append */ if (inopen) goto notinvis; tail("append"); setdot(); aiflag = exclam(); donewline(); vmacchng(0); deletenone(); setin(addr2); inappend = 1; (void) append(gettty, addr2); inappend = 0; nochng(); continue; case 'c': switch (peekchar()) { /* copy */ case 'o': tail("copy"); vmacchng(0); vi_move(); continue; /* crypt */ case 'r': tail("crypt"); crflag = -1; ent_crypt: setnoaddr(); xflag = 1; if (permflag) (void) crypt_close(perm); permflag = 1; if ((kflag = run_setkey(perm, (key = vgetpass( gettext("Enter key:"))))) == -1) { xflag = 0; kflag = 0; crflag = 0; smerror(gettext("Encryption facility " "not available\n")); } if (kflag == 0) crflag = 0; continue; /* cd */ case 'd': tail("cd"); goto changdir; /* chdir */ case 'h': ignchar(); if (peekchar() == 'd') { register unsigned char *p; tail2of("chdir"); changdir: if (savedfile[0] == '/' || !value(vi_WARN)) (void) exclam(); else (void) quickly(); if (skipend()) { p = (unsigned char *) getenv("HOME"); if (p == NULL) error(gettext( "Home directory" /*CSTYLED*/ " unknown")); } else getone(), p = file; eol(); if (chdir(p) < 0) filioerr(p); if (savedfile[0] != '/') edited = 0; continue; } if (inopen) tailprim("change", 2, 1); tail2of("change"); break; default: if (inopen) goto notinvis; tail("change"); break; } /* change */ aiflag = exclam(); #ifdef XPG4ONLY setcount2(); donewline(); #else /* XPG6 and Solaris */ setCNL(); #endif /* XPG4ONLY */ vmacchng(0); setin(addr1); delete(0); inappend = 1; if (append(gettty, addr1 - 1) == 0) { #ifdef XPG4 /* * P2003.2/D9:5.10.7.2.4, p. 646, * assertion 214(A). If nothing changed, * set dot to the line preceding the lines * to be changed. */ dot = addr1 - 1; #else /* XPG4 */ dot = addr1; #endif /* XPG4 */ if (dot > dol) dot = dol; } inappend = 0; nochng(); continue; /* delete */ case 'd': /* * Caution: dp and dl have special meaning already. */ tail("delete"); c = cmdreg(); #ifdef XPG4ONLY setcount2(); donewline(); #else /* XPG6 and Solaris */ setCNL(); #endif /* XPG4ONLY */ vmacchng(0); if (c) YANKreg(c); delete(0); appendnone(); continue; /* edit */ /* ex */ case 'e': if (crflag == 2 || crflag == -2) crflag = -1; tail(peekchar() == 'x' ? "ex" : "edit"); editcmd: if (!exclam() && chng) c = 'E'; gotfile = 0; if (c == 'E') { if (inopen && !value(vi_AUTOWRITE)) { filename(c); gotfile = 1; } ungetchar(lastchar()); if (!exclam()) { ckaw(); if (chng && dol > zero) { xchng = 0; error(value(vi_TERSE) ? gettext("No write") : gettext("No write since " "last change (:%s! " "overrides)"), Command); } } } if (gotfile == 0) filename(c); setnoaddr(); doecmd: init(); addr2 = zero; laste++; sync(); rop(c); nochng(); continue; /* file */ case 'f': tail("file"); setnoaddr(); filename(c); noonl(); /* * synctmp(); */ continue; /* global */ case 'g': tail("global"); global(!exclam()); nochng(); continue; /* insert */ case 'i': if (inopen) goto notinvis; tail("insert"); setdot(); nonzero(); aiflag = exclam(); donewline(); vmacchng(0); deletenone(); setin(addr2); inappend = 1; (void) append(gettty, addr2 - 1); inappend = 0; if (dot == zero && dol > zero) dot = one; nochng(); continue; /* join */ case 'j': tail("join"); c = exclam(); setcount(); nonzero(); donewline(); vmacchng(0); #ifdef XPG4ONLY /* * if no count was specified, addr1 == addr2. if only * 1 range arg was specified, inc addr2 to allow * joining of the next line. */ if (given < 2 && (addr1 == addr2) && (addr2 != dol)) addr2++; #else /* XPG6 and Solaris */ if (given < 2 && addr2 != dol) addr2++; #endif /* XPG4ONLY */ join(c); continue; /* k */ case 'k': casek: pastwh(); c = getchar(); if (endcmd(c)) serror((vi_TERSE) ? gettext("Mark what?") : gettext("%s requires following " "letter"), Command); donewline(); if (!islower(c)) error((vi_TERSE) ? gettext("Bad mark") : gettext("Mark must specify a letter")); setdot(); nonzero(); names[c - 'a'] = *addr2 &~ 01; anymarks = 1; continue; /* list */ case 'l': tail("list"); #ifdef XPG4ONLY setcount2(); donewline(); #else /* XPG6 and Solaris */ setCNL(); #endif /* XPG4ONLY */ (void) setlist(1); pflag = 0; goto print; case 'm': if (peekchar() == 'a') { ignchar(); if (peekchar() == 'p') { /* map */ tail2of("map"); setnoaddr(); mapcmd(0, 0); continue; } /* mark */ tail2of("mark"); goto casek; } /* move */ tail("move"); vmacchng(0); vi_move(); continue; case 'n': if (peekchar() == 'u') { tail("number"); goto numberit; } /* next */ tail("next"); setnoaddr(); if (!exclam()) { ckaw(); if (chng && dol > zero) { xchng = 0; error(value(vi_TERSE) ? gettext("No write") : gettext("No write since last " "change (:%s! overrides)"), Command); } } if (getargs()) makargs(); next(); c = 'e'; filename(c); goto doecmd; /* open */ case 'o': tail("open"); oop(); pflag = 0; nochng(); continue; case 'p': case 'P': switch (peekchar()) { #ifdef TAG_STACK /* pop */ case 'o': tail("pop"); poptag(exclam()); if (!inopen) lchng = chng - 1; else nochng(); continue; #endif /* put */ case 'u': tail("put"); setdot(); c = cmdreg(); eol(); vmacchng(0); if (c) putreg(c); else put(); continue; case 'r': ignchar(); if (peekchar() == 'e') { /* preserve */ tail2of("preserve"); eol(); if (preserve() == 0) error(gettext( "Preserve failed!")); else { #ifdef XPG4 /* * error() incs errcnt. this is * misleading here; and a * violation of POSIX. so call * noerror() instead. * this is for assertion ex:222. */ noerror( gettext("File preserved.")); #else /* XPG4 */ error( gettext("File preserved.")); #endif /* XPG4 */ } } tail2of("print"); break; default: tail("print"); break; } /* print */ setCNL(); pflag = 0; print: nonzero(); if (clear_screen && span() > lines) { flush1(); vclear(); } /* * poffset is nonzero if trailing + or - flags * were given, and in that case we need to * adjust dot before printing a line. */ if (poffset == 0) plines(addr1, addr2, 1); else dot = addr2; continue; /* quit */ case 'q': tail("quit"); setnoaddr(); c = quickly(); eol(); if (!c) quit: if (nomore()) continue; if (inopen) { vgoto(WECHO, 0); if (!ateopr()) vnfl(); else { tostop(); } flush(); setty(normf); ixlatctl(1); } cleanup(1); exit(errcnt); case 'r': if (peekchar() == 'e') { ignchar(); switch (peekchar()) { /* rewind */ case 'w': tail2of("rewind"); setnoaddr(); if (!exclam()) { ckaw(); if (chng && dol > zero) error((vi_TERSE) ? /*CSTYLED*/ gettext("No write") : gettext("No write " "since last " "change (:rewi" /*CSTYLED*/ "nd! overrides)")); } eol(); erewind(); next(); c = 'e'; ungetchar(lastchar()); filename(c); goto doecmd; /* recover */ case 'c': tail2of("recover"); setnoaddr(); c = 'e'; if (!exclam() && chng) c = 'E'; filename(c); if (c == 'E') { ungetchar(lastchar()); (void) quickly(); } init(); addr2 = zero; laste++; sync(); recover(); rop2(); revocer(); if (status == 0) rop3(c); if (dol != zero) change(); nochng(); continue; } tail2of("read"); } else tail("read"); /* read */ if (crflag == 2 || crflag == -2) /* restore crflag for new input text */ crflag = -1; if (savedfile[0] == 0 && dol == zero) c = 'e'; pastwh(); vmacchng(0); if (peekchar() == '!') { setdot(); ignchar(); unix0(0); vi_filter(0); continue; } filename(c); rop(c); nochng(); if (inopen && endline && addr1 > zero && addr1 < dol) dot = addr1 + 1; continue; case 's': switch (peekchar()) { /* * Caution: 2nd char cannot be c, g, or r * because these have meaning to substitute. */ /* set */ case 'e': tail("set"); setnoaddr(); set(); continue; /* shell */ case 'h': tail("shell"); setNAEOL(); vnfl(); putpad(exit_ca_mode); flush(); resetterm(); unixwt(1, unixex("-i", (char *)0, 0, 0)); vcontin(0); continue; /* source */ case 'o': #ifdef notdef if (inopen) goto notinvis; #endif tail("source"); setnoaddr(); getone(); eol(); source(file, 0); continue; #ifdef SIGTSTP /* stop, suspend */ case 't': tail("stop"); goto suspend; case 'u': #ifdef XPG4 /* * for POSIX, "su" with no other distinguishing * characteristics, maps to "s". Re. P1003.D11, * 5.10.7.3. * * so, unless the "su" is followed by a "s" or * a "!", we assume that the user means "s". */ switch (d = peekchar()) { case 's': case '!': #endif /* XPG4 */ tail("suspend"); suspend: c = exclam(); eol(); if (!c) ckaw(); onsusp(0); continue; #ifdef XPG4 } #endif /* XPG4 */ #endif } /* fall into ... */ /* & */ /* ~ */ /* substitute */ case '&': case '~': Command = (unsigned char *)"substitute"; if (c == 's') tail(Command); vmacchng(0); if (!substitute(c)) pflag = 0; continue; /* t */ case 't': if (peekchar() == 'a') { tail("tag"); tagfind(exclam()); if (!inopen) lchng = chng - 1; else nochng(); continue; } tail("t"); vmacchng(0); vi_move(); continue; case 'u': if (peekchar() == 'n') { ignchar(); switch (peekchar()) { /* unmap */ case 'm': tail2of("unmap"); setnoaddr(); mapcmd(1, 0); continue; /* unabbreviate */ case 'a': tail2of("unabbreviate"); setnoaddr(); mapcmd(1, 1); anyabbrs = 1; continue; } /* undo */ tail2of("undo"); } else tail("undo"); setnoaddr(); markDOT(); c = exclam(); donewline(); undo(c); continue; case 'v': switch (peekchar()) { case 'e': /* version */ tail("version"); setNAEOL(); printf("%s", Version); noonl(); continue; /* visual */ case 'i': tail("visual"); if (inopen) { c = 'e'; goto editcmd; } vop(); pflag = 0; nochng(); continue; } /* v */ tail("v"); global(0); nochng(); continue; /* write */ case 'w': c = peekchar(); tail(c == 'q' ? "wq" : "write"); wq: if (skipwh() && peekchar() == '!') { pofix(); ignchar(); setall(); unix0(0); vi_filter(1); } else { setall(); if (c == 'q') write_quit = 1; else write_quit = 0; wop(1); nochng(); } if (c == 'q') goto quit; continue; /* X: crypt */ case 'X': crflag = -1; /* determine if file is encrypted */ goto ent_crypt; case 'C': crflag = 1; /* assume files read in are encrypted */ goto ent_crypt; /* xit */ case 'x': tail("xit"); if (!chng) goto quit; c = 'q'; goto wq; /* yank */ case 'y': tail("yank"); c = cmdreg(); #ifdef XPG4ONLY setcount2(); #else /* XPG6 and Solaris */ setcount(); #endif /* XPG4ONLY */ eol(); vmacchng(0); if (c) YANKreg(c); else yank(); continue; /* z */ case 'z': zop(0); pflag = 0; continue; /* * */ /* @ */ case '*': case '@': c = getchar(); if (c == '\n' || c == '\r') ungetchar(c); if (any(c, "@*\n\r")) c = lastmac; if (isupper(c)) c = tolower(c); if (!islower(c)) error(gettext("Bad register")); donewline(); setdot(); cmdmac(c); continue; /* | */ case '|': endline = 0; goto caseline; /* \n */ case '\n': endline = 1; caseline: notempty(); if (addr2 == 0) { if (cursor_up != NOSTR && c == '\n' && !inglobal) c = CTRL('k'); if (inglobal) addr1 = addr2 = dot; else { if (dot == dol) error((vi_TERSE) ? gettext("At EOF") : gettext("At end-of-file")); addr1 = addr2 = dot + 1; } } setdot(); nonzero(); if (seensemi) addr1 = addr2; getline(*addr1); if (c == CTRL('k')) { flush1(); destline--; if (hadpr) shudclob = 1; } plines(addr1, addr2, 1); continue; /* " */ case '"': comment(); continue; /* # */ case '#': numberit: setCNL(); (void) setnumb(1); pflag = 0; goto print; /* = */ case '=': donewline(); setall(); if (inglobal == 2) pofix(); printf("%d", lineno(addr2)); noonl(); continue; /* ! */ case '!': if (addr2 != 0) { vmacchng(0); unix0(0); setdot(); vi_filter(2); } else { unix0(1); pofix(); putpad(exit_ca_mode); flush(); resetterm(); unixwt(1, unixex("-c", uxb, 0, 0)); vclrech(1); /* vcontin(0); */ nochng(); } continue; /* < */ /* > */ case '<': case '>': for (cnt = 1; peekchar() == c; cnt++) ignchar(); setCNL(); vmacchng(0); shift(c, cnt); continue; /* ^D */ /* EOF */ case CTRL('d'): case EOF: if (exitoneof) { if (addr2 != 0) dot = addr2; return; } if (!isatty(0)) { if (intty) /* * Chtty sys call at UCB may cause a * input which was a tty to suddenly be * turned into /dev/null. */ onhup(0); return; } if (addr2 != 0) { setlastchar('\n'); putnl(); } if (dol == zero) { if (addr2 == 0) putnl(); notempty(); } ungetchar(EOF); zop(hadpr); continue; default: if (!isalpha(c) || !isascii(c)) break; ungetchar(c); tailprim("", 0, 0); } ungetchar(c); { int length; char multic[MULTI_BYTE_MAX]; wchar_t wchar; length = _mbftowc(multic, &wchar, getchar, &peekc); if (length < 0) length = -length; multic[length] = '\0'; error((vi_TERSE) ? gettext("What?") : gettext("Unknown command character '%s'"), multic); } } }