/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2000 by Sun Microsystems, Inc.
* All rights reserved.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <setjmp.h>
#include <sys/stat.h>
#include <fcode/private.h>
#include <fcode/log.h>
void (*to_ptr)(fcode_env_t *env) = do_set_action;
jmp_buf *jmp_buf_ptr = NULL;
char *
parse_a_string(fcode_env_t *env, int *lenp)
{
parse_word(env);
return (pop_a_string(env, lenp));
}
void
constant(fcode_env_t *env)
{
char *name;
int len;
name = parse_a_string(env, &len);
env->instance_mode = 0;
make_common_access(env, name, len, 1, 0,
&do_constant, &do_constant, NULL);
}
void
buffer_colon(fcode_env_t *env)
{
char *name;
int len;
PUSH(DS, 0);
name = parse_a_string(env, &len);
make_common_access(env, name, len, 2,
env->instance_mode, &noop, &noop, &set_buffer_actions);
}
void
value(fcode_env_t *env)
{
char *name;
int len;
name = parse_a_string(env, &len);
make_common_access(env, name, len, 1,
env->instance_mode, &noop, &noop, &set_value_actions);
}
void
variable(fcode_env_t *env)
{
char *name;
int len;
PUSH(DS, 0);
name = parse_a_string(env, &len);
make_common_access(env, name, len, 1,
env->instance_mode, &instance_variable, &do_create, NULL);
}
void
defer(fcode_env_t *env)
{
static void (*crash_ptr)(fcode_env_t *env) = do_crash;
char *name;
int len;
PUSH(DS, (fstack_t)&crash_ptr);
name = parse_a_string(env, &len);
make_common_access(env, name, len, 1,
env->instance_mode, &noop, &noop, &set_defer_actions);
}
void
field(fcode_env_t *env)
{
char *name;
int len;
over(env);
name = parse_a_string(env, &len);
make_common_access(env, name, len, 1, 0, &do_field, &do_field, NULL);
add(env);
}
void
bye(fcode_env_t *env)
{
exit(0);
}
void
do_resume(fcode_env_t *env)
{
if (env->interactive) env->interactive--;
COMPLETE_INTERRUPT;
}
/*
* In interactive mode, jmp_buf_ptr should be non-null.
*/
void
return_to_interact(fcode_env_t *env)
{
if (jmp_buf_ptr)
longjmp(*jmp_buf_ptr, 1);
}
void
do_interact(fcode_env_t *env)
{
int level;
jmp_buf jmp_env;
jmp_buf *ojmp_ptr;
error_frame new;
input_typ *old_input = env->input;
log_message(MSG_INFO, "Type resume to return\n");
env->interactive++;
level = env->interactive;
ojmp_ptr = jmp_buf_ptr;
jmp_buf_ptr = &jmp_env;
env->input->separator = ' ';
env->input->maxlen = 256;
env->input->buffer = MALLOC(env->input->maxlen);
env->input->scanptr = env->input->buffer;
if (setjmp(jmp_env)) {
if (in_forth_abort > 1) {
RS = env->rs0;
DS = env->ds0;
MYSELF = 0;
IP = 0;
env->input = old_input;
env->order_depth = 0;
} else {
RS = new.rs;
DS = new.ds;
MYSELF = new.myself;
IP = new.ip;
env->input = old_input;
}
do_forth(env);
do_definitions(env);
in_forth_abort = 0;
} else {
new.rs = RS;
new.ds = DS;
new.myself = MYSELF;
new.ip = IP;
}
while (env->interactive == level) {
int wlen;
char *p;
DEBUGF(SHOW_RS, output_return_stack(env, 0, MSG_FC_DEBUG));
DEBUGF(SHOW_STACK, output_data_stack(env, MSG_FC_DEBUG));
#define USE_READLINE
#ifdef USE_READLINE
{
char *line;
void read_line(fcode_env_t *);
read_line(env);
if ((line = pop_a_string(env, NULL)) == NULL)
continue;
env->input->scanptr = strcpy(env->input->buffer, line);
}
#else
if (isatty(fileno(stdin)))
printf("ok ");
env->input->scanptr = fgets(env->input->buffer,
env->input->maxlen, stdin);
if (feof(stdin))
break;
if (env->input->scanptr == NULL)
continue;
#endif
if ((p = strpbrk(env->input->scanptr, "\n\r")) != NULL)
*p = '\0';
if ((wlen = strlen(env->input->scanptr)) == 0)
continue;
PUSH(DS, (fstack_t)env->input->buffer);
PUSH(DS, wlen);
evaluate(env);
}
jmp_buf_ptr = ojmp_ptr;
FREE(env->input->buffer);
}
static void
temp_base(fcode_env_t *env, fstack_t base)
{
fstack_t obase;
obase = env->num_base;
env->num_base = base;
parse_word(env);
evaluate(env);
env->num_base = obase;
}
static void
temp_decimal(fcode_env_t *env)
{
temp_base(env, 10);
}
static void
temp_hex(fcode_env_t *env)
{
temp_base(env, 0x10);
}
static void
temp_binary(fcode_env_t *env)
{
temp_base(env, 2);
}
static void
do_hex(fcode_env_t *env)
{
env->num_base = 0x10;
}
static void
do_decimal(fcode_env_t *env)
{
env->num_base = 10;
}
static void
do_binary(fcode_env_t *env)
{
env->num_base = 2;
}
static void
do_clear(fcode_env_t *env)
{
DS = env->ds0;
}
static void
action_one(fcode_env_t *env)
{
do_tick(env);
if (env->state) {
COMPILE_TOKEN(&to_ptr);
} else {
PUSH(DS, 1);
perform_action(env);
}
}
void
do_if(fcode_env_t *env)
{
branch_common(env, 1, 1, 0);
}
void
do_else(fcode_env_t *env)
{
branch_common(env, 1, 0, 1);
bresolve(env);
}
void
do_then(fcode_env_t *env)
{
bresolve(env);
}
void
do_of(fcode_env_t *env)
{
branch_common(env, 0, 2, 0);
}
void
load_file(fcode_env_t *env)
{
int fd;
int len, n;
char *name;
char *buffer;
struct stat buf;
CHECK_DEPTH(env, 2, "load-file");
name = pop_a_string(env, &len);
log_message(MSG_INFO, "load_file: '%s'\n", name);
fd = open(name, O_RDONLY);
if (fd < 0) {
forth_perror(env, "Can't open '%s'", name);
}
fstat(fd, &buf);
len = buf.st_size;
buffer = MALLOC(len);
if (buffer == 0)
forth_perror(env, "load_file: MALLOC(%d)", len);
if ((n = read(fd, buffer, len)) < 0)
forth_perror(env, "read error '%s'", name);
close(fd);
PUSH(DS, (fstack_t)buffer);
PUSH(DS, (fstack_t)n);
}
void
load(fcode_env_t *env)
{
parse_word(env);
if (TOS > 0)
load_file(env);
}
void
fevaluate(fcode_env_t *env)
{
char *buffer;
int bytes, len;
two_dup(env);
buffer = pop_a_string(env, &len);
for (bytes = 0; bytes < len; bytes++) {
if ((buffer[bytes] == '\n') || (buffer[bytes] == '\r'))
buffer[bytes] = ' ';
}
evaluate(env);
}
void
fload(fcode_env_t *env)
{
char *buffer;
load(env);
two_dup(env);
buffer = pop_a_string(env, NULL);
fevaluate(env);
FREE(buffer);
}
#include <sys/termio.h>
#define MAX_LINE_BUF 20
static char *history_lines[MAX_LINE_BUF];
int num_lines = 0;
static void
add_line_to_history(fcode_env_t *env, char *line)
{
int i;
if (num_lines < MAX_LINE_BUF)
history_lines[num_lines++] = STRDUP(line);
else {
FREE(history_lines[0]);
for (i = 0; i < MAX_LINE_BUF - 1; i++)
history_lines[i] = history_lines[i + 1];
history_lines[MAX_LINE_BUF - 1] = STRDUP(line);
}
}
static void
do_emit_chars(fcode_env_t *env, char c, int n)
{
int i;
for (i = 0; i < n; i++)
do_emit(env, c);
}
static void
do_emit_str(fcode_env_t *env, char *str, int n)
{
int i;
for (i = 0; i < n; i++)
do_emit(env, *str++);
}
static char *
find_next_word(char *cursor, char *eol)
{
while (cursor < eol && *cursor != ' ')
cursor++;
while (cursor < eol && *cursor == ' ')
cursor++;
return (cursor);
}
static char *
find_prev_word(char *buf, char *cursor)
{
int skippedword = 0;
if (cursor == buf)
return (cursor);
cursor--;
while (cursor > buf && *cursor == ' ')
cursor--;
while (cursor > buf && *cursor != ' ') {
skippedword++;
cursor--;
}
if (skippedword && *cursor == ' ')
cursor++;
return (cursor);
}
void
redraw_line(fcode_env_t *env, char *prev_l, char *prev_cursor, char *prev_eol,
char *new_l, char *new_cursor, char *new_eol)
{
int len;
/* backup to beginning of previous line */
do_emit_chars(env, '\b', prev_cursor - prev_l);
/* overwrite new line */
do_emit_str(env, new_l, new_eol - new_l);
/* Output blanks to erase previous line chars if old line was longer */
len = max(0, (prev_eol - prev_l) - (new_eol - new_l));
do_emit_chars(env, ' ', len);
/* Backup cursor for new line */
do_emit_chars(env, '\b', len + (new_eol - new_cursor));
}
#define MAX_LINE_SIZE 256
static void
do_save_buf(char *save_buf, char *buf, int n)
{
n = max(0, min(n, MAX_LINE_SIZE));
memcpy(save_buf, buf, n);
save_buf[n] = '\0';
}
char prompt_string[80] = "ok ";
void
read_line(fcode_env_t *env)
{
char buf[MAX_LINE_SIZE+1], save_buf[MAX_LINE_SIZE+1];
char save_line[MAX_LINE_SIZE+1];
char *p, *cursor, *eol, *tp, *cp;
fstack_t d;
int saw_esc = 0, do_quote = 0, i, cur_line, len, my_line, save_cursor;
struct termio termio, savetermio;
if (!isatty(fileno(stdin))) {
fgets(buf, sizeof (buf), stdin);
push_string(env, buf, strlen(buf));
return;
}
printf(prompt_string);
fflush(stdout);
ioctl(fileno(stdin), TCGETA, &termio);
savetermio = termio;
termio.c_lflag &= ~(ICANON|ECHO|ECHOE|IEXTEN);
termio.c_cc[VTIME] = 0;
termio.c_cc[VMIN] = 1;
ioctl(fileno(stdin), TCSETA, &termio);
my_line = cur_line = num_lines;
save_buf[0] = '\0';
for (cursor = eol = buf; ; ) {
for (d = FALSE; d == FALSE; d = POP(DS))
keyquestion(env);
key(env);
d = POP(DS);
if (do_quote) {
do_quote = 0;
if ((cursor - buf) < MAX_LINE_SIZE) {
*cursor++ = d;
if (cursor > eol)
eol = cursor;
do_emit(env, d);
}
continue;
}
if (saw_esc) {
saw_esc = 0;
switch (d) {
default: /* Ignore anything else */
continue;
case 'b': /* Move backward one word */
case 'B':
tp = find_prev_word(buf, cursor);
if (tp < cursor) {
do_emit_chars(env, '\b', cursor - tp);
cursor = tp;
}
continue;
case 'f': /* Move forward one word */
case 'F':
tp = find_next_word(cursor, eol);
if (tp > cursor) {
do_emit_str(env, tp, tp - cursor);
cursor = tp;
}
continue;
case 'h': /* Erase from beginning of word to */
case 'H': /* just before cursor, saving chars */
d = CTRL('w');
break;
case 'd':
case 'D':
tp = find_next_word(cursor, eol);
if (tp <= cursor)
continue;
len = tp - cursor;
do_save_buf(save_buf, cursor, len);
memmove(cursor, tp, eol - tp);
redraw_line(env, buf, cursor, eol, buf, cursor,
eol - len);
eol -= len;
continue;
}
}
switch (d) {
default:
if ((cursor - buf) < MAX_LINE_SIZE) {
*cursor++ = d;
if (cursor > eol)
eol = cursor;
do_emit(env, d);
}
continue;
case CTRL('['): /* saw esc. character */
saw_esc = 1;
continue;
case CTRL('f'): /* move forward one char */
if (cursor < eol)
do_emit(env, *cursor++);
continue;
case CTRL('a'): /* cursor to beginning of line */
do_emit_chars(env, '\b', cursor - buf);
cursor = buf;
continue;
case CTRL('e'): /* cursor to end of line */
do_emit_str(env, cursor, eol - cursor);
cursor = eol;
continue;
case CTRL('n'): /* Move to next line in buffer */
case CTRL('p'): /* Move to previous line in buffer */
if (d == CTRL('p')) {
if (cur_line <= 0)
continue;
if (my_line == cur_line) {
do_save_buf(save_line, buf, eol - buf);
save_cursor = cursor - buf;
}
cur_line--;
} else {
if (cur_line >= num_lines)
continue;
cur_line++;
if (cur_line == num_lines) {
len = strlen(save_line);
redraw_line(env, buf, cursor, eol,
save_line, save_line + save_cursor,
save_line + len);
strcpy(buf, save_line);
eol = buf + len;
cursor = buf + save_cursor;
continue;
}
}
p = history_lines[cur_line];
len = strlen(p);
redraw_line(env, buf, cursor, eol, p, p, p + len);
strcpy(buf, history_lines[cur_line]);
cursor = buf;
eol = buf + len;
continue;
case CTRL('o'): /* Insert newline */
continue;
case CTRL('k'): /* Erase from cursor to eol, saving */
/* chars, at eol, joins two lines */
if (cursor == eol) {
if (cur_line >= num_lines)
continue;
if (cur_line == num_lines - 1) {
p = save_line;
len = strlen(save_line);
num_lines -= 1;
my_line = num_lines;
} else {
cur_line++;
p = history_lines[cur_line];
len = strlen(p);
}
len = min(len, MAX_LINE_SIZE - (eol - buf));
memcpy(eol, p, len);
redraw_line(env, buf, cursor, eol, buf, cursor,
eol + len);
eol += len;
continue;
}
do_save_buf(save_buf, cursor, eol - cursor);
redraw_line(env, buf, cursor, eol, buf, cursor,
cursor);
eol = cursor;
continue;
case CTRL('w'): /* Erase word */
tp = find_prev_word(buf, cursor);
if (tp == cursor)
continue;
len = cursor - tp;
do_save_buf(save_buf, tp, len);
memmove(tp, cursor, eol - cursor);
redraw_line(env, buf, cursor, eol, buf, cursor - len,
eol - len);
eol -= len;
cursor -= len;
continue;
case CTRL('u'): /* Erases line, saving chars */
do_save_buf(save_buf, buf, eol - buf);
redraw_line(env, buf, cursor, eol, buf, buf, buf);
cursor = buf;
eol = buf;
continue;
case CTRL('y'): /* Insert save buffer before cursor */
len = min(strlen(save_buf),
MAX_LINE_SIZE - (eol - buf));
if (len == 0)
continue;
memmove(cursor + len, cursor, eol - cursor);
memcpy(cursor, save_buf, len);
redraw_line(env, buf, cursor, eol, buf, cursor + len,
eol + len);
cursor += len;
eol += len;
continue;
case CTRL('q'): /* Quote next char */
do_quote = 1;
continue;
case CTRL('l'): /* Display edit buffer */
do_emit(env, '\n');
for (i = 0; i < num_lines; i++) {
do_emit_str(env, history_lines[i],
strlen(history_lines[i]));
do_emit(env, '\n');
}
redraw_line(env, buf, buf, buf, buf, cursor, eol);
continue;
case CTRL('r'): /* redraw line */
redraw_line(env, buf, cursor, eol, buf, cursor, eol);
continue;
case CTRL('c'): /* Exit script editor */
continue;
case CTRL('b'): /* backup cursor */
if (cursor <= buf)
continue;
cursor--;
do_emit(env, '\b');
continue;
case CTRL('h'): /* Backspace */
case 0x7f: /* DEL */
if (cursor <= buf)
continue;
memmove(cursor - 1, cursor, eol - cursor);
redraw_line(env, buf, cursor, eol, buf, cursor - 1,
eol - 1);
cursor--;
eol--;
continue;
case '\r':
case '\n':
*eol = '\0';
do_emit(env, '\n');
break;
}
break;
}
add_line_to_history(env, buf);
ioctl(fileno(stdin), TCSETA, &savetermio);
push_string(env, buf, strlen(buf));
}
static void
set_prompt(fcode_env_t *env)
{
char *prompt;
if ((prompt = parse_a_string(env, NULL)) != NULL)
strncpy(prompt_string, prompt, sizeof (prompt_string));
}
#pragma init(_init)
static void
_init(void)
{
fcode_env_t *env = initial_env;
ASSERT(env);
NOTICE;
FORTH(IMMEDIATE, "if", do_if);
FORTH(IMMEDIATE, "else", do_else);
FORTH(IMMEDIATE, "then", do_then);
FORTH(IMMEDIATE, "case", bcase);
FORTH(IMMEDIATE, "of", do_of);
FORTH(IMMEDIATE, "endof", do_else);
FORTH(IMMEDIATE, "endcase", bendcase);
FORTH(IMMEDIATE, "value", value);
FORTH(IMMEDIATE, "variable", variable);
FORTH(IMMEDIATE, "constant", constant);
FORTH(IMMEDIATE, "defer", defer);
FORTH(IMMEDIATE, "buffer:", buffer_colon);
FORTH(IMMEDIATE, "field", field);
FORTH(IMMEDIATE, "struct", zero);
FORTH(IMMEDIATE, "to", action_one);
FORTH(IMMEDIATE, "d#", temp_decimal);
FORTH(IMMEDIATE, "h#", temp_hex);
FORTH(IMMEDIATE, "b#", temp_binary);
FORTH(0, "decimal", do_decimal);
FORTH(0, "hex", do_hex);
FORTH(0, "binary", do_binary);
FORTH(0, "clear", do_clear);
FORTH(IMMEDIATE, "bye", bye);
FORTH(0, "interact", do_interact);
FORTH(IMMEDIATE, "resume", do_resume);
FORTH(0, "fload", fload);
FORTH(0, "load", load);
FORTH(0, "read-line", read_line);
FORTH(0, "set-prompt", set_prompt);
}