Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

/*
 * 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;
}