head	1.12;
access;
symbols
	perseant-exfatfs-base-20250801:1.12
	perseant-exfatfs-base-20240630:1.12
	perseant-exfatfs:1.12.0.36
	perseant-exfatfs-base:1.12
	cjep_sun2x:1.12.0.34
	cjep_sun2x-base:1.12
	cjep_staticlib_x-base1:1.12
	cjep_staticlib_x:1.12.0.32
	cjep_staticlib_x-base:1.12
	phil-wifi-20200421:1.12
	phil-wifi-20200411:1.12
	phil-wifi-20200406:1.12
	pgoyette-compat-merge-20190127:1.12
	pgoyette-compat-20190127:1.12
	pgoyette-compat-20190118:1.12
	pgoyette-compat-1226:1.12
	pgoyette-compat-1126:1.12
	pgoyette-compat-1020:1.12
	pgoyette-compat-0930:1.12
	pgoyette-compat-0906:1.12
	pgoyette-compat-0728:1.12
	pgoyette-compat-0625:1.12
	pgoyette-compat-0521:1.12
	pgoyette-compat-0502:1.12
	pgoyette-compat-0422:1.12
	pgoyette-compat-0415:1.12
	pgoyette-compat-0407:1.12
	pgoyette-compat-0330:1.12
	pgoyette-compat-0322:1.12
	pgoyette-compat-0315:1.12
	pgoyette-compat:1.12.0.30
	pgoyette-compat-base:1.12
	perseant-stdc-iso10646:1.12.0.28
	perseant-stdc-iso10646-base:1.12
	prg-localcount2-base3:1.12
	prg-localcount2-base2:1.12
	prg-localcount2-base1:1.12
	prg-localcount2:1.12.0.26
	prg-localcount2-base:1.12
	pgoyette-localcount-20170426:1.12
	bouyer-socketcan-base1:1.12
	pgoyette-localcount-20170320:1.12
	bouyer-socketcan:1.12.0.24
	bouyer-socketcan-base:1.12
	pgoyette-localcount-20170107:1.12
	pgoyette-localcount-20161104:1.12
	localcount-20160914:1.12
	pgoyette-localcount-20160806:1.12
	pgoyette-localcount-20160726:1.12
	pgoyette-localcount:1.12.0.22
	pgoyette-localcount-base:1.12
	netbsd-5-2-3-RELEASE:1.12
	netbsd-5-1-5-RELEASE:1.12
	yamt-pagecache-base9:1.12
	yamt-pagecache-tag8:1.12
	tls-earlyentropy:1.12.0.18
	tls-earlyentropy-base:1.12
	riastradh-xf86-video-intel-2-7-1-pre-2-21-15:1.12
	riastradh-drm2-base3:1.12
	netbsd-5-2-2-RELEASE:1.12
	netbsd-5-1-4-RELEASE:1.12
	netbsd-5-2-1-RELEASE:1.12
	netbsd-5-1-3-RELEASE:1.12
	agc-symver:1.12.0.20
	agc-symver-base:1.12
	tls-maxphys-base:1.12
	yamt-pagecache-base8:1.12
	netbsd-5-2:1.12.0.16
	yamt-pagecache-base7:1.12
	netbsd-5-2-RELEASE:1.12
	netbsd-5-2-RC1:1.12
	yamt-pagecache-base6:1.12
	yamt-pagecache-base5:1.12
	yamt-pagecache-base4:1.12
	netbsd-5-1-2-RELEASE:1.12
	netbsd-5-1-1-RELEASE:1.12
	yamt-pagecache-base3:1.12
	yamt-pagecache-base2:1.12
	yamt-pagecache:1.12.0.14
	yamt-pagecache-base:1.12
	bouyer-quota2-nbase:1.12
	bouyer-quota2:1.12.0.12
	bouyer-quota2-base:1.12
	matt-nb5-pq3:1.12.0.10
	matt-nb5-pq3-base:1.12
	netbsd-5-1:1.12.0.8
	netbsd-5-1-RELEASE:1.12
	netbsd-5-1-RC4:1.12
	netbsd-5-1-RC3:1.12
	netbsd-5-1-RC2:1.12
	netbsd-5-1-RC1:1.12
	netbsd-5-0-2-RELEASE:1.12
	netbsd-5-0-1-RELEASE:1.12
	jym-xensuspend-nbase:1.12
	netbsd-5-0:1.12.0.6
	netbsd-5-0-RELEASE:1.12
	netbsd-5-0-RC4:1.12
	netbsd-5-0-RC3:1.12
	netbsd-5-0-RC2:1.12
	jym-xensuspend:1.12.0.4
	jym-xensuspend-base:1.12
	netbsd-5-0-RC1:1.12
	netbsd-5:1.12.0.2
	netbsd-5-base:1.12
	matt-mips64-base2:1.11
	matt-mips64:1.11.0.22
	mjf-devfs2:1.11.0.20
	mjf-devfs2-base:1.11
	netbsd-4-0-1-RELEASE:1.11
	wrstuden-revivesa-base-3:1.11
	wrstuden-revivesa-base-2:1.11
	wrstuden-fixsa-newbase:1.11
	wrstuden-revivesa-base-1:1.11
	yamt-pf42-base4:1.11
	yamt-pf42-base3:1.11
	hpcarm-cleanup-nbase:1.11
	yamt-pf42-baseX:1.11
	yamt-pf42-base2:1.11
	wrstuden-revivesa:1.11.0.18
	wrstuden-revivesa-base:1.11
	yamt-pf42:1.11.0.16
	yamt-pf42-base:1.11
	keiichi-mipv6-nbase:1.11
	keiichi-mipv6:1.11.0.14
	keiichi-mipv6-base:1.11
	matt-armv6-nbase:1.11
	matt-armv6-prevmlocking:1.11
	wrstuden-fixsa-base-1:1.11
	netbsd-4-0:1.11.0.12
	netbsd-4-0-RELEASE:1.11
	cube-autoconf:1.11.0.10
	cube-autoconf-base:1.11
	netbsd-4-0-RC5:1.11
	netbsd-4-0-RC4:1.11
	netbsd-4-0-RC3:1.11
	netbsd-4-0-RC2:1.11
	netbsd-4-0-RC1:1.11
	matt-armv6:1.11.0.8
	matt-armv6-base:1.11
	matt-mips64-base:1.11
	hpcarm-cleanup:1.11.0.6
	hpcarm-cleanup-base:1.11
	netbsd-3-1-1-RELEASE:1.10
	netbsd-3-0-3-RELEASE:1.10
	wrstuden-fixsa:1.11.0.4
	wrstuden-fixsa-base:1.11
	abandoned-netbsd-4-base:1.10
	abandoned-netbsd-4:1.10.0.8
	netbsd-3-1:1.10.0.10
	netbsd-3-1-RELEASE:1.10
	netbsd-3-0-2-RELEASE:1.10
	netbsd-3-1-RC4:1.10
	netbsd-3-1-RC3:1.10
	netbsd-3-1-RC2:1.10
	netbsd-3-1-RC1:1.10
	netbsd-4:1.11.0.2
	netbsd-4-base:1.11
	chap-midi-nbase:1.10
	netbsd-3-0-1-RELEASE:1.10
	chap-midi:1.10.0.6
	chap-midi-base:1.10
	netbsd-3-0:1.10.0.4
	netbsd-3-0-RELEASE:1.10
	netbsd-3-0-RC6:1.10
	netbsd-3-0-RC5:1.10
	netbsd-3-0-RC4:1.10
	netbsd-3-0-RC3:1.10
	netbsd-3-0-RC2:1.10
	netbsd-3-0-RC1:1.10
	netbsd-2-0-3-RELEASE:1.9
	netbsd-2-1:1.9.0.8
	netbsd-2-1-RELEASE:1.9
	netbsd-2-1-RC6:1.9
	netbsd-2-1-RC5:1.9
	netbsd-2-1-RC4:1.9
	netbsd-2-1-RC3:1.9
	netbsd-2-1-RC2:1.9
	netbsd-2-1-RC1:1.9
	netbsd-2-0-2-RELEASE:1.9
	netbsd-3:1.10.0.2
	netbsd-3-base:1.10
	netbsd-2-0-1-RELEASE:1.9
	netbsd-2:1.9.0.6
	netbsd-2-base:1.9
	netbsd-2-0-RELEASE:1.9
	netbsd-2-0-RC5:1.9
	netbsd-2-0-RC4:1.9
	netbsd-2-0-RC3:1.9
	netbsd-2-0-RC2:1.9
	netbsd-2-0-RC1:1.9
	netbsd-2-0:1.9.0.4
	netbsd-2-0-base:1.9
	netbsd-1-6-PATCH002-RELEASE:1.9
	netbsd-1-6-PATCH002:1.9
	netbsd-1-6-PATCH002-RC4:1.9
	netbsd-1-6-PATCH002-RC3:1.9
	netbsd-1-6-PATCH002-RC2:1.9
	netbsd-1-6-PATCH002-RC1:1.9
	netbsd-1-6-PATCH001:1.9
	netbsd-1-6-PATCH001-RELEASE:1.9
	netbsd-1-6-PATCH001-RC3:1.9
	netbsd-1-6-PATCH001-RC2:1.9
	netbsd-1-6-PATCH001-RC1:1.9
	fvdl_fs64_base:1.9
	netbsd-1-6-RELEASE:1.9
	netbsd-1-6-RC3:1.9
	netbsd-1-6-RC2:1.9
	netbsd-1-6-RC1:1.9
	netbsd-1-6:1.9.0.2
	netbsd-1-6-base:1.9
	netbsd-1-5-PATCH003:1.3.10.1
	netbsd-1-5-PATCH002:1.3.10.1
	netbsd-1-5-PATCH001:1.3.10.1
	nvi_1_79:1.1.1.4
	netbsd-1-5-RELEASE:1.3.10.1
	netbsd-1-5-BETA2:1.3.10.1
	netbsd-1-5-BETA:1.3.10.1
	netbsd-1-4-PATCH003:1.3.2.1
	netbsd-1-5-ALPHA2:1.3
	netbsd-1-5:1.3.0.10
	netbsd-1-5-base:1.3
	minoura-xpg4dl-base:1.3
	minoura-xpg4dl:1.3.0.8
	netbsd-1-4-PATCH002:1.3
	wrstuden-devbsize-19991221:1.3
	wrstuden-devbsize:1.3.0.6
	wrstuden-devbsize-base:1.3
	comdex-fall-1999:1.3.0.4
	comdex-fall-1999-base:1.3
	netbsd-1-4-PATCH001:1.3
	netbsd-1-4-RELEASE:1.3
	netbsd-1-4:1.3.0.2
	netbsd-1-4-base:1.3
	netbsd-1-3-PATCH003:1.1.1.3
	netbsd-1-3-PATCH003-CANDIDATE2:1.1.1.3
	netbsd-1-3-PATCH003-CANDIDATE1:1.1.1.3
	netbsd-1-3-PATCH003-CANDIDATE0:1.1.1.3
	netbsd-1-3-PATCH002:1.1.1.3
	netbsd-1-3-PATCH001:1.1.1.3
	netbsd-1-3-RELEASE:1.1.1.3
	netbsd-1-3-BETA:1.1.1.3
	netbsd-1-3:1.1.1.3.0.2
	netbsd-1-3-base:1.1.1.3
	netbsd-1-2-PATCH001:1.1.1.3
	netbsd-1-2-RELEASE:1.1.1.3
	netbsd-1-2-BETA:1.1.1.3
	netbsd-1-2:1.1.1.3.0.4
	netbsd-1-2-base:1.1.1.3
	nvi_1_66:1.1.1.3
	BOSTIC:1.1.1
	netbsd-1-1-PATCH001:1.1.1.2
	netbsd-1-1-RELEASE:1.1.1.2
	netbsd-1-1:1.1.1.2.0.4
	netbsd-1-1-base:1.1.1.2
	netbsd-1-0-PATCH06:1.1.1.2
	netbsd-1-0-PATCH05:1.1.1.2
	netbsd-1-0-PATCH04:1.1.1.2
	netbsd-1-0-PATCH03:1.1.1.2
	netbsd-1-0-PATCH02:1.1.1.2
	netbsd-1-0-PATCH1:1.1.1.2
	netbsd-1-0-PATCH0:1.1.1.2
	netbsd-1-0-RELEASE:1.1.1.2
	netbsd-1-0:1.1.1.2.0.2
	nvi-1-34b:1.1.1.2
	nvi-1-33b:1.1.1.1
	bostic-nvi:1.1.1;
locks; strict;
comment	@ * @;


