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
any(c,s)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
backtab(i)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
change()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 */
column(cp)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 */
comment()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
Copy(to,from,size)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
copyw(to,from,size)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
copywR(to,from,size)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
ctlof(c)115 ctlof(c)
116 int c;
117 {
118
119 return (c == TRIM ? '?' : c | ('A' - 1));
120 }
121
dingdong()122 dingdong()
123 {
124
125 if (VB)
126 putpad(VB);
127 else if (value(ERRORBELLS))
128 putch('\207');
129 }
130
fixindent(indent)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
filioerr(cp)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 *
genindent(indent)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
getDOT()170 getDOT()
171 {
172
173 getline(*dot);
174 }
175
176 line *
getmark(c)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
getn(cp)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
ignnEOF()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
iswhite(c)211 iswhite(c)
212 int c;
213 {
214
215 return (c == ' ' || c == '\t');
216 }
217
junk(c)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
killed()238 killed()
239 {
240
241 killcnt(addr2 - addr1 + 1);
242 }
243
killcnt(cnt)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
lineno(a)265 lineno(a)
266 line *a;
267 {
268
269 return (a - zero);
270 }
271
lineDOL()272 lineDOL()
273 {
274
275 return (lineno(dol));
276 }
277
lineDOT()278 lineDOT()
279 {
280
281 return (lineno(dot));
282 }
283
markDOT()284 markDOT()
285 {
286
287 markpr(dot);
288 }
289
markpr(which)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
markreg(c)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 *
mesg(str)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*/
merror(seekpt,i)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
merror1(seekpt)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
morelines()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
nonzero()412 nonzero()
413 {
414
415 if (addr1 == zero) {
416 notempty();
417 error("Nonzero address required@on this command");
418 }
419 }
420
notable(i)421 notable(i)
422 int i;
423 {
424
425 return (hush == 0 && !inglobal && i > value(REPORT));
426 }
427
428
notempty()429 notempty()
430 {
431
432 if (dol == zero)
433 error("No lines@in the buffer");
434 }
435
436
netchHAD(cnt)437 netchHAD(cnt)
438 int cnt;
439 {
440
441 netchange(lineDOL() - cnt);
442 }
443
netchange(i)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
putmark(addr)464 putmark(addr)
465 line *addr;
466 {
467
468 putmk1(addr, putline());
469 }
470
putmk1(addr,n)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 *
plural(i)488 plural(i)
489 long i;
490 {
491
492 return (i == 1 ? "" : "s");
493 }
494
495 int qcount();
496 short vcntcol;
497
qcolumn(lim,gp)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
qcount(c)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
reverse(a1,a2)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
save(a1,a2)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
save12()583 save12()
584 {
585
586 save(addr1, addr2);
587 }
588
saveall()589 saveall()
590 {
591
592 save(one, dol);
593 }
594
span()595 span()
596 {
597
598 return (addr2 - addr1 + 1);
599 }
600
ex_sync()601 ex_sync()
602 {
603
604 chng = 0;
605 tchng = 0;
606 xchng = 0;
607 }
608
609
skipwh()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*/
smerror(seekpt,cp)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 *
strend(cp)645 strend(cp)
646 register char *cp;
647 {
648
649 while (*cp)
650 cp++;
651 return (cp);
652 }
653
strcLIN(dp)654 strcLIN(dp)
655 char *dp;
656 {
657
658 CP(linebuf, dp);
659 }
660
syserror()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 */
tabcol(col,ts)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 *
vfindcol(i)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 *
vskipwh(cp)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 *
vpastwh(cp)718 vpastwh(cp)
719 register char *cp;
720 {
721
722 while (iswhite(*cp))
723 cp++;
724 return (cp);
725 }
726
whitecnt(cp)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
Ignore(a)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
markit(addr)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
onemt()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
onhup()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
onintr()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 */
setrupt()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
preserve()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
ex_exit(i)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
onsusp()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