xref: /original-bsd/usr.bin/ex/ex_cmds.c (revision d25e1985)
1 /* Copyright (c) 1980 Regents of the University of California */
2 static char *sccsid = "@(#)ex_cmds.c	5.3 09/12/80";
3 #include "ex.h"
4 #include "ex_argv.h"
5 #include "ex_temp.h"
6 #include "ex_tty.h"
7 
8 bool	pflag, nflag;
9 int	poffset;
10 
11 #define	nochng()	lchng = chng
12 
13 /*
14  * Main loop for command mode command decoding.
15  * A few commands are executed here, but main function
16  * is to strip command addresses, do a little address oriented
17  * processing and call command routines to do the real work.
18  */
19 commands(noprompt, exitoneof)
20 	bool noprompt, exitoneof;
21 {
22 	register line *addr;
23 	register int c;
24 	register int lchng;
25 	int given;
26 	int seensemi;
27 	int cnt;
28 	bool hadpr;
29 
30 	resetflav();
31 	nochng();
32 	for (;;) {
33 		/*
34 		 * If dot at last command
35 		 * ended up at zero, advance to one if there is a such.
36 		 */
37 		if (dot <= zero) {
38 			dot = zero;
39 			if (dol > zero)
40 				dot = one;
41 		}
42 		shudclob = 0;
43 
44 		/*
45 		 * If autoprint or trailing print flags,
46 		 * print the line at the specified offset
47 		 * before the next command.
48 		 */
49 		if (pflag ||
50 		    lchng != chng && value(AUTOPRINT) && !inglobal && !inopen && endline) {
51 			pflag = 0;
52 			nochng();
53 			if (dol != zero) {
54 				addr1 = addr2 = dot + poffset;
55 				if (addr1 < one || addr1 > dol)
56 error("Offset out-of-bounds|Offset after command too large");
57 				setdot1();
58 				goto print;
59 			}
60 		}
61 		nochng();
62 
63 		/*
64 		 * Print prompt if appropriate.
65 		 * If not in global flush output first to prevent
66 		 * going into pfast mode unreasonably.
67 		 */
68 		if (inglobal == 0) {
69 			flush();
70 			if (!hush && value(PROMPT) && !globp && !noprompt && endline) {
71 				putchar(':');
72 				hadpr = 1;
73 			}
74 			TSYNC();
75 		}
76 
77 		/*
78 		 * Gobble up the address.
79 		 * Degenerate addresses yield ".".
80 		 */
81 		addr2 = 0;
82 		given = seensemi = 0;
83 		do {
84 			addr1 = addr2;
85 			addr = address(0);
86 			c = getcd();
87 			if (addr == 0)
88 				if (c == ',')
89 					addr = dot;
90 				else if (addr1 != 0) {
91 					addr2 = dot;
92 					break;
93 				} else
94 					break;
95 			addr2 = addr;
96 			given++;
97 			if (c == ';') {
98 				c = ',';
99 				dot = addr;
100 				seensemi = 1;
101 			}
102 		} while (c == ',');
103 		if (c == '%') {
104 			/* %: same as 1,$ */
105 			addr1 = one;
106 			addr2 = dol;
107 			given = 2;
108 			c = getchar();
109 		}
110 		if (addr1 == 0)
111 			addr1 = addr2;
112 		if (c == ':')
113 			c = getchar();
114 
115 		/*
116 		 * Set command name for special character commands.
117 		 */
118 		tailspec(c);
119 
120 		/*
121 		 * If called via : escape from open or visual, limit
122 		 * the set of available commands here to save work below.
123 		 */
124 		if (inopen) {
125 			if (c=='\n' || c=='\r' || c==CTRL(d) || c==EOF) {
126 				if (addr2)
127 					dot = addr2;
128 				if (c == EOF)
129 					return;
130 				continue;
131 			}
132 			if (any(c, "o"))
133 notinvis:
134 				tailprim(Command, 1, 1);
135 		}
136 choice:
137 		switch (c) {
138 
139 		case 'a':
140 
141 			switch(peekchar()) {
142 			case 'b':
143 /* abbreviate */
144 				tail("abbreviate");
145 				setnoaddr();
146 				mapcmd(0, 1);
147 				anyabbrs = 1;
148 				continue;
149 			case 'r':
150 /* args */
151 				tail("args");
152 				setnoaddr();
153 				eol();
154 				pargs();
155 				continue;
156 			}
157 
158 /* append */
159 			if (inopen)
160 				goto notinvis;
161 			tail("append");
162 			setdot();
163 			aiflag = exclam();
164 			newline();
165 			vmacchng(0);
166 			deletenone();
167 			setin(addr2);
168 			inappend = 1;
169 			ignore(append(gettty, addr2));
170 			inappend = 0;
171 			nochng();
172 			continue;
173 
174 		case 'c':
175 			switch (peekchar()) {
176 
177 /* copy */
178 			case 'o':
179 				tail("copy");
180 				vmacchng(0);
181 				move();
182 				continue;
183 
184 #ifdef CHDIR
185 /* cd */
186 			case 'd':
187 				tail("cd");
188 				goto changdir;
189 
190 /* chdir */
191 			case 'h':
192 				ignchar();
193 				if (peekchar() == 'd') {
194 					register char *p;
195 					tail2of("chdir");
196 changdir:
197 					if (savedfile[0] == '/' || !value(WARN))
198 						ignore(exclam());
199 					else
200 						ignore(quickly());
201 					if (skipend()) {
202 						p = getenv("HOME");
203 						if (p == NULL)
204 							error("Home directory unknown");
205 					} else
206 						getone(), p = file;
207 					eol();
208 					if (chdir(p) < 0)
209 						filioerr(p);
210 					if (savedfile[0] != '/')
211 						edited = 0;
212 					continue;
213 				}
214 				if (inopen)
215 					tailprim("change", 2, 1);
216 				tail2of("change");
217 				break;
218 
219 #endif
220 			default:
221 				if (inopen)
222 					goto notinvis;
223 				tail("change");
224 				break;
225 			}
226 /* change */
227 			aiflag = exclam();
228 			setCNL();
229 			vmacchng(0);
230 			setin(addr1);
231 			delete(0);
232 			inappend = 1;
233 			ignore(append(gettty, addr1 - 1));
234 			inappend = 0;
235 			nochng();
236 			continue;
237 
238 /* delete */
239 		case 'd':
240 			/*
241 			 * Caution: dp and dl have special meaning already.
242 			 */
243 			tail("delete");
244 			c = cmdreg();
245 			setCNL();
246 			vmacchng(0);
247 			if (c)
248 				YANKreg(c);
249 			delete(0);
250 			appendnone();
251 			continue;
252 
253 /* edit */
254 /* ex */
255 		case 'e':
256 			tail(peekchar() == 'x' ? "ex" : "edit");
257 editcmd:
258 			if (!exclam() && chng)
259 				c = 'E';
260 			filename(c);
261 			if (c == 'E') {
262 				ungetchar(lastchar());
263 				ignore(quickly());
264 			}
265 			setnoaddr();
266 doecmd:
267 			init();
268 			addr2 = zero;
269 			laste++;
270 			sync();
271 			rop(c);
272 			nochng();
273 			continue;
274 
275 /* file */
276 		case 'f':
277 			tail("file");
278 			setnoaddr();
279 			filename(c);
280 			noonl();
281 /*
282 			synctmp();
283 */
284 			continue;
285 
286 /* global */
287 		case 'g':
288 			tail("global");
289 			global(!exclam());
290 			nochng();
291 			continue;
292 
293 /* insert */
294 		case 'i':
295 			if (inopen)
296 				goto notinvis;
297 			tail("insert");
298 			setdot();
299 			nonzero();
300 			aiflag = exclam();
301 			newline();
302 			vmacchng(0);
303 			deletenone();
304 			setin(addr2);
305 			inappend = 1;
306 			ignore(append(gettty, addr2 - 1));
307 			inappend = 0;
308 			if (dot == zero && dol > zero)
309 				dot = one;
310 			nochng();
311 			continue;
312 
313 /* join */
314 		case 'j':
315 			tail("join");
316 			c = exclam();
317 			setcount();
318 			nonzero();
319 			newline();
320 			vmacchng(0);
321 			if (given < 2 && addr2 != dol)
322 				addr2++;
323 			join(c);
324 			continue;
325 
326 /* k */
327 		case 'k':
328 casek:
329 			pastwh();
330 			c = getchar();
331 			if (endcmd(c))
332 				serror("Mark what?|%s requires following letter", Command);
333 			newline();
334 			if (!islower(c))
335 				error("Bad mark|Mark must specify a letter");
336 			setdot();
337 			nonzero();
338 			names[c - 'a'] = *addr2 &~ 01;
339 			anymarks = 1;
340 			continue;
341 
342 /* list */
343 		case 'l':
344 			tail("list");
345 			setCNL();
346 			ignorf(setlist(1));
347 			pflag = 0;
348 			goto print;
349 
350 		case 'm':
351 			if (peekchar() == 'a') {
352 				ignchar();
353 				if (peekchar() == 'p') {
354 /* map */
355 					tail2of("map");
356 					setnoaddr();
357 					mapcmd(0, 0);
358 					continue;
359 				}
360 /* mark */
361 				tail2of("mark");
362 				goto casek;
363 			}
364 /* move */
365 			tail("move");
366 			vmacchng(0);
367 			move();
368 			continue;
369 
370 		case 'n':
371 			if (peekchar() == 'u') {
372 				tail("number");
373 				goto numberit;
374 			}
375 /* next */
376 			tail("next");
377 			setnoaddr();
378 			ckaw();
379 			ignore(quickly());
380 			if (getargs())
381 				makargs();
382 			next();
383 			c = 'e';
384 			filename(c);
385 			goto doecmd;
386 
387 /* open */
388 		case 'o':
389 			tail("open");
390 			oop();
391 			pflag = 0;
392 			nochng();
393 			continue;
394 
395 		case 'p':
396 		case 'P':
397 			switch (peekchar()) {
398 
399 /* put */
400 			case 'u':
401 				tail("put");
402 				setdot();
403 				c = cmdreg();
404 				eol();
405 				vmacchng(0);
406 				if (c)
407 					putreg(c);
408 				else
409 					put();
410 				continue;
411 
412 			case 'r':
413 				ignchar();
414 				if (peekchar() == 'e') {
415 /* preserve */
416 					tail2of("preserve");
417 					eol();
418 					if (preserve() == 0)
419 						error("Preserve failed!");
420 					else
421 						error("File preserved.");
422 				}
423 				tail2of("print");
424 				break;
425 
426 			default:
427 				tail("print");
428 				break;
429 			}
430 /* print */
431 			setCNL();
432 			pflag = 0;
433 print:
434 			nonzero();
435 			if (CL && span() > LINES) {
436 				flush1();
437 				vclear();
438 			}
439 			plines(addr1, addr2, 1);
440 			continue;
441 
442 /* quit */
443 		case 'q':
444 			tail("quit");
445 			setnoaddr();
446 			c = quickly();
447 			eol();
448 			if (!c)
449 quit:
450 				nomore();
451 			if (inopen) {
452 				vgoto(WECHO, 0);
453 				if (!ateopr())
454 					vnfl();
455 				else {
456 					tostop();
457 					/* replaced by tostop
458 					putpad(VE);
459 					putpad(KE);
460 					*/
461 				}
462 				flush();
463 				setty(normf);
464 			}
465 			cleanup(1);
466 			exit(0);
467 
468 		case 'r':
469 			if (peekchar() == 'e') {
470 				ignchar();
471 				switch (peekchar()) {
472 
473 /* rewind */
474 				case 'w':
475 					tail2of("rewind");
476 					setnoaddr();
477 					if (!exclam()) {
478 						ckaw();
479 						if (chng && dol > zero)
480 							error("No write@since last chage (:rewind! overrides)");
481 					}
482 					eol();
483 					erewind();
484 					next();
485 					c = 'e';
486 					ungetchar(lastchar());
487 					filename(c);
488 					goto doecmd;
489 
490 /* recover */
491 				case 'c':
492 					tail2of("recover");
493 					setnoaddr();
494 					c = 'e';
495 					if (!exclam() && chng)
496 						c = 'E';
497 					filename(c);
498 					if (c == 'E') {
499 						ungetchar(lastchar());
500 						ignore(quickly());
501 					}
502 					init();
503 					addr2 = zero;
504 					laste++;
505 					sync();
506 					recover();
507 					rop2();
508 					revocer();
509 					if (status == 0)
510 						rop3(c);
511 					if (dol != zero)
512 						change();
513 					nochng();
514 					continue;
515 				}
516 				tail2of("read");
517 			} else
518 				tail("read");
519 /* read */
520 			if (savedfile[0] == 0 && dol == zero)
521 				c = 'e';
522 			pastwh();
523 			vmacchng(0);
524 			if (peekchar() == '!') {
525 				setdot();
526 				ignchar();
527 				unix0(0);
528 				filter(0);
529 				continue;
530 			}
531 			filename(c);
532 			rop(c);
533 			nochng();
534 			if (inopen && endline && addr1 > zero && addr1 < dol)
535 				dot = addr1 + 1;
536 			continue;
537 
538 		case 's':
539 			switch (peekchar()) {
540 			/*
541 			 * Caution: 2nd char cannot be c, g, or r
542 			 * because these have meaning to substitute.
543 			 */
544 
545 /* set */
546 			case 'e':
547 				tail("set");
548 				setnoaddr();
549 				set();
550 				continue;
551 
552 /* shell */
553 			case 'h':
554 				tail("shell");
555 				setNAEOL();
556 				vnfl();
557 				putpad(TE);
558 				flush();
559 				unixwt(1, unixex("-i", (char *) 0, 0, 0));
560 				vcontin(0);
561 				continue;
562 
563 /* source */
564 			case 'o':
565 				if (inopen)
566 					goto notinvis;
567 				tail("source");
568 				setnoaddr();
569 				getone();
570 				eol();
571 				source(file, 0);
572 				continue;
573 #ifdef SIGTSTP
574 /* stop */
575 			case 't':
576 				tail("stop");
577 				if (!ldisc)
578 					error("Old tty driver|Not using new tty driver/shell");
579 				c = exclam();
580 				eol();
581 				if (!c)
582 					ckaw();
583 				eol();
584 				onsusp();
585 				continue;
586 #endif
587 
588 			}
589 			/* fall into ... */
590 
591 /* & */
592 /* ~ */
593 /* substitute */
594 		case '&':
595 		case '~':
596 			Command = "substitute";
597 			if (c == 's')
598 				tail(Command);
599 			vmacchng(0);
600 			if (!substitute(c))
601 				pflag = 0;
602 			continue;
603 
604 /* t */
605 		case 't':
606 			if (peekchar() == 'a') {
607 				tail("tag");
608 				tagfind(exclam());
609 				if (!inopen)
610 					lchng = chng - 1;
611 				else
612 					nochng();
613 				continue;
614 			}
615 			tail("t");
616 			vmacchng(0);
617 			move();
618 			continue;
619 
620 		case 'u':
621 			if (peekchar() == 'n') {
622 				ignchar();
623 				switch(peekchar()) {
624 /* unmap */
625 				case 'm':
626 					tail2of("unmap");
627 					setnoaddr();
628 					mapcmd(1, 0);
629 					continue;
630 /* unabbreviate */
631 				case 'a':
632 					tail2of("unabbreviate");
633 					setnoaddr();
634 					mapcmd(1, 1);
635 					anyabbrs = 1;
636 					continue;
637 				}
638 /* undo */
639 				tail2of("undo");
640 			} else
641 				tail("undo");
642 			setnoaddr();
643 			markDOT();
644 			c = exclam();
645 			newline();
646 			undo(c);
647 			continue;
648 
649 		case 'v':
650 			switch (peekchar()) {
651 
652 			case 'e':
653 /* version */
654 				tail("version");
655 				setNAEOL();
656 				printf("@(#) Version 3.5, 09/12/80."+5);
657 				noonl();
658 				continue;
659 
660 /* visual */
661 			case 'i':
662 				tail("visual");
663 				if (inopen) {
664 					c = 'e';
665 					goto editcmd;
666 				}
667 				vop();
668 				pflag = 0;
669 				nochng();
670 				continue;
671 			}
672 /* v */
673 			tail("v");
674 			global(0);
675 			nochng();
676 			continue;
677 
678 /* write */
679 		case 'w':
680 			c = peekchar();
681 			tail(c == 'q' ? "wq" : "write");
682 wq:
683 			if (skipwh() && peekchar() == '!') {
684 				pofix();
685 				ignchar();
686 				setall();
687 				unix0(0);
688 				filter(1);
689 			} else {
690 				setall();
691 				wop(1);
692 				nochng();
693 			}
694 			if (c == 'q')
695 				goto quit;
696 			continue;
697 
698 /* xit */
699 		case 'x':
700 			tail("xit");
701 			if (!chng)
702 				goto quit;
703 			c = 'q';
704 			goto wq;
705 
706 /* yank */
707 		case 'y':
708 			tail("yank");
709 			c = cmdreg();
710 			setcount();
711 			eol();
712 			vmacchng(0);
713 			if (c)
714 				YANKreg(c);
715 			else
716 				yank();
717 			continue;
718 
719 /* z */
720 		case 'z':
721 			zop(0);
722 			pflag = 0;
723 			continue;
724 
725 /* * */
726 /* @ */
727 		case '*':
728 		case '@':
729 			c = getchar();
730 			if (c=='\n' || c=='\r')
731 				ungetchar(c);
732 			if (any(c, "@*\n\r"))
733 				c = lastmac;
734 			if (isupper(c))
735 				c = tolower(c);
736 			if (!islower(c))
737 				error("Bad register");
738 			newline();
739 			setdot();
740 			cmdmac(c);
741 			continue;
742 
743 /* | */
744 		case '|':
745 			endline = 0;
746 			goto caseline;
747 
748 /* \n */
749 		case '\n':
750 			endline = 1;
751 caseline:
752 			notempty();
753 			if (addr2 == 0) {
754 				if (UP != NOSTR && c == '\n' && !inglobal)
755 					c = CTRL(k);
756 				if (inglobal)
757 					addr1 = addr2 = dot;
758 				else {
759 					if (dot == dol)
760 						error("At EOF|At end-of-file");
761 					addr1 = addr2 = dot + 1;
762 				}
763 			}
764 			setdot();
765 			nonzero();
766 			if (seensemi)
767 				addr1 = addr2;
768 			getline(*addr1);
769 			if (c == CTRL(k)) {
770 				flush1();
771 				destline--;
772 				if (hadpr)
773 					shudclob = 1;
774 			}
775 			plines(addr1, addr2, 1);
776 			continue;
777 
778 /* " */
779 		case '"':
780 			comment();
781 			continue;
782 
783 /* # */
784 		case '#':
785 numberit:
786 			setCNL();
787 			ignorf(setnumb(1));
788 			pflag = 0;
789 			goto print;
790 
791 /* = */
792 		case '=':
793 			newline();
794 			setall();
795 			if (inglobal == 2)
796 				pofix();
797 			printf("%d", lineno(addr2));
798 			noonl();
799 			continue;
800 
801 /* ! */
802 		case '!':
803 			if (addr2 != 0) {
804 				vmacchng(0);
805 				unix0(0);
806 				setdot();
807 				filter(2);
808 			} else {
809 				unix0(1);
810 				pofix();
811 				putpad(TE);
812 				flush();
813 				unixwt(1, unixex("-c", uxb, 0, 0));
814 				vclrech(1);	/* vcontin(0); */
815 				nochng();
816 			}
817 			continue;
818 
819 /* < */
820 /* > */
821 		case '<':
822 		case '>':
823 			for (cnt = 1; peekchar() == c; cnt++)
824 				ignchar();
825 			setCNL();
826 			vmacchng(0);
827 			shift(c, cnt);
828 			continue;
829 
830 /* ^D */
831 /* EOF */
832 		case CTRL(d):
833 		case EOF:
834 			if (exitoneof) {
835 				if (addr2 != 0)
836 					dot = addr2;
837 				return;
838 			}
839 			if (!isatty(0)) {
840 				if (intty)
841 					/*
842 					 * Chtty sys call at UCB may cause a
843 					 * input which was a tty to suddenly be
844 					 * turned into /dev/null.
845 					 */
846 					onhup();
847 				return;
848 			}
849 			if (addr2 != 0) {
850 				setlastchar('\n');
851 				putnl();
852 			}
853 			if (dol == zero) {
854 				if (addr2 == 0)
855 					putnl();
856 				notempty();
857 			}
858 			ungetchar(EOF);
859 			zop(hadpr);
860 			continue;
861 
862 		default:
863 			if (!isalpha(c))
864 				break;
865 			ungetchar(c);
866 			tailprim("", 0, 0);
867 		}
868 		error("What?|Unknown command character '%c'", c);
869 	}
870 }
871