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
do_verbose_emit(fcode_env_t * env)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
do_emit(fcode_env_t * env,uchar_t c)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
system_message(fcode_env_t * env,char * msg)80 system_message(fcode_env_t *env, char *msg)
81 {
82 throw_from_fclib(env, 1, msg);
83 }
84
85 void
emit(fcode_env_t * env)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
keyquestion(fcode_env_t * env)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
key(fcode_env_t * env)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
type(fcode_env_t * env)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
paren_cr(fcode_env_t * env)149 paren_cr(fcode_env_t *env)
150 {
151 do_emit(env, '\r');
152 }
153
154 void
fc_crlf(fcode_env_t * env)155 fc_crlf(fcode_env_t *env)
156 {
157 do_emit(env, '\n');
158 }
159
160 void
fc_num_out(fcode_env_t * env)161 fc_num_out(fcode_env_t *env)
162 {
163 PUSH(DS, (fstack_t)(&env->output_column));
164 }
165
166 void
fc_num_line(fcode_env_t * env)167 fc_num_line(fcode_env_t *env)
168 {
169 PUSH(DS, (fstack_t)(&env->output_line));
170 }
171
172 void
expect(fcode_env_t * env)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
span(fcode_env_t * env)190 span(fcode_env_t *env)
191 {
192 PUSH(DS, (fstack_t)&env->span);
193 }
194
195 void
do_ms(fcode_env_t * env)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
do_get_msecs(fcode_env_t * env)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
validfmt(char * fmt,cmn_fmt_t * cfstr)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
fmt_args(fcode_env_t * env,int cw,int fw,char format,long * arg,long long * llarg)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
fmt_str(fcode_env_t * env,char * fmt,char * fmtbuf,int bsize)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
fc_cmn_append(fcode_env_t * env)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
fc_cmn_end(fcode_env_t * env)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
fc_cmn_start(fcode_env_t * env,char * head,int path)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
fc_cmn_type(fcode_env_t * env)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
fc_cmn_msg(fcode_env_t * env)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
fc_cmn_note(fcode_env_t * env)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
fc_cmn_warn(fcode_env_t * env)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
fc_cmn_error(fcode_env_t * env)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
fc_cmn_fatal(fcode_env_t * env)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
_init(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