1.12
date	2008.10.29.16.49.35;	author christos;	state dead;
branches;
next	1.11;

1.11
date	2006.09.27.21.01.18;	author christos;	state Exp;
branches;
next	1.10;

1.10
date	2004.11.05.19.50.12;	author dsl;	state Exp;
branches;
next	1.9;

1.9
date	2002.04.09.01.47.31;	author thorpej;	state Exp;
branches;
next	1.8;

1.8
date	2001.10.20.22.30.31;	author yamt;	state Exp;
branches;
next	1.7;

1.7
date	2001.10.17.07.38.48;	author yamt;	state Exp;
branches;
next	1.6;

1.6
date	2001.05.01.16.46.11;	author aymeric;	state Exp;
branches;
next	1.5;

1.5
date	2001.03.31.11.37.46;	author aymeric;	state Exp;
branches;
next	1.4;

1.4
date	2000.10.11.14.46.21;	author is;	state Exp;
branches;
next	1.3;

1.3
date	98.07.26.23.14.40;	author mycroft;	state Exp;
branches
	1.3.2.1
	1.3.10.1;
next	1.2;

1.2
date	98.01.09.08.06.55;	author perry;	state Exp;
branches;
next	1.1;

1.1
date	94.08.17.16.19.05;	author cgd;	state Exp;
branches
	1.1.1.1;
next	;

1.3.2.1
date	2000.10.19.16.33.01;	author he;	state Exp;
branches;
next	;

1.3.10.1
date	2000.10.18.01.32.53;	author tv;	state Exp;
branches;
next	;

1.1.1.1
date	94.08.17.16.19.06;	author cgd;	state Exp;
branches;
next	1.1.1.2;

1.1.1.2
date	94.08.17.19.25.22;	author cgd;	state Exp;
branches
	1.1.1.2.2.1;
next	1.1.1.3;

1.1.1.3
date	96.05.20.02.04.54;	author mrg;	state Exp;
branches;
next	1.1.1.4;

1.1.1.4
date	2001.03.31.11.30.02;	author aymeric;	state Exp;
branches;
next	;

1.1.1.2.2.1
date	94.08.17.19.25.22;	author cgd;	state dead;
branches;
next	1.1.1.2.2.2;

1.1.1.2.2.2
date	94.08.17.19.25.23;	author cgd;	state Exp;
branches;
next	;


desc
@@


1.12
log
@bye old vi!
@
text
@/*	$NetBSD: msg.c,v 1.11 2006/09/27 21:01:18 christos Exp $	*/

/*-
 * Copyright (c) 1991, 1993, 1994
 *	The Regents of the University of California.  All rights reserved.
 * Copyright (c) 1991, 1993, 1994, 1995, 1996
 *	Keith Bostic.  All rights reserved.
 *
 * See the LICENSE file for redistribution information.
 */

#include "config.h"

#include <sys/cdefs.h>
#ifndef lint
#if 0
static const char sccsid[] = "@@(#)msg.c	10.48 (Berkeley) 9/15/96";
#else
__RCSID("$NetBSD: msg.c,v 1.11 2006/09/27 21:01:18 christos Exp $");
#endif
#endif /* not lint */

#include <sys/param.h>
#include <sys/types.h>		/* XXX: param.h may not have included types.h */
#include <sys/queue.h>
#include <sys/stat.h>
#include <sys/time.h>

#include <bitstring.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>

#ifdef __STDC__
#include <stdarg.h>
#else
#include <varargs.h>
#endif

#include "common.h"
#include "../vi/vi.h"

/*
 * msgq --
 *	Display a message.
 *
 * PUBLIC: void msgq __P((SCR *, mtype_t, const char *, ...));
 */
void
#ifdef __STDC__
msgq(SCR *sp, mtype_t mt, const char *fmt, ...)
#else
msgq(sp, mt, fmt, va_alist)
	SCR *sp;
	mtype_t mt;
        const char *fmt;
        va_dcl
#endif
{
#ifndef NL_ARGMAX
#define	__NL_ARGMAX	20		/* Set to 9 by System V. */
	struct {
		const char *str;	/* String pointer. */
		size_t	 arg;		/* Argument number. */
		size_t	 prefix;	/* Prefix string length. */
		size_t	 skip;		/* Skipped string length. */
		size_t	 suffix;	/* Suffix string length. */
	} str[__NL_ARGMAX];

	CHAR_T ch;
	const char *t, *u;
	char *rbp, *s_rbp;
	size_t cnt1, cnt2, soff;
#endif
	static int reenter;		/* STATIC: Re-entrancy check. */
	GS *gp;
	size_t blen, len, mlen, nlen;
	const char *p;
	char *bp, *mp;
        va_list ap;

	/*
	 * !!!
	 * It's possible to enter msg when there's no screen to hold the
	 * message.  If sp is NULL, ignore the special cases and put the
	 * message out to stderr.
	 */
	if (sp == NULL) {
		gp = NULL;
		if (mt == M_BERR)
			mt = M_ERR;
		else if (mt == M_VINFO)
			mt = M_INFO;
	} else {
		gp = sp->gp;
		switch (mt) {
		case M_BERR:
			if (F_ISSET(sp, SC_VI) && !O_ISSET(sp, O_VERBOSE)) {
				F_SET(gp, G_BELLSCHED);
				return;
			}
			mt = M_ERR;
			break;
		case M_VINFO:
			if (!O_ISSET(sp, O_VERBOSE))
				return;
			mt = M_INFO;
			/* FALLTHROUGH */
		case M_INFO:
			if (F_ISSET(sp, SC_EX_SILENT))
				return;
			break;
		case M_ERR:
		case M_SYSERR:
			break;
		default:
			abort();
		}
	}

	/*
	 * It's possible to reenter msg when it allocates space.  We're
	 * probably dead anyway, but there's no reason to drop core.
	 *
	 * XXX
	 * Yes, there's a race, but it should only be two instructions.
	 */
	if (reenter++)
		return;

	/* Get space for the message. */
	nlen = 1024;
	if (0) {
retry:		FREE_SPACE(sp, bp, blen);
		nlen *= 2;
	}
	bp = NULL;
	blen = 0;
	GET_SPACE_GOTO(sp, bp, blen, nlen);

	/*
	 * Error prefix.
	 *
	 * mp:	 pointer to the current next character to be written
	 * mlen: length of the already written characters
	 * blen: total length of the buffer
	 */
#define	REM	(blen - mlen)
	mp = bp;
	mlen = 0;
	if (mt == M_SYSERR) {
		p = msg_cat(sp, "020|Error: ", &len);
		if (REM < len)
			goto retry;
		memcpy(mp, p, len);
		mp += len;
		mlen += len;
	}

	/*
	 * If we're running an ex command that the user didn't enter, display
	 * the file name and line number prefix.
	 */
	if ((mt == M_ERR || mt == M_SYSERR) &&
	    sp != NULL && gp != NULL && gp->if_name != NULL) {
		for (p = gp->if_name; *p != '\0'; ++p) {
			len = snprintf(mp, REM, "%s", KEY_NAME(sp, *p));
			mp += len;
			if ((mlen += len) > blen)
				goto retry;
		}
		len = snprintf(mp, REM, ", %d: ", gp->if_lno);
		mp += len;
		if ((mlen += len) > blen)
			goto retry;
	}

	/* If nothing to format, we're done. */
	if (fmt == NULL)
		goto nofmt;
	fmt = msg_cat(sp, fmt, NULL);

#ifndef NL_ARGMAX
	/*
	 * Nvi should run on machines that don't support the numbered argument
	 * specifications (%[digit]*$).  We do this by reformatting the string
	 * so that we can hand it to vsprintf(3) and it will use the arguments
	 * in the right order.  When vsprintf returns, we put the string back
	 * into the right order.  It's undefined, according to SVID III, to mix
	 * numbered argument specifications with the standard style arguments,
	 * so this should be safe.
	 *
	 * In addition, we also need a character that is known to not occur in
	 * any vi message, for separating the parts of the string.  As callers
	 * of msgq are responsible for making sure that all the non-printable
	 * characters are formatted for printing before calling msgq, we use a
	 * random non-printable character selected at terminal initialization
	 * time.  This code isn't fast by any means, but as messages should be
	 * relatively short and normally have only a few arguments, it won't be
	 * too bad.  Regardless, nobody has come up with any other solution.
	 *
	 * The result of this loop is an array of pointers into the message
	 * string, with associated lengths and argument numbers.  The array
	 * is in the "correct" order, and the arg field contains the argument
	 * order.
	 */
	for (p = fmt, soff = 0; soff < __NL_ARGMAX;) {
		for (t = p; *p != '\0' && *p != '%'; ++p);
		if (*p == '\0')
			break;
		++p;
		if (!isdigit(*p)) {
			if (*p == '%')
				++p;
			continue;
		}
		for (u = p; *++p != '\0' && isdigit(*p););
		if (*p != '$')
			continue;

		/* Up to, and including the % character. */
		str[soff].str = t;
		str[soff].prefix = u - t;

		/* Up to, and including the $ character. */
		str[soff].arg = atoi(u);
		str[soff].skip = (p - u) + 1;
		if (str[soff].arg >= __NL_ARGMAX)
			goto ret;

		/* Up to, and including the conversion character. */
		for (u = p; (ch = *++p) != '\0';)
			if (isalpha(ch) &&
			    strchr("diouxXfeEgGcspn", ch) != NULL)
				break;
		str[soff].suffix = p - u;
		if (ch != '\0')
			++p;
		++soff;
	}

	/* If no magic strings, we're done. */
	if (soff == 0)
		goto format;

	 /* Get space for the reordered strings. */
	if ((rbp = malloc(nlen)) == NULL)
		goto ret;
	s_rbp = rbp;

	/*
	 * Reorder the strings into the message string based on argument
	 * order.
	 *
	 * !!!
	 * We ignore arguments that are out of order, i.e. if we don't find
	 * an argument, we continue.  Assume (almost certainly incorrectly)
	 * that whoever created the string knew what they were doing.
	 *
	 * !!!
	 * Brute force "sort", but since we don't expect more than one or two
	 * arguments in a string, the setup cost of a fast sort will be more
	 * expensive than the loop.
	 */
	for (cnt1 = 1; cnt1 <= soff; ++cnt1)
		for (cnt2 = 0; cnt2 < soff; ++cnt2)
			if (cnt1 == str[cnt2].arg) {
				memmove(s_rbp, str[cnt2].str, str[cnt2].prefix);
				memmove(s_rbp + str[cnt2].prefix,
				    str[cnt2].str + str[cnt2].prefix +
				    str[cnt2].skip, str[cnt2].suffix);
				s_rbp += str[cnt2].prefix + str[cnt2].suffix;
				*s_rbp++ =
				    gp == NULL ? DEFAULT_NOPRINT : gp->noprint;
				break;
			}
	*s_rbp = '\0';
	fmt = rbp;
#endif

#ifndef NL_ARGMAX
format:
#endif
	/* Format the arguments into the string. */
#ifdef __STDC__
        va_start(ap, fmt);
#else
        va_start(ap);
#endif
	len = vsnprintf(mp, REM, fmt, ap);
	va_end(ap);
	if (len >= nlen)
		goto retry;

#ifndef NL_ARGMAX
	if (soff == 0)
		goto nofmt;

	/*
	 * Go through the resulting string, and, for each separator character
	 * separated string, enter its new starting position and length in the
	 * array.
	 */
	for (p = t = mp, cnt1 = 1,
	    ch = gp == NULL ? DEFAULT_NOPRINT : gp->noprint; *p != '\0'; ++p)
		if (*p == ch) {
			for (cnt2 = 0; cnt2 < soff; ++cnt2)
				if (str[cnt2].arg == cnt1)
					break;
			str[cnt2].str = t;
			str[cnt2].prefix = p - t;
			t = p + 1;
			++cnt1;
		}

	/*
	 * Reorder the strings once again, putting them back into the
	 * message buffer.
	 *
	 * !!!
	 * Note, the length of the message gets decremented once for
	 * each substring, when we discard the separator character.
	 */
	for (s_rbp = rbp, cnt1 = 0; cnt1 < soff; ++cnt1) {
		memmove(rbp, str[cnt1].str, str[cnt1].prefix);
		rbp += str[cnt1].prefix;
		--len;
	}
	memmove(mp, s_rbp, rbp - s_rbp);

	/* Free the reordered string memory. */
	free(s_rbp);
#endif

nofmt:	mp += len;
	if ((mlen += len) > blen)
		goto retry;
	if (mt == M_SYSERR) {
		len = snprintf(mp, REM, ": %s", strerror(errno));
		mp += len;
		if ((mlen += len) > blen)
			goto retry;
		mt = M_ERR;
	}

	/* Add trailing newline. */
	if ((mlen += 1) > blen)
		goto retry;
	*mp = '\n';

	if (sp != NULL)
		(void)ex_fflush(sp);
	if (gp != NULL)
		gp->scr_msg(sp, mt, bp, mlen);
	else
		(void)fprintf(stderr, "%.*s", (int)mlen, bp);

	/* Cleanup. */
#ifndef NL_ARGMAX
ret:
#endif
	FREE_SPACE(sp, bp, blen);
alloc_err:
	reenter = 0;
}

