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