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