xref: /titanic_52/usr/src/cmd/vi/port/ex_cmds.c (revision f6db9f272f0061301cfaa1c0001b7d636eae31f4)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 
31 /* Copyright (c) 1981 Regents of the University of California */
32 
33 #pragma ident	"%Z%%M%	%I%	%E% SMI"
34 
35 #include "ex.h"
36 #include "ex_argv.h"
37 #include "ex_temp.h"
38 #include "ex_tty.h"
39 #include "ex_vis.h"
40 
41 bool	pflag, nflag;
42 int	poffset;
43 
44 #define	nochng()	lchng = chng
45 
46 
47 /*
48  * Main loop for command mode command decoding.
49  * A few commands are executed here, but main function
50  * is to strip command addresses, do a little address oriented
51  * processing and call command routines to do the real work.
52  */
53 extern unsigned char *Version;
54 void
55 commands(noprompt, exitoneof)
56 	bool noprompt, exitoneof;
57 {
58 	line *addr;
59 	int c;
60 	int lchng;
61 	int given;
62 	int seensemi;
63 	int cnt;
64 	bool hadpr;
65 	bool gotfile;
66 #ifdef XPG4
67 	int d;
68 #endif /* XPG4 */
69 	unsigned char *vgetpass();
70 
71 	resetflav();
72 	nochng();
73 	for (;;) {
74 		if (!firstpat)
75 			laste = 0;
76 		/*
77 		 * If dot at last command
78 		 * ended up at zero, advance to one if there is a such.
79 		 */
80 		if (dot <= zero) {
81 			dot = zero;
82 			if (dol > zero)
83 				dot = one;
84 		}
85 		shudclob = 0;
86 
87 		/*
88 		 * If autoprint or trailing print flags,
89 		 * print the line at the specified offset
90 		 * before the next command.
91 		 */
92 		if ((pflag || lchng != chng && value(vi_AUTOPRINT) &&
93 		    !inglobal && !inopen && endline) || poffset != 0) {
94 			pflag = 0;
95 			nochng();
96 			if (dol != zero) {
97 				addr1 = addr2 = dot + poffset;
98 				poffset = 0;
99 				if (addr1 < one || addr1 > dol)
100 					error(value(vi_TERSE) ?
101 					    gettext("Offset out-of-bounds") :
102 					    gettext("Offset after command "
103 						"too large"));
104 				dot = addr1;
105 				setdot1();
106 
107 				goto print;
108 			}
109 		}
110 		nochng();
111 
112 		/*
113 		 * Print prompt if appropriate.
114 		 * If not in global flush output first to prevent
115 		 * going into pfast mode unreasonably.
116 		 */
117 		if (inglobal == 0) {
118 			flush();
119 			if (!hush && value(vi_PROMPT) && !globp &&
120 			    !noprompt && endline) {
121 				putchar(':');
122 				hadpr = 1;
123 			}
124 			TSYNC();
125 		}
126 
127 		/*
128 		 * Gobble up the address.
129 		 * Degenerate addresses yield ".".
130 		 */
131 		addr2 = 0;
132 		given = seensemi = 0;
133 		do {
134 			addr1 = addr2;
135 			addr = address(0);
136 			c = getcd();
137 			if (addr == 0) {
138 				if (c == ',' || c == ';')
139 					addr = dot;
140 				else if (addr1 != 0) {
141 					addr2 = dot;
142 					break;
143 				} else
144 					break;
145 			}
146 			addr2 = addr;
147 			given++;
148 			if (c == ';') {
149 				c = ',';
150 				dot = addr;
151 				seensemi = 1;
152 			}
153 		} while (c == ',');
154 
155 		if (c == '%') {
156 			/* %: same as 1,$ */
157 			addr1 = one;
158 			addr2 = dol;
159 			given = 2;
160 			c = getchar();
161 		}
162 		if (addr1 == 0)
163 			addr1 = addr2;
164 
165 		/*
166 		 * eat multiple colons
167 		 */
168 		while (c == ':')
169 			c = getchar();
170 		/*
171 		 * Set command name for special character commands.
172 		 */
173 		tailspec(c);
174 
175 		/*
176 		 * If called via : escape from open or visual, limit
177 		 * the set of available commands here to save work below.
178 		 */
179 		if (inopen) {
180 			if (c == '\n' || c == '\r' ||
181 			    c == CTRL('d') || c == EOF) {
182 				if (addr2)
183 					dot = addr2;
184 				if (c == EOF)
185 					return;
186 				continue;
187 			}
188 			if (any(c, "o"))
189 notinvis:
190 				tailprim(Command, 1, 1);
191 		}
192 		switch (c) {
193 
194 		case 'a':
195 
196 			switch (peekchar()) {
197 			case 'b':
198 /* abbreviate */
199 				tail("abbreviate");
200 				setnoaddr();
201 				mapcmd(0, 1);
202 				anyabbrs = 1;
203 				continue;
204 			case 'r':
205 /* args */
206 				tail("args");
207 				setnoaddr();
208 				eol();
209 				pargs();
210 				continue;
211 			}
212 
213 /* append */
214 			if (inopen)
215 				goto notinvis;
216 			tail("append");
217 			setdot();
218 			aiflag = exclam();
219 			donewline();
220 			vmacchng(0);
221 			deletenone();
222 			setin(addr2);
223 			inappend = 1;
224 			(void) append(gettty, addr2);
225 			inappend = 0;
226 			nochng();
227 			continue;
228 
229 		case 'c':
230 			switch (peekchar()) {
231 
232 /* copy */
233 			case 'o':
234 				tail("copy");
235 				vmacchng(0);
236 				vi_move();
237 				continue;
238 
239 /* crypt */
240 			case 'r':
241 				tail("crypt");
242 				crflag = -1;
243 			ent_crypt:
244 				setnoaddr();
245 				xflag = 1;
246 				if (permflag)
247 					(void) crypt_close(perm);
248 				permflag = 1;
249 				if ((kflag = run_setkey(perm,
250 				    (key = vgetpass(
251 					gettext("Enter key:"))))) == -1) {
252 					xflag = 0;
253 					kflag = 0;
254 					crflag = 0;
255 					smerror(gettext("Encryption facility "
256 						    "not available\n"));
257 				}
258 				if (kflag == 0)
259 					crflag = 0;
260 				continue;
261 
262 /* cd */
263 			case 'd':
264 				tail("cd");
265 				goto changdir;
266 
267 /* chdir */
268 			case 'h':
269 				ignchar();
270 				if (peekchar() == 'd') {
271 					unsigned char *p;
272 					tail2of("chdir");
273 changdir:
274 					if (savedfile[0] == '/' ||
275 					    !value(vi_WARN))
276 						(void) exclam();
277 					else
278 						(void) quickly();
279 					if (skipend()) {
280 						p = (unsigned char *)
281 						    getenv("HOME");
282 						if (p == NULL)
283 							error(gettext(
284 								"Home directory"
285 								    /*CSTYLED*/
286 								    " unknown"));
287 					} else
288 						getone(), p = file;
289 					eol();
290 					if (chdir((char *)p) < 0)
291 						filioerr(p);
292 					if (savedfile[0] != '/')
293 						edited = 0;
294 					continue;
295 				}
296 				if (inopen)
297 					tailprim((unsigned char *)"change",
298 					    2, 1);
299 				tail2of("change");
300 				break;
301 
302 			default:
303 				if (inopen)
304 					goto notinvis;
305 				tail("change");
306 				break;
307 			}
308 /* change */
309 			aiflag = exclam();
310 #ifdef XPG4ONLY
311 			setcount2();
312 			donewline();
313 #else /* XPG6 and Solaris */
314 			setCNL();
315 #endif /* XPG4ONLY */
316 			vmacchng(0);
317 			setin(addr1);
318 			(void) delete(0);
319 			inappend = 1;
320 			if (append(gettty, addr1 - 1) == 0) {
321 #ifdef XPG4
322 				/*
323 				 * P2003.2/D9:5.10.7.2.4, p. 646,
324 				 * assertion 214(A). If nothing changed,
325 				 * set dot to the line preceding the lines
326 				 * to be changed.
327 				 */
328 				dot = addr1 - 1;
329 #else /* XPG4 */
330 				dot = addr1;
331 #endif /* XPG4 */
332 				if (dot > dol)
333 					dot = dol;
334 			}
335 			inappend = 0;
336 			nochng();
337 			continue;
338 
339 /* delete */
340 		case 'd':
341 			/*
342 			 * Caution: dp and dl have special meaning already.
343 			 */
344 			tail("delete");
345 			c = cmdreg();
346 #ifdef XPG4ONLY
347 			setcount2();
348 			donewline();
349 #else /* XPG6 and Solaris */
350 			setCNL();
351 #endif /* XPG4ONLY */
352 			vmacchng(0);
353 			if (c)
354 				(void) YANKreg(c);
355 			(void) delete(0);
356 			appendnone();
357 			continue;
358 
359 /* edit */
360 /* ex */
361 		case 'e':
362 			if (crflag == 2 || crflag == -2)
363 				crflag = -1;
364 			tail(peekchar() == 'x' ? "ex" : "edit");
365 editcmd:
366 			if (!exclam() && chng)
367 				c = 'E';
368 			gotfile = 0;
369 			if (c == 'E') {
370 				if (inopen && !value(vi_AUTOWRITE)) {
371 					filename(c);
372 					gotfile = 1;
373 				}
374 				ungetchar(lastchar());
375 				if (!exclam()) {
376 					ckaw();
377 					if (chng && dol > zero) {
378 						xchng = 0;
379 						error(value(vi_TERSE) ?
380 						    gettext("No write") :
381 						    gettext("No write since "
382 							"last change (:%s! "
383 							"overrides)"),
384 						    Command);
385 					}
386 				}
387 
388 			}
389 			if (gotfile == 0)
390 				filename(c);
391 			setnoaddr();
392 doecmd:
393 			init();
394 			addr2 = zero;
395 			laste++;
396 			sync();
397 			rop(c);
398 			nochng();
399 			continue;
400 
401 /* file */
402 		case 'f':
403 			tail("file");
404 			setnoaddr();
405 			filename(c);
406 			noonl();
407 /*
408  *			synctmp();
409  */
410 			continue;
411 
412 /* global */
413 		case 'g':
414 			tail("global");
415 			global(!exclam());
416 			nochng();
417 			continue;
418 
419 /* insert */
420 		case 'i':
421 			if (inopen)
422 				goto notinvis;
423 			tail("insert");
424 			setdot();
425 			nonzero();
426 			aiflag = exclam();
427 			donewline();
428 			vmacchng(0);
429 			deletenone();
430 			setin(addr2);
431 			inappend = 1;
432 			(void) append(gettty, addr2 - 1);
433 			inappend = 0;
434 			if (dot == zero && dol > zero)
435 				dot = one;
436 			nochng();
437 			continue;
438 
439 /* join */
440 		case 'j':
441 			tail("join");
442 			c = exclam();
443 			setcount();
444 			nonzero();
445 			donewline();
446 			vmacchng(0);
447 #ifdef XPG4ONLY
448 			/*
449 			 * if no count was specified, addr1 == addr2. if only
450 			 * 1 range arg was specified, inc addr2 to allow
451 			 * joining of the next line.
452 			 */
453 			if (given < 2 && (addr1 == addr2) && (addr2 != dol))
454 				addr2++;
455 
456 #else /* XPG6 and Solaris */
457 			if (given < 2 && addr2 != dol)
458 				addr2++;
459 #endif /* XPG4ONLY */
460 			(void) join(c);
461 			continue;
462 
463 /* k */
464 		case 'k':
465 casek:
466 			pastwh();
467 			c = getchar();
468 			if (endcmd(c))
469 				serror((vi_TERSE) ?
470 				    (unsigned char *)gettext("Mark what?") :
471 				    (unsigned char *)
472 				    gettext("%s requires following "
473 				    "letter"), Command);
474 			donewline();
475 			if (!islower(c))
476 				error((vi_TERSE) ? gettext("Bad mark") :
477 					gettext("Mark must specify a letter"));
478 			setdot();
479 			nonzero();
480 			names[c - 'a'] = *addr2 &~ 01;
481 			anymarks = 1;
482 			continue;
483 
484 /* list */
485 		case 'l':
486 			tail("list");
487 #ifdef XPG4ONLY
488 			setcount2();
489 			donewline();
490 #else /* XPG6 and Solaris */
491 			setCNL();
492 #endif /* XPG4ONLY */
493 			(void) setlist(1);
494 			pflag = 0;
495 			goto print;
496 
497 		case 'm':
498 			if (peekchar() == 'a') {
499 				ignchar();
500 				if (peekchar() == 'p') {
501 /* map */
502 					tail2of("map");
503 					setnoaddr();
504 					mapcmd(0, 0);
505 					continue;
506 				}
507 /* mark */
508 				tail2of("mark");
509 				goto casek;
510 			}
511 /* move */
512 			tail("move");
513 			vmacchng(0);
514 			vi_move();
515 			continue;
516 
517 		case 'n':
518 			if (peekchar() == 'u') {
519 				tail("number");
520 				goto numberit;
521 			}
522 /* next */
523 			tail("next");
524 			setnoaddr();
525 			if (!exclam()) {
526 				ckaw();
527 				if (chng && dol > zero) {
528 					xchng = 0;
529 					error(value(vi_TERSE) ?
530 					    gettext("No write") :
531 					    gettext("No write since last "
532 						"change (:%s! overrides)"),
533 					    Command);
534 				}
535 			}
536 
537 			if (getargs())
538 				makargs();
539 			next();
540 			c = 'e';
541 			filename(c);
542 			goto doecmd;
543 
544 /* open */
545 		case 'o':
546 			tail("open");
547 			oop();
548 			pflag = 0;
549 			nochng();
550 			continue;
551 
552 		case 'p':
553 		case 'P':
554 			switch (peekchar()) {
555 #ifdef TAG_STACK
556 /* pop */
557 			case 'o':
558 				tail("pop");
559 				poptag(exclam());
560 				if (!inopen)
561 					lchng = chng - 1;
562 				else
563 					nochng();
564 				continue;
565 #endif
566 
567 /* put */
568 			case 'u':
569 				tail("put");
570 				setdot();
571 				c = cmdreg();
572 				eol();
573 				vmacchng(0);
574 				if (c)
575 					(void) putreg(c);
576 				else
577 					(void) put();
578 				continue;
579 
580 			case 'r':
581 				ignchar();
582 				if (peekchar() == 'e') {
583 /* preserve */
584 					tail2of("preserve");
585 					eol();
586 					if (preserve() == 0)
587 						error(gettext(
588 						    "Preserve failed!"));
589 					else {
590 #ifdef XPG4
591 						/*
592 						 * error() incs errcnt. this is
593 						 * misleading here; and a
594 						 * violation of POSIX. so call
595 						 * noerror() instead.
596 						 * this is for assertion ex:222.
597 						 */
598 						noerror(
599 						    gettext("File preserved."));
600 
601 #else /* XPG4 */
602 						error(
603 						    gettext("File preserved."));
604 #endif /* XPG4 */
605 					}
606 				}
607 				tail2of("print");
608 				break;
609 
610 			default:
611 				tail("print");
612 				break;
613 			}
614 /* print */
615 			setCNL();
616 			pflag = 0;
617 print:
618 			nonzero();
619 			if (clear_screen && span() > lines) {
620 				flush1();
621 				vclear();
622 			}
623 			/*
624 			 * poffset is nonzero if trailing + or - flags
625 			 * were given, and in that case we need to
626 			 * adjust dot before printing a line.
627 			 */
628 			if (poffset == 0)
629 				plines(addr1, addr2, 1);
630 			else
631 				dot = addr2;
632 			continue;
633 
634 /* quit */
635 		case 'q':
636 			tail("quit");
637 			setnoaddr();
638 			c = quickly();
639 			eol();
640 			if (!c)
641 quit:
642 				if (nomore())
643 					continue;
644 			if (inopen) {
645 				vgoto(WECHO, 0);
646 				if (!ateopr())
647 					vnfl();
648 				else {
649 					tostop();
650 				}
651 				flush();
652 				setty(normf);
653 				ixlatctl(1);
654 			}
655 			cleanup(1);
656 			exit(errcnt);
657 
658 		case 'r':
659 			if (peekchar() == 'e') {
660 				ignchar();
661 				switch (peekchar()) {
662 
663 /* rewind */
664 				case 'w':
665 					tail2of("rewind");
666 					setnoaddr();
667 					if (!exclam()) {
668 						ckaw();
669 						if (chng && dol > zero)
670 							error((vi_TERSE) ?
671 							    /*CSTYLED*/
672 							    gettext("No write") :
673 							    gettext("No write "
674 								"since last "
675 								"change (:rewi"
676 								/*CSTYLED*/
677 								"nd! overrides)"));
678 					}
679 					eol();
680 					erewind();
681 					next();
682 					c = 'e';
683 					ungetchar(lastchar());
684 					filename(c);
685 					goto doecmd;
686 
687 /* recover */
688 				case 'c':
689 					tail2of("recover");
690 					setnoaddr();
691 					c = 'e';
692 					if (!exclam() && chng)
693 						c = 'E';
694 					filename(c);
695 					if (c == 'E') {
696 						ungetchar(lastchar());
697 						(void) quickly();
698 					}
699 					init();
700 					addr2 = zero;
701 					laste++;
702 					sync();
703 					recover();
704 					rop2();
705 					revocer();
706 					if (status == 0)
707 						rop3(c);
708 					if (dol != zero)
709 						change();
710 					nochng();
711 					continue;
712 				}
713 				tail2of("read");
714 			} else
715 				tail("read");
716 /* read */
717 			if (crflag == 2 || crflag == -2)
718 			/* restore crflag for new input text */
719 				crflag = -1;
720 			if (savedfile[0] == 0 && dol == zero)
721 				c = 'e';
722 			pastwh();
723 			vmacchng(0);
724 			if (peekchar() == '!') {
725 				setdot();
726 				ignchar();
727 				unix0(0, 1);
728 				(void) vi_filter(0);
729 				continue;
730 			}
731 			filename(c);
732 			rop(c);
733 			nochng();
734 			if (inopen && endline && addr1 > zero && addr1 < dol)
735 				dot = addr1 + 1;
736 			continue;
737 
738 		case 's':
739 			switch (peekchar()) {
740 			/*
741 			 * Caution: 2nd char cannot be c, g, or r
742 			 * because these have meaning to substitute.
743 			 */
744 
745 /* set */
746 			case 'e':
747 				tail("set");
748 				setnoaddr();
749 				set();
750 				continue;
751 
752 /* shell */
753 			case 'h':
754 				tail("shell");
755 				setNAEOL();
756 				vnfl();
757 				putpad((unsigned char *)exit_ca_mode);
758 				flush();
759 				resetterm();
760 				unixwt(1, unixex("-i", (char *)0, 0, 0));
761 				vcontin(0);
762 				continue;
763 
764 /* source */
765 			case 'o':
766 #ifdef notdef
767 				if (inopen)
768 					goto notinvis;
769 #endif
770 				tail("source");
771 				setnoaddr();
772 				getone();
773 				eol();
774 				source(file, 0);
775 				continue;
776 #ifdef SIGTSTP
777 /* stop, suspend */
778 			case 't':
779 				tail("stop");
780 				goto suspend;
781 			case 'u':
782 #ifdef XPG4
783 				/*
784 				 * for POSIX, "su" with no other distinguishing
785 				 * characteristics, maps to "s". Re. P1003.D11,
786 				 * 5.10.7.3.
787 				 *
788 				 * so, unless the "su" is followed by a "s" or
789 				 * a "!", we assume that the user means "s".
790 				 */
791 				switch (d = peekchar()) {
792 				case 's':
793 				case '!':
794 #endif /* XPG4 */
795 					tail("suspend");
796 suspend:
797 					c = exclam();
798 					eol();
799 					if (!c)
800 						ckaw();
801 					onsusp(0);
802 					continue;
803 #ifdef XPG4
804 				}
805 #endif /* XPG4 */
806 #endif
807 
808 			}
809 			/* fall into ... */
810 
811 /* & */
812 /* ~ */
813 /* substitute */
814 		case '&':
815 		case '~':
816 			Command = (unsigned char *)"substitute";
817 			if (c == 's')
818 				tail(Command);
819 			vmacchng(0);
820 			if (!substitute(c))
821 				pflag = 0;
822 			continue;
823 
824 /* t */
825 		case 't':
826 			if (peekchar() == 'a') {
827 				tail("tag");
828 				tagfind(exclam());
829 				if (!inopen)
830 					lchng = chng - 1;
831 				else
832 					nochng();
833 				continue;
834 			}
835 			tail("t");
836 			vmacchng(0);
837 			vi_move();
838 			continue;
839 
840 		case 'u':
841 			if (peekchar() == 'n') {
842 				ignchar();
843 				switch (peekchar()) {
844 /* unmap */
845 				case 'm':
846 					tail2of("unmap");
847 					setnoaddr();
848 					mapcmd(1, 0);
849 					continue;
850 /* unabbreviate */
851 				case 'a':
852 					tail2of("unabbreviate");
853 					setnoaddr();
854 					mapcmd(1, 1);
855 					anyabbrs = 1;
856 					continue;
857 				}
858 /* undo */
859 				tail2of("undo");
860 			} else
861 				tail("undo");
862 			setnoaddr();
863 			markDOT();
864 			c = exclam();
865 			donewline();
866 			undo(c);
867 			continue;
868 
869 		case 'v':
870 			switch (peekchar()) {
871 
872 			case 'e':
873 /* version */
874 				tail("version");
875 				setNAEOL();
876 				viprintf("%s", Version);
877 				noonl();
878 				continue;
879 
880 /* visual */
881 			case 'i':
882 				tail("visual");
883 				if (inopen) {
884 					c = 'e';
885 					goto editcmd;
886 				}
887 				vop();
888 				pflag = 0;
889 				nochng();
890 				continue;
891 			}
892 /* v */
893 			tail("v");
894 			global(0);
895 			nochng();
896 			continue;
897 
898 /* write */
899 		case 'w':
900 			c = peekchar();
901 			tail(c == 'q' ? "wq" : "write");
902 wq:
903 			if (skipwh() && peekchar() == '!') {
904 				pofix();
905 				ignchar();
906 				setall();
907 				unix0(0, 1);
908 				(void) vi_filter(1);
909 			} else {
910 				setall();
911 				if (c == 'q')
912 					write_quit = 1;
913 				else
914 					write_quit = 0;
915 				wop(1);
916 				nochng();
917 			}
918 			if (c == 'q')
919 				goto quit;
920 			continue;
921 /* X: crypt */
922 		case 'X':
923 			crflag = -1; /* determine if file is encrypted */
924 			goto ent_crypt;
925 
926 		case 'C':
927 			crflag = 1;  /* assume files read in are encrypted */
928 			goto ent_crypt;
929 
930 /* xit */
931 		case 'x':
932 			tail("xit");
933 			if (!chng)
934 				goto quit;
935 			c = 'q';
936 			goto wq;
937 
938 /* yank */
939 		case 'y':
940 			tail("yank");
941 			c = cmdreg();
942 #ifdef XPG4ONLY
943 			setcount2();
944 #else /* XPG6 and Solaris */
945 			setcount();
946 #endif /* XPG4ONLY */
947 			eol();
948 			vmacchng(0);
949 			if (c)
950 				(void) YANKreg(c);
951 			else
952 				(void) yank();
953 			continue;
954 
955 /* z */
956 		case 'z':
957 			zop(0);
958 			pflag = 0;
959 			continue;
960 
961 /* * */
962 /* @ */
963 		case '*':
964 		case '@':
965 			c = getchar();
966 			if (c == '\n' || c == '\r')
967 				ungetchar(c);
968 			if (any(c, "@*\n\r"))
969 				c = lastmac;
970 			if (isupper(c))
971 				c = tolower(c);
972 			if (!islower(c))
973 				error(gettext("Bad register"));
974 			donewline();
975 			setdot();
976 			cmdmac(c);
977 			continue;
978 
979 /* | */
980 		case '|':
981 			endline = 0;
982 			goto caseline;
983 
984 /* \n */
985 		case '\n':
986 			endline = 1;
987 caseline:
988 			notempty();
989 			if (addr2 == 0) {
990 				if (cursor_up != NOSTR && c == '\n' &&
991 				    !inglobal)
992 					c = CTRL('k');
993 				if (inglobal)
994 					addr1 = addr2 = dot;
995 				else {
996 					if (dot == dol)
997 						error((vi_TERSE) ?
998 						    gettext("At EOF") :
999 						    gettext("At end-of-file"));
1000 					addr1 = addr2 = dot + 1;
1001 				}
1002 			}
1003 			setdot();
1004 			nonzero();
1005 			if (seensemi)
1006 				addr1 = addr2;
1007 			getline(*addr1);
1008 			if (c == CTRL('k')) {
1009 				flush1();
1010 				destline--;
1011 				if (hadpr)
1012 					shudclob = 1;
1013 			}
1014 			plines(addr1, addr2, 1);
1015 			continue;
1016 
1017 /* " */
1018 		case '"':
1019 			comment();
1020 			continue;
1021 
1022 /* # */
1023 		case '#':
1024 numberit:
1025 			setCNL();
1026 			(void) setnumb(1);
1027 			pflag = 0;
1028 			goto print;
1029 
1030 /* = */
1031 		case '=':
1032 			donewline();
1033 			setall();
1034 			if (inglobal == 2)
1035 				pofix();
1036 			viprintf("%d", lineno(addr2));
1037 			noonl();
1038 			continue;
1039 
1040 /* ! */
1041 		case '!':
1042 			if (addr2 != 0) {
1043 				vmacchng(0);
1044 				unix0(0, 1);
1045 				setdot();
1046 				(void) vi_filter(2);
1047 			} else {
1048 				unix0(1, 1);
1049 				pofix();
1050 				putpad((unsigned char *)exit_ca_mode);
1051 				flush();
1052 				resetterm();
1053 				unixwt(1, unixex("-c", uxb, 0, 0));
1054 				vclrech(1);	/* vcontin(0); */
1055 				nochng();
1056 			}
1057 			continue;
1058 
1059 /* < */
1060 /* > */
1061 		case '<':
1062 		case '>':
1063 			for (cnt = 1; peekchar() == c; cnt++)
1064 				ignchar();
1065 			setCNL();
1066 			vmacchng(0);
1067 			shift(c, cnt);
1068 			continue;
1069 
1070 /* ^D */
1071 /* EOF */
1072 		case CTRL('d'):
1073 		case EOF:
1074 			if (exitoneof) {
1075 				if (addr2 != 0)
1076 					dot = addr2;
1077 				return;
1078 			}
1079 			if (!isatty(0)) {
1080 				if (intty)
1081 					/*
1082 					 * Chtty sys call at UCB may cause a
1083 					 * input which was a tty to suddenly be
1084 					 * turned into /dev/null.
1085 					 */
1086 					onhup(0);
1087 				return;
1088 			}
1089 			if (addr2 != 0) {
1090 				setlastchar('\n');
1091 				putnl();
1092 			}
1093 			if (dol == zero) {
1094 				if (addr2 == 0)
1095 					putnl();
1096 				notempty();
1097 			}
1098 			ungetchar(EOF);
1099 			zop(hadpr);
1100 			continue;
1101 		default:
1102 			if (!isalpha(c) || !isascii(c))
1103 				break;
1104 			ungetchar(c);
1105 			tailprim((unsigned char *)"", 0, 0);
1106 		}
1107 		ungetchar(c);
1108 		{
1109 			int length;
1110 			char multic[MULTI_BYTE_MAX];
1111 			wchar_t wchar;
1112 			length = _mbftowc(multic, &wchar, getchar, &peekc);
1113 			if (length < 0)
1114 				length = -length;
1115 			multic[length] = '\0';
1116 			error((vi_TERSE) ? gettext("What?") :
1117 				gettext("Unknown command character '%s'"),
1118 			    multic);
1119 		}
1120 	}
1121 }
1122