env.c revision 2
2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <ctype.h>
2N/A#include <stdio.h>
2N/A#include <unistd.h>
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A#include <sys/time.h>
2N/A
2N/A#include <fcode/private.h>
2N/A#include <fcode/log.h>
2N/A
2N/A
2N/Astatic variable_t verbose_emit;
2N/A
2N/Avoid
2N/Ado_verbose_emit(fcode_env_t *env)
2N/A{
2N/A verbose_emit ^= 1;
2N/A}
2N/A
2N/A/*
2N/A * Internal "emit".
2N/A * Note log_emit gathers up characters and issues a syslog or write to
2N/A * error log file if enabled.
2N/A */
2N/Avoid
2N/Ado_emit(fcode_env_t *env, uchar_t c)
2N/A{
2N/A if (verbose_emit)
2N/A log_message(MSG_ERROR, "emit(%x)\n", c);
2N/A
2N/A if (c == '\n') {
2N/A env->output_column = 0;
2N/A env->output_line++;
2N/A } else if (c == '\r')
2N/A env->output_column = 0;
2N/A else
2N/A env->output_column++;
2N/A if (isatty(fileno(stdout))) {
2N/A if ((c >= 0x20 && c <= 0x7f) || c == '\n' || c == '\r' ||
2N/A c == '\b')
2N/A putchar(c);
2N/A else if (c < 0x20)
2N/A printf("@%c", c + '@');
2N/A else
2N/A printf("\\%x", c);
2N/A fflush(stdout);
2N/A }
2N/A log_emit(c);
2N/A}
2N/A
2N/Avoid
2N/Asystem_message(fcode_env_t *env, char *msg)
2N/A{
2N/A throw_from_fclib(env, 1, msg);
2N/A}
2N/A
2N/Avoid
2N/Aemit(fcode_env_t *env)
2N/A{
2N/A fstack_t d;
2N/A
2N/A CHECK_DEPTH(env, 1, "emit");
2N/A d = POP(DS);
2N/A do_emit(env, d);
2N/A}
2N/A
2N/A#include <sys/time.h>
2N/A
2N/A/*
2N/A * 'key?' - abort if stdin is not a tty.
2N/A */
2N/Avoid
2N/Akeyquestion(fcode_env_t *env)
2N/A{
2N/A struct timeval timeval;
2N/A fd_set readfds;
2N/A int ret;
2N/A
2N/A if (isatty(fileno(stdin))) {
2N/A FD_ZERO(&readfds);
2N/A FD_SET(fileno(stdin), &readfds);
2N/A timeval.tv_sec = 0;
2N/A timeval.tv_usec = 1000;
2N/A ret = select(fileno(stdin) + 1, &readfds, NULL, NULL, &timeval);
2N/A if (FD_ISSET(fileno(stdin), &readfds))
2N/A PUSH(DS, TRUE);
2N/A else
2N/A PUSH(DS, FALSE);
2N/A } else
2N/A forth_abort(env, "'key?' called in non-interactive mode");
2N/A}
2N/A
2N/A/*
2N/A * 'key' - abort if stdin is not a tty, will block on read if char not avail.
2N/A */
2N/Avoid
2N/Akey(fcode_env_t *env)
2N/A{
2N/A uchar_t c;
2N/A
2N/A if (isatty(fileno(stdin))) {
2N/A read(fileno(stdin), &c, 1);
2N/A PUSH(DS, c);
2N/A } else
2N/A forth_abort(env, "'key' called in non-interactive mode");
2N/A}
2N/A
2N/Avoid
2N/Atype(fcode_env_t *env)
2N/A{
2N/A int len;
2N/A char *ptr;
2N/A
2N/A CHECK_DEPTH(env, 2, "type");
2N/A ptr = pop_a_string(env, &len);
2N/A while (len--)
2N/A do_emit(env, *ptr++);
2N/A}
2N/A
2N/Avoid
2N/Aparen_cr(fcode_env_t *env)
2N/A{
2N/A do_emit(env, '\r');
2N/A}
2N/A
2N/Avoid
2N/Afc_crlf(fcode_env_t *env)
2N/A{
2N/A do_emit(env, '\n');
2N/A}
2N/A
2N/Avoid
2N/Afc_num_out(fcode_env_t *env)
2N/A{
2N/A PUSH(DS, (fstack_t)(&env->output_column));
2N/A}
2N/A
2N/Avoid
2N/Afc_num_line(fcode_env_t *env)
2N/A{
2N/A PUSH(DS, (fstack_t)(&env->output_line));
2N/A}
2N/A
2N/Avoid
2N/Aexpect(fcode_env_t *env)
2N/A{
2N/A char *buf, *rbuf;
2N/A int len;
2N/A
2N/A CHECK_DEPTH(env, 2, "expect");
2N/A buf = pop_a_string(env, &len);
2N/A read_line(env);
2N/A rbuf = pop_a_string(env, NULL);
2N/A if (rbuf) {
2N/A strcpy(buf, rbuf);
2N/A env->span = strlen(buf);
2N/A } else
2N/A env->span = 0;
2N/A}
2N/A
2N/Avoid
2N/Aspan(fcode_env_t *env)
2N/A{
2N/A PUSH(DS, (fstack_t)&env->span);
2N/A}
2N/A
2N/Avoid
2N/Ado_ms(fcode_env_t *env)
2N/A{
2N/A fstack_t d;
2N/A timespec_t rqtp;
2N/A
2N/A CHECK_DEPTH(env, 1, "ms");
2N/A d = POP(DS);
2N/A if (d) {
2N/A rqtp.tv_sec = 0;
2N/A rqtp.tv_nsec = d*1000*1000;
2N/A nanosleep(&rqtp, 0);
2N/A }
2N/A}
2N/A
2N/Avoid
2N/Ado_get_msecs(fcode_env_t *env)
2N/A{
2N/A struct timeval tp;
2N/A long ms;
2N/A timespec_t rqtp;
2N/A
2N/A gettimeofday(&tp, NULL);
2N/A ms = (tp.tv_usec/1000) + (tp.tv_sec * 1000);
2N/A PUSH(DS, (fstack_t)ms);
2N/A rqtp.tv_sec = 0;
2N/A rqtp.tv_nsec = 1000*1000;
2N/A nanosleep(&rqtp, 0);
2N/A}
2N/A
2N/A#define CMN_MSG_SIZE 256
2N/A#define CMN_MAX_DIGITS 3
2N/A
2N/Atypedef struct CMN_MSG_T cmn_msg_t;
2N/A
2N/Astruct CMN_MSG_T {
2N/A char buf[CMN_MSG_SIZE];
2N/A int level;
2N/A int len;
2N/A cmn_msg_t *prev;
2N/A cmn_msg_t *next;
2N/A};
2N/A
2N/Atypedef struct CMN_FMT_T cmn_fmt_t;
2N/A
2N/Astruct CMN_FMT_T {
2N/A int fwidth; /* format field width */
2N/A int cwidth; /* column width specified in format */
2N/A char format; /* format type */
2N/A};
2N/A
2N/Astatic cmn_msg_t *root = NULL;
2N/Astatic int cmn_msg_level = 0;
2N/A
2N/A/*
2N/A * validfmt()
2N/A *
2N/A * Called by fmt_str() function to validate and extract formatting
2N/A * information from the supplied input buffer.
2N/A *
2N/A * Supported formats are:
2N/A * %c - character
2N/A * %d - signed decimal
2N/A * %x - unsigned hex
2N/A * %s - string
2N/A * %ld - signed 64 bit data
2N/A * %lx - unsigned 64 bit data
2N/A * %p - unsigned 64 bit data(pointer)
2N/A * %% - print as single "%" character
2N/A *
2N/A * Return values are:
2N/A * 0 - valid formatting
2N/A * 1 - invalid formatting found in the input buffer
2N/A * -1 - NULL pointer passed in for caller's receptacle
2N/A *
2N/A *
2N/A * For valid formatting, caller's supplied cmn_fmt_t elements are
2N/A * filled in:
2N/A * fwidth:
2N/A * > 0 - returned value is the field width
2N/A * < 0 - returned value is negation of field width for
2N/A * 64 bit data formats
2N/A * cwidth:
2N/A * formatted column width(if specified), otherwise 0
2N/A *
2N/A * format:
2N/A * contains the formatting(single) character
2N/A */
2N/Astatic int
2N/Avalidfmt(char *fmt, cmn_fmt_t *cfstr)
2N/A{
2N/A int isll = 0;
2N/A int *fwidth, *cwidth;
2N/A char *format;
2N/A char *dig1, *dig2;
2N/A char cdigs[CMN_MAX_DIGITS+1];
2N/A
2N/A if (cfstr == NULL)
2N/A return (-1);
2N/A
2N/A fwidth = &cfstr->fwidth;
2N/A cwidth = &cfstr->cwidth;
2N/A format = &cfstr->format;
2N/A *fwidth = *cwidth = 0;
2N/A *format = NULL;
2N/A dig1 = dig2 = NULL;
2N/A
2N/A /* check for left justification character */
2N/A if (*fmt == '-') {
2N/A fmt++;
2N/A (*fwidth)++;
2N/A
2N/A /* check for column width specification */
2N/A if (isdigit(*fmt))
2N/A dig1 = fmt; /* save ptr to first digit */
2N/A while (isdigit(*fmt)) {
2N/A fmt++;
2N/A (*fwidth)++;
2N/A }
2N/A /* if ljust specified w/o size, return format error */
2N/A if (*fwidth == 1) {
2N/A return (1);
2N/A }
2N/A dig2 = fmt; /* save ptr to last digit + 1 */
2N/A } else {
2N/A /* check for column width specification */
2N/A if (isdigit(*fmt)) {
2N/A dig1 = fmt; /* save ptr to first digit */
2N/A while (isdigit(*fmt)) {
2N/A fmt++;
2N/A (*fwidth)++;
2N/A }
2N/A dig2 = fmt; /* save ptr to last digit + 1 */
2N/A }
2N/A }
2N/A
2N/A /* if a column width was specified, save it in caller's struct */
2N/A if (dig1) {
2N/A int nbytes;
2N/A
2N/A nbytes = dig2 - dig1;
2N/A /* if too many digits in the width return error */
2N/A if (nbytes > CMN_MAX_DIGITS)
2N/A return (1);
2N/A strncpy(cdigs, dig1, nbytes);
2N/A cdigs[nbytes] = 0;
2N/A *cwidth = atoi(cdigs);
2N/A }
2N/A
2N/A /* check for long format specifier */
2N/A if (*fmt == 'l') {
2N/A fmt++;
2N/A (*fwidth)++;
2N/A isll = 1;
2N/A }
2N/A
2N/A /* process by specific format type */
2N/A switch (*fmt) {
2N/A case 'c':
2N/A case 's':
2N/A case '%':
2N/A if (isll)
2N/A return (1);
2N/A case 'd':
2N/A case 'x':
2N/A *format = *fmt;
2N/A (*fwidth)++;
2N/A break;
2N/A case 'p':
2N/A isll = 1; /* uses 64 bit format */
2N/A *format = *fmt;
2N/A (*fwidth)++;
2N/A break;
2N/A default:
2N/A return (1); /* unknown format type */
2N/A }
2N/A if (isll) {
2N/A *fwidth *= -1;
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * fmt_args()
2N/A *
2N/A * Called by fmt_str() to setup arguments for subsequent snprintf()
2N/A * calls. For cases not involving column width limitations, processing
2N/A * simply POPs the data stack as required to setup caller's arg(or
2N/A * llarg, as appropriate). When a column width is specified for output,
2N/A * a temporary buffer is constructed to contain snprintf() generated
2N/A * output for the argument. Testing is then performed to determine if
2N/A * the specified column width will require truncation of the output.
2N/A * If so, truncation of least significant digits is performed as
2N/A * necessary, and caller's arg(or llarg) is adjusted to obtain the
2N/A * specified column width.
2N/A *
2N/A */
2N/A
2N/Astatic void
2N/Afmt_args(fcode_env_t *env, int cw, int fw, char format, long *arg,
2N/A long long *llarg)
2N/A{
2N/A char *cbuf;
2N/A char snf[3];
2N/A int cbsize;
2N/A int cnv = 10, ndigits = 0;
2N/A
2N/A if (fw > 0) { /* check for normal (not long) formats */
2N/A
2N/A /* initialize format string for snprintf call */
2N/A snf[0] = '%';
2N/A snf[1] = format;
2N/A snf[2] = 0;
2N/A
2N/A /* process by format type */
2N/A switch (format) {
2N/A case 'x':
2N/A cnv = 16;
2N/A case 'd':
2N/A case 'c':
2N/A case 'p':
2N/A *arg = POP(DS);
2N/A break;
2N/A case 's':
2N/A POP(DS);
2N/A *arg = POP(DS);
2N/A break;
2N/A case '%':
2N/A return;
2N/A default:
2N/A log_message(MSG_ERROR,
2N/A "fmt_args:invalid format type! (%s)\n",
2N/A &format);
2N/A return;
2N/A }
2N/A
2N/A /* check if a column width was specified */
2N/A if (cw) {
2N/A /* allocate a scratch buffer */
2N/A cbsize = 2*(sizeof (long long)) + 1;
2N/A cbuf = MALLOC(cbsize);
2N/A
2N/A if (snprintf(cbuf, cbsize, snf, *arg) < 0)
2N/A log_message(MSG_ERROR,
2N/A "fmt_args: snprintf output error\n");
2N/A while ((cbuf[ndigits] != NULL) &&
2N/A (ndigits < cbsize))
2N/A ndigits++;
2N/A
2N/A /* if truncation is necessary, do it */
2N/A if (ndigits > cw) {
2N/A cbuf[cw] = 0;
2N/A if (format == 's') {
2N/A char *str;
2N/A str = (char *)*arg;
2N/A str[cw] = 0;
2N/A } else
2N/A *arg = strtol(cbuf,
2N/A (char **)NULL, cnv);
2N/A }
2N/A free(cbuf);
2N/A }
2N/A
2N/A } else { /* process long formats */
2N/A
2N/A *llarg = POP(DS);
2N/A
2N/A /* check if a column width was specified */
2N/A if (cw) {
2N/A /* allocate a scratch buffer */
2N/A cbsize = 2*(sizeof (long long)) + 1;
2N/A cbuf = MALLOC(cbsize);
2N/A
2N/A switch (format) {
2N/A case 'p':
2N/A cnv = 16;
2N/A if (snprintf(cbuf, cbsize, "%p", *llarg) < 0)
2N/A log_message(MSG_ERROR,
2N/A "fmt_args: snprintf error\n");
2N/A break;
2N/A case 'x':
2N/A cnv = 16;
2N/A if (snprintf(cbuf, cbsize, "%lx", *llarg) < 0)
2N/A log_message(MSG_ERROR,
2N/A "fmt_args: snprintf error\n");
2N/A break;
2N/A case 'd':
2N/A if (snprintf(cbuf, cbsize, "%ld", *llarg) < 0)
2N/A log_message(MSG_ERROR,
2N/A "fmt_args: snprintf error\n");
2N/A break;
2N/A default:
2N/A log_message(MSG_ERROR,
2N/A "invalid long format type! (l%s)\n",
2N/A &format);
2N/A free(cbuf);
2N/A return;
2N/A }
2N/A while ((cbuf[ndigits] != NULL) &&
2N/A (ndigits < cbsize)) {
2N/A ndigits++;
2N/A }
2N/A /* if truncation is necessary, do it */
2N/A if (ndigits > cw) {
2N/A cbuf[cw] = 0;
2N/A *llarg = strtoll(cbuf, (char **)NULL, cnv);
2N/A }
2N/A free(cbuf);
2N/A }
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * fmt_str()
2N/A *
2N/A * Extracts text from caller's input buffer, processes explicit
2N/A * formatting as necessary, and outputs formatted text to caller's
2N/A * receptacle.
2N/A *
2N/A * env - pointer to caller's fcode environment
2N/A * fmt - pointer to caller's input buffr
2N/A * fmtbuf - ponter to caller's receptacle buffer
2N/A * bsize - size of caller's fmtbuf buffer
2N/A *
2N/A * This function performs an initial test to determine if caller's
2N/A * input buffer contains formatting(specified by presence of "%")
2N/A * in the buffer. If so, validfmt() function is called to verify
2N/A * the formatting, after which the buffer is processed according
2N/A * to the field width specified by validfmt() output. Special
2N/A * processing is required when caller's buffer contains a double
2N/A * "%" ("%%"), in which case the second "%" is accepted as normal
2N/A * text.
2N/A */
2N/A
2N/Astatic void
2N/Afmt_str(fcode_env_t *env, char *fmt, char *fmtbuf, int bsize)
2N/A{
2N/A char tbuf[CMN_MSG_SIZE];
2N/A char *fmptr, *pct;
2N/A int l, cw, fw, bytes;
2N/A long arg;
2N/A long long llarg;
2N/A
2N/A *fmtbuf = 0;
2N/A if ((pct = strchr(fmt, '%')) != 0) {
2N/A cmn_fmt_t cfstr;
2N/A int vferr;
2N/A
2N/A l = strlen(pct++);
2N/A vferr = validfmt(pct, &cfstr);
2N/A if (!vferr) {
2N/A fw = cfstr.fwidth;
2N/A cw = cfstr.cwidth;
2N/A fmptr = &cfstr.format;
2N/A } else {
2N/A if (vferr < 0) {
2N/A log_message(MSG_ERROR,
2N/A "fmt_str: NULL ptr supplied to validfmt()\n");
2N/A return;
2N/A }
2N/A
2N/A bytes = pct - fmt;
2N/A strncpy(tbuf, fmt, bytes);
2N/A strncpy(tbuf+bytes, "%", 1);
2N/A strncpy(tbuf+bytes+1, fmt+bytes, 1);
2N/A bytes += 2;
2N/A tbuf[bytes] = 0;
2N/A
2N/A log_message(MSG_ERROR,
2N/A "fmt_str: invalid format type! (%s)\n",
2N/A tbuf+bytes-3);
2N/A
2N/A strncpy(fmtbuf, tbuf, bsize);
2N/A return;
2N/A }
2N/A
2N/A if (fw > 0) { /* process normal (not long) formats */
2N/A bytes = pct - fmt + fw;
2N/A strncpy(tbuf, fmt, bytes);
2N/A tbuf[bytes] = 0;
2N/A } else {
2N/A /* if here, fw must be a long format */
2N/A if (*fmptr == 'p') {
2N/A bytes = pct - fmt - fw;
2N/A strncpy(tbuf, fmt, bytes);
2N/A tbuf[bytes] = 0;
2N/A } else {
2N/A bytes = pct - fmt - fw - 2;
2N/A strncpy(tbuf, fmt, bytes);
2N/A tbuf[bytes] = 'l';
2N/A strncpy(tbuf+bytes+1, fmt+bytes, 2);
2N/A tbuf[bytes+1+2] = 0;
2N/A }
2N/A }
2N/A
2N/A /* if more input buffer to process, recurse */
2N/A if ((l - abs(fw)) != 0) {
2N/A fmt_str(env, pct+abs(fw), (tbuf + strlen(tbuf)),
2N/A CMN_MSG_SIZE - strlen(tbuf));
2N/A }
2N/A
2N/A /* call to extract args for snprintf() calls below */
2N/A fmt_args(env, cw, fw, *fmptr, &arg, &llarg);
2N/A
2N/A if (fw > 0) { /* process normal (not long) formats */
2N/A switch (*fmptr) {
2N/A case 'd':
2N/A case 'x':
2N/A case 'c':
2N/A case 's':
2N/A case 'p':
2N/A (void) snprintf(fmtbuf, bsize, tbuf, arg);
2N/A break;
2N/A case '%':
2N/A (void) snprintf(fmtbuf, bsize, tbuf);
2N/A break;
2N/A default:
2N/A log_message(MSG_ERROR,
2N/A "fmt_str: invalid format (%s)\n",
2N/A fmptr);
2N/A return;
2N/A }
2N/A
2N/A } else /* process long formats */
2N/A (void) snprintf(fmtbuf, bsize, tbuf, llarg);
2N/A
2N/A } else
2N/A strncpy(fmtbuf, fmt, bsize);
2N/A}
2N/A
2N/A/*
2N/A * fc_cmn_append()
2N/A *
2N/A * Pops data stack to obtain message text, and calls fmt_str()
2N/A * function to perform any message formatting necessary.
2N/A *
2N/A * This function is called from fc_cmn_end() or directly in
2N/A * processing a cmn-append token. Since a pre-existing message
2N/A * context is assumed, initial checking is performed to verify
2N/A * its existence.
2N/A */
2N/A
2N/Avoid
2N/Afc_cmn_append(fcode_env_t *env)
2N/A{
2N/A int len;
2N/A char *str;
2N/A
2N/A if (root == NULL) {
2N/A log_message(MSG_ERROR,
2N/A "fc_cmn_append: no message context for append\n");
2N/A return;
2N/A }
2N/A
2N/A len = POP(DS);
2N/A str = (char *)POP(DS);
2N/A
2N/A if ((root->len + len) < CMN_MSG_SIZE) {
2N/A fmt_str(env, str, root->buf+root->len, CMN_MSG_SIZE -
2N/A root->len);
2N/A root->len += len;
2N/A } else
2N/A log_message(MSG_ERROR,
2N/A "fc_cmn_append: append exceeds max msg size\n");
2N/A}
2N/A
2N/A/*
2N/A * fc_cmn_end()
2N/A *
2N/A * Process ]cmn-end token to log the message initiated by a preceding
2N/A * fc_cmn_start() call.
2N/A *
2N/A * Since nested cmn-xxx[ calls are supported, a test is made to determine
2N/A * if this is the final cmn-end of a nested sequence. If so, or if
2N/A * there was no nesting, log_message() is called with the appropriate
2N/A * text buffer. Otherwise, the root variable is adjusted to point to
2N/A * the preceding message in the sequence and links in the list are
2N/A * updated. No logging is performed until the final ]cmn-end of the
2N/A * sequence is processed; then, messages are logged in FIFO order.
2N/A */
2N/Avoid
2N/Afc_cmn_end(fcode_env_t *env)
2N/A{
2N/A cmn_msg_t *old;
2N/A
2N/A if (root == 0) {
2N/A log_message(MSG_ERROR, "]cmn-end call w/o buffer\n");
2N/A return;
2N/A }
2N/A
2N/A fc_cmn_append(env);
2N/A
2N/A if (root->prev == 0) {
2N/A cmn_msg_t *next;
2N/A do {
2N/A log_message(root->level, "%s\n", root->buf);
2N/A next = root->next;
2N/A free(root);
2N/A root = next;
2N/A } while (root);
2N/A } else {
2N/A old = root->prev;
2N/A old->next = root;
2N/A root = old;
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * fc_cmn_start()
2N/A *
2N/A * Generic function to begin a common message.
2N/A *
2N/A * Allocates a new cmn_msg_t to associate with the message, and sets
2N/A * up initial text as specified by callers' inputs:
2N/A *
2N/A * env - pointer to caller's fcode environment
2N/A * head - pointer to initial text portion of the message
2N/A * path - flag to indicate if a device path is to be generated
2N/A */
2N/Astatic void
2N/Afc_cmn_start(fcode_env_t *env, char *head, int path)
2N/A{
2N/A cmn_msg_t *new;
2N/A char *dpath;
2N/A
2N/A new = MALLOC(sizeof (cmn_msg_t));
2N/A new->prev = root;
2N/A if (root != 0)
2N/A root->next = new;
2N/A strcpy(new->buf, head);
2N/A new->len = strlen(head);
2N/A if (path && env->current_device) {
2N/A dpath = get_path(env, env->current_device);
2N/A strcpy(new->buf+new->len, dpath);
2N/A new->len += strlen(dpath);
2N/A strncpy(new->buf+new->len++, ": ", 2);
2N/A ++new->len;
2N/A free(dpath);
2N/A }
2N/A new->level = cmn_msg_level;
2N/A new->next = NULL;
2N/A root = new;
2N/A}
2N/A
2N/A/*
2N/A * fc_cmn_type()
2N/A *
2N/A * Process cmn-type[ token.
2N/A *
2N/A * Invokes fc_cmn_start() to create a message containing blank
2N/A * header and no device path information.
2N/A */
2N/Avoid
2N/Afc_cmn_type(fcode_env_t *env)
2N/A{
2N/A cmn_msg_level = MSG_INFO;
2N/A fc_cmn_start(env, "", 0);
2N/A}
2N/A
2N/A/*
2N/A * fc_cmn_msg()
2N/A *
2N/A * Process cmn-msg[ token.
2N/A *
2N/A * Invokes fc_cmn_start() to create a message containing blank
2N/A * header but specifying device path information.
2N/A */
2N/Avoid
2N/Afc_cmn_msg(fcode_env_t *env)
2N/A{
2N/A
2N/A cmn_msg_level = MSG_INFO;
2N/A fc_cmn_start(env, "", 1);
2N/A}
2N/A
2N/A/*
2N/A * fc_cmn_note()
2N/A *
2N/A * Process cmn-note[ token.
2N/A *
2N/A * Invokes fc_cmn_start() to create a message with NOTICE stamping in
2N/A * the header and specification of device path information.
2N/A */
2N/Avoid
2N/Afc_cmn_note(fcode_env_t *env)
2N/A{
2N/A cmn_msg_level = MSG_NOTE;
2N/A fc_cmn_start(env, "NOTICE: ", 1);
2N/A}
2N/A
2N/A/*
2N/A * fc_cmn_warn()
2N/A *
2N/A * Process cmn-warn[ token.
2N/A *
2N/A * Invokes fc_cmn_start() to create a message with WARNING stamping in
2N/A * the header and specification of device path information.
2N/A */
2N/Avoid
2N/Afc_cmn_warn(fcode_env_t *env)
2N/A{
2N/A cmn_msg_level = MSG_WARN;
2N/A fc_cmn_start(env, "WARNING: ", 1);
2N/A}
2N/A
2N/A/*
2N/A * fc_cmn_error()
2N/A *
2N/A * Process cmn-error[ token.
2N/A *
2N/A * Invokes fc_cmn_start() to create a message with ERROR stamping in
2N/A * the header and specification of device path information.
2N/A */
2N/Avoid
2N/Afc_cmn_error(fcode_env_t *env)
2N/A{
2N/A cmn_msg_level = MSG_ERROR;
2N/A fc_cmn_start(env, "ERROR: ", 1);
2N/A}
2N/A
2N/A/*
2N/A * fc_cmn_fatal()
2N/A *
2N/A * Process cmn-fatal[ token.
2N/A *
2N/A * Invokes fc_cmn_start() to create a message with FATAL stamping in
2N/A * the header and specification of device path information.
2N/A */
2N/Avoid
2N/Afc_cmn_fatal(fcode_env_t *env)
2N/A{
2N/A cmn_msg_level = MSG_FATAL;
2N/A fc_cmn_start(env, "FATAL: ", 1);
2N/A}
2N/A
2N/A#pragma init(_init)
2N/A
2N/Astatic void
2N/A_init(void)
2N/A{
2N/A fcode_env_t *env = initial_env;
2N/A ASSERT(env);
2N/A NOTICE;
2N/A
2N/A ANSI(0x088, 0, "span", span);
2N/A ANSI(0x08a, 0, "expect", expect);
2N/A
2N/A ANSI(0x08d, 0, "key?", keyquestion);
2N/A ANSI(0x08e, 0, "key", key);
2N/A ANSI(0x08f, 0, "emit", emit);
2N/A ANSI(0x090, 0, "type", type);
2N/A ANSI(0x091, 0, "(cr", paren_cr);
2N/A ANSI(0x092, 0, "cr", fc_crlf);
2N/A ANSI(0x093, 0, "#out", fc_num_out);
2N/A ANSI(0x094, 0, "#line", fc_num_line);
2N/A
2N/A FCODE(0x125, 0, "get-msecs", do_get_msecs);
2N/A FCODE(0x126, 0, "ms", do_ms);
2N/A
2N/A FORTH(0, "verbose-emit", do_verbose_emit);
2N/A FCODE(0x7e9, 0, "cmn-fatal[", fc_cmn_fatal);
2N/A FCODE(0x7ea, 0, "cmn-error[", fc_cmn_error);
2N/A FCODE(0x7eb, 0, "cmn-warn[", fc_cmn_warn);
2N/A FCODE(0x7ec, 0, "cmn-note[", fc_cmn_note);
2N/A FCODE(0x7ed, 0, "cmn-type[", fc_cmn_type);
2N/A FCODE(0x7ee, 0, "cmn-append", fc_cmn_append);
2N/A FCODE(0x7ef, 0, "]cmn-end", fc_cmn_end);
2N/A FCODE(0x7f0, 0, "cmn-msg[", fc_cmn_msg);
2N/A}