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