This is message.c in view mode; [Download] [Up]
/* message.c */
/* Copyright 1995 by Steve Kirkendall */
char id_message[] = "$Id: message.c,v 2.25 1996/06/28 01:37:02 steve Exp $";
#include "elvis.h"
#if USE_PROTOTYPES
# include <stdarg.h>
#else
# include <varargs.h>
#endif
#if USE_PROTOTYPES
static void translate(char *terse);
#endif
static CHAR verbose[200];
/* Copy a message into static verbose[] buffer, declared at the top of this
* file. If a buffer named "Elvis messages" exists, translate the message via
* that buffer along the way.
*/
static void translate(terse)
char *terse; /* terse form of error message */
{
BUFFER buf; /* the "Elvis messages" buffer */
MARKBUF mark; /* the start of the buffer */
CHAR *scan; /* used for scanning the buffer */
CHAR *build; /* used for copying chars into the verbose[] buffer */
BOOLEAN bol; /* are we at the start of a line? */
int match; /* used for counting characters that match */
/* Copy the terse string into the verbose buffer, as a default */
for (build = verbose, match = 0; terse[match]; )
{
*build++ = terse[match++];
}
*build = '\0';
/* if the "terse" option is on, then we're done */
if (o_terse)
{
return;
}
/* Find the "Elvis messages" buffer. If it doesn't exist, then
* no more translation is necessary.
*/
buf = buffind(toCHAR(MSG_BUF));
if (!buf)
{
return;
}
/* Scan the buffer for a line which starts with the terse message
* followed by a colon. If there is no such line, then we're done.
*/
for (scanalloc(&scan, marktmp(mark, buf, 0L)), match = 0, bol = True;
scan;
scannext(&scan))
{
/* if this is a newline, then set "bol" and zero "match" */
if (*scan == '\n')
{
bol = True;
match = 0;
continue;
}
/* if we're in the middle of a match, then check to see if
* this position in "Elvis messages" matches the terse message.
*/
if (match >= 0)
{
if (*scan != (terse[match] ? terse[match] : ':'))
{
match = -1;
continue;
}
if (!terse[match])
{
break;
}
match++;
}
}
/* if we get here and "scan" isn't NULL, then we've found the line
* that translates this terse message and "scan" is pointing at the
* ':' that marks the end of the terse text. Copy the verbose text
* after the ':' into the verbose[] variable.
*/
if (scan)
{
/* skip the ':' */
scannext(&scan);
/* at this point, the previous character was not a newline */
bol = False;
/* copy the verbose message from the buffer */
for (build = verbose; build < &verbose[QTY(verbose) - 1]; )
{
/* if non-whitespace, then copy the character */
if (*scan != ' ' && *scan != '\t' && *scan != '\n')
{
*build++ = *scan;
scannext(&scan);
continue;
}
/* skip whitespace */
while (scan && (*scan == ' ' || *scan == '\t' || *scan == '\n'))
{
bol = (*scan == '\n') ? True : False;
scannext(&scan);
}
/* if this non-whitespace character appeared right after
* a newline, then we're done. This is because an
* unindented line in this file always marks the start
* of the next terse message. We're also done if we
* hit the end of the buffer.
*/
if (!scan || bol)
{
break;
}
/* whitespace is converted into a single blank
* character, except at the very beginning of the
* message where it is deleted completely.
*/
if (build != verbose)
{
*build++ = ' ';
}
}
*build = '\0';
}
/* clean up */
scanfree(&scan);
}
/* output a message via the GUI. Before calling the GUI, it subjects
* the terse message to a series of transformations. First, the
* buffer "Elvis messages" is scanned to perform a user-configurable
* transformation, such as translating it into a native language.
* Then the message is evaluated via the calculate() function with
* its "asmsg" parameter set to True.
*
* The arg[] array passed into calculate() is built from the extra arguments
* supplied to msg. The beginning of the terse string can begin with a list
* of characters enclosed in square brackets to indicate how the arguments
* are to be converted to text strings. The conversion letters are:
* d The argument is a (long int), to be shown as a decimal number
* s The argument is a (char *)
* S The argument is a (CHAR *)
* c The argument is a (char), to be shown as a string of length 1
* C The argument is a (CHAR), to be shown as a string of length 1
* If no bracketted list appears at the start of the string, then it is assumed
* that the message has no extra arguments.
*
* For example, msg(MSG_info, "[s]\$1=$1, list=(list)", "foo") will output
* "$1=foo, list=false"
*/
#if USE_PROTOTYPES
void msg(MSGIMP imp, char *terse, ...)
{
#else
void msg(imp, terse, va_alist)
MSGIMP imp; /* message type */
char *terse; /* terse form of message (may contain %s or %d) */
va_dcl
{
#endif
va_list argptr;
CHAR *scan;
CHAR *arg[10];
char text[12], *str;
int i;
BUFFER buf;
MARKBUF mark;
BOOLEAN ding;
/* can't nest msg() calls. If another call is in progress, exit now */
if (*verbose)
{
return;
}
/* Convert any arguments to CHAR strings */
if (*terse == '[')
{
#if USE_PROTOTYPES
va_start(argptr, terse);
#else
va_start(argptr);
#endif
for (i = 0, terse++; *terse != ']'; i++, terse++)
{
assert(i < QTY(arg));
/* convert argument to a CHAR string */
switch (*terse)
{
case 'd':
sprintf(text, "%ld", va_arg(argptr, long));
arg[i] = toCHAR(text);
break;
case 'S':
arg[i] = va_arg(argptr, CHAR *);
if (!arg[i])
arg[i] = toCHAR("NULL");
break;
case 's':
str = va_arg(argptr, char *);
if (!str)
str = "NULL";
arg[i] = toCHAR(str);
break;
case 'c':
text[0] = va_arg(argptr, _char_);
text[1] = '\0';
arg[i] = toCHAR(text);
break;
case 'C':
text[0] = va_arg(argptr, _CHAR_);
text[1] = '\0';
arg[i] = toCHAR(text);
break;
default:
/* elvis source code should never have a
* bad format code.
*/
abort();
}
/* dynamically allocate a copy of that string. This
* is done because some parameter types use the text[]
* buffer, and a later argument may need to reuse that
* buffer.
*/
arg[i] = CHARdup(arg[i]);
}
va_end(argptr);
arg[i] = NULL;
/* move the terse pointer past the closing ']' character */
assert(*terse == ']');
terse++;
}
else /* no bracketted list at start of terse string */
{
/* no extra arguments */
arg[0] = NULL;
}
/* translate the terse message via "Elvis messages" buffer */
translate(terse);
/* expand any arguments or option names */
scan = calculate(verbose, arg, True);
if (!scan)
scan = calculate(toCHAR(terse), arg, True);
if (!scan)
scan = toCHAR(terse);
/* If it starts with a ^G character, then ring the bell. Also
* ring the bell if errorbells or warningbells is set
*/
switch (imp)
{
case MSG_ERROR: ding = o_errorbells; break;
case MSG_WARNING: ding = o_warningbells; break;
default: ding = False;
}
if (*scan == ELVCTRL('G'))
{
scan++;
ding = True;
}
/* copy the string into verbose[] */
CHARncpy(verbose, scan, QTY(verbose) - 1);
verbose[QTY(verbose) - 1] = '\0';
/* free the arg[] strings */
for (i = 0; arg[i]; i++)
{
safefree(arg[i]);
}
/* Status and fatal messages are shown immediately, without flushing
* the message buffer. During the initialization phase, other messages
* may also be output immediately.
*/
if (!verbose[0] && imp != MSG_FATAL)
{
/* ignore it. No output */
}
else if ((o_verbose && !windefault) || !gui
|| (eventcounter <= 1 && imp == MSG_ERROR)
|| imp == MSG_STATUS || imp == MSG_FATAL)
{
/* show the message */
if (gui && windefault)
{
/* Either the GUI will show it, or we will */
if (!gui->msg || !(*gui->msg)(windefault->gw, imp, verbose, (int)(scan - verbose)))
{
/* we have to show it... on bottom of window? */
drawmsg(windefault, imp, verbose, (int)CHARlen(verbose));
}
/* For fatal error messages, also write it to stderr */
if (imp == MSG_FATAL)
{
fprintf(stderr, "%s\n", verbose);
}
}
else
{
/* no GUI yet, so just write it to stdout/stderr */
#ifdef WIN16
fprintf(stderr, "%s\n", verbose);
#else
if (imp == MSG_FATAL)
{
fprintf(stderr, "%s\n", verbose);
}
else
{
printf("%s\r\n", verbose);
}
#endif
}
/* clean up & exit */
if (imp == MSG_FATAL)
{
o_tempsession = False;
sesclose();
if (gui) (*gui->term)();
#ifdef NDEBUG
exit(1);
#else
abort();
#endif
}
}
else
{
/* append the message to the message buffer */
buf = bufalloc(toCHAR(MSGQUEUE_BUF), 0);
o_internal(buf) = True;
(void)marktmp(mark, buf, o_bufchars(buf));
bufreplace(&mark, &mark, toCHAR("\n"), 1L);
bufreplace(&mark, &mark, verbose, (long)CHARlen(verbose));
bufreplace(&mark, &mark, toCHAR(imp>=MSG_ERROR ? "n" : " "), 1L);
}
/* if error, then alert the terminal */
if (imp >= MSG_ERROR)
{
mapalert();
o_exitcode = 1;
}
/* if we're supposed to ring the bell, and this GUI has a bell,
* then ring it.
*/
if (ding && gui && gui->beep && windefault)
{
guibeep(windefault);
}
/* Zero the first byte of verbose[], so we can tell that we aren't
* in the middle of a message anymore.
*/
*verbose = '\0';
}
/* This function flushes messages from the message queue to the current
* window. This function should be called before outputting ex text,
* before reading keystrokes, and when exiting elvis, after the GUI has
* been shut down but before the session file has been closed.
*/
void msgflush()
{
BUFFER buf;
MARK mark, end;
CHAR *cp;
int len;
MSGIMP imp;
/* if we have a GUI but no windows yet, then delay output */
if (gui && !windefault)
{
return;
}
/* locate the message queue buffer, if any. If it doesn't exist,
* or is empty, then we're done!
*/
buf = buffind(toCHAR(MSGQUEUE_BUF));
if (!buf || o_bufchars(buf) == 0)
{
return;
}
/* Copy each line into the "verbose" buffer. For each one, display
* the message as either info or an error.
*/
mark = markalloc(buf, 0);
for (scanalloc(&cp, mark), len = 0; cp; scannext(&cp))
{
if (*cp == '\n')
{
verbose[len] = '\0';
imp = (verbose[0]=='*' ? MSG_ERROR : MSG_INFO);
/* show the message */
if (gui && windefault)
{
/* Either the GUI will show it, or we will */
if (!gui->msg || !(*gui->msg)(windefault->gw, imp, verbose + 1, len - 1))
{
/* we have to show it... on bottom of window? */
drawmsg(windefault, imp, verbose + 1, len - 1);
}
}
else
{
/* no GUI yet, so just write it to stdout/stderr */
fprintf(imp >= MSG_ERROR ? stderr : stdout,
"%s\n", verbose + 1);
}
len = 0;
}
else if (len < QTY(verbose) - 2)
{
verbose[len++] = *cp;
}
}
scanfree(&cp);
/* cleanup */
end = markalloc(buf, o_bufchars(buf));
bufreplace(mark, end, NULL, 0);
markfree(mark);
markfree(end);
*verbose = '\0';
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.