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