4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
36 #include <fcode/private.h>
37 #include <fcode/log.h>
40 static variable_t verbose_emit
;
43 do_verbose_emit(fcode_env_t
*env
)
50 * Note log_emit gathers up characters and issues a syslog or write to
51 * error log file if enabled.
54 do_emit(fcode_env_t
*env
, uchar_t c
)
57 log_message(MSG_ERROR
, "emit(%x)\n", c
);
60 env
->output_column
= 0;
63 env
->output_column
= 0;
66 if (isatty(fileno(stdout
))) {
67 if ((c
>= 0x20 && c
<= 0x7f) || c
== '\n' || c
== '\r' ||
71 printf("@%c", c
+ '@');
80 system_message(fcode_env_t
*env
, char *msg
)
82 throw_from_fclib(env
, 1, msg
);
86 emit(fcode_env_t
*env
)
90 CHECK_DEPTH(env
, 1, "emit");
98 * 'key?' - abort if stdin is not a tty.
101 keyquestion(fcode_env_t
*env
)
103 struct timeval timeval
;
107 if (isatty(fileno(stdin
))) {
109 FD_SET(fileno(stdin
), &readfds
);
111 timeval
.tv_usec
= 1000;
112 ret
= select(fileno(stdin
) + 1, &readfds
, NULL
, NULL
, &timeval
);
113 if (FD_ISSET(fileno(stdin
), &readfds
))
118 forth_abort(env
, "'key?' called in non-interactive mode");
122 * 'key' - abort if stdin is not a tty, will block on read if char not avail.
125 key(fcode_env_t
*env
)
129 if (isatty(fileno(stdin
))) {
130 read(fileno(stdin
), &c
, 1);
133 forth_abort(env
, "'key' called in non-interactive mode");
137 type(fcode_env_t
*env
)
142 CHECK_DEPTH(env
, 2, "type");
143 ptr
= pop_a_string(env
, &len
);
145 do_emit(env
, *ptr
++);
149 paren_cr(fcode_env_t
*env
)
155 fc_crlf(fcode_env_t
*env
)
161 fc_num_out(fcode_env_t
*env
)
163 PUSH(DS
, (fstack_t
)(&env
->output_column
));
167 fc_num_line(fcode_env_t
*env
)
169 PUSH(DS
, (fstack_t
)(&env
->output_line
));
173 expect(fcode_env_t
*env
)
178 CHECK_DEPTH(env
, 2, "expect");
179 buf
= pop_a_string(env
, &len
);
181 rbuf
= pop_a_string(env
, NULL
);
184 env
->span
= strlen(buf
);
190 span(fcode_env_t
*env
)
192 PUSH(DS
, (fstack_t
)&env
->span
);
196 do_ms(fcode_env_t
*env
)
201 CHECK_DEPTH(env
, 1, "ms");
205 rqtp
.tv_nsec
= d
*1000*1000;
211 do_get_msecs(fcode_env_t
*env
)
217 gettimeofday(&tp
, NULL
);
218 ms
= (tp
.tv_usec
/1000) + (tp
.tv_sec
* 1000);
219 PUSH(DS
, (fstack_t
)ms
);
221 rqtp
.tv_nsec
= 1000*1000;
225 #define CMN_MSG_SIZE 256
226 #define CMN_MAX_DIGITS 3
228 typedef struct CMN_MSG_T cmn_msg_t
;
231 char buf
[CMN_MSG_SIZE
];
238 typedef struct CMN_FMT_T cmn_fmt_t
;
241 int fwidth
; /* format field width */
242 int cwidth
; /* column width specified in format */
243 char format
; /* format type */
246 static cmn_msg_t
*root
= NULL
;
247 static int cmn_msg_level
= 0;
252 * Called by fmt_str() function to validate and extract formatting
253 * information from the supplied input buffer.
255 * Supported formats are:
257 * %d - signed decimal
260 * %ld - signed 64 bit data
261 * %lx - unsigned 64 bit data
262 * %p - unsigned 64 bit data(pointer)
263 * %% - print as single "%" character
266 * 0 - valid formatting
267 * 1 - invalid formatting found in the input buffer
268 * -1 - NULL pointer passed in for caller's receptacle
271 * For valid formatting, caller's supplied cmn_fmt_t elements are
274 * > 0 - returned value is the field width
275 * < 0 - returned value is negation of field width for
276 * 64 bit data formats
278 * formatted column width(if specified), otherwise 0
281 * contains the formatting(single) character
284 validfmt(char *fmt
, cmn_fmt_t
*cfstr
)
287 int *fwidth
, *cwidth
;
290 char cdigs
[CMN_MAX_DIGITS
+1];
295 fwidth
= &cfstr
->fwidth
;
296 cwidth
= &cfstr
->cwidth
;
297 format
= &cfstr
->format
;
298 *fwidth
= *cwidth
= 0;
302 /* check for left justification character */
307 /* check for column width specification */
309 dig1
= fmt
; /* save ptr to first digit */
310 while (isdigit(*fmt
)) {
314 /* if ljust specified w/o size, return format error */
318 dig2
= fmt
; /* save ptr to last digit + 1 */
320 /* check for column width specification */
322 dig1
= fmt
; /* save ptr to first digit */
323 while (isdigit(*fmt
)) {
327 dig2
= fmt
; /* save ptr to last digit + 1 */
331 /* if a column width was specified, save it in caller's struct */
335 nbytes
= dig2
- dig1
;
336 /* if too many digits in the width return error */
337 if (nbytes
> CMN_MAX_DIGITS
)
339 strncpy(cdigs
, dig1
, nbytes
);
341 *cwidth
= atoi(cdigs
);
344 /* check for long format specifier */
351 /* process by specific format type */
364 isll
= 1; /* uses 64 bit format */
369 return (1); /* unknown format type */
380 * Called by fmt_str() to setup arguments for subsequent snprintf()
381 * calls. For cases not involving column width limitations, processing
382 * simply POPs the data stack as required to setup caller's arg(or
383 * llarg, as appropriate). When a column width is specified for output,
384 * a temporary buffer is constructed to contain snprintf() generated
385 * output for the argument. Testing is then performed to determine if
386 * the specified column width will require truncation of the output.
387 * If so, truncation of least significant digits is performed as
388 * necessary, and caller's arg(or llarg) is adjusted to obtain the
389 * specified column width.
394 fmt_args(fcode_env_t
*env
, int cw
, int fw
, char format
, long *arg
,
400 int cnv
= 10, ndigits
= 0;
402 if (fw
> 0) { /* check for normal (not long) formats */
404 /* initialize format string for snprintf call */
409 /* process by format type */
425 log_message(MSG_ERROR
,
426 "fmt_args:invalid format type! (%s)\n",
431 /* check if a column width was specified */
433 /* allocate a scratch buffer */
434 cbsize
= 2*(sizeof (long long)) + 1;
435 cbuf
= MALLOC(cbsize
);
437 if (snprintf(cbuf
, cbsize
, snf
, *arg
) < 0)
438 log_message(MSG_ERROR
,
439 "fmt_args: snprintf output error\n");
440 while ((cbuf
[ndigits
] != NULL
) &&
444 /* if truncation is necessary, do it */
458 } else { /* process long formats */
462 /* check if a column width was specified */
464 /* allocate a scratch buffer */
465 cbsize
= 2*(sizeof (long long)) + 1;
466 cbuf
= MALLOC(cbsize
);
471 if (snprintf(cbuf
, cbsize
, "%p", *llarg
) < 0)
472 log_message(MSG_ERROR
,
473 "fmt_args: snprintf error\n");
477 if (snprintf(cbuf
, cbsize
, "%lx", *llarg
) < 0)
478 log_message(MSG_ERROR
,
479 "fmt_args: snprintf error\n");
482 if (snprintf(cbuf
, cbsize
, "%ld", *llarg
) < 0)
483 log_message(MSG_ERROR
,
484 "fmt_args: snprintf error\n");
487 log_message(MSG_ERROR
,
488 "invalid long format type! (l%s)\n",
493 while ((cbuf
[ndigits
] != NULL
) &&
494 (ndigits
< cbsize
)) {
497 /* if truncation is necessary, do it */
500 *llarg
= strtoll(cbuf
, (char **)NULL
, cnv
);
510 * Extracts text from caller's input buffer, processes explicit
511 * formatting as necessary, and outputs formatted text to caller's
514 * env - pointer to caller's fcode environment
515 * fmt - pointer to caller's input buffr
516 * fmtbuf - ponter to caller's receptacle buffer
517 * bsize - size of caller's fmtbuf buffer
519 * This function performs an initial test to determine if caller's
520 * input buffer contains formatting(specified by presence of "%")
521 * in the buffer. If so, validfmt() function is called to verify
522 * the formatting, after which the buffer is processed according
523 * to the field width specified by validfmt() output. Special
524 * processing is required when caller's buffer contains a double
525 * "%" ("%%"), in which case the second "%" is accepted as normal
530 fmt_str(fcode_env_t
*env
, char *fmt
, char *fmtbuf
, int bsize
)
532 char tbuf
[CMN_MSG_SIZE
];
534 int l
, cw
, fw
, bytes
;
539 if ((pct
= strchr(fmt
, '%')) != 0) {
544 vferr
= validfmt(pct
, &cfstr
);
548 fmptr
= &cfstr
.format
;
551 log_message(MSG_ERROR
,
552 "fmt_str: NULL ptr supplied to validfmt()\n");
557 strncpy(tbuf
, fmt
, bytes
);
558 strncpy(tbuf
+bytes
, "%", 1);
559 strncpy(tbuf
+bytes
+1, fmt
+bytes
, 1);
563 log_message(MSG_ERROR
,
564 "fmt_str: invalid format type! (%s)\n",
567 strncpy(fmtbuf
, tbuf
, bsize
);
571 if (fw
> 0) { /* process normal (not long) formats */
572 bytes
= pct
- fmt
+ fw
;
573 strncpy(tbuf
, fmt
, bytes
);
576 /* if here, fw must be a long format */
578 bytes
= pct
- fmt
- fw
;
579 strncpy(tbuf
, fmt
, bytes
);
582 bytes
= pct
- fmt
- fw
- 2;
583 strncpy(tbuf
, fmt
, bytes
);
585 strncpy(tbuf
+bytes
+1, fmt
+bytes
, 2);
590 /* if more input buffer to process, recurse */
591 if ((l
- abs(fw
)) != 0) {
592 fmt_str(env
, pct
+abs(fw
), (tbuf
+ strlen(tbuf
)),
593 CMN_MSG_SIZE
- strlen(tbuf
));
596 /* call to extract args for snprintf() calls below */
597 fmt_args(env
, cw
, fw
, *fmptr
, &arg
, &llarg
);
599 if (fw
> 0) { /* process normal (not long) formats */
606 (void) snprintf(fmtbuf
, bsize
, tbuf
, arg
);
609 (void) snprintf(fmtbuf
, bsize
, tbuf
);
612 log_message(MSG_ERROR
,
613 "fmt_str: invalid format (%s)\n",
618 } else /* process long formats */
619 (void) snprintf(fmtbuf
, bsize
, tbuf
, llarg
);
622 strncpy(fmtbuf
, fmt
, bsize
);
628 * Pops data stack to obtain message text, and calls fmt_str()
629 * function to perform any message formatting necessary.
631 * This function is called from fc_cmn_end() or directly in
632 * processing a cmn-append token. Since a pre-existing message
633 * context is assumed, initial checking is performed to verify
638 fc_cmn_append(fcode_env_t
*env
)
644 log_message(MSG_ERROR
,
645 "fc_cmn_append: no message context for append\n");
650 str
= (char *)POP(DS
);
652 if ((root
->len
+ len
) < CMN_MSG_SIZE
) {
653 fmt_str(env
, str
, root
->buf
+root
->len
, CMN_MSG_SIZE
-
657 log_message(MSG_ERROR
,
658 "fc_cmn_append: append exceeds max msg size\n");
664 * Process ]cmn-end token to log the message initiated by a preceeding
665 * fc_cmn_start() call.
667 * Since nested cmn-xxx[ calls are supported, a test is made to determine
668 * if this is the final cmn-end of a nested sequence. If so, or if
669 * there was no nesting, log_message() is called with the appropriate
670 * text buffer. Otherwise, the root variable is adjusted to point to
671 * the preceeding message in the sequence and links in the list are
672 * updated. No logging is performed until the final ]cmn-end of the
673 * sequence is processed; then, messages are logged in FIFO order.
676 fc_cmn_end(fcode_env_t
*env
)
681 log_message(MSG_ERROR
, "]cmn-end call w/o buffer\n");
687 if (root
->prev
== 0) {
690 log_message(root
->level
, "%s\n", root
->buf
);
705 * Generic function to begin a common message.
707 * Allocates a new cmn_msg_t to associate with the message, and sets
708 * up initial text as specified by callers' inputs:
710 * env - pointer to caller's fcode environment
711 * head - pointer to initial text portion of the message
712 * path - flag to indicate if a device path is to be generated
715 fc_cmn_start(fcode_env_t
*env
, char *head
, int path
)
720 new = MALLOC(sizeof (cmn_msg_t
));
724 strcpy(new->buf
, head
);
725 new->len
= strlen(head
);
726 if (path
&& env
->current_device
) {
727 dpath
= get_path(env
, env
->current_device
);
728 strcpy(new->buf
+new->len
, dpath
);
729 new->len
+= strlen(dpath
);
730 strncpy(new->buf
+new->len
++, ": ", 2);
734 new->level
= cmn_msg_level
;
742 * Process cmn-type[ token.
744 * Invokes fc_cmn_start() to create a message containing blank
745 * header and no device path information.
748 fc_cmn_type(fcode_env_t
*env
)
750 cmn_msg_level
= MSG_INFO
;
751 fc_cmn_start(env
, "", 0);
757 * Process cmn-msg[ token.
759 * Invokes fc_cmn_start() to create a message containing blank
760 * header but specifying device path information.
763 fc_cmn_msg(fcode_env_t
*env
)
766 cmn_msg_level
= MSG_INFO
;
767 fc_cmn_start(env
, "", 1);
773 * Process cmn-note[ token.
775 * Invokes fc_cmn_start() to create a message with NOTICE stamping in
776 * the header and specification of device path information.
779 fc_cmn_note(fcode_env_t
*env
)
781 cmn_msg_level
= MSG_NOTE
;
782 fc_cmn_start(env
, "NOTICE: ", 1);
788 * Process cmn-warn[ token.
790 * Invokes fc_cmn_start() to create a message with WARNING stamping in
791 * the header and specification of device path information.
794 fc_cmn_warn(fcode_env_t
*env
)
796 cmn_msg_level
= MSG_WARN
;
797 fc_cmn_start(env
, "WARNING: ", 1);
803 * Process cmn-error[ token.
805 * Invokes fc_cmn_start() to create a message with ERROR stamping in
806 * the header and specification of device path information.
809 fc_cmn_error(fcode_env_t
*env
)
811 cmn_msg_level
= MSG_ERROR
;
812 fc_cmn_start(env
, "ERROR: ", 1);
818 * Process cmn-fatal[ token.
820 * Invokes fc_cmn_start() to create a message with FATAL stamping in
821 * the header and specification of device path information.
824 fc_cmn_fatal(fcode_env_t
*env
)
826 cmn_msg_level
= MSG_FATAL
;
827 fc_cmn_start(env
, "FATAL: ", 1);
835 fcode_env_t
*env
= initial_env
;
839 ANSI(0x088, 0, "span", span
);
840 ANSI(0x08a, 0, "expect", expect
);
842 ANSI(0x08d, 0, "key?", keyquestion
);
843 ANSI(0x08e, 0, "key", key
);
844 ANSI(0x08f, 0, "emit", emit
);
845 ANSI(0x090, 0, "type", type
);
846 ANSI(0x091, 0, "(cr", paren_cr
);
847 ANSI(0x092, 0, "cr", fc_crlf
);
848 ANSI(0x093, 0, "#out", fc_num_out
);
849 ANSI(0x094, 0, "#line", fc_num_line
);
851 FCODE(0x125, 0, "get-msecs", do_get_msecs
);
852 FCODE(0x126, 0, "ms", do_ms
);
854 FORTH(0, "verbose-emit", do_verbose_emit
);
855 FCODE(0x7e9, 0, "cmn-fatal[", fc_cmn_fatal
);
856 FCODE(0x7ea, 0, "cmn-error[", fc_cmn_error
);
857 FCODE(0x7eb, 0, "cmn-warn[", fc_cmn_warn
);
858 FCODE(0x7ec, 0, "cmn-note[", fc_cmn_note
);
859 FCODE(0x7ed, 0, "cmn-type[", fc_cmn_type
);
860 FCODE(0x7ee, 0, "cmn-append", fc_cmn_append
);
861 FCODE(0x7ef, 0, "]cmn-end", fc_cmn_end
);
862 FCODE(0x7f0, 0, "cmn-msg[", fc_cmn_msg
);