dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / efcode / engine / env.c
blob0fe815baed78ad07f7ea321e6c87f00bbda9e346
1 /*
2 * CDDL HEADER START
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.
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]
20 * CDDL HEADER END
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
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>
36 #include <fcode/private.h>
37 #include <fcode/log.h>
40 static variable_t verbose_emit;
42 void
43 do_verbose_emit(fcode_env_t *env)
45 verbose_emit ^= 1;
49 * Internal "emit".
50 * Note log_emit gathers up characters and issues a syslog or write to
51 * error log file if enabled.
53 void
54 do_emit(fcode_env_t *env, uchar_t c)
56 if (verbose_emit)
57 log_message(MSG_ERROR, "emit(%x)\n", c);
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);
76 log_emit(c);
79 void
80 system_message(fcode_env_t *env, char *msg)
82 throw_from_fclib(env, 1, msg);
85 void
86 emit(fcode_env_t *env)
88 fstack_t d;
90 CHECK_DEPTH(env, 1, "emit");
91 d = POP(DS);
92 do_emit(env, d);
95 #include <sys/time.h>
98 * 'key?' - abort if stdin is not a tty.
100 void
101 keyquestion(fcode_env_t *env)
103 struct timeval timeval;
104 fd_set readfds;
105 int ret;
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");
122 * 'key' - abort if stdin is not a tty, will block on read if char not avail.
124 void
125 key(fcode_env_t *env)
127 uchar_t c;
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");
136 void
137 type(fcode_env_t *env)
139 int len;
140 char *ptr;
142 CHECK_DEPTH(env, 2, "type");
143 ptr = pop_a_string(env, &len);
144 while (len--)
145 do_emit(env, *ptr++);
148 void
149 paren_cr(fcode_env_t *env)
151 do_emit(env, '\r');
154 void
155 fc_crlf(fcode_env_t *env)
157 do_emit(env, '\n');
160 void
161 fc_num_out(fcode_env_t *env)
163 PUSH(DS, (fstack_t)(&env->output_column));
166 void
167 fc_num_line(fcode_env_t *env)
169 PUSH(DS, (fstack_t)(&env->output_line));
172 void
173 expect(fcode_env_t *env)
175 char *buf, *rbuf;
176 int len;
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;
189 void
190 span(fcode_env_t *env)
192 PUSH(DS, (fstack_t)&env->span);
195 void
196 do_ms(fcode_env_t *env)
198 fstack_t d;
199 timespec_t rqtp;
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);
210 void
211 do_get_msecs(fcode_env_t *env)
213 struct timeval tp;
214 long ms;
215 timespec_t rqtp;
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);
225 #define CMN_MSG_SIZE 256
226 #define CMN_MAX_DIGITS 3
228 typedef struct CMN_MSG_T cmn_msg_t;
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;
238 typedef struct CMN_FMT_T cmn_fmt_t;
240 struct 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;
250 * validfmt()
252 * Called by fmt_str() function to validate and extract formatting
253 * information from the supplied input buffer.
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
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
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
280 * format:
281 * contains the formatting(single) character
283 static int
284 validfmt(char *fmt, cmn_fmt_t *cfstr)
286 int isll = 0;
287 int *fwidth, *cwidth;
288 char *format;
289 char *dig1, *dig2;
290 char cdigs[CMN_MAX_DIGITS+1];
292 if (cfstr == NULL)
293 return (-1);
295 fwidth = &cfstr->fwidth;
296 cwidth = &cfstr->cwidth;
297 format = &cfstr->format;
298 *fwidth = *cwidth = 0;
299 *format = NULL;
300 dig1 = dig2 = NULL;
302 /* check for left justification character */
303 if (*fmt == '-') {
304 fmt++;
305 (*fwidth)++;
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)++;
314 /* if ljust specified w/o size, return format error */
315 if (*fwidth == 1) {
316 return (1);
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)++;
327 dig2 = fmt; /* save ptr to last digit + 1 */
331 /* if a column width was specified, save it in caller's struct */
332 if (dig1) {
333 int nbytes;
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);
344 /* check for long format specifier */
345 if (*fmt == 'l') {
346 fmt++;
347 (*fwidth)++;
348 isll = 1;
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 */
371 if (isll) {
372 *fwidth *= -1;
374 return (0);
378 * fmt_args()
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.
393 static void
394 fmt_args(fcode_env_t *env, int cw, int fw, char format, long *arg,
395 long long *llarg)
397 char *cbuf;
398 char snf[3];
399 int cbsize;
400 int cnv = 10, ndigits = 0;
402 if (fw > 0) { /* check for normal (not long) formats */
404 /* initialize format string for snprintf call */
405 snf[0] = '%';
406 snf[1] = format;
407 snf[2] = 0;
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;
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);
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++;
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);
455 free(cbuf);
458 } else { /* process long formats */
460 *llarg = POP(DS);
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);
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;
493 while ((cbuf[ndigits] != NULL) &&
494 (ndigits < cbsize)) {
495 ndigits++;
497 /* if truncation is necessary, do it */
498 if (ndigits > cw) {
499 cbuf[cw] = 0;
500 *llarg = strtoll(cbuf, (char **)NULL, cnv);
502 free(cbuf);
508 * fmt_str()
510 * Extracts text from caller's input buffer, processes explicit
511 * formatting as necessary, and outputs formatted text to caller's
512 * receptacle.
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
526 * text.
529 static void
530 fmt_str(fcode_env_t *env, char *fmt, char *fmtbuf, int bsize)
532 char tbuf[CMN_MSG_SIZE];
533 char *fmptr, *pct;
534 int l, cw, fw, bytes;
535 long arg;
536 long long llarg;
538 *fmtbuf = 0;
539 if ((pct = strchr(fmt, '%')) != 0) {
540 cmn_fmt_t cfstr;
541 int vferr;
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;
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;
563 log_message(MSG_ERROR,
564 "fmt_str: invalid format type! (%s)\n",
565 tbuf+bytes-3);
567 strncpy(fmtbuf, tbuf, bsize);
568 return;
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;
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 */
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;
618 } else /* process long formats */
619 (void) snprintf(fmtbuf, bsize, tbuf, llarg);
621 } else
622 strncpy(fmtbuf, fmt, bsize);
626 * fc_cmn_append()
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
634 * its existence.
637 void
638 fc_cmn_append(fcode_env_t *env)
640 int len;
641 char *str;
643 if (root == NULL) {
644 log_message(MSG_ERROR,
645 "fc_cmn_append: no message context for append\n");
646 return;
649 len = POP(DS);
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 -
654 root->len);
655 root->len += len;
656 } else
657 log_message(MSG_ERROR,
658 "fc_cmn_append: append exceeds max msg size\n");
662 * fc_cmn_end()
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.
675 void
676 fc_cmn_end(fcode_env_t *env)
678 cmn_msg_t *old;
680 if (root == 0) {
681 log_message(MSG_ERROR, "]cmn-end call w/o buffer\n");
682 return;
685 fc_cmn_append(env);
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;
703 * fc_cmn_start()
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
714 static void
715 fc_cmn_start(fcode_env_t *env, char *head, int path)
717 cmn_msg_t *new;
718 char *dpath;
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);
734 new->level = cmn_msg_level;
735 new->next = NULL;
736 root = new;
740 * fc_cmn_type()
742 * Process cmn-type[ token.
744 * Invokes fc_cmn_start() to create a message containing blank
745 * header and no device path information.
747 void
748 fc_cmn_type(fcode_env_t *env)
750 cmn_msg_level = MSG_INFO;
751 fc_cmn_start(env, "", 0);
755 * fc_cmn_msg()
757 * Process cmn-msg[ token.
759 * Invokes fc_cmn_start() to create a message containing blank
760 * header but specifying device path information.
762 void
763 fc_cmn_msg(fcode_env_t *env)
766 cmn_msg_level = MSG_INFO;
767 fc_cmn_start(env, "", 1);
771 * fc_cmn_note()
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.
778 void
779 fc_cmn_note(fcode_env_t *env)
781 cmn_msg_level = MSG_NOTE;
782 fc_cmn_start(env, "NOTICE: ", 1);
786 * fc_cmn_warn()
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.
793 void
794 fc_cmn_warn(fcode_env_t *env)
796 cmn_msg_level = MSG_WARN;
797 fc_cmn_start(env, "WARNING: ", 1);
801 * fc_cmn_error()
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.
808 void
809 fc_cmn_error(fcode_env_t *env)
811 cmn_msg_level = MSG_ERROR;
812 fc_cmn_start(env, "ERROR: ", 1);
816 * fc_cmn_fatal()
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.
823 void
824 fc_cmn_fatal(fcode_env_t *env)
826 cmn_msg_level = MSG_FATAL;
827 fc_cmn_start(env, "FATAL: ", 1);
830 #pragma init(_init)
832 static void
833 _init(void)
835 fcode_env_t *env = initial_env;
836 ASSERT(env);
837 NOTICE;
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);