/*
 * msgq_str --
 *	Display a message with an embedded string.
 *
 * PUBLIC: void msgq_str __P((SCR *, mtype_t, char *, char *));
 */
void
msgq_str(sp, mtype, str, fmt)
	SCR *sp;
	mtype_t mtype;
	const char *str, *fmt;
{
	int nf, sv_errno;
	char *p;

	if (str == NULL) {
		msgq(sp, mtype, fmt);
		return;
	}

	sv_errno = errno;
	p = msg_print(sp, str, &nf);
	errno = sv_errno;
	msgq(sp, mtype, fmt, p);
	if (nf)
		FREE_SPACE(sp, p, 0);
}

/*
 * mod_rpt --
 *	Report on the lines that changed.
 *
 * !!!
 * Historic vi documentation (USD:15-8) claimed that "The editor will also
 * always tell you when a change you make affects text which you cannot see."
 * This wasn't true -- edit a large file and do "100d|1".  We don't implement
 * this semantic since it requires tracking each line that changes during a
 * command instead of just keeping count.
 *
 * Line counts weren't right in historic vi, either.  For example, given the
 * file:
 *	abc
 *	def
 * the command 2d}, from the 'b' would report that two lines were deleted,
 * not one.
 *
 * PUBLIC: void mod_rpt __P((SCR *));
 */
void
mod_rpt(sp)
	SCR *sp;
{
	static char * const action[] = {
		"293|added",
		"294|changed",
		"295|deleted",
		"296|joined",
		"297|moved",
		"298|shifted",
		"299|yanked",
	};
	static char * const lines[] = {
		"300|line",
		"301|lines",
	};
	recno_t total;
	u_long rptval;
	int first, cnt;
	size_t blen, len, tlen;
	const char *t;
	char * const *ap;
	char *bp, *p;

	/* Change reports are turned off in batch mode. */
	if (F_ISSET(sp, SC_EX_SILENT))
		return;

	/* Reset changing line number. */
	sp->rptlchange = OOBLNO;

	/*
	 * Don't build a message if not enough changed.
	 *
	 * !!!
	 * And now, a vi clone test.  Historically, vi reported if the number
	 * of changed lines was > than the value, not >=, unless it was a yank
	 * command, which used >=.  No lie.  Furthermore, an action was never
	 * reported for a single line action.  This is consistent for actions
	 * other than yank, but yank didn't report single line actions even if
	 * the report edit option was set to 1.  In addition, setting report to
	 * 0 in the 4BSD historic vi was equivalent to setting it to 1, for an
	 * unknown reason (this bug was fixed in System III/V at some point).
	 * I got complaints, so nvi conforms to System III/V historic practice
	 * except that we report a yank of 1 line if report is set to 1.
	 */
#define	ARSIZE(a)	sizeof(a) / sizeof (*a)
#define	MAXNUM		25
	rptval = O_VAL(sp, O_REPORT);
	for (cnt = 0, total = 0; cnt < ARSIZE(action); ++cnt)
		total += sp->rptlines[cnt];
	if (total == 0)
		return;
	if (total <= rptval && sp->rptlines[L_YANKED] < rptval) {
		for (cnt = 0; cnt < ARSIZE(action); ++cnt)
			sp->rptlines[cnt] = 0;
		return;
	}

	/* Build and display the message. */
	GET_SPACE_GOTO(sp, bp, blen, sizeof(action) * MAXNUM + 1);
	for (p = bp, first = 1, tlen = 0,
	    ap = action, cnt = 0; cnt < ARSIZE(action); ++ap, ++cnt)
		if (sp->rptlines[cnt] != 0) {
			if (first)
				first = 0;
			else {
				*p++ = ';';
				*p++ = ' ';
				tlen += 2;
			}
			len = snprintf(p, MAXNUM, "%u ", sp->rptlines[cnt]);
			p += len;
			tlen += len;
			t = msg_cat(sp,
			    lines[sp->rptlines[cnt] == 1 ? 0 : 1], &len);
			memcpy(p, t, len);
			p += len;
			tlen += len;
			*p++ = ' ';
			++tlen;
			t = msg_cat(sp, *ap, &len);
			memcpy(p, t, len);
			p += len;
			tlen += len;
			sp->rptlines[cnt] = 0;
		}

	/* Add trailing newline. */
	*p = '\n';
	++tlen;

	(void)ex_fflush(sp);
	sp->gp->scr_msg(sp, M_INFO, bp, tlen);

	FREE_SPACE(sp, bp, blen);
alloc_err:
	return;

#undef ARSIZE
#undef MAXNUM
}

/*
 * msgq_status --
 *	Report on the file's status.
 *
 * PUBLIC: void msgq_status __P((SCR *, recno_t, u_int));
 */
void
msgq_status(sp, lno, flags)
	SCR *sp;
	recno_t lno;
	u_int flags;
{
	recno_t last;
	size_t blen, len;
	int cnt, needsep;
	const char *t;
	char **ap, *bp, *np, *p, *s;

	/* Get sufficient memory. */
	len = strlen(sp->frp->name);
	GET_SPACE_GOTO(sp, bp, blen, len * MAX_CHARACTER_COLUMNS + 128);
	p = bp;

	/* Copy in the filename. */
	for (p = bp, t = sp->frp->name; *t != '\0'; ++t) {
		len = KEY_LEN(sp, *t);
		memcpy(p, KEY_NAME(sp, *t), len);
		p += len;
	}
	np = p;
	*p++ = ':';
	*p++ = ' ';

	/* Copy in the argument count. */
	if (F_ISSET(sp, SC_STATUS_CNT) && sp->argv != NULL) {
		for (cnt = 0, ap = sp->argv; *ap != NULL; ++ap, ++cnt);
		if (cnt > 1) {
			(void)sprintf(p,
			    msg_cat(sp, "317|%d files to edit", NULL), cnt);
			p += strlen(p);
			*p++ = ':';
			*p++ = ' ';
		}
		F_CLR(sp, SC_STATUS_CNT);
	}

	/*
	 * See nvi/exf.c:file_init() for a description of how and when the
	 * read-only bit is set.
	 *
	 * !!!
	 * The historic display for "name changed" was "[Not edited]".
	 */
	needsep = 0;
	if (F_ISSET(sp->frp, FR_NEWFILE)) {
		F_CLR(sp->frp, FR_NEWFILE);
		t = msg_cat(sp, "021|new file", &len);
		memcpy(p, t, len);
		p += len;
		needsep = 1;
	} else {
		if (F_ISSET(sp->frp, FR_NAMECHANGE)) {
			t = msg_cat(sp, "022|name changed", &len);
			memcpy(p, t, len);
			p += len;
			needsep = 1;
		}
		if (needsep) {
			*p++ = ',';
			*p++ = ' ';
		}
		if (F_ISSET(sp->ep, F_MODIFIED))
			t = msg_cat(sp, "023|modified", &len);
		else
			t = msg_cat(sp, "024|unmodified", &len);
		memcpy(p, t, len);
		p += len;
		needsep = 1;
	}
	if (F_ISSET(sp->frp, FR_UNLOCKED)) {
		if (needsep) {
			*p++ = ',';
			*p++ = ' ';
		}
		t = msg_cat(sp, "025|UNLOCKED", &len);
		memcpy(p, t, len);
		p += len;
		needsep = 1;
	}
	if (O_ISSET(sp, O_READONLY)) {
		if (needsep) {
			*p++ = ',';
			*p++ = ' ';
		}
		t = msg_cat(sp, "026|readonly", &len);
		memcpy(p, t, len);
		p += len;
		needsep = 1;
	}
	if (needsep) {
		*p++ = ':';
		*p++ = ' ';
	}
	if (LF_ISSET(MSTAT_SHOWLAST)) {
		if (db_last(sp, &last))
			return;
		if (last == 0) {
			t = msg_cat(sp, "028|empty file", &len);
			memcpy(p, t, len);
			p += len;
		} else {
			t = msg_cat(sp, "027|line %lu of %lu [%ld%%]", &len);
			(void)sprintf(p, t, lno, last, (lno * 100) / last);
			p += strlen(p);
		}
	} else {
		t = msg_cat(sp, "029|line %lu", &len);
		(void)sprintf(p, t, lno);
		p += strlen(p);
	}
#ifdef DEBUG
	(void)sprintf(p, " (pid %lu)", (u_long)getpid());
	p += strlen(p);
#endif
	*p++ = '\n';
	len = p - bp;

	/*
	 * There's a nasty problem with long path names.  Cscope and tags files
	 * can result in long paths and vi will request a continuation key from
	 * the user as soon as it starts the screen.  Unfortunately, the user
	 * has already typed ahead, and chaos results.  If we assume that the
	 * characters in the filenames and informational messages only take a
	 * single screen column each, we can trim the filename.
	 *
	 * XXX
	 * Status lines get put up at fairly awkward times.  For example, when
	 * you do a filter read (e.g., :read ! echo foo) in the top screen of a
	 * split screen, we have to repaint the status lines for all the screens
	 * below the top screen.  We don't want users having to enter continue
	 * characters for those screens.  Make it really hard to screw this up.
	 */
	s = bp;
	if (LF_ISSET(MSTAT_TRUNCATE) && len > sp->cols) {
		for (; s < np && (*s != '/' || (p - s) > sp->cols - 3); ++s);
		if (s == np) {
			s = p - (sp->cols - 5);
			*--s = ' ';
		}
		*--s = '.';
		*--s = '.';
		*--s = '.';
		len = p - s;
	}

	/* Flush any waiting ex messages. */
	(void)ex_fflush(sp);

	sp->gp->scr_msg(sp, M_INFO, s, len);

	FREE_SPACE(sp, bp, blen);
alloc_err:
	return;
}

/*
 * msg_open --
 *	Open the message catalogs.
 *
 * PUBLIC: int msg_open __P((SCR *, char *));
 */
int
msg_open(sp, file)
	SCR *sp;
	char *file;
{
	/*
	 * !!!
	 * Assume that the first file opened is the system default, and that
	 * all subsequent ones user defined.  Only display error messages
	 * if we can't open the user defined ones -- it's useful to know if
	 * the system one wasn't there, but if nvi is being shipped with an
	 * installed system, the file will be there, if it's not, then the
	 * message will be repeated every time nvi is started up.
	 */
	static int first = 1;
	DB *db;
	DBT data, key;
	recno_t msgno;
	char *p, *t, buf[MAXPATHLEN];

	if ((p = strrchr(file, '/')) != NULL && p[1] == '\0' &&
	    ((t = getenv("LC_MESSAGES")) != NULL && t[0] != '\0' ||
	    (t = getenv("LANG")) != NULL && t[0] != '\0')) {
		(void)snprintf(buf, sizeof(buf), "%s%s", file, t);
		p = buf;
	} else
		p = file;
	if ((db = dbopen(p,
	    O_NONBLOCK | O_RDONLY, 0, DB_RECNO, NULL)) == NULL) {
		if (first) {
			first = 0;
			return (1);
		}
		msgq_str(sp, M_SYSERR, p, "%s");
		return (1);
	}

	/*
	 * Test record 1 for the magic string.  The msgq call is here so
	 * the message catalog build finds it.
	 */
#define	VMC	"VI_MESSAGE_CATALOG"
	key.data = &msgno;
	key.size = sizeof(recno_t);
	msgno = 1;
	if (db->get(db, &key, &data, 0) != 0 ||
	    data.size != sizeof(VMC) - 1 ||
	    memcmp(data.data, VMC, sizeof(VMC) - 1)) {
		(void)db->close(db);
		if (first) {
			first = 0;
			return (1);
		}
		msgq_str(sp, M_ERR, p,
		    "030|The file %s is not a message catalog");
		return (1);
	}
	first = 0;

	if (sp->gp->msg != NULL)
		(void)sp->gp->msg->close(sp->gp->msg);
	sp->gp->msg = db;
	return (0);
}

/*
 * msg_close --
 *	Close the message catalogs.
 *
 * PUBLIC: void msg_close __P((GS *));
 */
