xref: /original-bsd/usr.bin/ex/ex_subr.c (revision 306a1b7a)
1 /*-
2  * Copyright (c) 1980, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.proprietary.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)ex_subr.c	8.1 (Berkeley) 06/09/93";
10 #endif /* not lint */
11 
12 #include "ex.h"
13 #include "ex_re.h"
14 #include "ex_tty.h"
15 #include "ex_vis.h"
16 #include "pathnames.h"
17 
18 /*
19  * Random routines, in alphabetical order.
20  */
21 
22 any(c, s)
23 	int c;
24 	register char *s;
25 {
26 	register int x;
27 
28 	while (x = *s++)
29 		if (x == c)
30 			return (1);
31 	return (0);
32 }
33 
34 backtab(i)
35 	register int i;
36 {
37 	register int j;
38 
39 	j = i % value(SHIFTWIDTH);
40 	if (j == 0)
41 		j = value(SHIFTWIDTH);
42 	i -= j;
43 	if (i < 0)
44 		i = 0;
45 	return (i);
46 }
47 
48 change()
49 {
50 
51 	tchng++;
52 	chng = tchng;
53 }
54 
55 /*
56  * Column returns the number of
57  * columns occupied by printing the
58  * characters through position cp of the
59  * current line.
60  */
61 column(cp)
62 	register char *cp;
63 {
64 
65 	if (cp == 0)
66 		cp = &linebuf[LBSIZE - 2];
67 	return (qcolumn(cp, (char *) 0));
68 }
69 
70 /*
71  * Ignore a comment to the end of the line.
72  * This routine eats the trailing newline so don't call newline().
73  */
74 comment()
75 {
76 	register int c;
77 
78 	do {
79 		c = ex_getchar();
80 	} while (c != '\n' && c != EOF);
81 	if (c == EOF)
82 		ungetchar(c);
83 }
84 
85 Copy(to, from, size)
86 	register char *from, *to;
87 	register int size;
88 {
89 
90 	if (size > 0)
91 		do
92 			*to++ = *from++;
93 		while (--size > 0);
94 }
95 
96 copyw(to, from, size)
97 	register line *from, *to;
98 	register int size;
99 {
100 	if (size > 0)
101 		do
102 			*to++ = *from++;
103 		while (--size > 0);
104 }
105 
106 copywR(to, from, size)
107 	register line *from, *to;
108 	register int size;
109 {
110 
111 	while (--size >= 0)
112 		to[size] = from[size];
113 }
114 
115 ctlof(c)
116 	int c;
117 {
118 
119 	return (c == TRIM ? '?' : c | ('A' - 1));
120 }
121 
122 dingdong()
123 {
124 
125 	if (VB)
126 		putpad(VB);
127 	else if (value(ERRORBELLS))
128 		putch('\207');
129 }
130 
131 fixindent(indent)
132 	int indent;
133 {
134 	register int i;
135 	register char *cp;
136 
137 	i = whitecnt(genbuf);
138 	cp = vpastwh(genbuf);
139 	if (*cp == 0 && i == indent && linebuf[0] == 0) {
140 		genbuf[0] = 0;
141 		return (i);
142 	}
143 	CP(genindent(i), cp);
144 	return (i);
145 }
146 
147 filioerr(cp)
148 	char *cp;
149 {
150 	register int oerrno = errno;
151 
152 	lprintf("\"%s\"", cp);
153 	errno = oerrno;
154 	syserror();
155 }
156 
157 char *
158 genindent(indent)
159 	register int indent;
160 {
161 	register char *cp;
162 
163 	for (cp = genbuf; indent >= value(TABSTOP); indent -= value(TABSTOP))
164 		*cp++ = '\t';
165 	for (; indent > 0; indent--)
166 		*cp++ = ' ';
167 	return (cp);
168 }
169 
170 getDOT()
171 {
172 
173 	getline(*dot);
174 }
175 
176 line *
177 getmark(c)
178 	register int c;
179 {
180 	register line *addr;
181 
182 	for (addr = one; addr <= dol; addr++)
183 		if (names[c - 'a'] == (*addr &~ 01)) {
184 			return (addr);
185 		}
186 	return (0);
187 }
188 
189 getn(cp)
190 	register char *cp;
191 {
192 	register int i = 0;
193 
194 	while (isdigit(*cp))
195 		i = i * 10 + *cp++ - '0';
196 	if (*cp)
197 		return (0);
198 	return (i);
199 }
200 
201 ignnEOF()
202 {
203 	register int c = ex_getchar();
204 
205 	if (c == EOF)
206 		ungetchar(c);
207 	else if (c=='"')
208 		comment();
209 }
210 
211 iswhite(c)
212 	int c;
213 {
214 
215 	return (c == ' ' || c == '\t');
216 }
217 
218 junk(c)
219 	register int c;
220 {
221 
222 	if (c && !value(BEAUTIFY))
223 		return (0);
224 	if (c >= ' ' && c != TRIM)
225 		return (0);
226 	switch (c) {
227 
228 	case '\t':
229 	case '\n':
230 	case '\f':
231 		return (0);
232 
233 	default:
234 		return (1);
235 	}
236 }
237 
238 killed()
239 {
240 
241 	killcnt(addr2 - addr1 + 1);
242 }
243 
244 killcnt(cnt)
245 	register int cnt;
246 {
247 
248 	if (inopen) {
249 		notecnt = cnt;
250 		notenam = notesgn = "";
251 		return;
252 	}
253 	if (!notable(cnt))
254 		return;
255 	ex_printf("%d lines", cnt);
256 	if (value(TERSE) == 0) {
257 		ex_printf(" %c%s", Command[0] | ' ', Command + 1);
258 		if (Command[strlen(Command) - 1] != 'e')
259 			ex_putchar('e');
260 		ex_putchar('d');
261 	}
262 	putNFL();
263 }
264 
265 lineno(a)
266 	line *a;
267 {
268 
269 	return (a - zero);
270 }
271 
272 lineDOL()
273 {
274 
275 	return (lineno(dol));
276 }
277 
278 lineDOT()
279 {
280 
281 	return (lineno(dot));
282 }
283 
284 markDOT()
285 {
286 
287 	markpr(dot);
288 }
289 
290 markpr(which)
291 	line *which;
292 {
293 
294 	if ((inglobal == 0 || inopen) && which <= endcore) {
295 		names['z'-'a'+1] = *which & ~01;
296 		if (inopen)
297 			ncols['z'-'a'+1] = cursor;
298 	}
299 }
300 
301 markreg(c)
302 	register int c;
303 {
304 
305 	if (c == '\'' || c == '`')
306 		return ('z' + 1);
307 	if (c >= 'a' && c <= 'z')
308 		return (c);
309 	return (0);
310 }
311 
312 /*
313  * Mesg decodes the terse/verbose strings. Thus
314  *	'xxx@yyy' -> 'xxx' if terse, else 'xxx yyy'
315  *	'xxx|yyy' -> 'xxx' if terse, else 'yyy'
316  * All others map to themselves.
317  */
318 char *
319 mesg(str)
320 	register char *str;
321 {
322 	register char *cp;
323 
324 	str = strcpy(genbuf, str);
325 	for (cp = str; *cp; cp++)
326 		switch (*cp) {
327 
328 		case '@':
329 			if (value(TERSE))
330 				*cp = 0;
331 			else
332 				*cp = ' ';
333 			break;
334 
335 		case '|':
336 			if (value(TERSE) == 0)
337 				return (cp + 1);
338 			*cp = 0;
339 			break;
340 		}
341 	return (str);
342 }
343 
344 /*VARARGS2*/
345 merror(seekpt, i)
346 #ifndef EXSTRINGS
347 	char *seekpt;
348 #else
349 # ifdef lint
350 	char *seekpt;
351 # else
352 	int seekpt;
353 # endif
354 #endif
355 	int i;
356 {
357 	register char *cp = linebuf;
358 
359 	if (seekpt == 0)
360 		return;
361 	merror1(seekpt);
362 	if (*cp == '\n')
363 		putnl(), cp++;
364 	if (inopen > 0 && CE)
365 		vclreol();
366 	if (SO && SE)
367 		putpad(SO);
368 	ex_printf(mesg(cp), i);
369 	if (SO && SE)
370 		putpad(SE);
371 }
372 
373 merror1(seekpt)
374 #ifndef EXSTRINGS
375 	char *seekpt;
376 #else
377 # ifdef lint
378 	char *seekpt;
379 # else
380 	int seekpt;
381 # endif
382 #endif
383 {
384 
385 #ifndef EXSTRINGS
386 	strcpy(linebuf, seekpt);
387 #else
388 	lseek(erfile, (long) seekpt, 0);
389 	if (read(erfile, linebuf, 128) < 2)
390 		CP(linebuf, "ERROR");
391 #endif
392 }
393 
394 morelines()
395 {
396 #ifdef UNIX_SBRK
397 	char *sbrk();
398 
399 	if ((int) sbrk(1024 * sizeof (line)) == -1)
400 		return (-1);
401 	endcore += 1024;
402 	return (0);
403 #else
404 	/*
405 	 * We can never be guaranteed that we can get more memory
406 	 * beyond "endcore".  So we just punt every time.
407 	 */
408 	return -1;
409 #endif
410 }
411 
412 nonzero()
413 {
414 
415 	if (addr1 == zero) {
416 		notempty();
417 		error("Nonzero address required@on this command");
418 	}
419 }
420 
421 notable(i)
422 	int i;
423 {
424 
425 	return (hush == 0 && !inglobal && i > value(REPORT));
426 }
427 
428 
429 notempty()
430 {
431 
432 	if (dol == zero)
433 		error("No lines@in the buffer");
434 }
435 
436 
437 netchHAD(cnt)
438 	int cnt;
439 {
440 
441 	netchange(lineDOL() - cnt);
442 }
443 
444 netchange(i)
445 	register int i;
446 {
447 	register char *cp;
448 
449 	if (i > 0)
450 		notesgn = cp = "more ";
451 	else
452 		notesgn = cp = "fewer ", i = -i;
453 	if (inopen) {
454 		notecnt = i;
455 		notenam = "";
456 		return;
457 	}
458 	if (!notable(i))
459 		return;
460 	ex_printf(mesg("%d %slines@in file after %s"), i, cp, Command);
461 	putNFL();
462 }
463 
464 putmark(addr)
465 	line *addr;
466 {
467 
468 	putmk1(addr, putline());
469 }
470 
471 putmk1(addr, n)
472 	register line *addr;
473 	int n;
474 {
475 	register line *markp;
476 	register oldglobmk;
477 
478 	oldglobmk = *addr & 1;
479 	*addr &= ~1;
480 	for (markp = (anymarks ? names : &names['z'-'a'+1]);
481 	  markp <= &names['z'-'a'+1]; markp++)
482 		if (*markp == *addr)
483 			*markp = n;
484 	*addr = n | oldglobmk;
485 }
486 
487 char *
488 plural(i)
489 	long i;
490 {
491 
492 	return (i == 1 ? "" : "s");
493 }
494 
495 int	qcount();
496 short	vcntcol;
497 
498 qcolumn(lim, gp)
499 	register char *lim, *gp;
500 {
501 	register int x;
502 	int (*OO)();
503 
504 	OO = Outchar;
505 	Outchar = qcount;
506 	vcntcol = 0;
507 	if (lim != NULL)
508 		x = lim[1], lim[1] = 0;
509 	pline(0);
510 	if (lim != NULL)
511 		lim[1] = x;
512 	if (gp)
513 		while (*gp)
514 			ex_putchar(*gp++);
515 	Outchar = OO;
516 	return (vcntcol);
517 }
518 
519 int
520 qcount(c)
521 	int c;
522 {
523 
524 	if (c == '\t') {
525 		vcntcol += value(TABSTOP) - vcntcol % value(TABSTOP);
526 		return;
527 	}
528 	vcntcol++;
529 }
530 
531 reverse(a1, a2)
532 	register line *a1, *a2;
533 {
534 	register line t;
535 
536 	for (;;) {
537 		t = *--a2;
538 		if (a2 <= a1)
539 			return;
540 		*a2 = *a1;
541 		*a1++ = t;
542 	}
543 }
544 
545 save(a1, a2)
546 	line *a1;
547 	register line *a2;
548 {
549 	register int more;
550 
551 	if (!FIXUNDO)
552 		return;
553 #ifdef TRACE
554 	if (trace)
555 		vudump("before save");
556 #endif
557 	undkind = UNDNONE;
558 	undadot = dot;
559 	more = (a2 - a1 + 1) - (unddol - dol);
560 	while (more > (endcore - truedol))
561 		if (morelines() < 0)
562 #ifdef UNIX_SBRK
563 			error("Out of memory@saving lines for undo - try using ed");
564 #else
565 			error("Out of memory@saving lines for undo - try increasing linelimit");
566 #endif
567 	if (more)
568 		(*(more > 0 ? copywR : copyw))(unddol + more + 1, unddol + 1,
569 		    (truedol - unddol));
570 	unddol += more;
571 	truedol += more;
572 	copyw(dol + 1, a1, a2 - a1 + 1);
573 	undkind = UNDALL;
574 	unddel = a1 - 1;
575 	undap1 = a1;
576 	undap2 = a2 + 1;
577 #ifdef TRACE
578 	if (trace)
579 		vudump("after save");
580 #endif
581 }
582 
583 save12()
584 {
585 
586 	save(addr1, addr2);
587 }
588 
589 saveall()
590 {
591 
592 	save(one, dol);
593 }
594 
595 span()
596 {
597 
598 	return (addr2 - addr1 + 1);
599 }
600 
601 ex_sync()
602 {
603 
604 	chng = 0;
605 	tchng = 0;
606 	xchng = 0;
607 }
608 
609 
610 skipwh()
611 {
612 	register int wh;
613 
614 	wh = 0;
615 	while (iswhite(peekchar())) {
616 		wh++;
617 		ignchar();
618 	}
619 	return (wh);
620 }
621 
622 /*VARARGS2*/
623 smerror(seekpt, cp)
624 #ifdef lint
625 	char *seekpt;
626 #else
627 	int seekpt;
628 #endif
629 	char *cp;
630 {
631 
632 	if (seekpt == 0)
633 		return;
634 	merror1(seekpt);
635 	if (inopen && CE)
636 		vclreol();
637 	if (SO && SE)
638 		putpad(SO);
639 	lprintf(mesg(linebuf), cp);
640 	if (SO && SE)
641 		putpad(SE);
642 }
643 
644 char *
645 strend(cp)
646 	register char *cp;
647 {
648 
649 	while (*cp)
650 		cp++;
651 	return (cp);
652 }
653 
654 strcLIN(dp)
655 	char *dp;
656 {
657 
658 	CP(linebuf, dp);
659 }
660 
661 syserror()
662 {
663 	char *strerror();
664 
665 	dirtcnt = 0;
666 	ex_putchar(' ');
667 	error(strerror(errno));
668 }
669 
670 /*
671  * Return the column number that results from being in column col and
672  * hitting a tab, where tabs are set every ts columns.  Work right for
673  * the case where col > COLUMNS, even if ts does not divide COLUMNS.
674  */
675 tabcol(col, ts)
676 int col, ts;
677 {
678 	int offset, result;
679 
680 	if (col >= COLUMNS) {
681 		offset = COLUMNS * (col/COLUMNS);
682 		col -= offset;
683 	} else
684 		offset = 0;
685 	result = col + ts - (col % ts) + offset;
686 	return (result);
687 }
688 
689 char *
690 vfindcol(i)
691 	int i;
692 {
693 	register char *cp;
694 	register int (*OO)() = Outchar;
695 
696 	Outchar = qcount;
697 	ignore(qcolumn(linebuf - 1, NOSTR));
698 	for (cp = linebuf; *cp && vcntcol < i; cp++)
699 		ex_putchar(*cp);
700 	if (cp != linebuf)
701 		cp--;
702 	Outchar = OO;
703 	return (cp);
704 }
705 
706 char *
707 vskipwh(cp)
708 	register char *cp;
709 {
710 
711 	while (iswhite(*cp) && cp[1])
712 		cp++;
713 	return (cp);
714 }
715 
716 
717 char *
718 vpastwh(cp)
719 	register char *cp;
720 {
721 
722 	while (iswhite(*cp))
723 		cp++;
724 	return (cp);
725 }
726 
727 whitecnt(cp)
728 	register char *cp;
729 {
730 	register int i;
731 
732 	i = 0;
733 	for (;;)
734 		switch (*cp++) {
735 
736 		case '\t':
737 			i += value(TABSTOP) - i % value(TABSTOP);
738 			break;
739 
740 		case ' ':
741 			i++;
742 			break;
743 
744 		default:
745 			return (i);
746 		}
747 }
748 
749 #ifdef lint
750 Ignore(a)
751 	char *a;
752 {
753 
754 	a = a;
755 }
756 
757 Ignorf(a)
758 	int (*a)();
759 {
760 
761 	a = a;
762 }
763 #endif
764 
765 markit(addr)
766 	line *addr;
767 {
768 
769 	if (addr != dot && addr >= one && addr <= dol)
770 		markDOT();
771 }
772 
773 /*
774  * The following code is defensive programming against a bug in the
775  * pdp-11 overlay implementation.  Sometimes it goes nuts and asks
776  * for an overlay with some garbage number, which generates an emt
777  * trap.  This is a less than elegant solution, but it is somewhat
778  * better than core dumping and losing your work, leaving your tty
779  * in a weird state, etc.
780  */
781 int _ovno;
782 void
783 onemt()
784 {
785 	signal(SIGEMT, onemt);
786 	/* 2 and 3 are valid on 11/40 type vi, so */
787 	if (_ovno < 0 || _ovno > 3)
788 		_ovno = 0;
789 	error("emt trap, _ovno is %d @ - try again");
790 }
791 
792 /*
793  * When a hangup occurs our actions are similar to a preserve
794  * command.  If the buffer has not been [Modified], then we do
795  * nothing but remove the temporary files and exit.
796  * Otherwise, we sync the temp file and then attempt a preserve.
797  * If the preserve succeeds, we unlink our temp files.
798  * If the preserve fails, we leave the temp files as they are
799  * as they are a backup even without preservation if they
800  * are not removed.
801  */
802 void
803 onhup()
804 {
805 
806 	/*
807 	 * USG tty driver can send multiple HUP's!!
808 	 */
809 	signal(SIGINT, SIG_IGN);
810 	signal(SIGHUP, SIG_IGN);
811 	if (chng == 0) {
812 		cleanup(1);
813 		ex_exit(0);
814 	}
815 	if (setexit() == 0) {
816 		if (preserve()) {
817 			cleanup(1);
818 			ex_exit(0);
819 		}
820 	}
821 	ex_exit(1);
822 }
823 
824 /*
825  * An interrupt occurred.  Drain any output which
826  * is still in the output buffering pipeline.
827  * Catch interrupts again.  Unless we are in visual
828  * reset the output state (out of -nl mode, e.g).
829  * Then like a normal error (with the \n before Interrupt
830  * suppressed in visual mode).
831  */
832 void
833 onintr()
834 {
835 
836 #ifndef CBREAK
837 	signal(SIGINT, onintr);
838 #else
839 	signal(SIGINT, inopen ? vintr : onintr);
840 #endif
841 	alarm(0);	/* in case we were called from map */
842 	draino();
843 	if (!inopen) {
844 		pstop();
845 		setlastchar('\n');
846 #ifdef CBREAK
847 	}
848 #else
849 	} else
850 		vraw();
851 #endif
852 	error("\nInterrupt" + inopen);
853 }
854 
855 /*
856  * If we are interruptible, enable interrupts again.
857  * In some critical sections we turn interrupts off,
858  * but not very often.
859  */
860 setrupt()
861 {
862 
863 	if (ruptible) {
864 #ifndef CBREAK
865 		signal(SIGINT, onintr);
866 #else
867 		signal(SIGINT, inopen ? vintr : onintr);
868 #endif
869 #ifdef SIGTSTP
870 		if (dosusp)
871 			signal(SIGTSTP, onsusp);
872 #endif
873 	}
874 }
875 
876 preserve()
877 {
878 
879 #ifdef VMUNIX
880 	tflush();
881 #endif
882 	synctmp();
883 	pid = vfork();
884 	if (pid < 0)
885 		return (0);
886 	if (pid == 0) {
887 		close(0);
888 		dup(tfile);
889 		execl(_PATH_EXPRESERVE, "expreserve", (char *) 0);
890 		ex_exit(1);
891 	}
892 	waitfor();
893 	if (rpid == pid && status == 0)
894 		return (1);
895 	return (0);
896 }
897 
898 #ifndef V6
899 ex_exit(i)
900 	int i;
901 {
902 
903 # ifdef TRACE
904 	if (trace)
905 		fclose(trace);
906 # endif
907 	_exit(i);
908 }
909 #endif
910 
911 #ifdef SIGTSTP
912 /*
913  * We have just gotten a susp.  Suspend and prepare to resume.
914  */
915 void
916 onsusp()
917 {
918 	ttymode f;
919 	struct winsize win;
920 
921 	f = setty(normf);
922 	vnfl();
923 	putpad(TE);
924 	flush();
925 
926 	(void) sigsetmask(0);
927 	signal(SIGTSTP, SIG_DFL);
928 	kill(0, SIGTSTP);
929 
930 	/* the pc stops here */
931 
932 	signal(SIGTSTP, onsusp);
933 	vcontin(0);
934 	ignore(setty(f));
935 	if (!inopen)
936 		error((char *) 0);
937 	else {
938 #ifdef	TIOCGWINSZ
939 		if (ioctl(0, TIOCGWINSZ, &win) >= 0)
940 			if (win.ws_row != winsz.ws_row ||
941 			    win.ws_col != winsz.ws_col)
942 				winch();
943 #endif
944 		if (vcnt < 0) {
945 			vcnt = -vcnt;
946 			if (state == VISUAL)
947 				vclear();
948 			else if (state == CRTOPEN)
949 				vcnt = 0;
950 		}
951 		vdirty(0, LINES);
952 		vrepaint(cursor);
953 	}
954 }
955 #endif
956