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