1: /* Copyright (c) 1981 Regents of the University of California */
2: static char *sccsid = "@(#)ex_subr.c 7.2 7/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: ()
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: #ifdef pdp11
388: #define MAXDATA (56*1024)
389: register char *end;
390:
391: if ((int) sbrk(1024 * sizeof (line)) != -1) {
392: endcore += 1024;
393: } else {
394: if (endcore >= MAXDATA)
395: return(-1);
396: end = MAXDATA;
397: /*
398: * Ask for end+2 since we want end to be last used location.
399: */
400: while (brk(end+2) == -1)
401: end -= 64;
402: if (end <= (char *)endcore)
403: return(-1);
404: endcore = (line *) end;
405: }
406: return(0);
407: #else
408: if ((int) sbrk(1024 * sizeof (line)) == -1)
409: return (-1);
410: endcore += 1024;
411: return (0);
412: #endif
413: }
414:
415: nonzero()
416: {
417:
418: if (addr1 == zero) {
419: notempty();
420: error("Nonzero address required@on this command");
421: }
422: }
423:
424: notable(i)
425: int i;
426: {
427:
428: return (hush == 0 && !inglobal && i > value(REPORT));
429: }
430:
431:
432: notempty()
433: {
434:
435: if (dol == zero)
436: error("No lines@in the buffer");
437: }
438:
439:
440: netchHAD(cnt)
441: int cnt;
442: {
443:
444: netchange(lineDOL() - cnt);
445: }
446:
447: netchange(i)
448: register int i;
449: {
450: register char *cp;
451:
452: if (i > 0)
453: notesgn = cp = "more ";
454: else
455: notesgn = cp = "fewer ", i = -i;
456: if (inopen) {
457: notecnt = i;
458: notenam = "";
459: return;
460: }
461: if (!notable(i))
462: return;
463: printf(mesg("%d %slines@in file after %s"), i, cp, Command);
464: putNFL();
465: }
466:
467: putmark(addr)
468: line *addr;
469: {
470:
471: putmk1(addr, putline());
472: }
473:
474: putmk1(addr, n)
475: register line *addr;
476: int n;
477: {
478: register line *markp;
479: register oldglobmk;
480:
481: oldglobmk = *addr & 1;
482: *addr &= ~1;
483: for (markp = (anymarks ? names : &names['z'-'a'+1]);
484: markp <= &names['z'-'a'+1]; markp++)
485: if (*markp == *addr)
486: *markp = n;
487: *addr = n | oldglobmk;
488: }
489:
490: char *
491: plural(i)
492: long i;
493: {
494:
495: return (i == 1 ? "" : "s");
496: }
497:
498: int qcount();
499: short vcntcol;
500:
501: qcolumn(lim, gp)
502: register char *lim, *gp;
503: {
504: register int x;
505: int (*OO)();
506:
507: OO = Outchar;
508: Outchar = qcount;
509: vcntcol = 0;
510: if (lim != NULL)
511: x = lim[1], lim[1] = 0;
512: pline(0);
513: if (lim != NULL)
514: lim[1] = x;
515: if (gp)
516: while (*gp)
517: putchar(*gp++);
518: Outchar = OO;
519: return (vcntcol);
520: }
521:
522: int
523: qcount(c)
524: int c;
525: {
526:
527: if (c == '\t') {
528: vcntcol += value(TABSTOP) - vcntcol % value(TABSTOP);
529: return;
530: }
531: vcntcol++;
532: }
533:
534: reverse(a1, a2)
535: register line *a1, *a2;
536: {
537: register line t;
538:
539: for (;;) {
540: t = *--a2;
541: if (a2 <= a1)
542: return;
543: *a2 = *a1;
544: *a1++ = t;
545: }
546: }
547:
548: save(a1, a2)
549: line *a1;
550: register line *a2;
551: {
552: register int more;
553:
554: if (!FIXUNDO)
555: return;
556: #ifdef TRACE
557: if (trace)
558: vudump("before save");
559: #endif
560: undkind = UNDNONE;
561: undadot = dot;
562: more = (a2 - a1 + 1) - (unddol - dol);
563: while (more > (endcore - truedol))
564: if (morelines() < 0)
565: error("Out of memory@saving lines for undo - try using ed");
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: 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: #define std_nerrs (sizeof std_errlist / sizeof std_errlist[0])
644:
645: #define error(i) i
646:
647: #ifdef lint
648: char *std_errlist[] = {
649: #else
650: # ifdef VMUNIX
651: char *std_errlist[] = {
652: # else
653: short std_errlist[] = {
654: # endif
655: #endif
656: error("Error 0"),
657: error("Not super-user"),
658: error("No such file or directory"),
659: error("No such process"),
660: error("Interrupted system call"),
661: error("Physical I/O error"),
662: error("No such device or address"),
663: error("Argument list too long"),
664: error("Exec format error"),
665: error("Bad file number"),
666: error("No children"),
667: error("No more processes"),
668: error("Not enough core"),
669: error("Permission denied"),
670: error("Bad address"),
671: error("Block device required"),
672: error("Mount device busy"),
673: error("File exists"),
674: error("Cross-device link"),
675: error("No such device"),
676: error("Not a directory"),
677: error("Is a directory"),
678: error("Invalid argument"),
679: error("File table overflow"),
680: error("Too many open files"),
681: error("Not a typewriter"),
682: error("Text file busy"),
683: error("File too large"),
684: error("No space left on device"),
685: error("Illegal seek"),
686: error("Read-only file system"),
687: error("Too many links"),
688: error("Broken pipe"),
689: #ifndef V6
690: error("Math argument"),
691: error("Result too large"),
692: #endif
693: error("Quota exceeded") /* Berkeley quota systems only */
694: };
695:
696: #undef error
697:
698: char *
699: strend(cp)
700: register char *cp;
701: {
702:
703: while (*cp)
704: cp++;
705: return (cp);
706: }
707:
708: strcLIN(dp)
709: char *dp;
710: {
711:
712: CP(linebuf, dp);
713: }
714:
715: syserror()
716: {
717: register int e = errno;
718:
719: dirtcnt = 0;
720: putchar(' ');
721: edited = 0; /* for temp file errors, for example */
722: if (e >= 0 && errno <= std_nerrs)
723: error(std_errlist[e]);
724: else
725: error("System error %d", e);
726: }
727:
728: /*
729: * Return the column number that results from being in column col and
730: * hitting a tab, where tabs are set every ts columns. Work right for
731: * the case where col > COLUMNS, even if ts does not divide COLUMNS.
732: */
733: tabcol(col, ts)
734: int col, ts;
735: {
736: int offset, result;
737:
738: if (col >= COLUMNS) {
739: offset = COLUMNS * (col/COLUMNS);
740: col -= offset;
741: } else
742: offset = 0;
743: result = col + ts - (col % ts) + offset;
744: return (result);
745: }
746:
747: char *
748: vfindcol(i)
749: int i;
750: {
751: register char *cp;
752: register int (*OO)() = Outchar;
753:
754: Outchar = qcount;
755: ignore(qcolumn(linebuf - 1, NOSTR));
756: for (cp = linebuf; *cp && vcntcol < i; cp++)
757: putchar(*cp);
758: if (cp != linebuf)
759: cp--;
760: Outchar = OO;
761: return (cp);
762: }
763:
764: char *
765: vskipwh(cp)
766: register char *cp;
767: {
768:
769: while (iswhite(*cp) && cp[1])
770: cp++;
771: return (cp);
772: }
773:
774:
775: char *
776: vpastwh(cp)
777: register char *cp;
778: {
779:
780: while (iswhite(*cp))
781: cp++;
782: return (cp);
783: }
784:
785: whitecnt(cp)
786: register char *cp;
787: {
788: register int i;
789:
790: i = 0;
791: for (;;)
792: switch (*cp++) {
793:
794: case '\t':
795: i += value(TABSTOP) - i % value(TABSTOP);
796: break;
797:
798: case ' ':
799: i++;
800: break;
801:
802: default:
803: return (i);
804: }
805: }
806:
807: #ifdef lint
808: Ignore(a)
809: char *a;
810: {
811:
812: a = a;
813: }
814:
815: Ignorf(a)
816: int (*a)();
817: {
818:
819: a = a;
820: }
821: #endif
822:
823: markit(addr)
824: line *addr;
825: {
826:
827: if (addr != dot && addr >= one && addr <= dol)
828: markDOT();
829: }
830:
831: /*
832: * The following code is defensive programming against a bug in the
833: * pdp-11 overlay implementation. Sometimes it goes nuts and asks
834: * for an overlay with some garbage number, which generates an emt
835: * trap. This is a less than elegant solution, but it is somewhat
836: * better than core dumping and losing your work, leaving your tty
837: * in a weird state, etc.
838: */
839: int _ovno;
840: onemt()
841: {
842: int oovno;
843:
844: signal(SIGEMT, onemt);
845: oovno = _ovno;
846: /* 2 and 3 are valid on 11/40 type vi, so */
847: if (_ovno < 0 || _ovno > 3)
848: _ovno = 0;
849: error("emt trap, _ovno is %d @ - try again");
850: }
851:
852: /*
853: * When a hangup occurs our actions are similar to a preserve
854: * command. If the buffer has not been [Modified], then we do
855: * nothing but remove the temporary files and exit.
856: * Otherwise, we sync the temp file and then attempt a preserve.
857: * If the preserve succeeds, we unlink our temp files.
858: * If the preserve fails, we leave the temp files as they are
859: * as they are a backup even without preservation if they
860: * are not removed.
861: */
862: onhup()
863: {
864:
865: /*
866: * USG tty driver can send multiple HUP's!!
867: */
868: signal(SIGINT, SIG_IGN);
869: signal(SIGHUP, SIG_IGN);
870: if (chng == 0) {
871: cleanup(1);
872: exit(0);
873: }
874: if (setexit() == 0) {
875: if (preserve()) {
876: cleanup(1);
877: exit(0);
878: }
879: }
880: exit(1);
881: }
882:
883: /*
884: * An interrupt occurred. Drain any output which
885: * is still in the output buffering pipeline.
886: * Catch interrupts again. Unless we are in visual
887: * reset the output state (out of -nl mode, e.g).
888: * Then like a normal error (with the \n before Interrupt
889: * suppressed in visual mode).
890: */
891: onintr()
892: {
893:
894: #ifndef CBREAK
895: signal(SIGINT, onintr);
896: #else
897: signal(SIGINT, inopen ? vintr : onintr);
898: #endif
899: alarm(0); /* in case we were called from map */
900: draino();
901: if (!inopen) {
902: pstop();
903: setlastchar('\n');
904: #ifdef CBREAK
905: }
906: #else
907: } else
908: vraw();
909: #endif
910: error("\nInterrupt" + inopen);
911: }
912:
913: /*
914: * If we are interruptible, enable interrupts again.
915: * In some critical sections we turn interrupts off,
916: * but not very often.
917: */
918: setrupt()
919: {
920:
921: if (ruptible) {
922: #ifndef CBREAK
923: signal(SIGINT, onintr);
924: #else
925: signal(SIGINT, inopen ? vintr : onintr);
926: #endif
927: #ifdef SIGTSTP
928: if (dosusp)
929: signal(SIGTSTP, onsusp);
930: #endif
931: }
932: }
933:
934: preserve()
935: {
936:
937: #ifdef VMUNIX
938: tflush();
939: #endif
940: synctmp();
941: pid = fork();
942: if (pid < 0)
943: return (0);
944: if (pid == 0) {
945: close(0);
946: dup(tfile);
947: execl(EXPRESERVE, "expreserve", (char *) 0);
948: exit(1);
949: }
950: waitfor();
951: if (rpid == pid && status == 0)
952: return (1);
953: return (0);
954: }
955:
956: #ifndef V6
957: exit(i)
958: int i;
959: {
960:
961: # ifdef TRACE
962: if (trace)
963: fclose(trace);
964: # endif
965: _exit(i);
966: }
967: #endif
968:
969: #ifdef SIGTSTP
970: /*
971: * We have just gotten a susp. Suspend and prepare to resume.
972: */
973: onsusp()
974: {
975: ttymode f;
976:
977: f = setty(normf);
978: vnfl();
979: putpad(TE);
980: flush();
981:
982: signal(SIGTSTP, SIG_DFL);
983: kill(0, SIGTSTP);
984:
985: /* the pc stops here */
986:
987: signal(SIGTSTP, onsusp);
988: vcontin(0);
989: setty(f);
990: if (!inopen)
991: error(0);
992: else {
993: if (vcnt < 0) {
994: vcnt = -vcnt;
995: if (state == VISUAL)
996: vclear();
997: else if (state == CRTOPEN)
998: vcnt = 0;
999: }
1000: vdirty(0, LINES);
1001: vrepaint(cursor);
1002: }
1003: }