/* * This code contains changes by * Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved. * * Conditions 1, 2, and 4 and the no-warranty notice below apply * to these changes. * * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * Redistributions of source code and documentation must retain the * above copyright notice, this list of conditions and the following * disclaimer. * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed or owned by Caldera * International, Inc. * Neither the name of Caldera International, Inc. nor the names of * other contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)ex_subr.c 1.37 (gritter) 2/15/05"; #endif #endif /* from ex_subr.c 7.10.1 (2.11BSD) 1996/3/22 */ #include "ex.h" #include "ex_re.h" #include "ex_tty.h" #include "ex_vis.h" static short lastsc; /* * Random routines, in alphabetical order. */ int any(int c, register char *s) { register int x; while (x = *s++) if (x == c) return (1); return (0); } int backtab(register int i) { register int j; j = i % value(SHIFTWIDTH); if (j == 0) j = value(SHIFTWIDTH); i -= j; if (i < 0) i = 0; return (i); } void change(void) { tchng++; chng = tchng; fixedzero = 0; } /* * Column returns the number of * columns occupied by printing the * characters through position cp of the * current line. */ int column(register char *cp) { if (cp == 0) cp = &linebuf[LBSIZE - 2]; return (qcolumn(cp, NULL)); } int lcolumn(register char *cp) { return column(cp) - (lastsc - 1); } /* * Ignore a comment to the end of the line. * This routine eats the trailing newline so don't call newline(). */ void comment(void) { register int c; do { c = getchar(); } while (c != '\n' && c != EOF); if (c == EOF) ungetchar(c); } void Copy(register char *to, register char *from, register int size) { if (size > 0) do *to++ = *from++; while (--size > 0); } void copyw(register line *to, register line *from, register int size) { if (size > 0) do *to++ = *from++; while (--size > 0); } void copywR(register line *to, register line *from, register int size) { while (--size >= 0) to[size] = from[size]; } int ctlof(int c) { return (c == DELETE ? '?' : c | ('A' - 1)); } void dingdong(void) { if (VB) putpad(VB); else if (value(ERRORBELLS)) putch('\207'); } int fixindent(int indent) { register int i; register char *cp; i = whitecnt(genbuf); cp = vpastwh(genbuf); if (*cp == 0 && i == indent && linebuf[0] == 0) { genbuf[0] = 0; return (i); } CP(genindent(i), cp); return (i); } void filioerr(char *cp) { register int oerrno = errno; lprintf("\"%s\"", cp); errno = oerrno; syserror(); } char * genindent(register int indent) { register char *cp; for (cp = genbuf; indent >= value(TABSTOP); indent -= value(TABSTOP)) *cp++ = '\t'; for (; indent > 0; indent--) *cp++ = ' '; return (cp); } void getDOT(void) { getline(*dot); } line * getmark(register int c) { register line *addr; for (addr = one; addr <= dol; addr++) if (names[c - 'a'] == (*addr &~ 01)) { return (addr); } return (0); } int getn(register char *cp) { register int i = 0; while (isdigit(*cp&0377)) i = i * 10 + *cp++ - '0'; if (*cp) return (0); return (i); } void ignnEOF(void) { register int c = getchar(); if (c == EOF) ungetchar(c); else if (c=='"') comment(); } int is_white(int c) { #ifndef BIT8 return (c == ' ' || c == '\t'); #else return (isspace(c&0377) && c != '\n' && c != '\r' && c != '\f' && c != '\v'); #endif } int junk(register int c) { if (c && !value(BEAUTIFY)) return (0); #ifndef BIT8 if (c >= ' ' && c != DELETE) #else if (printable(c)) #endif return (0); switch (c) { case '\t': case '\n': case '\f': return (0); default: return (1); } } void killed(void) { killcnt(addr2 - addr1 + 1); } void killcnt(register int cnt) { if (inopen) { notecnt = cnt; notenam = notesgn = ""; return; } if (!notable(cnt)) return; printf(catgets(catd, 1, 170, "%d lines"), cnt); if (value(TERSE) == 0) { printf(catgets(catd, 1, 171, " %c%s"), Command[0] | ' ', Command + 1); if (Command[strlen(Command) - 1] != 'e') putchar('e'); putchar('d'); } putNFL(); } int lineno(line *a) { return (a - zero); } int lineDOL(void) { return (lineno(dol)); } int lineDOT(void) { return (lineno(dot)); } void markDOT(void) { markpr(dot); } void markpr(line *which) { if ((inglobal == 0 || inopen) && which <= endcore) { names['z'-'a'+1] = *which & ~01; if (inopen) ncols['z'-'a'+1] = cursor; } } int markreg(register int c) { if (c == '\'' || c == '`') return ('z' + 1); if (c >= 'a' && c <= 'z') return (c); return (0); } /* * Mesg decodes the terse/verbose strings. Thus * 'xxx@yyy' -> 'xxx' if terse, else 'xxx yyy' * 'xxx|yyy' -> 'xxx' if terse, else 'yyy' * All others map to themselves. */ char * mesg(register char *str) { register char *cp; str = strcpy(genbuf, str); for (cp = str; *cp; cp++) switch (*cp) { case '@': if (value(TERSE)) *cp = 0; else *cp = ' '; break; case '|': if (value(TERSE) == 0) return (cp + 1); *cp = 0; break; } return (str); } void merror1(intptr_t seekpt) { #ifdef VMUNIX strcpy(linebuf, (char *)seekpt); #else lseek(erfile, (off_t) seekpt, SEEK_SET); if (read(erfile, linebuf, 128) < 2) CP(linebuf, "ERROR"); #endif } /*VARARGS2*/ void vmerror(char *seekpt, va_list ap) { register char *cp = linebuf; if (seekpt == 0) return; merror1((intptr_t)seekpt); if (*cp == '\n') putnl(), cp++; if (inopen > 0 && CE) vclreol(); if (SO && SE) putpad(SO); vprintf(mesg(cp), ap); if (SO && SE) putpad(SE); } void merror(char *cp, ...) { va_list ap; if (cp == NULL) return; va_start(ap, cp); vmerror(cp, ap); va_end(ap); } int morelines(void) { #ifdef _SC_PAGESIZE static long pg; if (pg == 0) { pg = sysconf(_SC_PAGESIZE); if (pg <= 0 || pg >= 65536) pg = 4096; pg /= sizeof (line); } if ((char *)sbrk(pg * sizeof (line)) == (char *)-1) return (-1); endcore += pg; return (0); #else /* !_SC_PAGESIZE */ if (sbrk(1024 * sizeof (line)) == (char *)-1) return (-1); endcore += 1024; return (0); #endif /* !_SC_PAGESIZE */ } void nonzero(void) { if (addr1 == zero) { notempty(); error(catgets(catd, 1, 172, "Nonzero address required@on this command")); } } int notable(int i) { return (hush == 0 && !inglobal && i > value(REPORT)); } void notempty(void) { if (dol == zero) error(catgets(catd, 1, 173, "No lines@in the buffer")); } void netchHAD(int cnt) { netchange(lineDOL() - cnt); } void netchange(register int i) { register char *cp; if (i > 0) notesgn = cp = catgets(catd, 1, 174, "more "); else notesgn = cp = catgets(catd, 1, 175, "fewer "), i = -i; if (inopen) { notecnt = i; notenam = catgets(catd, 1, 176, ""); return; } if (!notable(i)) return; printf(mesg(catgets(catd, 1, 177, "%d %slines@in file after %s")), i, cp, Command); putNFL(); } /* * Print an escape sequence corresponding to c. */ #ifdef BIT8 int printof(int c) { char *nums = "01234567"; int d; #ifdef MB if (mb_cur_max > 1 && (c & INVBIT) == 0 && c & ~0177) { char mb[MB_LEN_MAX]; int i, n, x = EOF; if ((n = wctomb(mb, c & TRIM)) <= 0) { n = 1; *mb = 0; } for (i = 0; i < n; i++) { x = printof(mb[i] | INVBIT); if (i+1 < n) normchar(x); } return x; } #endif /* MB */ c &= 0377; if (c < 040 || c == DELETE) { normchar('^'); return (c == DELETE ? '?' : c | ('A' - 1)); } normchar('\\'); normchar(nums[(c & ~077) >> 6]); c &= 077; d = c & 07; if (c > d) normchar(nums[(c - d) >> 3]); else normchar(nums[0]); return nums[d]; } #endif void putmark(line *addr) { putmk1(addr, putline()); } void putmk1(register line *addr, int n) { register line *markp; register int oldglobmk; oldglobmk = *addr & 1; *addr &= ~1; for (markp = (anymarks ? names : &names['z'-'a'+1]); markp <= &names['z'-'a'+1]; markp++) if (*markp == *addr) *markp = n; *addr = n | oldglobmk; } char * plural(long i) { return (i == 1 ? catgets(catd, 1, 178, "") : catgets(catd, 1, 179, "s")); } static short vcntcol; int qcolumn(register char *lim, register char *gp) { register int x = 0, n = 1; int c, i; int (*OO)(); OO = Outchar; Outchar = qcount; vcntcol = 0; if (lim != NULL) { if (lim < linebuf) { lim = linebuf; n = 0; } else n = skipright(linebuf, lim); x = lim[n], lim[n] = 0; } pline(0); if (lim != NULL) lim[n] = x; if (gp) while (*gp) { nextc(c, gp, i); putchar(c); gp += i; } Outchar = OO; return (vcntcol); } int qcount(int c) { if (c == '\t') { vcntcol += value(TABSTOP) - vcntcol % value(TABSTOP); lastsc = 1; return c; } /* * Take account of filler characters inserted at the end of * the visual line if a multi-column character does not fit. */ lastsc = colsc(c&TRIM&~MULTICOL); while (vcntcol < WCOLS && vcntcol + lastsc - 1 >= WCOLS) vcntcol++; vcntcol += c & MULTICOL ? 1 : lastsc; return c; } void reverse(register line *a1, register line *a2) { register line t; for (;;) { t = *--a2; if (a2 <= a1) return; *a2 = *a1; *a1++ = t; } } void save(line *a1, register line *a2) { register int more; if (!FIXUNDO) return; #ifdef TRACE if (trace) vudump("before save"); #endif undkind = UNDNONE; undadot = dot; more = (a2 - a1 + 1) - (unddol - dol); while (more > (endcore - truedol)) if (morelines() < 0) error(catgets(catd, 1, 180, "Out of memory@saving lines for undo - try using ed")); if (more) (*(more > 0 ? copywR : copyw))(unddol + more + 1, unddol + 1, (truedol - unddol)); unddol += more; truedol += more; copyw(dol + 1, a1, a2 - a1 + 1); undkind = UNDALL; unddel = a1 - 1; undap1 = a1; undap2 = a2 + 1; #ifdef TRACE if (trace) vudump("after save"); #endif } void save12(void) { save(addr1, addr2); } void saveall(void) { save(one, dol); } int span(void) { return (addr2 - addr1 + 1); } void synced(void) { chng = 0; tchng = 0; xchng = 0; } int skipwh(void) { register int wh; wh = 0; while (is_white(peekchar())) { wh++; ignchar(); } return (wh); } void vsmerror(char *seekpt, va_list ap) { if (seekpt == 0) return; merror1((intptr_t)seekpt); if (inopen && CE) vclreol(); if (SO && SE) putpad(SO); vlprintf(mesg(linebuf), ap); if (SO && SE) putpad(SE); } void smerror(char *seekpt, ...) { va_list ap; if (seekpt == NULL) return; va_start(ap, seekpt); vsmerror(seekpt, ap); va_end(ap); } char * strend(register char *cp) { while (*cp) cp++; return (cp); } void strcLIN(char *dp) { CP(linebuf, dp); } void syserror(void) { dirtcnt = 0; putchar(' '); error("%s", strerror(errno)); } /* * Return the column number that results from being in column col and * hitting a tab, where tabs are set every ts columns. Work right for * the case where col > TCOLUMNS, even if ts does not divide TCOLUMNS. */ int tabcol(int col, int ts) { int offset, result; if (col >= TCOLUMNS) { offset = TCOLUMNS * (col/TCOLUMNS); col -= offset; } else offset = 0; result = col + ts - (col % ts) + offset; return (result); } char * vfindcol(int i) { register char *cp; register int (*OO)() = Outchar; int c, n = 0; Outchar = qcount; ignore(qcolumn(linebuf - 1, NOSTR)); for (cp = linebuf; *cp && vcntcol < i; cp += n) { nextc(c, cp, n); putchar(c); } if (cp != linebuf) cp -= n; Outchar = OO; return (cp); } char * vskipwh(register char *cp) { while (is_white(*cp) && cp[1]) cp++; return (cp); } char * vpastwh(register char *cp) { while (is_white(*cp)) cp++; return (cp); } int whitecnt(register char *cp) { register int i; i = 0; for (;;) switch (*cp++) { case '\t': i += value(TABSTOP) - i % value(TABSTOP); break; case ' ': i++; break; default: return (i); } } void markit(line *addr) { if (addr != dot && addr >= one && addr <= dol) markDOT(); } #ifdef SIGEMT /* * The following code is defensive programming against a bug in the * pdp-11 overlay implementation. Sometimes it goes nuts and asks * for an overlay with some garbage number, which generates an emt * trap. This is a less than elegant solution, but it is somewhat * better than core dumping and losing your work, leaving your tty * in a weird state, etc. */ int _ovno; void onemt(int signum) { int oovno; oovno = _ovno; /* 2 and 3 are valid on 11/40 type vi, so */ if (_ovno < 0 || _ovno > 3) _ovno = 0; error(catgets(catd, 1, 181, "emt trap, _ovno is %d @ - try again")); } #endif /* * When a hangup occurs our actions are similar to a preserve * command. If the buffer has not been [Modified], then we do * nothing but remove the temporary files and exit. * Otherwise, we sync the temp file and then attempt a preserve. * If the preserve succeeds, we unlink our temp files. * If the preserve fails, we leave the temp files as they are * as they are a backup even without preservation if they * are not removed. */ void onhup(int signum) { /* * USG tty driver can send multiple HUP's!! */ signal(SIGINT, SIG_IGN); signal(SIGHUP, SIG_IGN); if (chng == 0) { cleanup(1); exitex(0); } if (setexit() == 0) { if (preserve()) { cleanup(1); exitex(0); } } exitex(1); } /* * An interrupt occurred. Drain any output which * is still in the output buffering pipeline. * Catch interrupts again. Unless we are in visual * reset the output state (out of -nl mode, e.g). * Then like a normal error (with the \n before Interrupt * suppressed in visual mode). */ void onintr(int signum) { alarm(0); /* in case we were called from map */ draino(); if (!inopen) { pstop(); setlastchar('\n'); } error(catgets(catd, 1, 182, "\nInterrupt") + inopen); } /* * If we are interruptible, enable interrupts again. * In some critical sections we turn interrupts off, * but not very often. */ void setrupt(void) { if (ruptible) { signal(SIGINT, inopen ? vintr : onintr); #ifdef SIGTSTP if (dosusp) signal(SIGTSTP, onsusp); #endif } } int preserve(void) { #ifdef INCORB tflush(); #endif synctmp(); pid = fork(); if (pid < 0) return (0); if (pid == 0) { close(0); dup(tfile); execl(EXPRESERVE, "expreserve", (char *)0); exitex(1); } waitfor(); if (rpid == pid && status == 0) return (1); return (0); } int exitex(int i) { # ifdef TRACE if (trace) fclose(trace); # endif if (failed != 0 && i == 0) i = failed; _exit(i); /*NOTREACHED*/ return 0; } #ifdef SIGTSTP /* * We have just gotten a susp. Suspend and prepare to resume. */ void onsusp(int signum) { struct termios f; /* int omask; */ #ifdef TIOCGWINSZ struct winsize win; #endif sigset_t set; f = setty(normf); vnfl(); putpad(TE); flush(); sigemptyset(&set); sigprocmask(SIG_SETMASK, &set, NULL); signal(SIGTSTP, SIG_DFL); kill(0, SIGTSTP); /* the pc stops here */ signal(SIGTSTP, onsusp); vcontin(0); setty(f); if (!inopen) error(0); #ifdef TIOCGWINSZ else { if (ioctl(0, TIOCGWINSZ, &win) >= 0) if (win.ws_row != winsz.ws_row || win.ws_col != winsz.ws_col) onwinch(SIGWINCH); if (vcnt < 0) { vcnt = -vcnt; if (state == VISUAL) vclear(); else if (state == CRTOPEN) vcnt = 0; } vdirty(0, TLINES); vrepaint(cursor); } #endif /* TIOCGWINSZ */ } #endif /* SIGTSTP */ /* * For regular strcpy(), source and destination may not overlap. */ char * movestr(char *s1, const char *s2) { char *cp = s1; while (*s1++ = *s2++); return cp; } /* * strcpy() checking the maximum size of s1, printing msg in case of overflow. */ char * safecp(char *s1, const char *s2, size_t max, char *msg, ...) { va_list ap; char *cp = s1; while (max--) if ((*s1++ = *s2++) == '\0') return cp; va_start(ap, msg); verror(msg, ap); va_end(ap); exitex(0175); /*NOTREACHED*/ return NULL; } /* * strcat() checking the maximum size of s1, printing msg in case of overflow. */ char * safecat(char *s1, const char *s2, size_t max, char *msg, ...) { va_list ap; char *cp = s1; while (max && *s1) max--, s1++; while (max--) if ((*s1++ = *s2++) == '\0') return cp; va_start(ap, msg); verror(msg, ap); va_end(ap); exitex(0175); /*NOTREACHED*/ return NULL; }