xref: /original-bsd/usr.bin/ex/ex_cmds.c (revision 6219b5e8)
1 /* Copyright (c) 1981 Regents of the University of California */
2 static char *sccsid = "@(#)ex_cmds.c	7.8	03/19/85";
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 					nochng();
520 					continue;
521 				}
522 				tail2of("read");
523 			} else
524 				tail("read");
525 /* read */
526 			if (savedfile[0] == 0 && dol == zero)
527 				c = 'e';
528 			pastwh();
529 			vmacchng(0);
530 			if (peekchar() == '!') {
531 				setdot();
532 				ignchar();
533 				unix0(0);
534 				filter(0);
535 				continue;
536 			}
537 			filename(c);
538 			rop(c);
539 			nochng();
540 			if (inopen && endline && addr1 > zero && addr1 < dol)
541 				dot = addr1 + 1;
542 			continue;
543 
544 		case 's':
545 			switch (peekchar()) {
546 			/*
547 			 * Caution: 2nd char cannot be c, g, or r
548 			 * because these have meaning to substitute.
549 			 */
550 
551 /* set */
552 			case 'e':
553 				tail("set");
554 				setnoaddr();
555 				set();
556 				continue;
557 
558 /* shell */
559 			case 'h':
560 				tail("shell");
561 				setNAEOL();
562 				vnfl();
563 				putpad(TE);
564 				flush();
565 				unixwt(1, unixex("-i", (char *) 0, 0, 0));
566 				vcontin(0);
567 				continue;
568 
569 /* source */
570 			case 'o':
571 #ifdef notdef
572 				if (inopen)
573 					goto notinvis;
574 #endif
575 				tail("source");
576 				setnoaddr();
577 				getone();
578 				eol();
579 				source(file, 0);
580 				continue;
581 #ifdef SIGTSTP
582 /* stop, suspend */
583 			case 't':
584 				tail("stop");
585 				goto suspend;
586 			case 'u':
587 				tail("suspend");
588 suspend:
589 				if (!ldisc)
590 					error("Old tty driver|Not using new tty driver/shell");
591 				c = exclam();
592 				eol();
593 				if (!c)
594 					ckaw();
595 				onsusp();
596 				continue;
597 #endif
598 
599 			}
600 			/* fall into ... */
601 
602 /* & */
603 /* ~ */
604 /* substitute */
605 		case '&':
606 		case '~':
607 			Command = "substitute";
608 			if (c == 's')
609 				tail(Command);
610 			vmacchng(0);
611 			if (!substitute(c))
612 				pflag = 0;
613 			continue;
614 
615 /* t */
616 		case 't':
617 			if (peekchar() == 'a') {
618 				tail("tag");
619 				tagfind(exclam());
620 				if (!inopen)
621 					lchng = chng - 1;
622 				else
623 					nochng();
624 				continue;
625 			}
626 			tail("t");
627 			vmacchng(0);
628 			move();
629 			continue;
630 
631 		case 'u':
632 			if (peekchar() == 'n') {
633 				ignchar();
634 				switch(peekchar()) {
635 /* unmap */
636 				case 'm':
637 					tail2of("unmap");
638 					setnoaddr();
639 					mapcmd(1, 0);
640 					continue;
641 /* unabbreviate */
642 				case 'a':
643 					tail2of("unabbreviate");
644 					setnoaddr();
645 					mapcmd(1, 1);
646 					anyabbrs = 1;
647 					continue;
648 				}
649 /* undo */
650 				tail2of("undo");
651 			} else
652 				tail("undo");
653 			setnoaddr();
654 			markDOT();
655 			c = exclam();
656 			newline();
657 			undo(c);
658 			continue;
659 
660 		case 'v':
661 			switch (peekchar()) {
662 
663 			case 'e':
664 /* version */
665 				tail("version");
666 				setNAEOL();
667 				printf("@(#) Version 3.7, 03/19/85."+5);
668 				noonl();
669 				continue;
670 
671 /* visual */
672 			case 'i':
673 				tail("visual");
674 				if (inopen) {
675 					c = 'e';
676 					goto editcmd;
677 				}
678 				vop();
679 				pflag = 0;
680 				nochng();
681 				continue;
682 			}
683 /* v */
684 			tail("v");
685 			global(0);
686 			nochng();
687 			continue;
688 
689 /* write */
690 		case 'w':
691 			c = peekchar();
692 			tail(c == 'q' ? "wq" : "write");
693 wq:
694 			if (skipwh() && peekchar() == '!') {
695 				pofix();
696 				ignchar();
697 				setall();
698 				unix0(0);
699 				filter(1);
700 			} else {
701 				setall();
702 				wop(1);
703 				nochng();
704 			}
705 			if (c == 'q')
706 				goto quit;
707 			continue;
708 
709 /* xit */
710 		case 'x':
711 			tail("xit");
712 			if (!chng)
713 				goto quit;
714 			c = 'q';
715 			goto wq;
716 
717 /* yank */
718 		case 'y':
719 			tail("yank");
720 			c = cmdreg();
721 			setcount();
722 			eol();
723 			vmacchng(0);
724 			if (c)
725 				YANKreg(c);
726 			else
727 				yank();
728 			continue;
729 
730 /* z */
731 		case 'z':
732 			zop(0);
733 			pflag = 0;
734 			continue;
735 
736 /* * */
737 /* @ */
738 		case '*':
739 		case '@':
740 			c = getchar();
741 			if (c=='\n' || c=='\r')
742 				ungetchar(c);
743 			if (any(c, "@*\n\r"))
744 				c = lastmac;
745 			if (isupper(c))
746 				c = tolower(c);
747 			if (!islower(c))
748 				error("Bad register");
749 			newline();
750 			setdot();
751 			cmdmac(c);
752 			continue;
753 
754 /* | */
755 		case '|':
756 			endline = 0;
757 			goto caseline;
758 
759 /* \n */
760 		case '\n':
761 			endline = 1;
762 caseline:
763 			notempty();
764 			if (addr2 == 0) {
765 				if (UP != NOSTR && c == '\n' && !inglobal)
766 					c = CTRL(k);
767 				if (inglobal)
768 					addr1 = addr2 = dot;
769 				else {
770 					if (dot == dol)
771 						error("At EOF|At end-of-file");
772 					addr1 = addr2 = dot + 1;
773 				}
774 			}
775 			setdot();
776 			nonzero();
777 			if (seensemi)
778 				addr1 = addr2;
779 			getline(*addr1);
780 			if (c == CTRL(k)) {
781 				flush1();
782 				destline--;
783 				if (hadpr)
784 					shudclob = 1;
785 			}
786 			plines(addr1, addr2, 1);
787 			continue;
788 
789 /* " */
790 		case '"':
791 			comment();
792 			continue;
793 
794 /* # */
795 		case '#':
796 numberit:
797 			setCNL();
798 			ignorf(setnumb(1));
799 			pflag = 0;
800 			goto print;
801 
802 /* = */
803 		case '=':
804 			newline();
805 			setall();
806 			if (inglobal == 2)
807 				pofix();
808 			printf("%d", lineno(addr2));
809 			noonl();
810 			continue;
811 
812 /* ! */
813 		case '!':
814 			if (addr2 != 0) {
815 				vmacchng(0);
816 				unix0(0);
817 				setdot();
818 				filter(2);
819 			} else {
820 				unix0(1);
821 				pofix();
822 				putpad(TE);
823 				flush();
824 				unixwt(1, unixex("-c", uxb, 0, 0));
825 				vclrech(1);	/* vcontin(0); */
826 				nochng();
827 			}
828 			continue;
829 
830 /* < */
831 /* > */
832 		case '<':
833 		case '>':
834 			for (cnt = 1; peekchar() == c; cnt++)
835 				ignchar();
836 			setCNL();
837 			vmacchng(0);
838 			shift(c, cnt);
839 			continue;
840 
841 /* ^D */
842 /* EOF */
843 		case CTRL(d):
844 		case EOF:
845 			if (exitoneof) {
846 				if (addr2 != 0)
847 					dot = addr2;
848 				return;
849 			}
850 			if (!isatty(0)) {
851 				if (intty)
852 					/*
853 					 * Chtty sys call at UCB may cause a
854 					 * input which was a tty to suddenly be
855 					 * turned into /dev/null.
856 					 */
857 					onhup();
858 				return;
859 			}
860 			if (addr2 != 0) {
861 				setlastchar('\n');
862 				putnl();
863 			}
864 			if (dol == zero) {
865 				if (addr2 == 0)
866 					putnl();
867 				notempty();
868 			}
869 			ungetchar(EOF);
870 			zop(hadpr);
871 			continue;
872 
873 		default:
874 			if (!isalpha(c))
875 				break;
876 			ungetchar(c);
877 			tailprim("", 0, 0);
878 		}
879 		error("What?|Unknown command character '%c'", c);
880 	}
881 }
882