void
msg_close(gp)
	GS *gp;
{
	if (gp->msg != NULL)
		(void)gp->msg->close(gp->msg);
}

/*
 * msg_cont --
 *	Return common continuation messages.
 *
 * PUBLIC: const char *msg_cmsg __P((SCR *, cmsg_t, size_t *));
 */
const char *
msg_cmsg(sp, which, lenp)
	SCR *sp;
	cmsg_t which;
	size_t *lenp;
{
	switch (which) {
	case CMSG_CONF:
		return (msg_cat(sp, "268|confirm? [ynq]", lenp));
	case CMSG_CONT:
		return (msg_cat(sp, "269|Press any key to continue: ", lenp));
	case CMSG_CONT_EX:
		return (msg_cat(sp,
	    "270|Press any key to continue [: to enter more ex commands]: ",
		    lenp));
	case CMSG_CONT_R:
		return (msg_cat(sp, "161|Press Enter to continue: ", lenp));
	case CMSG_CONT_S:
		return (msg_cat(sp, "275| cont?", lenp));
	case CMSG_CONT_Q:
		return (msg_cat(sp,
		    "271|Press any key to continue [q to quit]: ", lenp));
	default:
		abort();
	}
	/* NOTREACHED */
}

/*
 * msg_cat --
 *	Return a single message from the catalog, plus its length.
 *
 * !!!
 * Only a single catalog message can be accessed at a time, if multiple
 * ones are needed, they must be copied into local memory.
 *
 * PUBLIC: const char *msg_cat __P((SCR *, const char *, size_t *));
 */
const char *
msg_cat(sp, str, lenp)
	SCR *sp;
	const char *str;
	size_t *lenp;
{
	GS *gp;
	DBT data, key;
	recno_t msgno;

	/*
	 * If it's not a catalog message, i.e. has doesn't have a leading
	 * number and '|' symbol, we're done.
	 */
	if (isdigit((unsigned char)str[0]) &&
	    isdigit((unsigned char)str[1]) &&
	    isdigit((unsigned char)str[2]) && str[3] == '|') {
		key.data = &msgno;
		key.size = sizeof(recno_t);
		msgno = atoi(str);

		/*
		 * XXX
		 * Really sleazy hack -- we put an extra character on the
		 * end of the format string, and then we change it to be
		 * the nul termination of the string.  There ought to be
		 * a better way.  Once we can allocate multiple temporary
		 * memory buffers, maybe we can use one of them instead.
		 */
		gp = sp == NULL ? NULL : sp->gp;
		if (gp != NULL && gp->msg != NULL &&
		    gp->msg->get(gp->msg, &key, &data, 0) == 0 &&
		    data.size != 0) {
			if (lenp != NULL)
				*lenp = data.size - 1;
			((char *)data.data)[data.size - 1] = '\0';
			return (data.data);
		}
		str = &str[4];
	}
	if (lenp != NULL)
		*lenp = strlen(str);
	return (str);
}

/*
 * msg_print --
 *	Return a printable version of a string, in allocated memory.
 *
 * PUBLIC: char *msg_print __P((SCR *, const char *, int *));
 */
char *
msg_print(sp, s, needfree)
	SCR *sp;
	const char *s;
	int *needfree;
{
	size_t blen, nlen;
	const char *cp;
	char *bp = NULL, *ep, *p, *t;

	*needfree = 0;

	for (cp = s; *cp != '\0'; ++cp)
		if (!isprint((unsigned char)*cp))
			break;
	if (*cp == '\0')
		return ((char *)s);	/* SAFE: needfree set to 0. */

	assert(sp != NULL);

	nlen = 0;
retry:		
	if (*needfree) {
		FREE_SPACE(sp, bp, blen);
		*needfree = 0;
	}
	nlen += 256;
	GET_SPACE_GOTO(sp, bp, blen, nlen);
	*needfree = 1;

	for (p = bp, ep = (bp + blen) - 1, cp = s; *cp != '\0' && p < ep; ++cp)
		for (t = KEY_NAME(sp, *cp); *t != '\0' && p < ep; *p++ = *t++)
			continue;
	if (p == ep)
		goto retry;
	*p = '\0';
	return (bp);
alloc_err:
	return "";
}
@


1.11
log
@Coverity CID 793 uncovered a bunch of issues:
1. needfree = 0, should have been *needfree = 0
2. this function will not work with sp == NULL, since KEY_NAME dereferences it.
3. the if (0) {
  label:
  }
  code is disgusting.

Remove all the nasty and impossible code and fix 1.
@
text
@d1 1
a1 1
/*	$NetBSD: msg.c,v 1.10 2004/11/05 19:50:12 dsl Exp $	*/
d19 1
a19 1
__RCSID("$NetBSD: msg.c,v 1.10 2004/11/05 19:50:12 dsl Exp $");
@


1.10
log
@Add (unsigned char) cast to ctype functions
@
text
@d1 1
a1 1
/*	$NetBSD: msg.c,v 1.9 2002/04/09 01:47:31 thorpej Exp $	*/
d19 1
a19 1
__RCSID("$NetBSD: msg.c,v 1.9 2002/04/09 01:47:31 thorpej Exp $");
d38 1
d878 1
a878 1
	char *bp, *ep, *p, *t;
