xref: /original-bsd/usr.bin/ex/ex_cmds.c (revision 23a40993)
1 /* Copyright (c) 1981 Regents of the University of California */
2 static char *sccsid = "@(#)ex_cmds.c	7.7	06/10/83";
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 #ifdef VMUNIX
274 			tlaste();
275 #endif
276 			laste = 0;
277 			sync();
278 			nochng();
279 			continue;
280 
281 /* file */
282 		case 'f':
283 			tail("file");
284 			setnoaddr();
285 			filename(c);
286 			noonl();
287 /*
288 			synctmp();
289 */
290 			continue;
291 
292 /* global */
293 		case 'g':
294 			tail("global");
295 			global(!exclam());
296 			nochng();
297 			continue;
298 
299 /* insert */
300 		case 'i':
301 			if (inopen)
302 				goto notinvis;
303 			tail("insert");
304 			setdot();
305 			nonzero();
306 			aiflag = exclam();
307 			newline();
308 			vmacchng(0);
309 			deletenone();
310 			setin(addr2);
311 			inappend = 1;
312 			ignore(append(gettty, addr2 - 1));
313 			inappend = 0;
314 			if (dot == zero && dol > zero)
315 				dot = one;
316 			nochng();
317 			continue;
318 
319 /* join */
320 		case 'j':
321 			tail("join");
322 			c = exclam();
323 			setcount();
324 			nonzero();
325 			newline();
326 			vmacchng(0);
327 			if (given < 2 && addr2 != dol)
328 				addr2++;
329 			join(c);
330 			continue;
331 
332 /* k */
333 		case 'k':
334 casek:
335 			pastwh();
336 			c = getchar();
337 			if (endcmd(c))
338 				serror("Mark what?|%s requires following letter", Command);
339 			newline();
340 			if (!islower(c))
341 				error("Bad mark|Mark must specify a letter");
342 			setdot();
343 			nonzero();
344 			names[c - 'a'] = *addr2 &~ 01;
345 			anymarks = 1;
346 			continue;
347 
348 /* list */
349 		case 'l':
350 			tail("list");
351 			setCNL();
352 			ignorf(setlist(1));
353 			pflag = 0;
354 			goto print;
355 
356 		case 'm':
357 			if (peekchar() == 'a') {
358 				ignchar();
359 				if (peekchar() == 'p') {
360 /* map */
361 					tail2of("map");
362 					setnoaddr();
363 					mapcmd(0, 0);
364 					continue;
365 				}
366 /* mark */
367 				tail2of("mark");
368 				goto casek;
369 			}
370 /* move */
371 			tail("move");
372 			vmacchng(0);
373 			move();
374 			continue;
375 
376 		case 'n':
377 			if (peekchar() == 'u') {
378 				tail("number");
379 				goto numberit;
380 			}
381 /* next */
382 			tail("next");
383 			setnoaddr();
384 			ckaw();
385 			ignore(quickly());
386 			if (getargs())
387 				makargs();
388 			next();
389 			c = 'e';
390 			filename(c);
391 			goto doecmd;
392 
393 /* open */
394 		case 'o':
395 			tail("open");
396 			oop();
397 			pflag = 0;
398 			nochng();
399 			continue;
400 
401 		case 'p':
402 		case 'P':
403 			switch (peekchar()) {
404 
405 /* put */
406 			case 'u':
407 				tail("put");
408 				setdot();
409 				c = cmdreg();
410 				eol();
411 				vmacchng(0);
412 				if (c)
413 					putreg(c);
414 				else
415 					put();
416 				continue;
417 
418 			case 'r':
419 				ignchar();
420 				if (peekchar() == 'e') {
421 /* preserve */
422 					tail2of("preserve");
423 					eol();
424 					if (preserve() == 0)
425 						error("Preserve failed!");
426 					else
427 						error("File preserved.");
428 				}
429 				tail2of("print");
430 				break;
431 
432 			default:
433 				tail("print");
434 				break;
435 			}
436 /* print */
437 			setCNL();
438 			pflag = 0;
439 print:
440 			nonzero();
441 			if (CL && span() > LINES) {
442 				flush1();
443 				vclear();
444 			}
445 			plines(addr1, addr2, 1);
446 			continue;
447 
448 /* quit */
449 		case 'q':
450 			tail("quit");
451 			setnoaddr();
452 			c = quickly();
453 			eol();
454 			if (!c)
455 quit:
456 				nomore();
457 			if (inopen) {
458 				vgoto(WECHO, 0);
459 				if (!ateopr())
460 					vnfl();
461 				else {
462 					tostop();
463 				}
464 				flush();
465 				setty(normf);
466 			}
467 			cleanup(1);
468 			exit(0);
469 
470 		case 'r':
471 			if (peekchar() == 'e') {
472 				ignchar();
473 				switch (peekchar()) {
474 
475 /* rewind */
476 				case 'w':
477 					tail2of("rewind");
478 					setnoaddr();
479 					if (!exclam()) {
480 						ckaw();
481 						if (chng && dol > zero)
482 							error("No write@since last chage (:rewind! overrides)");
483 					}
484 					eol();
485 					erewind();
486 					next();
487 					c = 'e';
488 					ungetchar(lastchar());
489 					filename(c);
490 					goto doecmd;
491 
492 /* recover */
493 				case 'c':
494 					tail2of("recover");
495 					setnoaddr();
496 					c = 'e';
497 					if (!exclam() && chng)
498 						c = 'E';
499 					filename(c);
500 					if (c == 'E') {
501 						ungetchar(lastchar());
502 						ignore(quickly());
503 					}
504 					init();
505 					addr2 = zero;
506 					laste++;
507 					sync();
508 					recover();
509 					rop2();
510 					revocer();
511 					if (status == 0)
512 						rop3(c);
513 					if (dol != zero)
514 						change();
515 #ifdef VMUNIX
516 					tlaste();
517 #endif
518 					laste = 0;
519 					sync();
520 					nochng();
521 					continue;
522 				}
523 				tail2of("read");
524 			} else
525 				tail("read");
526 /* read */
527 			if (savedfile[0] == 0 && dol == zero)
528 				c = 'e';
529 			pastwh();
530 			vmacchng(0);
531 			if (peekchar() == '!') {
532 				setdot();
533 				ignchar();
534 				unix0(0);
535 				filter(0);
536 				continue;
537 			}
538 			filename(c);
539 			rop(c);
540 			nochng();
541 			if (inopen && endline && addr1 > zero && addr1 < dol)
542 				dot = addr1 + 1;
543 			continue;
544 
545 		case 's':
546 			switch (peekchar()) {
547 			/*
548 			 * Caution: 2nd char cannot be c, g, or r
549 			 * because these have meaning to substitute.
550 			 */
551 
552 /* set */
553 			case 'e':
554 				tail("set");
555 				setnoaddr();
556 				set();
557 				continue;
558 
559 /* shell */
560 			case 'h':
561 				tail("shell");
562 				setNAEOL();
563 				vnfl();
564 				putpad(TE);
565 				flush();
566 				unixwt(1, unixex("-i", (char *) 0, 0, 0));
567 				vcontin(0);
568 				continue;
569 
570 /* source */
571 			case 'o':
572 #ifdef notdef
573 				if (inopen)
574 					goto notinvis;
575 #endif
576 				tail("source");
577 				setnoaddr();
578 				getone();
579 				eol();
580 				source(file, 0);
581 				continue;
582 #ifdef SIGTSTP
583 /* stop, suspend */
584 			case 't':
585 				tail("stop");
586 				goto suspend;
587 			case 'u':
588 				tail("suspend");
589 suspend:
590 				if (!ldisc)
591 					error("Old tty driver|Not using new tty driver/shell");
592 				c = exclam();
593 				eol();
594 				if (!c)
595 					ckaw();
596 				onsusp();
597 				continue;
598 #endif
599 
600 			}
601 			/* fall into ... */
602 
603 /* & */
604 /* ~ */
605 /* substitute */
606 		case '&':
607 		case '~':
608 			Command = "substitute";
609 			if (c == 's')
610 				tail(Command);
611 			vmacchng(0);
612 			if (!substitute(c))
613 				pflag = 0;
614 			continue;
615 
616 /* t */
617 		case 't':
618 			if (peekchar() == 'a') {
619 				tail("tag");
620 				tagfind(exclam());
621 				if (!inopen)
622 					lchng = chng - 1;
623 				else
624 					nochng();
625 				continue;
626 			}
627 			tail("t");
628 			vmacchng(0);
629 			move();
630 			continue;
631 
632 		case 'u':
633 			if (peekchar() == 'n') {
634 				ignchar();
635 				switch(peekchar()) {
636 /* unmap */
637 				case 'm':
638 					tail2of("unmap");
639 					setnoaddr();
640 					mapcmd(1, 0);
641 					continue;
642 /* unabbreviate */
643 				case 'a':
644 					tail2of("unabbreviate");
645 					setnoaddr();
646 					mapcmd(1, 1);
647 					anyabbrs = 1;
648 					continue;
649 				}
650 /* undo */
651 				tail2of("undo");
652 			} else
653 				tail("undo");
654 			setnoaddr();
655 			markDOT();
656 			c = exclam();
657 			newline();
658 			undo(c);
659 			continue;
660 
661 		case 'v':
662 			switch (peekchar()) {
663 
664 			case 'e':
665 /* version */
666 				tail("version");
667 				setNAEOL();
668 				printf("@(#) Version 3.7, 06/10/83."+5);
669 				noonl();
670 				continue;
671 
672 /* visual */
673 			case 'i':
674 				tail("visual");
675 				if (inopen) {
676 					c = 'e';
677 					goto editcmd;
678 				}
679 				vop();
680 				pflag = 0;
681 				nochng();
682 				continue;
683 			}
684 /* v */
685 			tail("v");
686 			global(0);
687 			nochng();
688 			continue;
689 
690 /* write */
691 		case 'w':
692 			c = peekchar();
693 			tail(c == 'q' ? "wq" : "write");
694 wq:
695 			if (skipwh() && peekchar() == '!') {
696 				pofix();
697 				ignchar();
698 				setall();
699 				unix0(0);
700 				filter(1);
701 			} else {
702 				setall();
703 				wop(1);
704 				nochng();
705 			}
706 			if (c == 'q')
707 				goto quit;
708 			continue;
709 
710 /* xit */
711 		case 'x':
712 			tail("xit");
713 			if (!chng)
714 				goto quit;
715 			c = 'q';
716 			goto wq;
717 
718 /* yank */
719 		case 'y':
720 			tail("yank");
721 			c = cmdreg();
722 			setcount();
723 			eol();
724 			vmacchng(0);
725 			if (c)
726 				YANKreg(c);
727 			else
728 				yank();
729 			continue;
730 
731 /* z */
732 		case 'z':
733 			zop(0);
734 			pflag = 0;
735 			continue;
736 
737 /* * */
738 /* @ */
739 		case '*':
740 		case '@':
741 			c = getchar();
742 			if (c=='\n' || c=='\r')
743 				ungetchar(c);
744 			if (any(c, "@*\n\r"))
745 				c = lastmac;
746 			if (isupper(c))
747 				c = tolower(c);
748 			if (!islower(c))
749 				error("Bad register");
750 			newline();
751 			setdot();
752 			cmdmac(c);
753 			continue;
754 
755 /* | */
756 		case '|':
757 			endline = 0;
758 			goto caseline;
759 
760 /* \n */
761 		case '\n':
762 			endline = 1;
763 caseline:
764 			notempty();
765 			if (addr2 == 0) {
766 				if (UP != NOSTR && c == '\n' && !inglobal)
767 					c = CTRL(k);
768 				if (inglobal)
769 					addr1 = addr2 = dot;
770 				else {
771 					if (dot == dol)
772 						error("At EOF|At end-of-file");
773 					addr1 = addr2 = dot + 1;
774 				}
775 			}
776 			setdot();
777 			nonzero();
778 			if (seensemi)
779 				addr1 = addr2;
780 			getline(*addr1);
781 			if (c == CTRL(k)) {
782 				flush1();
783 				destline--;
784 				if (hadpr)
785 					shudclob = 1;
786 			}
787 			plines(addr1, addr2, 1);
788 			continue;
789 
790 /* " */
791 		case '"':
792 			comment();
793 			continue;
794 
795 /* # */
796 		case '#':
797 numberit:
798 			setCNL();
799 			ignorf(setnumb(1));
800 			pflag = 0;
801 			goto print;
802 
803 /* = */
804 		case '=':
805 			newline();
806 			setall();
807 			if (inglobal == 2)
808 				pofix();
809 			printf("%d", lineno(addr2));
810 			noonl();
811 			continue;
812 
813 /* ! */
814 		case '!':
815 			if (addr2 != 0) {
816 				vmacchng(0);
817 				unix0(0);
818 				setdot();
819 				filter(2);
820 			} else {
821 				unix0(1);
822 				pofix();
823 				putpad(TE);
824 				flush();
825 				unixwt(1, unixex("-c", uxb, 0, 0));
826 				vclrech(1);	/* vcontin(0); */
827 				nochng();
828 			}
829 			continue;
830 
831 /* < */
832 /* > */
833 		case '<':
834 		case '>':
835 			for (cnt = 1; peekchar() == c; cnt++)
836 				ignchar();
837 			setCNL();
838 			vmacchng(0);
839 			shift(c, cnt);
840 			continue;
841 
842 /* ^D */
843 /* EOF */
844 		case CTRL(d):
845 		case EOF:
846 			if (exitoneof) {
847 				if (addr2 != 0)
848 					dot = addr2;
849 				return;
850 			}
851 			if (!isatty(0)) {
852 				if (intty)
853 					/*
854 					 * Chtty sys call at UCB may cause a
855 					 * input which was a tty to suddenly be
856 					 * turned into /dev/null.
857 					 */
858 					onhup();
859 				return;
860 			}
861 			if (addr2 != 0) {
862 				setlastchar('\n');
863 				putnl();
864 			}
865 			if (dol == zero) {
866 				if (addr2 == 0)
867 					putnl();
868 				notempty();
869 			}
870 			ungetchar(EOF);
871 			zop(hadpr);
872 			continue;
873 
874 		default:
875 			if (!isalpha(c))
876 				break;
877 			ungetchar(c);
878 			tailprim("", 0, 0);
879 		}
880 		error("What?|Unknown command character '%c'", c);
881 	}
882 }
883