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