ex_cmds.c revision 802:73b56fb6544b
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
41bool	pflag, nflag;
42int	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 */
53extern unsigned char *Version;
54void
55commands(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"))
189notinvis:
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");
273changdir:
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");
365editcmd:
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();
392doecmd:
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':
465casek:
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;
617print:
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)
641quit:
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");
796suspend:
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");
902wq:
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;
987caseline:
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 '#':
1024numberit:
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