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