d888 2
d891 4
a894 6
	if (0) {
retry:		if (sp == NULL)
			free(bp);
		else
			FREE_SPACE(sp, bp, blen);
		needfree = 0;
d897 1
a897 8
	if (sp == NULL) {
		if ((bp = malloc(nlen)) == NULL)
			goto alloc_err;
	} else
		GET_SPACE_GOTO(sp, bp, blen, nlen);
	if (0) {
alloc_err:	return ("");
	}
d901 2
a902 1
		for (t = KEY_NAME(sp, *cp); *t != '\0' && p < ep; *p++ = *t++);
d907 2
@


1.9
log
@Use __RCSID() and __COPYRIGHT().
@
text
@d1 1
a1 1
/*	$NetBSD: msg.c,v 1.8 2001/10/20 22:30:31 yamt Exp $	*/
d19 1
a19 1
__RCSID("$NetBSD$");
d832 3
a834 2
	if (isdigit(str[0]) &&
	    isdigit(str[1]) && isdigit(str[2]) && str[3] == '|') {
d882 1
a882 1
		if (!isprint(*cp))
@


1.8
log
@backout my miscommit.
pointed by Aymeric Vincent.
@
text
@d1 1
a1 1
/*	$NetBSD: msg.c,v 1.6 2001/05/01 16:46:11 aymeric Exp $	*/
d14 1
d16 1
d18 3
@


1.7
log
@fix format string bugs.
pointed by Tomoaki IMAMURA in PR/14271.
@
text
@d51 1
a51 1
msgq(SCR *sp, mtype_t mt, const char *fmt0, ...)
a74 3
#ifdef __STDC__
	const char *fmt = fmt0;
#endif
d286 1
a286 1
        va_start(ap, fmt0);
@


1.6
log
@shutup most (non-intrusive) gcc warnings
@
text
@d1 1
a1 1
/*	$NetBSD: msg.c,v 1.5 2001/03/31 11:37:46 aymeric Exp $	*/
d51 1
a51 1
msgq(SCR *sp, mtype_t mt, const char *fmt, ...)
d75 3
d289 1
a289 1
        va_start(ap, fmt);
@


1.5
log
@merge changes after import of nvi 1.79
@
text
@d1 1
a1 1
/*	$NetBSD: msg.c,v 1.4 2000/10/11 14:46:21 is Exp $	*/
d69 5
a75 1
	CHAR_T ch;
d77 3
a79 3
	size_t blen, cnt1, cnt2, len, mlen, nlen, soff;
	const char *p, *t, *u;
	char *bp, *mp, *rbp, *s_rbp;
d281 4
a284 1
format:	/* Format the arguments into the string. */
d359 4
a362 1
ret:	FREE_SPACE(sp, bp, blen);
d487 1
a487 1
			len = snprintf(p, MAXNUM, "%lu ", sp->rptlines[cnt]);
a530 1
	static int poisoned;
@


1.4
log
@More format string cleanup by sommerfeld.
@
text
@d1 1
a1 1
/*	$NetBSD: msg.c,v 1.3 1998/07/26 23:14:40 mycroft Exp $	*/
d15 1
a15 1
static const char sccsid[] = "@@(#)msg.c	10.36 (Berkeley) 5/15/96";
d151 1
a151 1
		memmove(mp, p, len);
d386 1
a386 1
 * msgq_rpt --
d403 1
a403 1
 * PUBLIC: void msgq_rpt __P((SCR *));
d406 1
a406 1
msgq_rpt(sp)
d477 1
a477 2
			len = snprintf(p, MAXNUM, "%lu ", 
				       (long)sp->rptlines[cnt]);
d482 1
a482 1
			memmove(p, t, len);
d488 1
a488 1
			memmove(p, t, len);
d521 1
d523 2
d526 1
a526 3
	char *bp, *np, *p, *s;
	int needsep;
	size_t blen, len;
d528 1
d530 1
a530 1
	GET_SPACE_GOTO(sp, bp, blen, len + 128);
d533 6
a538 2
	memmove(p, sp->frp->name, len);
	p += len;
d543 13
d567 1
a567 1
		memmove(p, t, len);
d573 1
a573 1
			memmove(p, t, len);
d585 1
a585 1
		memmove(p, t, len);
d595 1
a595 1
		memmove(p, t, len);
d605 1
a605 1
		memmove(p, t, len);
d618 1
a618 1
			memmove(p, t, len);
d635 1
d644 7
d653 11
a663 13
	if (LF_ISSET(MSTAT_TRUNCATE))
		if ((p - s) >= sp->cols) {
			for (; s < np &&
			    (*s != '/' || (p - s) > sp->cols - 3); ++s);
			if (s == np)
				s = bp;
			else {
				*--s = '.';
				*--s = '.';
				*--s = '.';
			}
		}
	len = p - s;
d702 3
a704 3
	    ((t = getenv("LANG")) != NULL && t[0] != '\0' ||
	    (t = getenv("LC_MESSAGES")) != NULL && t[0] != '\0')) {
		(void)snprintf(buf, sizeof(buf), "%svi_%s", file, t);
@


1.3
log
@const poisoning.
@
text
@d1 1
a1 1
/*	$NetBSD: msg.c,v 1.2 1998/01/09 08:06:55 perry Exp $	*/
d477 2
a478 1
			len = snprintf(p, MAXNUM, "%lu ", sp->rptlines[cnt]);
@


1.3.2.1
log
@Pull up revision 1.4 (requested by he):
  Format string cleanup.
@
text
@d1 1
a1 1
/*	$NetBSD$	*/
d477 1
a477 2
			len = snprintf(p, MAXNUM, "%lu ", 
				       (long)sp->rptlines[cnt]);
@


1.3.10.1
log
@Pullup usr.bin string format fixes [is].
See "cvs log" for explicit revision numbers per file, from sommerfeld.
@
text
@d1 1
a1 1
/*	$NetBSD: msg.c,v 1.4 2000/10/11 14:46:21 is Exp $	*/
d477 1
a477 2
			len = snprintf(p, MAXNUM, "%lu ", 
				       (long)sp->rptlines[cnt]);
@


1.2
log
@RCS Id Police.
@
text
@d1 1
a1 1
/*	$NetBSD$	*/
d367 1
a367 1
	char *str, *fmt;
@


1.1
log
@Initial revision
@
text
@d1 2
d6 2
d9 1
a9 27
 * 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.
d12 2
d15 1
a15 1
static const char sccsid[] = "@@(#)msg.c	8.11 (Berkeley) 8/17/94";
d18 2
a19 1
#include <sys/types.h>
d21 1
d25 1
a28 1
#include <signal.h>
a31 1
#include <termios.h>
d40 2
a41 5
#include "compat.h"
#include <db.h>
#include <regex.h>

#include "vi.h"
d46 2
d51 1
a51 1
msgq(SCR *sp, enum msgtype mt, const char *fmt, ...)
d55 2
a56 2
	enum msgtype mt;
        char *fmt;
d60 16
a76 2
	size_t len;
	char msgbuf[1024];
a77 5
#ifdef __STDC__
        va_start(ap, fmt);
#else
        va_start(ap);
#endif
d79 35
a113 13
	 * It's possible to enter msg when there's no screen to hold
	 * the message.  If sp is NULL, ignore the special cases and
	 * just build the message, using __global_list.
	 */
	if (sp == NULL)
		goto nullsp;

	switch (mt) {
	case M_BERR:
		if (!F_ISSET(sp, S_EXSILENT) &&
		    F_ISSET(sp->gp, G_STDIN_TTY) && !O_ISSET(sp, O_VERBOSE)) {
			F_SET(sp, S_BELLSCHED);
			return;
a114 16
		mt = M_ERR;
		break;
	case M_VINFO:
		if (!O_ISSET(sp, O_VERBOSE))
			return;
		mt = M_INFO;
		/* FALLTHROUGH */
	case M_INFO:
		if (F_ISSET(sp, S_EXSILENT))
			return;
		break;
	case M_ERR:
	case M_SYSERR:
		break;
	default:
		abort();
d117 19
a135 1
nullsp:	len = 0;
d137 10
a146 1
#define	EPREFIX	"Error: "
d148 6
a153 2
		memmove(msgbuf, EPREFIX, sizeof(EPREFIX) - 1);
		len += sizeof(EPREFIX) - 1;
d156 16
a171 5
	if (sp != NULL && sp->if_name != NULL) {
		len += snprintf(msgbuf + len, sizeof(msgbuf) - len,
		    "%s, %d: ", sp->if_name, sp->if_lno);
		if (len >= sizeof(msgbuf))
			goto err;
d174 4
a177 12
	if (fmt != NULL) {
		len += vsnprintf(msgbuf + len, sizeof(msgbuf) - len, fmt, ap);
		if (len >= sizeof(msgbuf))
			goto err;
	}

	if (mt == M_SYSERR) {
		len += snprintf(msgbuf + len,
		    sizeof(msgbuf) - len, ": %s", strerror(errno));
		if (len >= sizeof(msgbuf))
			goto err;
	}
d179 1
d181 21
a201 2
	 * If len >= the size, some characters were discarded.
	 * Ignore trailing nul.
d203 23
a225 2
err:	if (len >= sizeof(msgbuf))
		len = sizeof(msgbuf) - 1;
d227 14
a240 6
#ifdef DEBUG
	if (sp != NULL)
		TRACE(sp, "%.*s\n", len, msgbuf);
#endif
	msg_app(__global_list, sp, mt == M_ERR ? 1 : 0, msgbuf, len);
}
d242 4
a245 15
/*
 * msg_app --
 *	Append a message into the queue.  This can fail, but there's
 *	nothing we can do if it does.
 */
void
msg_app(gp, sp, inv_video, p, len)
	GS *gp;
	SCR *sp;
	int inv_video;
	char *p;
	size_t len;
{
	static int reenter;		/* STATIC: Re-entrancy check. */
	MSG *mp, *nmp;
d248 12
a259 2
	 * It's possible to reenter msg when it allocates space.
	 * We're probably dead anyway, but no reason to drop core.
d261 30
a290 3
	if (reenter)
		return;
	reenter = 1;
d293 3
a295 3
	 * We can be entered as the result of a signal arriving, trying
	 * to sync the file and failing.  This shouldn't be a hot spot,
	 * block the signals.
d297 11
a307 1
	SIGBLOCK(gp);
d310 6
a315 2
	 * Find an empty structure, or allocate a new one.  Use the
	 * screen structure if it exists, otherwise the global one.
d317 4
a320 14
	if (sp != NULL) {
		if ((mp = sp->msgq.lh_first) == NULL) {
			CALLOC(sp, mp, MSG *, 1, sizeof(MSG));
			if (mp == NULL)
				goto ret;
			LIST_INSERT_HEAD(&sp->msgq, mp, q);
			goto store;
		}
	} else if ((mp = gp->msgq.lh_first) == NULL) {
		CALLOC(sp, mp, MSG *, 1, sizeof(MSG));
		if (mp == NULL)
			goto ret;
		LIST_INSERT_HEAD(&gp->msgq, mp, q);
		goto store;
d322 15
a336 8
	while (!F_ISSET(mp, M_EMPTY) && mp->q.le_next != NULL)
		mp = mp->q.le_next;
	if (!F_ISSET(mp, M_EMPTY)) {
		CALLOC(sp, nmp, MSG *, 1, sizeof(MSG));
		if (nmp == NULL)
			goto ret;
		LIST_INSERT_AFTER(mp, nmp, q);
		mp = nmp;
d339 32
a370 4
	/* Get enough memory for the message. */
store:	if (len > mp->blen &&
	    (mp->mbuf = binc(sp, mp->mbuf, &mp->blen, len)) == NULL)
		goto ret;
d372 4
a375 4
	/* Store the message. */
	memmove(mp->mbuf, p, len);
	mp->len = len;
	mp->flags = inv_video ? M_INV_VIDEO : 0;
d377 6
a382 2
ret:	reenter = 0;
	SIGUNBLOCK(gp);
d386 1
a386 1
 * msg_rpt --
d392 3
a394 3
 * This isn't true -- edit a large file and do "100d|1".  We don't implement
 * this semantic as it would require that we track each line that changes
 * during a command instead of just keeping count.
d402 2
d405 2
a406 2
int
msg_rpt(sp, is_message)
a407 1
	int is_message;
d410 11
a420 3
		"added", "changed", "deleted", "joined", "moved",
		"left shifted", "right shifted", "yanked",
		NULL,
d425 2
a426 1
	size_t blen, len;
d428 1
a428 1
	char *bp, *p, number[40];
d430 3
a432 2
	if (F_ISSET(sp, S_EXSILENT))
		return (0);
d434 2
a435 2
	if ((rptval = O_VAL(sp, O_REPORT)) == 0)
		goto norpt;
d437 27
a463 2
	GET_SPACE_RET(sp, bp, blen, 512);
	p = bp;
d465 4
a468 2
	total = 0;
	for (ap = action, cnt = 0, first = 1; *ap != NULL; ++ap, ++cnt)
d470 19
a488 5
			total += sp->rptlines[cnt];
			len = snprintf(number, sizeof(number),
			    "%s%lu lines %s",
			    first ? "" : "; ", sp->rptlines[cnt], *ap);
			memmove(p, number, len);
d490 2
a491 1
			first = 0;
d494 6
a499 17
	/*
	 * If nothing to report, return.
	 *
	 * !!!
	 * And now, a special vi clone test.  Historically, vi reported if
	 * the number of changed lines was > than the value, not >=.  Which
	 * means that users can't report on single line changes, btw.)  In
	 * any case, if it was a yank command, it was >=, not >.  No lie.  I
	 * got complaints, so we do it right.
	 */
	if (total > rptval || sp->rptlines[L_YANKED] >= rptval) {
		*p = '\0';
		if (is_message)
			msgq(sp, M_INFO, "%s", bp);
		else
			ex_printf(EXCOOKIE, "%s\n", bp);
	}
d502 2
d505 2
a506 4
	/* Clear after each report. */
norpt:	sp->rptlchange = OOBLNO;
	memset(sp->rptlines, 0, sizeof(sp->rptlines));
	return (0);
d510 1
a510 1
 * msg_status --
d512 2
d515 2
a516 2
int
msg_status(sp, ep, lno, showlast)
a517 1
	EXF *ep;
d519 1
a519 1
	int showlast;
d522 14
a535 3
	char *mo, *nc, *nf, *pid, *ro, *ul;
#ifdef DEBUG
	char pbuf[50];
a536 5
	(void)snprintf(pbuf, sizeof(pbuf), " (pid %u)", getpid());
	pid = pbuf;
#else
	pid = "";
#endif
d538 2
a539 2
	 * See nvi/exf.c:file_init() for a description of how and
	 * when the read-only bit is set.
d544 1
d547 4
a550 2
		nf = "new file";
		mo = nc = "";
a551 1
		nf = "";
d553 48
a600 3
			nc = "name changed";
			mo = F_ISSET(ep, F_MODIFIED) ?
			    ", modified" : ", unmodified";
d602 86
a687 3
			nc = "";
			mo = F_ISSET(ep, F_MODIFIED) ?
			    "modified" : "unmodified";
d689 2
d692 15
a706 4
	ro = F_ISSET(sp->frp, FR_RDONLY) ? ", readonly" : "";
	ul = F_ISSET(sp->frp, FR_UNLOCKED) ? ", UNLOCKED" : "";
	if (showlast) {
		if (file_lline(sp, ep, &last))
d708 10
a717 11
		if (last >= 1)
			msgq(sp, M_INFO,
			    "%s: %s%s%s%s%s: line %lu of %lu [%ld%%]%s",
			    sp->frp->name, nf, nc, mo, ul, ro, lno,
			    last, (lno * 100) / last, pid);
		else
			msgq(sp, M_INFO, "%s: %s%s%s%s%s: empty file%s",
			    sp->frp->name, nf, nc, mo, ul, ro, pid);
	} else
		msgq(sp, M_INFO, "%s: %s%s%s%s%s: line %lu%s",
		    sp->frp->name, nf, nc, mo, ul, ro, lno, pid);
a720 1
#ifdef MSG_CATALOG
d722 4
a725 2
 * get_msg --
 *	Return a format based on a message number.
d727 54
a780 2
char *
get_msg(sp, msgno)
d782 2
a783 1
	char *s_msgno;
d785 1
a786 1
	GS *gp;
a787 1
	char *msg, *p;
d789 26
a814 10
	gp = sp == NULL ? __global_list : sp->gp;
	if (gp->msgdb == NULL) {
		p = sp == NULL ? _PATH_MSGDEF : O_STR(sp, O_CATALOG);
		if ((gp->msgdb = dbopen(p,
		    O_NONBLOCK | O_RDONLY, 444, DB_RECNO, NULL)) == NULL) {
			if ((fmt = malloc(256)) == NULL)
				return ("");
			(void)snprintf(fmt,
			    "unable to open %s: %s", p, strerror(errno));
			return (fmt);
d816 38
d855 17
a871 17
	msgno = atoi(s_msgno);
	key.data = &msgno;
	key.size = sizeof(recno_t);
	switch (gp->msgdb->get(gp->msgdb, &key, &data, 0)) {
	case 0:
		return (data.data);
	case 1:
		p = "no catalog record %ls";
		break;
	case -1:
		p = "catalog record %s: %s";
		break;
	}
	if ((fmt = malloc(256)) == NULL)
		return ("");
	(void)snprintf(fmt, p, msgno, strerror(errno));
	return (fmt);
a872 1
#endif
@


1.1.1.1
log
@new public version of nvi
@
text
@@


1.1.1.2
log
@new public version of nvi
@
text
@d35 1
a35 1
static char sccsid[] = "@@(#)msg.c	8.12 (Berkeley) 8/17/94";
@


1.1.1.2.2.1
log
@file msg.c was added on branch netbsd-1-0 on 1994-08-17 19:25:23 +0000
@
text
@d1 428
@


1.1.1.2.2.2
log
@new public version of nvi
@
text
@a0 428
/*-
 * Copyright (c) 1991, 1993, 1994
 *	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.
 */

#ifndef lint
static char sccsid[] = "@@(#)msg.c	8.12 (Berkeley) 8/17/94";
#endif /* not lint */

#include <sys/types.h>
#include <sys/queue.h>
#include <sys/time.h>

#include <bitstring.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>

#ifdef __STDC__
#include <stdarg.h>
#else
#include <varargs.h>
#endif

#include "compat.h"
#include <db.h>
#include <regex.h>

#include "vi.h"

/*
 * msgq --
 *	Display a message.
 */
void
#ifdef __STDC__
msgq(SCR *sp, enum msgtype mt, const char *fmt, ...)
#else
msgq(sp, mt, fmt, va_alist)
	SCR *sp;
	enum msgtype mt;
        char *fmt;
        va_dcl
#endif
{
        va_list ap;
	size_t len;
	char msgbuf[1024];

#ifdef __STDC__
        va_start(ap, fmt);
#else
        va_start(ap);
#endif
	/*
	 * It's possible to enter msg when there's no screen to hold
	 * the message.  If sp is NULL, ignore the special cases and
	 * just build the message, using __global_list.
	 */
	if (sp == NULL)
		goto nullsp;

	switch (mt) {
	case M_BERR:
		if (!F_ISSET(sp, S_EXSILENT) &&
		    F_ISSET(sp->gp, G_STDIN_TTY) && !O_ISSET(sp, O_VERBOSE)) {
			F_SET(sp, S_BELLSCHED);
			return;
		}
		mt = M_ERR;
		break;
	case M_VINFO:
		if (!O_ISSET(sp, O_VERBOSE))
			return;
		mt = M_INFO;
		/* FALLTHROUGH */
	case M_INFO:
		if (F_ISSET(sp, S_EXSILENT))
			return;
		break;
	case M_ERR:
	case M_SYSERR:
		break;
	default:
		abort();
	}

nullsp:	len = 0;

#define	EPREFIX	"Error: "
	if (mt == M_SYSERR) {
		memmove(msgbuf, EPREFIX, sizeof(EPREFIX) - 1);
		len += sizeof(EPREFIX) - 1;
	}

	if (sp != NULL && sp->if_name != NULL) {
		len += snprintf(msgbuf + len, sizeof(msgbuf) - len,
		    "%s, %d: ", sp->if_name, sp->if_lno);
		if (len >= sizeof(msgbuf))
			goto err;
	}

	if (fmt != NULL) {
		len += vsnprintf(msgbuf + len, sizeof(msgbuf) - len, fmt, ap);
		if (len >= sizeof(msgbuf))
			goto err;
	}

	if (mt == M_SYSERR) {
		len += snprintf(msgbuf + len,
		    sizeof(msgbuf) - len, ": %s", strerror(errno));
		if (len >= sizeof(msgbuf))
			goto err;
	}

	/*
	 * If len >= the size, some characters were discarded.
	 * Ignore trailing nul.
	 */
err:	if (len >= sizeof(msgbuf))
		len = sizeof(msgbuf) - 1;

#ifdef DEBUG
	if (sp != NULL)
		TRACE(sp, "%.*s\n", len, msgbuf);
#endif
	msg_app(__global_list, sp, mt == M_ERR ? 1 : 0, msgbuf, len);
}

/*
 * msg_app --
 *	Append a message into the queue.  This can fail, but there's
 *	nothing we can do if it does.
 */
void
msg_app(gp, sp, inv_video, p, len)
	GS *gp;
	SCR *sp;
	int inv_video;
	char *p;
	size_t len;
{
	static int reenter;		/* STATIC: Re-entrancy check. */
	MSG *mp, *nmp;

	/*
	 * It's possible to reenter msg when it allocates space.
	 * We're probably dead anyway, but no reason to drop core.
	 */
	if (reenter)
		return;
	reenter = 1;

	/*
	 * We can be entered as the result of a signal arriving, trying
	 * to sync the file and failing.  This shouldn't be a hot spot,
	 * block the signals.
	 */
	SIGBLOCK(gp);

	/*
	 * Find an empty structure, or allocate a new one.  Use the
	 * screen structure if it exists, otherwise the global one.
	 */
	if (sp != NULL) {
		if ((mp = sp->msgq.lh_first) == NULL) {
			CALLOC(sp, mp, MSG *, 1, sizeof(MSG));
			if (mp == NULL)
				goto ret;
			LIST_INSERT_HEAD(&sp->msgq, mp, q);
			goto store;
		}
	} else if ((mp = gp->msgq.lh_first) == NULL) {
		CALLOC(sp, mp, MSG *, 1, sizeof(MSG));
		if (mp == NULL)
			goto ret;
		LIST_INSERT_HEAD(&gp->msgq, mp, q);
		goto store;
	}
	while (!F_ISSET(mp, M_EMPTY) && mp->q.le_next != NULL)
		mp = mp->q.le_next;
	if (!F_ISSET(mp, M_EMPTY)) {
		CALLOC(sp, nmp, MSG *, 1, sizeof(MSG));
		if (nmp == NULL)
			goto ret;
		LIST_INSERT_AFTER(mp, nmp, q);
		mp = nmp;
	}

	/* Get enough memory for the message. */
store:	if (len > mp->blen &&
	    (mp->mbuf = binc(sp, mp->mbuf, &mp->blen, len)) == NULL)
		goto ret;

	/* Store the message. */
	memmove(mp->mbuf, p, len);
	mp->len = len;
	mp->flags = inv_video ? M_INV_VIDEO : 0;

ret:	reenter = 0;
	SIGUNBLOCK(gp);
}

/*
 * msg_rpt --
 *	Report on the lines that changed.
 *
 * !!!
 * Historic vi documentation (USD:15-8) claimed that "The editor will also
 * always tell you when a change you make affects text which you cannot see."
 * This isn't true -- edit a large file and do "100d|1".  We don't implement
 * this semantic as it would require that we track each line that changes
 * during a command instead of just keeping count.
 *
 * Line counts weren't right in historic vi, either.  For example, given the
 * file:
 *	abc
 *	def
 * the command 2d}, from the 'b' would report that two lines were deleted,
 * not one.
 */
int
msg_rpt(sp, is_message)
	SCR *sp;
	int is_message;
{
	static char * const action[] = {
		"added", "changed", "deleted", "joined", "moved",
		"left shifted", "right shifted", "yanked",
		NULL,
	};
	recno_t total;
	u_long rptval;
	int first, cnt;
	size_t blen, len;
	char * const *ap;
	char *bp, *p, number[40];

	if (F_ISSET(sp, S_EXSILENT))
		return (0);

	if ((rptval = O_VAL(sp, O_REPORT)) == 0)
		goto norpt;

	GET_SPACE_RET(sp, bp, blen, 512);
	p = bp;

	total = 0;
	for (ap = action, cnt = 0, first = 1; *ap != NULL; ++ap, ++cnt)
		if (sp->rptlines[cnt] != 0) {
			total += sp->rptlines[cnt];
			len = snprintf(number, sizeof(number),
			    "%s%lu lines %s",
			    first ? "" : "; ", sp->rptlines[cnt], *ap);
			memmove(p, number, len);
			p += len;
			first = 0;
		}

	/*
	 * If nothing to report, return.
	 *
	 * !!!
	 * And now, a special vi clone test.  Historically, vi reported if
	 * the number of changed lines was > than the value, not >=.  Which
	 * means that users can't report on single line changes, btw.)  In
	 * any case, if it was a yank command, it was >=, not >.  No lie.  I
	 * got complaints, so we do it right.
	 */
	if (total > rptval || sp->rptlines[L_YANKED] >= rptval) {
		*p = '\0';
		if (is_message)
			msgq(sp, M_INFO, "%s", bp);
		else
			ex_printf(EXCOOKIE, "%s\n", bp);
	}

	FREE_SPACE(sp, bp, blen);

	/* Clear after each report. */
norpt:	sp->rptlchange = OOBLNO;
	memset(sp->rptlines, 0, sizeof(sp->rptlines));
	return (0);
}

/*
 * msg_status --
 *	Report on the file's status.
 */
int
msg_status(sp, ep, lno, showlast)
	SCR *sp;
	EXF *ep;
	recno_t lno;
	int showlast;
{
	recno_t last;
	char *mo, *nc, *nf, *pid, *ro, *ul;
#ifdef DEBUG
	char pbuf[50];

	(void)snprintf(pbuf, sizeof(pbuf), " (pid %u)", getpid());
	pid = pbuf;
#else
	pid = "";
#endif
	/*
	 * See nvi/exf.c:file_init() for a description of how and
	 * when the read-only bit is set.
	 *
	 * !!!
	 * The historic display for "name changed" was "[Not edited]".
	 */
	if (F_ISSET(sp->frp, FR_NEWFILE)) {
		F_CLR(sp->frp, FR_NEWFILE);
		nf = "new file";
		mo = nc = "";
	} else {
		nf = "";
		if (F_ISSET(sp->frp, FR_NAMECHANGE)) {
			nc = "name changed";
			mo = F_ISSET(ep, F_MODIFIED) ?
			    ", modified" : ", unmodified";
		} else {
			nc = "";
			mo = F_ISSET(ep, F_MODIFIED) ?
			    "modified" : "unmodified";
		}
	}
	ro = F_ISSET(sp->frp, FR_RDONLY) ? ", readonly" : "";
	ul = F_ISSET(sp->frp, FR_UNLOCKED) ? ", UNLOCKED" : "";
	if (showlast) {
		if (file_lline(sp, ep, &last))
			return (1);
		if (last >= 1)
			msgq(sp, M_INFO,
			    "%s: %s%s%s%s%s: line %lu of %lu [%ld%%]%s",
			    sp->frp->name, nf, nc, mo, ul, ro, lno,
			    last, (lno * 100) / last, pid);
		else
			msgq(sp, M_INFO, "%s: %s%s%s%s%s: empty file%s",
			    sp->frp->name, nf, nc, mo, ul, ro, pid);
	} else
		msgq(sp, M_INFO, "%s: %s%s%s%s%s: line %lu%s",
		    sp->frp->name, nf, nc, mo, ul, ro, lno, pid);
	return (0);
}

#ifdef MSG_CATALOG
/*
 * get_msg --
 *	Return a format based on a message number.
 */
char *
get_msg(sp, msgno)
	SCR *sp;
	char *s_msgno;
{
	DBT data, key;
	GS *gp;
	recno_t msgno;
	char *msg, *p;

	gp = sp == NULL ? __global_list : sp->gp;
	if (gp->msgdb == NULL) {
		p = sp == NULL ? _PATH_MSGDEF : O_STR(sp, O_CATALOG);
		if ((gp->msgdb = dbopen(p,
		    O_NONBLOCK | O_RDONLY, 444, DB_RECNO, NULL)) == NULL) {
			if ((fmt = malloc(256)) == NULL)
				return ("");
			(void)snprintf(fmt,
			    "unable to open %s: %s", p, strerror(errno));
			return (fmt);
		}
	}
	msgno = atoi(s_msgno);
	key.data = &msgno;
	key.size = sizeof(recno_t);
	switch (gp->msgdb->get(gp->msgdb, &key, &data, 0)) {
	case 0:
		return (data.data);
	case 1:
		p = "no catalog record %ls";
		break;
	case -1:
		p = "catalog record %s: %s";
		break;
	}
	if ((fmt = malloc(256)) == NULL)
		return ("");
	(void)snprintf(fmt, p, msgno, strerror(errno));
	return (fmt);
}
#endif
@


1.1.1.3
log
@import of nvi 1.66
@
text
@a3 2
 * Copyright (c) 1991, 1993, 1994, 1995, 1996
 *	Keith Bostic.  All rights reserved.
d5 27
a31 1
 * See the LICENSE file for redistribution information.
a33 2
#include "config.h"

d35 1
a35 1
static const char sccsid[] = "@@(#)msg.c	10.36 (Berkeley) 5/15/96";
d38 1
a38 2
#include <sys/param.h>
#include <sys/types.h>		/* XXX: param.h may not have included types.h */
a39 1
#include <sys/stat.h>
a42 1
#include <ctype.h>
d46 1
d50 1
d59 5
a63 2
#include "common.h"
#include "../vi/vi.h"
a67 2
 *
 * PUBLIC: void msgq __P((SCR *, mtype_t, const char *, ...));
d71 1
a71 1
msgq(SCR *sp, mtype_t mt, const char *fmt, ...)
d75 2
a76 2
	mtype_t mt;
        const char *fmt;
a79 16
#ifndef NL_ARGMAX
#define	__NL_ARGMAX	20		/* Set to 9 by System V. */
	struct {
		const char *str;	/* String pointer. */
		size_t	 arg;		/* Argument number. */
		size_t	 prefix;	/* Prefix string length. */
		size_t	 skip;		/* Skipped string length. */
		size_t	 suffix;	/* Suffix string length. */
	} str[__NL_ARGMAX];
#endif
	static int reenter;		/* STATIC: Re-entrancy check. */
	CHAR_T ch;
	GS *gp;
	size_t blen, cnt1, cnt2, len, mlen, nlen, soff;
	const char *p, *t, *u;
	char *bp, *mp, *rbp, *s_rbp;
d81 2
d84 5
d90 13
a102 35
	 * !!!
	 * It's possible to enter msg when there's no screen to hold the
	 * message.  If sp is NULL, ignore the special cases and put the
	 * message out to stderr.
	 */
	if (sp == NULL) {
		gp = NULL;
		if (mt == M_BERR)
			mt = M_ERR;
		else if (mt == M_VINFO)
			mt = M_INFO;
	} else {
		gp = sp->gp;
		switch (mt) {
		case M_BERR:
			if (F_ISSET(sp, SC_VI) && !O_ISSET(sp, O_VERBOSE)) {
				F_SET(gp, G_BELLSCHED);
				return;
			}
			mt = M_ERR;
			break;
		case M_VINFO:
			if (!O_ISSET(sp, O_VERBOSE))
				return;
			mt = M_INFO;
			/* FALLTHROUGH */
		case M_INFO:
			if (F_ISSET(sp, SC_EX_SILENT))
				return;
			break;
		case M_ERR:
		case M_SYSERR:
			break;
		default:
			abort();
d104 16
d122 1
a122 9
	/*
	 * It's possible to reenter msg when it allocates space.  We're
	 * probably dead anyway, but there's no reason to drop core.
	 *
	 * XXX
	 * Yes, there's a race, but it should only be two instructions.
	 */
	if (reenter++)
		return;
d124 5
a128 9
	/* Get space for the message. */
	nlen = 1024;
	if (0) {
retry:		FREE_SPACE(sp, bp, blen);
		nlen *= 2;
	}
	bp = NULL;
	blen = 0;
	GET_SPACE_GOTO(sp, bp, blen, nlen);
d130 5
a134 17
	/*
	 * Error prefix.
	 *
	 * mp:	 pointer to the current next character to be written
	 * mlen: length of the already written characters
	 * blen: total length of the buffer
	 */
#define	REM	(blen - mlen)
	mp = bp;
	mlen = 0;
	if (mt == M_SYSERR) {
		p = msg_cat(sp, "020|Error: ", &len);
		if (REM < len)
			goto retry;
		memmove(mp, p, len);
		mp += len;
		mlen += len;
d137 4
a140 16
	/*
	 * If we're running an ex command that the user didn't enter, display
	 * the file name and line number prefix.
	 */
	if ((mt == M_ERR || mt == M_SYSERR) &&
	    sp != NULL && gp != NULL && gp->if_name != NULL) {
		for (p = gp->if_name; *p != '\0'; ++p) {
			len = snprintf(mp, REM, "%s", KEY_NAME(sp, *p));
			mp += len;
			if ((mlen += len) > blen)
				goto retry;
		}
		len = snprintf(mp, REM, ", %d: ", gp->if_lno);
		mp += len;
		if ((mlen += len) > blen)
			goto retry;
d143 6
a148 4
	/* If nothing to format, we're done. */
	if (fmt == NULL)
		goto nofmt;
	fmt = msg_cat(sp, fmt, NULL);
a149 1
#ifndef NL_ARGMAX
d151 2
a152 21
	 * Nvi should run on machines that don't support the numbered argument
	 * specifications (%[digit]*$).  We do this by reformatting the string
	 * so that we can hand it to vsprintf(3) and it will use the arguments
	 * in the right order.  When vsprintf returns, we put the string back
	 * into the right order.  It's undefined, according to SVID III, to mix
	 * numbered argument specifications with the standard style arguments,
	 * so this should be safe.
	 *
	 * In addition, we also need a character that is known to not occur in
	 * any vi message, for separating the parts of the string.  As callers
	 * of msgq are responsible for making sure that all the non-printable
	 * characters are formatted for printing before calling msgq, we use a
	 * random non-printable character selected at terminal initialization
	 * time.  This code isn't fast by any means, but as messages should be
	 * relatively short and normally have only a few arguments, it won't be
	 * too bad.  Regardless, nobody has come up with any other solution.
	 *
	 * The result of this loop is an array of pointers into the message
	 * string, with associated lengths and argument numbers.  The array
	 * is in the "correct" order, and the arg field contains the argument
	 * order.
d154 2
a155 23
	for (p = fmt, soff = 0; soff < __NL_ARGMAX;) {
		for (t = p; *p != '\0' && *p != '%'; ++p);
		if (*p == '\0')
			break;
		++p;
		if (!isdigit(*p)) {
			if (*p == '%')
				++p;
			continue;
		}
		for (u = p; *++p != '\0' && isdigit(*p););
		if (*p != '$')
			continue;

		/* Up to, and including the % character. */
		str[soff].str = t;
		str[soff].prefix = u - t;

		/* Up to, and including the $ character. */
		str[soff].arg = atoi(u);
		str[soff].skip = (p - u) + 1;
		if (str[soff].arg >= __NL_ARGMAX)
			goto ret;
d157 6
a162 14
		/* Up to, and including the conversion character. */
		for (u = p; (ch = *++p) != '\0';)
			if (isalpha(ch) &&
			    strchr("diouxXfeEgGcspn", ch) != NULL)
				break;
		str[soff].suffix = p - u;
		if (ch != '\0')
			++p;
		++soff;
	}

	/* If no magic strings, we're done. */
	if (soff == 0)
		goto format;
d164 15
a178 4
	 /* Get space for the reordered strings. */
	if ((rbp = malloc(nlen)) == NULL)
		goto ret;
	s_rbp = rbp;
d181 2
a182 12
	 * Reorder the strings into the message string based on argument
	 * order.
	 *
	 * !!!
	 * We ignore arguments that are out of order, i.e. if we don't find
	 * an argument, we continue.  Assume (almost certainly incorrectly)
	 * that whoever created the string knew what they were doing.
	 *
	 * !!!
	 * Brute force "sort", but since we don't expect more than one or two
	 * arguments in a string, the setup cost of a fast sort will be more
	 * expensive than the loop.
d184 3
a186 30
	for (cnt1 = 1; cnt1 <= soff; ++cnt1)
		for (cnt2 = 0; cnt2 < soff; ++cnt2)
			if (cnt1 == str[cnt2].arg) {
				memmove(s_rbp, str[cnt2].str, str[cnt2].prefix);
				memmove(s_rbp + str[cnt2].prefix,
				    str[cnt2].str + str[cnt2].prefix +
				    str[cnt2].skip, str[cnt2].suffix);
				s_rbp += str[cnt2].prefix + str[cnt2].suffix;
				*s_rbp++ =
				    gp == NULL ? DEFAULT_NOPRINT : gp->noprint;
				break;
			}
	*s_rbp = '\0';
	fmt = rbp;
#endif

format:	/* Format the arguments into the string. */
#ifdef __STDC__
        va_start(ap, fmt);
#else
        va_start(ap);
#endif
	len = vsnprintf(mp, REM, fmt, ap);
	va_end(ap);
	if (len >= nlen)
		goto retry;

#ifndef NL_ARGMAX
	if (soff == 0)
		goto nofmt;
d189 3
a191 3
	 * Go through the resulting string, and, for each separator character
	 * separated string, enter its new starting position and length in the
	 * array.
d193 1
a193 11
	for (p = t = mp, cnt1 = 1,
	    ch = gp == NULL ? DEFAULT_NOPRINT : gp->noprint; *p != '\0'; ++p)
		if (*p == ch) {
			for (cnt2 = 0; cnt2 < soff; ++cnt2)
				if (str[cnt2].arg == cnt1)
					break;
			str[cnt2].str = t;
			str[cnt2].prefix = p - t;
			t = p + 1;
			++cnt1;
		}
d196 2
a197 6
	 * Reorder the strings once again, putting them back into the
	 * message buffer.
	 *
	 * !!!
	 * Note, the length of the message gets decremented once for
	 * each substring, when we discard the separator character.
d199 14
a212 4
	for (s_rbp = rbp, cnt1 = 0; cnt1 < soff; ++cnt1) {
		memmove(rbp, str[cnt1].str, str[cnt1].prefix);
		rbp += str[cnt1].prefix;
		--len;
d214 8
a221 15
	memmove(mp, s_rbp, rbp - s_rbp);

	/* Free the reordered string memory. */
	free(s_rbp);
#endif

nofmt:	mp += len;
	if ((mlen += len) > blen)
		goto retry;
	if (mt == M_SYSERR) {
		len = snprintf(mp, REM, ": %s", strerror(errno));
		mp += len;
		if ((mlen += len) > blen)
			goto retry;
		mt = M_ERR;
d224 4
a227 4
	/* Add trailing newline. */
	if ((mlen += 1) > blen)
		goto retry;
	*mp = '\n';
d229 4
a232 12
	if (sp != NULL)
		(void)ex_fflush(sp);
	if (gp != NULL)
		gp->scr_msg(sp, mt, bp, mlen);
	else
		(void)fprintf(stderr, "%.*s", (int)mlen, bp);

	/* Cleanup. */
ret:	FREE_SPACE(sp, bp, blen);
alloc_err:
	reenter = 0;
}
d234 2
a235 26
/*
 * msgq_str --
 *	Display a message with an embedded string.
 *
 * PUBLIC: void msgq_str __P((SCR *, mtype_t, char *, char *));
 */
void
msgq_str(sp, mtype, str, fmt)
	SCR *sp;
	mtype_t mtype;
	char *str, *fmt;
{
	int nf, sv_errno;
	char *p;

	if (str == NULL) {
		msgq(sp, mtype, fmt);
		return;
	}

	sv_errno = errno;
	p = msg_print(sp, str, &nf);
	errno = sv_errno;
	msgq(sp, mtype, fmt, p);
	if (nf)
		FREE_SPACE(sp, p, 0);
d239 1
a239 1
 * msgq_rpt --
d245 3
a247 3
 * This wasn't true -- edit a large file and do "100d|1".  We don't implement
 * this semantic since it requires tracking each line that changes during a
 * command instead of just keeping count.
a254 2
 *
 * PUBLIC: void msgq_rpt __P((SCR *));
d256 2
a257 2
void
msgq_rpt(sp)
d259 1
d262 3
a264 11
		"293|added",
		"294|changed",
		"295|deleted",
		"296|joined",
		"297|moved",
		"298|shifted",
		"299|yanked",
	};
	static char * const lines[] = {
		"300|line",
		"301|lines",
d269 1
a269 2
	size_t blen, len, tlen;
	const char *t;
d271 1
a271 1
	char *bp, *p;
d273 2
a274 3
	/* Change reports are turned off in batch mode. */
	if (F_ISSET(sp, SC_EX_SILENT))
		return;
d276 2
a277 2
	/* Reset changing line number. */
	sp->rptlchange = OOBLNO;
d279 2
a280 27
	/*
	 * Don't build a message if not enough changed.
	 *
	 * !!!
	 * And now, a vi clone test.  Historically, vi reported if the number
	 * of changed lines was > than the value, not >=, unless it was a yank
	 * command, which used >=.  No lie.  Furthermore, an action was never
	 * reported for a single line action.  This is consistent for actions
	 * other than yank, but yank didn't report single line actions even if
	 * the report edit option was set to 1.  In addition, setting report to
	 * 0 in the 4BSD historic vi was equivalent to setting it to 1, for an
	 * unknown reason (this bug was fixed in System III/V at some point).
	 * I got complaints, so nvi conforms to System III/V historic practice
	 * except that we report a yank of 1 line if report is set to 1.
	 */
#define	ARSIZE(a)	sizeof(a) / sizeof (*a)
#define	MAXNUM		25
	rptval = O_VAL(sp, O_REPORT);
	for (cnt = 0, total = 0; cnt < ARSIZE(action); ++cnt)
		total += sp->rptlines[cnt];
	if (total == 0)
		return;
	if (total <= rptval && sp->rptlines[L_YANKED] < rptval) {
		for (cnt = 0; cnt < ARSIZE(action); ++cnt)
			sp->rptlines[cnt] = 0;
		return;
	}
d282 2
a283 4
	/* Build and display the message. */
	GET_SPACE_GOTO(sp, bp, blen, sizeof(action) * MAXNUM + 1);
	for (p = bp, first = 1, tlen = 0,
	    ap = action, cnt = 0; cnt < ARSIZE(action); ++ap, ++cnt)
d285 5
a289 8
			if (first)
				first = 0;
			else {
				*p++ = ';';
				*p++ = ' ';
				tlen += 2;
			}
			len = snprintf(p, MAXNUM, "%lu ", sp->rptlines[cnt]);
d291 1
a291 13
			tlen += len;
			t = msg_cat(sp,
			    lines[sp->rptlines[cnt] == 1 ? 0 : 1], &len);
			memmove(p, t, len);
			p += len;
			tlen += len;
			*p++ = ' ';
			++tlen;
			t = msg_cat(sp, *ap, &len);
			memmove(p, t, len);
			p += len;
			tlen += len;
			sp->rptlines[cnt] = 0;
d294 17
a310 6
	/* Add trailing newline. */
	*p = '\n';
	++tlen;

	(void)ex_fflush(sp);
	sp->gp->scr_msg(sp, M_INFO, bp, tlen);
a312 2
alloc_err:
	return;
d314 4
a317 2
#undef ARSIZE
#undef MAXNUM
d321 1
a321 1
 * msgq_status --
a322 2
 *
 * PUBLIC: void msgq_status __P((SCR *, recno_t, u_int));
d324 2
a325 2
void
msgq_status(sp, lno, flags)
d327 1
d329 1
a329 1
	u_int flags;
d332 3
a334 14
	const char *t;
	char *bp, *np, *p, *s;
	int needsep;
	size_t blen, len;

	len = strlen(sp->frp->name);
	GET_SPACE_GOTO(sp, bp, blen, len + 128);
	p = bp;

	memmove(p, sp->frp->name, len);
	p += len;
	np = p;
	*p++ = ':';
	*p++ = ' ';
d336 5
d342 2
a343 2
	 * See nvi/exf.c:file_init() for a description of how and when the
	 * read-only bit is set.
a347 1
	needsep = 0;
d350 2
a351 4
		t = msg_cat(sp, "021|new file", &len);
		memmove(p, t, len);
		p += len;
		needsep = 1;
d353 1
d355 3
a357 48
			t = msg_cat(sp, "022|name changed", &len);
			memmove(p, t, len);
			p += len;
			needsep = 1;
		}
		if (needsep) {
			*p++ = ',';
			*p++ = ' ';
		}
		if (F_ISSET(sp->ep, F_MODIFIED))
			t = msg_cat(sp, "023|modified", &len);
		else
			t = msg_cat(sp, "024|unmodified", &len);
		memmove(p, t, len);
		p += len;
		needsep = 1;
	}
	if (F_ISSET(sp->frp, FR_UNLOCKED)) {
		if (needsep) {
			*p++ = ',';
			*p++ = ' ';
		}
		t = msg_cat(sp, "025|UNLOCKED", &len);
		memmove(p, t, len);
		p += len;
		needsep = 1;
	}
	if (O_ISSET(sp, O_READONLY)) {
		if (needsep) {
			*p++ = ',';
			*p++ = ' ';
		}
		t = msg_cat(sp, "026|readonly", &len);
		memmove(p, t, len);
		p += len;
		needsep = 1;
	}
	if (needsep) {
		*p++ = ':';
		*p++ = ' ';
	}
	if (LF_ISSET(MSTAT_SHOWLAST)) {
		if (db_last(sp, &last))
			return;
		if (last == 0) {
			t = msg_cat(sp, "028|empty file", &len);
			memmove(p, t, len);
			p += len;
d359 3
a361 3
			t = msg_cat(sp, "027|line %lu of %lu [%ld%%]", &len);
			(void)sprintf(p, t, lno, last, (lno * 100) / last);
			p += strlen(p);
a362 4
	} else {
		t = msg_cat(sp, "029|line %lu", &len);
		(void)sprintf(p, t, lno);
		p += strlen(p);
d364 17
a380 37
#ifdef DEBUG
	(void)sprintf(p, " (pid %lu)", (u_long)getpid());
	p += strlen(p);
#endif
	*p++ = '\n';

	/*
	 * There's a nasty problem with long path names.  Cscope and tags files
	 * can result in long paths and vi will request a continuation key from
	 * the user as soon as it starts the screen.  Unfortunately, the user
	 * has already typed ahead, and chaos results.  If we assume that the
	 * characters in the filenames and informational messages only take a
	 * single screen column each, we can trim the filename.
	 */
	s = bp;
	if (LF_ISSET(MSTAT_TRUNCATE))
		if ((p - s) >= sp->cols) {
			for (; s < np &&
			    (*s != '/' || (p - s) > sp->cols - 3); ++s);
			if (s == np)
				s = bp;
			else {
				*--s = '.';
				*--s = '.';
				*--s = '.';
			}
		}
	len = p - s;

	/* Flush any waiting ex messages. */
	(void)ex_fflush(sp);

	sp->gp->scr_msg(sp, M_INFO, s, len);

	FREE_SPACE(sp, bp, blen);
alloc_err:
	return;
d383 1
d385 2
a386 4
 * msg_open --
 *	Open the message catalogs.
 *
 * PUBLIC: int msg_open __P((SCR *, char *));
d388 2
a389 2
int
msg_open(sp, file)
d391 1
a391 1
	char *file;
a392 11
	/*
	 * !!!
	 * Assume that the first file opened is the system default, and that
	 * all subsequent ones user defined.  Only display error messages
	 * if we can't open the user defined ones -- it's useful to know if
	 * the system one wasn't there, but if nvi is being shipped with an
	 * installed system, the file will be there, if it's not, then the
	 * message will be repeated every time nvi is started up.
	 */
	static int first = 1;
	DB *db;
d394 1
d396 1
a396 1
	char *p, *t, buf[MAXPATHLEN];
d398 10
a407 12
	if ((p = strrchr(file, '/')) != NULL && p[1] == '\0' &&
	    ((t = getenv("LANG")) != NULL && t[0] != '\0' ||
	    (t = getenv("LC_MESSAGES")) != NULL && t[0] != '\0')) {
		(void)snprintf(buf, sizeof(buf), "%svi_%s", file, t);
		p = buf;
	} else
		p = file;
	if ((db = dbopen(p,
	    O_NONBLOCK | O_RDONLY, 0, DB_RECNO, NULL)) == NULL) {
		if (first) {
			first = 0;
			return (1);
a408 2
		msgq_str(sp, M_SYSERR, p, "%s");
		return (1);
d410 1
a410 6

	/*
	 * Test record 1 for the magic string.  The msgq call is here so
	 * the message catalog build finds it.
	 */
#define	VMC	"VI_MESSAGE_CATALOG"
d413 14
a426 172
	msgno = 1;
	if (db->get(db, &key, &data, 0) != 0 ||
	    data.size != sizeof(VMC) - 1 ||
	    memcmp(data.data, VMC, sizeof(VMC) - 1)) {
		(void)db->close(db);
		if (first) {
			first = 0;
			return (1);
		}
		msgq_str(sp, M_ERR, p,
		    "030|The file %s is not a message catalog");
		return (1);
	}
	first = 0;

	if (sp->gp->msg != NULL)
		(void)sp->gp->msg->close(sp->gp->msg);
	sp->gp->msg = db;
	return (0);
}

/*
 * msg_close --
 *	Close the message catalogs.
 *
 * PUBLIC: void msg_close __P((GS *));
 */
void
msg_close(gp)
	GS *gp;
{
	if (gp->msg != NULL)
		(void)gp->msg->close(gp->msg);
}

/*
 * msg_cont --
 *	Return common continuation messages.
 *
 * PUBLIC: const char *msg_cmsg __P((SCR *, cmsg_t, size_t *));
 */
const char *
msg_cmsg(sp, which, lenp)
	SCR *sp;
	cmsg_t which;
	size_t *lenp;
{
	switch (which) {
	case CMSG_CONF:
		return (msg_cat(sp, "268|confirm? [ynq]", lenp));
	case CMSG_CONT:
		return (msg_cat(sp, "269|Press any key to continue: ", lenp));
	case CMSG_CONT_EX:
		return (msg_cat(sp,
	    "270|Press any key to continue [: to enter more ex commands]: ",
		    lenp));
	case CMSG_CONT_R:
		return (msg_cat(sp, "161|Press Enter to continue: ", lenp));
	case CMSG_CONT_S:
		return (msg_cat(sp, "275| cont?", lenp));
	case CMSG_CONT_Q:
		return (msg_cat(sp,
		    "271|Press any key to continue [q to quit]: ", lenp));
	default:
		abort();
	}
	/* NOTREACHED */
}

/*
 * msg_cat --
 *	Return a single message from the catalog, plus its length.
 *
 * !!!
 * Only a single catalog message can be accessed at a time, if multiple
 * ones are needed, they must be copied into local memory.
 *
 * PUBLIC: const char *msg_cat __P((SCR *, const char *, size_t *));
 */
const char *
msg_cat(sp, str, lenp)
	SCR *sp;
	const char *str;
	size_t *lenp;
{
	GS *gp;
	DBT data, key;
	recno_t msgno;

	/*
	 * If it's not a catalog message, i.e. has doesn't have a leading
	 * number and '|' symbol, we're done.
	 */
	if (isdigit(str[0]) &&
	    isdigit(str[1]) && isdigit(str[2]) && str[3] == '|') {
		key.data = &msgno;
		key.size = sizeof(recno_t);
		msgno = atoi(str);

		/*
		 * XXX
		 * Really sleazy hack -- we put an extra character on the
		 * end of the format string, and then we change it to be
		 * the nul termination of the string.  There ought to be
		 * a better way.  Once we can allocate multiple temporary
		 * memory buffers, maybe we can use one of them instead.
		 */
		gp = sp == NULL ? NULL : sp->gp;
		if (gp != NULL && gp->msg != NULL &&
		    gp->msg->get(gp->msg, &key, &data, 0) == 0 &&
		    data.size != 0) {
			if (lenp != NULL)
				*lenp = data.size - 1;
			((char *)data.data)[data.size - 1] = '\0';
			return (data.data);
		}
		str = &str[4];
	}
	if (lenp != NULL)
		*lenp = strlen(str);
	return (str);
}

/*
 * msg_print --
 *	Return a printable version of a string, in allocated memory.
 *
 * PUBLIC: char *msg_print __P((SCR *, const char *, int *));
 */
char *
msg_print(sp, s, needfree)
	SCR *sp;
	const char *s;
	int *needfree;
{
	size_t blen, nlen;
	const char *cp;
	char *bp, *ep, *p, *t;

	*needfree = 0;

	for (cp = s; *cp != '\0'; ++cp)
		if (!isprint(*cp))
			break;
	if (*cp == '\0')
		return ((char *)s);	/* SAFE: needfree set to 0. */

	nlen = 0;
	if (0) {
retry:		if (sp == NULL)
			free(bp);
		else
			FREE_SPACE(sp, bp, blen);
		needfree = 0;
	}
	nlen += 256;
	if (sp == NULL) {
		if ((bp = malloc(nlen)) == NULL)
			goto alloc_err;
	} else
		GET_SPACE_GOTO(sp, bp, blen, nlen);
	if (0) {
alloc_err:	return ("");
	}
	*needfree = 1;

	for (p = bp, ep = (bp + blen) - 1, cp = s; *cp != '\0' && p < ep; ++cp)
		for (t = KEY_NAME(sp, *cp); *t != '\0' && p < ep; *p++ = *t++);
	if (p == ep)
		goto retry;
	*p = '\0';
	return (bp);
d428 1
@


1.1.1.4
log
@import of nvi 1.79
@
text
@d13 1
a13 1
static const char sccsid[] = "@@(#)msg.c	10.48 (Berkeley) 9/15/96";
d149 1
a149 1
		memcpy(mp, p, len);
d384 1
a384 1
 * mod_rpt --
d401 1
a401 1
 * PUBLIC: void mod_rpt __P((SCR *));
d404 1
a404 1
mod_rpt(sp)
d480 1
a480 1
			memcpy(p, t, len);
d486 1
a486 1
			memcpy(p, t, len);
a518 1
	static int poisoned;
d520 3
a523 3
	int cnt, needsep;
	const char *t;
	char **ap, *bp, *np, *p, *s;
a524 1
	/* Get sufficient memory. */
d526 1
a526 1
	GET_SPACE_GOTO(sp, bp, blen, len * MAX_CHARACTER_COLUMNS + 128);
d529 2
a530 6
	/* Copy in the filename. */
	for (p = bp, t = sp->frp->name; *t != '\0'; ++t) {
		len = KEY_LEN(sp, *t);
		memcpy(p, KEY_NAME(sp, *t), len);
		p += len;
	}
a534 13
	/* Copy in the argument count. */
	if (F_ISSET(sp, SC_STATUS_CNT) && sp->argv != NULL) {
		for (cnt = 0, ap = sp->argv; *ap != NULL; ++ap, ++cnt);
		if (cnt > 1) {
			(void)sprintf(p,
			    msg_cat(sp, "317|%d files to edit", NULL), cnt);
			p += strlen(p);
			*p++ = ':';
			*p++ = ' ';
		}
		F_CLR(sp, SC_STATUS_CNT);
	}

d546 1
a546 1
		memcpy(p, t, len);
d552 1
a552 1
			memcpy(p, t, len);
d564 1
a564 1
		memcpy(p, t, len);
d574 1
a574 1
		memcpy(p, t, len);
d584 1
a584 1
		memcpy(p, t, len);
d597 1
a597 1
			memcpy(p, t, len);
a613 1
	len = p - bp;
a621 7
	 *
	 * XXX
	 * Status lines get put up at fairly awkward times.  For example, when
	 * you do a filter read (e.g., :read ! echo foo) in the top screen of a
	 * split screen, we have to repaint the status lines for all the screens
	 * below the top screen.  We don't want users having to enter continue
	 * characters for those screens.  Make it really hard to screw this up.
d624 13
a636 11
	if (LF_ISSET(MSTAT_TRUNCATE) && len > sp->cols) {
		for (; s < np && (*s != '/' || (p - s) > sp->cols - 3); ++s);
		if (s == np) {
			s = p - (sp->cols - 5);
			*--s = ' ';
		}
		*--s = '.';
		*--s = '.';
		*--s = '.';
		len = p - s;
	}
d675 3
a677 3
	    ((t = getenv("LC_MESSAGES")) != NULL && t[0] != '\0' ||
	    (t = getenv("LANG")) != NULL && t[0] != '\0')) {
		(void)snprintf(buf, sizeof(buf), "%s%s", file, t);
@

