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