xref: /original-bsd/usr.bin/ex/ex_cmds.c (revision c8876cb1)
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.13 (Berkeley) 06/29/90";
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 				c = exclam();
597 				eol();
598 				if (!c)
599 					ckaw();
600 				onsusp();
601 				continue;
602 #endif
603 
604 			}
605 			/* fall into ... */
606 
607 /* & */
608 /* ~ */
609 /* substitute */
610 		case '&':
611 		case '~':
612 			Command = "substitute";
613 			if (c == 's')
614 				tail(Command);
615 			vmacchng(0);
616 			if (!substitute(c))
617 				pflag = 0;
618 			continue;
619 
620 /* t */
621 		case 't':
622 			if (peekchar() == 'a') {
623 				tail("tag");
624 				tagfind(exclam());
625 				if (!inopen)
626 					lchng = chng - 1;
627 				else
628 					nochng();
629 				continue;
630 			}
631 			tail("t");
632 			vmacchng(0);
633 			move();
634 			continue;
635 
636 		case 'u':
637 			if (peekchar() == 'n') {
638 				ignchar();
639 				switch(peekchar()) {
640 /* unmap */
641 				case 'm':
642 					tail2of("unmap");
643 					setnoaddr();
644 					mapcmd(1, 0);
645 					continue;
646 /* unabbreviate */
647 				case 'a':
648 					tail2of("unabbreviate");
649 					setnoaddr();
650 					mapcmd(1, 1);
651 					anyabbrs = 1;
652 					continue;
653 				}
654 /* undo */
655 				tail2of("undo");
656 			} else
657 				tail("undo");
658 			setnoaddr();
659 			markDOT();
660 			c = exclam();
661 			newline();
662 			undo(c);
663 			continue;
664 
665 		case 'v':
666 			switch (peekchar()) {
667 
668 			case 'e':
669 /* version */
670 				tail("version");
671 				setNAEOL();
672 				ex_printf("@(#) Version 3.7, 6/7/85."+5);
673 				noonl();
674 				continue;
675 
676 /* visual */
677 			case 'i':
678 				tail("visual");
679 				if (inopen) {
680 					c = 'e';
681 					goto editcmd;
682 				}
683 				vop();
684 				pflag = 0;
685 				nochng();
686 				continue;
687 			}
688 /* v */
689 			tail("v");
690 			global(0);
691 			nochng();
692 			continue;
693 
694 /* write */
695 		case 'w':
696 			c = peekchar();
697 			tail(c == 'q' ? "wq" : "write");
698 wq:
699 			if (skipwh() && peekchar() == '!') {
700 				pofix();
701 				ignchar();
702 				setall();
703 				unix0(0);
704 				filter(1);
705 			} else {
706 				setall();
707 				wop(1);
708 				nochng();
709 			}
710 			if (c == 'q')
711 				goto quit;
712 			continue;
713 
714 /* xit */
715 		case 'x':
716 			tail("xit");
717 			if (!chng)
718 				goto quit;
719 			c = 'q';
720 			goto wq;
721 
722 /* yank */
723 		case 'y':
724 			tail("yank");
725 			c = cmdreg();
726 			setcount();
727 			eol();
728 			vmacchng(0);
729 			if (c)
730 				YANKreg(c);
731 			else
732 				yank();
733 			continue;
734 
735 /* z */
736 		case 'z':
737 			zop(0);
738 			pflag = 0;
739 			continue;
740 
741 /* * */
742 /* @ */
743 		case '*':
744 		case '@':
745 			c = ex_getchar();
746 			if (c=='\n' || c=='\r')
747 				ungetchar(c);
748 			if (any(c, "@*\n\r"))
749 				c = lastmac;
750 			if (isupper(c))
751 				c = tolower(c);
752 			if (!islower(c))
753 				error("Bad register");
754 			newline();
755 			setdot();
756 			cmdmac(c);
757 			continue;
758 
759 /* | */
760 		case '|':
761 			endline = 0;
762 			goto caseline;
763 
764 /* \n */
765 		case '\n':
766 			endline = 1;
767 caseline:
768 			notempty();
769 			if (addr2 == 0) {
770 				if (UP != NOSTR && c == '\n' && !inglobal)
771 					c = CTRL('k');
772 				if (inglobal)
773 					addr1 = addr2 = dot;
774 				else {
775 					if (dot == dol)
776 						error("At EOF|At end-of-file");
777 					addr1 = addr2 = dot + 1;
778 				}
779 			}
780 			setdot();
781 			nonzero();
782 			if (seensemi)
783 				addr1 = addr2;
784 			getline(*addr1);
785 			if (c == CTRL('k')) {
786 				flush1();
787 				destline--;
788 				if (hadpr)
789 					shudclob = 1;
790 			}
791 			plines(addr1, addr2, 1);
792 			continue;
793 
794 /* " */
795 		case '"':
796 			comment();
797 			continue;
798 
799 /* # */
800 		case '#':
801 numberit:
802 			setCNL();
803 			ignorf(setnumb(1));
804 			pflag = 0;
805 			goto print;
806 
807 /* = */
808 		case '=':
809 			newline();
810 			setall();
811 			if (inglobal == 2)
812 				pofix();
813 			ex_printf("%d", lineno(addr2));
814 			noonl();
815 			continue;
816 
817 /* ! */
818 		case '!':
819 			if (addr2 != 0) {
820 				vmacchng(0);
821 				unix0(0);
822 				setdot();
823 				filter(2);
824 			} else {
825 				unix0(1);
826 				pofix();
827 				putpad(TE);
828 				flush();
829 				unixwt(1, unixex("-c", uxb, 0, 0));
830 				vclrech(1);	/* vcontin(0); */
831 				nochng();
832 			}
833 			continue;
834 
835 /* < */
836 /* > */
837 		case '<':
838 		case '>':
839 			for (cnt = 1; peekchar() == c; cnt++)
840 				ignchar();
841 			setCNL();
842 			vmacchng(0);
843 			shift(c, cnt);
844 			continue;
845 
846 /* ^D */
847 /* EOF */
848 		case CTRL('d'):
849 		case EOF:
850 			if (exitoneof) {
851 				if (addr2 != 0)
852 					dot = addr2;
853 				return;
854 			}
855 			if (!isatty(0)) {
856 				if (intty)
857 					/*
858 					 * Chtty sys call at UCB may cause a
859 					 * input which was a tty to suddenly be
860 					 * turned into /dev/null.
861 					 */
862 					onhup();
863 				return;
864 			}
865 			if (addr2 != 0) {
866 				setlastchar('\n');
867 				putnl();
868 			}
869 			if (dol == zero) {
870 				if (addr2 == 0)
871 					putnl();
872 				notempty();
873 			}
874 			ungetchar(EOF);
875 			zop(hadpr);
876 			continue;
877 
878 		default:
879 			if (!isalpha(c))
880 				break;
881 			ungetchar(c);
882 			tailprim("", 0, 0);
883 		}
884 		error("What?|Unknown command character '%c'", c);
885 	}
886 }
887