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