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