xref: /titanic_41/usr/src/lib/efcode/engine/env.c (revision 70025d765b044c6d8594bb965a2247a61e991a99)
1 /*
2  * CDDL HEADER START
3  *
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
7  * with the License.
8  *
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.
13  *
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]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <ctype.h>
30 #include <stdio.h>
31 #include <unistd.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <sys/time.h>
35 
36 #include <fcode/private.h>
37 #include <fcode/log.h>
38 
39 
40 static variable_t verbose_emit;
41 
42 void
43 do_verbose_emit(fcode_env_t *env)
44 {
45 	verbose_emit ^= 1;
46 }
47 
48 /*
49  * Internal "emit".
50  * Note log_emit gathers up characters and issues a syslog or write to
51  * error log file if enabled.
52  */
53 void
54 do_emit(fcode_env_t *env, uchar_t c)
55 {
56 	if (verbose_emit)
57 		log_message(MSG_ERROR, "emit(%x)\n", c);
58 
59 	if (c == '\n') {
60 		env->output_column = 0;
61 		env->output_line++;
62 	} else if (c == '\r')
63 		env->output_column = 0;
64 	else
65 		env->output_column++;
66 	if (isatty(fileno(stdout))) {
67 		if ((c >= 0x20 && c <= 0x7f) || c == '\n' || c == '\r' ||
68 		    c == '\b')
69 			putchar(c);
70 		else if (c < 0x20)
71 			printf("@%c", c + '@');
72 		else
73 			printf("\\%x", c);
74 		fflush(stdout);
75 	}
76 	log_emit(c);
77 }
78 
79 void
80 system_message(fcode_env_t *env, char *msg)
81 {
82 	throw_from_fclib(env, 1, msg);
83 }
84 
85 void
86 emit(fcode_env_t *env)
87 {
88 	fstack_t d;
89 
90 	CHECK_DEPTH(env, 1, "emit");
91 	d = POP(DS);
92 	do_emit(env, d);
93 }
94 
95 #include <sys/time.h>
96 
97 /*
98  * 'key?' - abort if stdin is not a tty.
99  */
100 void
101 keyquestion(fcode_env_t *env)
102 {
103 	struct timeval timeval;
104 	fd_set readfds;
105 	int ret;
106 
107 	if (isatty(fileno(stdin))) {
108 		FD_ZERO(&readfds);
109 		FD_SET(fileno(stdin), &readfds);
110 		timeval.tv_sec = 0;
111 		timeval.tv_usec = 1000;
112 		ret = select(fileno(stdin) + 1, &readfds, NULL, NULL, &timeval);
113 		if (FD_ISSET(fileno(stdin), &readfds))
114 			PUSH(DS, TRUE);
115 		else
116 			PUSH(DS, FALSE);
117 	} else
118 		forth_abort(env, "'key?' called in non-interactive mode");
119 }
120 
121 /*
122  * 'key' - abort if stdin is not a tty, will block on read if char not avail.
123  */
124 void
125 key(fcode_env_t *env)
126 {
127 	uchar_t c;
128 
129 	if (isatty(fileno(stdin))) {
130 		read(fileno(stdin), &c, 1);
131 		PUSH(DS, c);
132 	} else
133 		forth_abort(env, "'key' called in non-interactive mode");
134 }
135 
136 void
137 type(fcode_env_t *env)
138 {
139 	int len;
140 	char *ptr;
141 
142 	CHECK_DEPTH(env, 2, "type");
143 	ptr = pop_a_string(env, &len);
144 	while (len--)
145 		do_emit(env, *ptr++);
146 }
147 
148 void
149 paren_cr(fcode_env_t *env)
150 {
151 	do_emit(env, '\r');
152 }
153 
154 void
155 fc_crlf(fcode_env_t *env)
156 {
157 	do_emit(env, '\n');
158 }
159 
160 void
161 fc_num_out(fcode_env_t *env)
162 {
163 	PUSH(DS, (fstack_t)(&env->output_column));
164 }
165 
166 void
167 fc_num_line(fcode_env_t *env)
168 {
169 	PUSH(DS, (fstack_t)(&env->output_line));
170 }
171 
172 void
173 expect(fcode_env_t *env)
174 {
175 	char *buf, *rbuf;
176 	int len;
177 
178 	CHECK_DEPTH(env, 2, "expect");
179 	buf = pop_a_string(env, &len);
180 	read_line(env);
181 	rbuf = pop_a_string(env, NULL);
182 	if (rbuf) {
183 		strcpy(buf, rbuf);
184 		env->span = strlen(buf);
185 	} else
186 		env->span = 0;
187 }
188 
189 void
190 span(fcode_env_t *env)
191 {
192 	PUSH(DS, (fstack_t)&env->span);
193 }
194 
195 void
196 do_ms(fcode_env_t *env)
197 {
198 	fstack_t d;
199 	timespec_t rqtp;
200 
201 	CHECK_DEPTH(env, 1, "ms");
202 	d = POP(DS);
203 	if (d) {
204 		rqtp.tv_sec = 0;
205 		rqtp.tv_nsec = d*1000*1000;
206 		nanosleep(&rqtp, 0);
207 	}
208 }
209 
210 void
211 do_get_msecs(fcode_env_t *env)
212 {
213 	struct timeval tp;
214 	long ms;
215 	timespec_t rqtp;
216 
217 	gettimeofday(&tp, NULL);
218 	ms = (tp.tv_usec/1000) + (tp.tv_sec * 1000);
219 	PUSH(DS, (fstack_t)ms);
220 	rqtp.tv_sec = 0;
221 	rqtp.tv_nsec = 1000*1000;
222 	nanosleep(&rqtp, 0);
223 }
224 
225 #define	CMN_MSG_SIZE	256
226 #define	CMN_MAX_DIGITS	3
227 
228 typedef struct CMN_MSG_T cmn_msg_t;
229 
230 struct CMN_MSG_T {
231 	char		buf[CMN_MSG_SIZE];
232 	int		level;
233 	int 		len;
234 	cmn_msg_t	*prev;
235 	cmn_msg_t	*next;
236 };
237 
238 typedef struct CMN_FMT_T cmn_fmt_t;
239 
240 struct CMN_FMT_T {
241 	int	fwidth;	/* format field width */
242 	int	cwidth; /* column width specified in format */
243 	char	format; /* format type */
244 };
245 
246 static cmn_msg_t 	*root = NULL;
247 static int		cmn_msg_level = 0;
248 
249 /*
250  *	validfmt()
251  *
252  * Called by fmt_str() function to validate and extract formatting
253  * information from the supplied input buffer.
254  *
255  * Supported formats are:
256  *	%c - character
257  *	%d - signed decimal
258  *	%x - unsigned hex
259  *	%s - string
260  *	%ld - signed 64 bit data
261  *	%lx - unsigned 64 bit data
262  *	%p - unsigned 64 bit data(pointer)
263  *	%% - print as single "%" character
264  *
265  * Return values are:
266  *	0  - valid formatting
267  *	1  - invalid formatting found in the input buffer
268  *	-1 - NULL pointer passed in for caller's receptacle
269  *
270  *
271  * For valid formatting, caller's supplied cmn_fmt_t elements are
272  * filled in:
273  *	fwidth:
274  * 		> 0 - returned value is the field width
275  *		< 0 - returned value is negation of field width for
276  *			64 bit data formats
277  *	cwidth:
278  *	  formatted column width(if specified), otherwise 0
279  *
280  *	format:
281  *	  contains the formatting(single) character
282  */
283 static int
284 validfmt(char *fmt, cmn_fmt_t *cfstr)
285 {
286 	int	isll = 0;
287 	int	*fwidth, *cwidth;
288 	char	*format;
289 	char	*dig1, *dig2;
290 	char	cdigs[CMN_MAX_DIGITS+1];
291 
292 	if (cfstr == NULL)
293 		return (-1);
294 
295 	fwidth = &cfstr->fwidth;
296 	cwidth = &cfstr->cwidth;
297 	format = &cfstr->format;
298 	*fwidth = *cwidth = 0;
299 	*format = NULL;
300 	dig1 = dig2 = NULL;
301 
302 	/* check for left justification character */
303 	if (*fmt == '-') {
304 		fmt++;
305 		(*fwidth)++;
306 
307 		/* check for column width specification */
308 		if (isdigit(*fmt))
309 			dig1 = fmt;	/* save ptr to first digit */
310 		while (isdigit(*fmt)) {
311 			fmt++;
312 			(*fwidth)++;
313 		}
314 		/* if ljust specified w/o size, return format error */
315 		if (*fwidth == 1) {
316 			return (1);
317 		}
318 		dig2 = fmt;		/* save ptr to last digit + 1 */
319 	} else {
320 		/* check for column width specification */
321 		if (isdigit(*fmt)) {
322 			dig1 = fmt;	/* save ptr to first digit */
323 			while (isdigit(*fmt)) {
324 				fmt++;
325 				(*fwidth)++;
326 			}
327 			dig2 = fmt;	/* save ptr to last digit + 1 */
328 		}
329 	}
330 
331 	/* if a column width was specified, save it in caller's struct */
332 	if (dig1) {
333 		int nbytes;
334 
335 		nbytes = dig2 - dig1;
336 		/* if too many digits in the width return error */
337 		if (nbytes > CMN_MAX_DIGITS)
338 			return (1);
339 		strncpy(cdigs, dig1, nbytes);
340 		cdigs[nbytes] = 0;
341 		*cwidth = atoi(cdigs);
342 	}
343 
344 	/* check for long format specifier */
345 	if (*fmt == 'l') {
346 		fmt++;
347 		(*fwidth)++;
348 		isll = 1;
349 	}
350 
351 	/* process by specific format type */
352 	switch (*fmt) {
353 	case 'c':
354 	case 's':
355 	case '%':
356 		if (isll)
357 			return (1);
358 	case 'd':
359 	case 'x':
360 		*format = *fmt;
361 		(*fwidth)++;
362 		break;
363 	case 'p':
364 		isll = 1; 		/* uses 64 bit format */
365 		*format = *fmt;
366 		(*fwidth)++;
367 		break;
368 	default:
369 		return (1);		/* unknown format type */
370 	}
371 	if (isll) {
372 		*fwidth *= -1;
373 	}
374 	return (0);
375 }
376 
377 /*
378  *	fmt_args()
379  *
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.
390  *
391  */
392 
393 static void
394 fmt_args(fcode_env_t *env, int cw, int fw, char format, long *arg,
395 	long long *llarg)
396 {
397 	char	*cbuf;
398 	char	snf[3];
399 	int	cbsize;
400 	int	cnv = 10, ndigits = 0;
401 
402 	if (fw > 0) {	/* check for normal (not long) formats */
403 
404 		/* initialize format string for snprintf call */
405 		snf[0] = '%';
406 		snf[1] = format;
407 		snf[2] = 0;
408 
409 		/* process by format type */
410 		switch (format) {
411 		case 'x':
412 			cnv = 16;
413 		case 'd':
414 		case 'c':
415 		case 'p':
416 			*arg = POP(DS);
417 			break;
418 		case 's':
419 			POP(DS);
420 			*arg = POP(DS);
421 			break;
422 		case '%':
423 			return;
424 		default:
425 			log_message(MSG_ERROR,
426 				"fmt_args:invalid format type! (%s)\n",
427 					&format);
428 			return;
429 		}
430 
431 		/* check if a column width was specified */
432 		if (cw) {
433 			/* allocate a scratch buffer */
434 			cbsize = 2*(sizeof (long long)) + 1;
435 			cbuf = MALLOC(cbsize);
436 
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) &&
441 				(ndigits < cbsize))
442 				ndigits++;
443 
444 			/* if truncation is necessary, do it */
445 			if (ndigits > cw) {
446 				cbuf[cw] = 0;
447 				if (format == 's') {
448 					char *str;
449 					str = (char *)*arg;
450 					str[cw] = 0;
451 				} else
452 					*arg = strtol(cbuf,
453 						(char **)NULL, cnv);
454 			}
455 			free(cbuf);
456 		}
457 
458 	} else {	/* process long formats */
459 
460 		*llarg = POP(DS);
461 
462 		/* check if a column width was specified */
463 		if (cw) {
464 			/* allocate a scratch buffer */
465 			cbsize = 2*(sizeof (long long)) + 1;
466 			cbuf = MALLOC(cbsize);
467 
468 			switch (format) {
469 			case 'p':
470 				cnv = 16;
471 				if (snprintf(cbuf, cbsize, "%p", *llarg) < 0)
472 					log_message(MSG_ERROR,
473 						"fmt_args: snprintf error\n");
474 				break;
475 			case 'x':
476 				cnv = 16;
477 				if (snprintf(cbuf, cbsize, "%lx", *llarg) < 0)
478 					log_message(MSG_ERROR,
479 					    "fmt_args: snprintf error\n");
480 				break;
481 			case 'd':
482 				if (snprintf(cbuf, cbsize, "%ld", *llarg) < 0)
483 					log_message(MSG_ERROR,
484 						"fmt_args: snprintf error\n");
485 				break;
486 			default:
487 				log_message(MSG_ERROR,
488 				    "invalid long format type! (l%s)\n",
489 						&format);
490 				free(cbuf);
491 				return;
492 			}
493 			while ((cbuf[ndigits] != NULL) &&
494 				(ndigits < cbsize)) {
495 				ndigits++;
496 			}
497 			/* if truncation is necessary, do it */
498 			if (ndigits > cw) {
499 				cbuf[cw] = 0;
500 				*llarg = strtoll(cbuf, (char **)NULL, cnv);
501 			}
502 			free(cbuf);
503 		}
504 	}
505 }
506 
507 /*
508  *	fmt_str()
509  *
510  * Extracts text from caller's input buffer, processes explicit
511  * formatting as necessary, and outputs formatted text to caller's
512  * receptacle.
513  *
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
518  *
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
526  * text.
527  */
528 
529 static void
530 fmt_str(fcode_env_t *env, char *fmt, char *fmtbuf, int bsize)
531 {
532 	char	tbuf[CMN_MSG_SIZE];
533 	char	*fmptr, *pct;
534 	int	l, cw, fw, bytes;
535 	long	arg;
536 	long long llarg;
537 
538 	*fmtbuf = 0;
539 	if ((pct = strchr(fmt, '%')) != 0) {
540 		cmn_fmt_t	cfstr;
541 		int		vferr;
542 
543 		l = strlen(pct++);
544 		vferr = validfmt(pct, &cfstr);
545 		if (!vferr) {
546 			fw = cfstr.fwidth;
547 			cw = cfstr.cwidth;
548 			fmptr = &cfstr.format;
549 		} else {
550 			if (vferr < 0) {
551 			log_message(MSG_ERROR,
552 			    "fmt_str: NULL ptr supplied to validfmt()\n");
553 			return;
554 			}
555 
556 			bytes = pct - fmt;
557 			strncpy(tbuf, fmt, bytes);
558 			strncpy(tbuf+bytes, "%", 1);
559 			strncpy(tbuf+bytes+1, fmt+bytes, 1);
560 			bytes += 2;
561 			tbuf[bytes] = 0;
562 
563 			log_message(MSG_ERROR,
564 				"fmt_str: invalid format type! (%s)\n",
565 				tbuf+bytes-3);
566 
567 			strncpy(fmtbuf, tbuf, bsize);
568 			return;
569 		}
570 
571 		if (fw > 0) {	/* process normal (not long) formats */
572 			bytes = pct - fmt + fw;
573 			strncpy(tbuf, fmt, bytes);
574 			tbuf[bytes] = 0;
575 		} else {
576 			/* if here, fw must be a long format */
577 			if (*fmptr == 'p') {
578 				bytes = pct - fmt - fw;
579 				strncpy(tbuf, fmt, bytes);
580 				tbuf[bytes] = 0;
581 			} else {
582 				bytes = pct - fmt - fw - 2;
583 				strncpy(tbuf, fmt, bytes);
584 				tbuf[bytes] = 'l';
585 				strncpy(tbuf+bytes+1, fmt+bytes, 2);
586 				tbuf[bytes+1+2] = 0;
587 			}
588 		}
589 
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));
594 		}
595 
596 		/* call to extract args for snprintf() calls below */
597 		fmt_args(env, cw, fw, *fmptr, &arg, &llarg);
598 
599 		if (fw > 0) {	/* process normal (not long) formats */
600 			switch (*fmptr) {
601 			case 'd':
602 			case 'x':
603 			case 'c':
604 			case 's':
605 			case 'p':
606 				(void) snprintf(fmtbuf, bsize, tbuf, arg);
607 				break;
608 			case '%':
609 				(void) snprintf(fmtbuf, bsize, tbuf);
610 				break;
611 			default:
612 				log_message(MSG_ERROR,
613 				    "fmt_str: invalid format (%s)\n",
614 					fmptr);
615 				return;
616 			}
617 
618 		} else	/* process long formats */
619 			(void) snprintf(fmtbuf, bsize, tbuf, llarg);
620 
621 	} else
622 		strncpy(fmtbuf, fmt, bsize);
623 }
624 
625 /*
626  *	fc_cmn_append()
627  *
628  * Pops data stack to obtain message text, and calls fmt_str()
629  * function to perform any message formatting necessary.
630  *
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
634  * its existence.
635  */
636 
637 void
638 fc_cmn_append(fcode_env_t *env)
639 {
640 	int len;
641 	char *str;
642 
643 	if (root == NULL) {
644 		log_message(MSG_ERROR,
645 			"fc_cmn_append: no message context for append\n");
646 		return;
647 	}
648 
649 	len = POP(DS);
650 	str = (char *)POP(DS);
651 
652 	if ((root->len + len) < CMN_MSG_SIZE) {
653 		fmt_str(env, str, root->buf+root->len, CMN_MSG_SIZE -
654 			root->len);
655 		root->len += len;
656 	} else
657 		log_message(MSG_ERROR,
658 			"fc_cmn_append: append exceeds max msg size\n");
659 }
660 
661 /*
662  *	fc_cmn_end()
663  *
664  * Process ]cmn-end token to log the message initiated by a preceeding
665  * fc_cmn_start() call.
666  *
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.
674  */
675 void
676 fc_cmn_end(fcode_env_t *env)
677 {
678 	cmn_msg_t *old;
679 
680 	if (root == 0) {
681 		log_message(MSG_ERROR, "]cmn-end call w/o buffer\n");
682 		return;
683 	}
684 
685 	fc_cmn_append(env);
686 
687 	if (root->prev == 0) {
688 		cmn_msg_t *next;
689 		do {
690 			log_message(root->level, "%s\n", root->buf);
691 			next  = root->next;
692 			free(root);
693 			root = next;
694 		} while (root);
695 	} else {
696 		old = root->prev;
697 		old->next = root;
698 		root = old;
699 	}
700 }
701 
702 /*
703  *	fc_cmn_start()
704  *
705  * Generic function to begin a common message.
706  *
707  * Allocates a new cmn_msg_t to associate with the message, and sets
708  * up initial text as specified by callers' inputs:
709  *
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
713  */
714 static void
715 fc_cmn_start(fcode_env_t *env, char *head, int path)
716 {
717 	cmn_msg_t *new;
718 	char		*dpath;
719 
720 	new = MALLOC(sizeof (cmn_msg_t));
721 	new->prev = root;
722 	if (root != 0)
723 		root->next = new;
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);
731 		++new->len;
732 		free(dpath);
733 	}
734 	new->level = cmn_msg_level;
735 	new->next = NULL;
736 	root = new;
737 }
738 
739 /*
740  *	fc_cmn_type()
741  *
742  * Process cmn-type[ token.
743  *
744  * Invokes fc_cmn_start() to create a message containing blank
745  * header and no device path information.
746  */
747 void
748 fc_cmn_type(fcode_env_t *env)
749 {
750 	cmn_msg_level = MSG_INFO;
751 	fc_cmn_start(env, "", 0);
752 }
753 
754 /*
755  *	fc_cmn_msg()
756  *
757  * Process cmn-msg[ token.
758  *
759  * Invokes fc_cmn_start() to create a message containing blank
760  * header but specifying device path information.
761  */
762 void
763 fc_cmn_msg(fcode_env_t *env)
764 {
765 
766 	cmn_msg_level = MSG_INFO;
767 	fc_cmn_start(env, "", 1);
768 }
769 
770 /*
771  *	fc_cmn_note()
772  *
773  * Process cmn-note[ token.
774  *
775  * Invokes fc_cmn_start() to create a message with NOTICE stamping in
776  * the header and specification of device path information.
777  */
778 void
779 fc_cmn_note(fcode_env_t *env)
780 {
781 	cmn_msg_level = MSG_NOTE;
782 	fc_cmn_start(env, "NOTICE: ", 1);
783 }
784 
785 /*
786  *	fc_cmn_warn()
787  *
788  * Process cmn-warn[ token.
789  *
790  * Invokes fc_cmn_start() to create a message with WARNING stamping in
791  * the header and specification of device path information.
792  */
793 void
794 fc_cmn_warn(fcode_env_t *env)
795 {
796 	cmn_msg_level = MSG_WARN;
797 	fc_cmn_start(env, "WARNING: ", 1);
798 }
799 
800 /*
801  *	fc_cmn_error()
802  *
803  * Process cmn-error[ token.
804  *
805  * Invokes fc_cmn_start() to create a message with ERROR stamping in
806  * the header and specification of device path information.
807  */
808 void
809 fc_cmn_error(fcode_env_t *env)
810 {
811 	cmn_msg_level = MSG_ERROR;
812 	fc_cmn_start(env, "ERROR: ", 1);
813 }
814 
815 /*
816  *	fc_cmn_fatal()
817  *
818  * Process cmn-fatal[ token.
819  *
820  * Invokes fc_cmn_start() to create a message with FATAL stamping in
821  * the header and specification of device path information.
822  */
823 void
824 fc_cmn_fatal(fcode_env_t *env)
825 {
826 	cmn_msg_level = MSG_FATAL;
827 	fc_cmn_start(env, "FATAL: ", 1);
828 }
829 
830 #pragma init(_init)
831 
832 static void
833 _init(void)
834 {
835 	fcode_env_t *env = initial_env;
836 	ASSERT(env);
837 	NOTICE;
838 
839 	ANSI(0x088, 0,		"span",			span);
840 	ANSI(0x08a, 0,		"expect",		expect);
841 
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);
850 
851 	FCODE(0x125, 0,		"get-msecs",		do_get_msecs);
852 	FCODE(0x126, 0,		"ms",			do_ms);
853 
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);
863 }
864