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