/**************************************************************
 *
 *	CRISP - Custom Reduced Instruction Set Programmers Editor
 *
 *	(C) Paul Fox, 1989, 1990, 1991
 *
 *    Please See COPYLEFT notice.
 *
 **************************************************************/
# include	"list.h"
# include	"clk.h"

int	switch_level;
int	imode = TRUE;
int	breaksw_flag;
int	break_flag;
int	cont_flag;
int	loop_count;
int	tab_char = TRUE;
int	doing_selfinsert;		/* Used by undo collapser.	*/
static char	cwd[80];

color_t	tmp_col_table;
extern char	character;

char	*color_names[] = {
	"black",
	"blue",
	"green",
	"cyan",
	"red",
	"magenta",
	"brown",
	"white",
	"dark-grey",
	"light-blue",
	"light-green",
	"light-cyan",
	"light-red",
	"light-magenta",
	"light-yellow",
	"light-white",
	NULL
	};

struct	colp {
	int	max;
	u_int16	*arg;
	char	*msg;
	} col_msg[] = {
		16,	&tmp_col_table.c_background,	"dbackground",
		16,	&tmp_col_table.c_normal,	"dforeground",
		16,	&tmp_col_table.c_select,        "dselected title",
		16,	&tmp_col_table.c_messages,     	"dnormal message",
		16,	&tmp_col_table.c_error, 	"derror message",
		256,	&tmp_col_table.c_fg,		"xhighlight",
		0
		};

/**********************************************************************/
/*   Prototypes							      */
/**********************************************************************/
char	*getcwd();
int	stat();
void	inq_called();
void	set_calling_name();
void	del();
void	set_top_left();
void	del_to_eol();
void	color();
void	inq_mode();
void	inq_macro();
void	ins_mode();
void	exist();
void	set_msg_level();
void	inq_msg_level();
void	del_char();
void	backspace();
void	del_line();
void	exec_cmd();
void	get_parm();
void	put_parm();
char *	get_cwd();
void	inq_names();
void	goto_line();
void	goto_old_line();
void	nothing();
void	do_while();
void	do_break();
void	do_continue();
void	do_switch();
void	do_if();
void	do_tabs();
void	move_rel();
void	move_abs();
void	insert();
void	selfinsert();
void	do_abort();
void	do_version();
void	returns();
void	inq_kbd_char();
void	read_char PROTO((void));
void	first_time();
void	do_upper();
void	do_lower();
void	inq_environment();
void	do_read();
void	inq_position();
void	do_getwd();
void	do_cd PROTO((void));
void	redraw();
void	do_getpid();
void	inq_system();
void	do_time();
void	do_date();
void	use_tab_char();
void	inq_line_length();
void	do_sleep();
int	get_color_name PROTO((char *));

void
inq_called()
{
	acc_assign_str(ms_cnt <= 1 ? "" : mac_stack[ms_cnt-2].name, -1);
}
void
set_calling_name()
{	char *cp = get_str(1);
	mac_stack[ms_cnt-1].name = strdup(cp);
	
}
void
remove_file()
{	long	ret;

	ret = system_call(unlink(get_str(1)));
	acc_assign_int((long)ret);
}

void
del_to_eol()
{	LINE	*lp = linep(*cur_line);
	int	col = current_offset(*cur_col, FALSE);
	RSIZE	n = llength(lp) - col;
	
	if (n > 0)
		ldelete(n);
}
/**********************************************************************/
/*   Macro to get the current screen colors.			      */
/**********************************************************************/
void
get_color()
{
	argv_assign(1, (long) col_table.c_background);
	argv_assign(2, (long) col_table.c_normal);
	argv_assign(3, (long) col_table.c_select);
	argv_assign(4, (long) col_table.c_messages);
	argv_assign(5, (long) col_table.c_error);
	argv_assign(6, (long) col_table.c_bg);
	argv_assign(7, (long) col_table.c_fg);
}
/**********************************************************************/
/*   Macro to set the current screen colors.			      */
/**********************************************************************/
void
color()
{	register struct colp *colp;
	int	col = 1;
	int	ask = argv[1].l_flags == F_NULL;
	char	buf[80];
	char	buf1[80];
	int	val;
	color_t	old_table;
	
	/***********************************************/
	/*   Remember  current  color  settings so we  */
	/*   can   avoid   redrawing   screen  if  no  */
	/*   changes made to color.		       */
	/***********************************************/
	old_table = col_table;
	tmp_col_table = col_table;
	tmp_col_table.c_fg = (col_table.c_bg << 4) | col_table.c_fg;
	for (colp = col_msg; colp->msg; colp++, col++) {
		char	*msg = colp->msg;
		int	hex = *msg++ == 'x';
		if (ask) {
			val = *colp->arg;
			sprintf(buf1, "Enter %s color number <", msg);
			sprintf(buf1+strlen(buf1), hex ? "0x%02x>: " : "%d>: ", val);
			if (ereply(buf1, buf, sizeof buf) != TRUE)
				return;
			if (buf[0] != NULL) {
				if (isdigit(buf[0])) {
					if (hex)
						sscanf(buf, "%x", &val);
					else
						val = atoi(buf);
					}
				else
					val = get_color_name(buf);
				}
			}
		else {
			if (argv[col].l_flags == F_INT)
				val = argv[col].l_int;
			else
				val = get_color_name(get_str(col));
			}
		if (val >= colp->max) {
			errorf("Invalid color: must be 0-%d.", colp->max-1);
			return;
			}
		if (val >= 0)
			*colp->arg = val;
		}
	if (argv[7].l_flags != F_NULL) {
		tmp_col_table.c_fg = get_color_name(get_str(7)) |
			(tmp_col_table.c_fg << 4);
		}
	col_table = tmp_col_table;
	col_table.c_bg = (col_table.c_fg >> 4) & 7;
	col_table.c_fg &= 0x0f;
	
	if (old_table.c_background != col_table.c_background ||
	    old_table.c_normal != col_table.c_normal ||
	    old_table.c_select != col_table.c_select ||
	    old_table.c_messages != col_table.c_messages ||
	    old_table.c_error != col_table.c_error) {
		vtblanks();
		ewprintf("");
		screen_garbled = TRUE;
		}
}
int
get_color_name(buf)
char	*buf;
{	register int val;

	for (val = 0; color_names[val]; val++) {
		if (str_icmp(color_names[val], buf) == 0)
			break;
		}
	return val;
}
void
inq_mode()
{
	acc_assign_int((long) imode);
}
void
inq_macro()
{	

	acc_assign_int((long) (lookup_macro(get_str(1)) != NULL));
}
void
ins_mode()
{
	acc_assign_int((long) imode);
	if (argv[1].l_flags == F_NULL)
		imode = !imode;
	else
		imode = (int) argv[1].l_flags;
	if (scrfn.scr_cursor == NULL)
		line_col(TRUE);
	print_cursor(imode);
}
void
exist()
{	struct stat stat_buf;

	if (system_call(stat(get_str(1), &stat_buf)) < 0)
		acc_assign_int((long) 0);
	else
		acc_assign_int((long) 1);
}
void
set_msg_level()
{
	msg_level = (int) argv[1].l_int;
}
void
inq_msg_level()
{
	acc_assign_int((long) msg_level);
}
void
del_char()
{	int n = argv[1].l_flags == F_INT ? argv[1].l_int : 1;

	if (n > 0) {
		current_offset(*cur_col, TRUE);
		ldelete((RSIZE) n);
		}
}
void
backspace()
{

	if (imode) {
		argv[1].l_flags = F_INT;
		argv[1].l_int = 1;
		prev_char();
		if (acc_get_ival() == 0)
			return;
		ldelete((RSIZE) 1);
		}
	else {
		backchar(1);
		if (!tab_replace())
			ldelete((RSIZE) 1);
		if (!imode) {
			l_insert(' ');
			backchar(1);
			}
		}
}
void
del_line()
{	LINE	*lp = linep(*cur_line);
	int	saved_col = *cur_col;
	u_dot();
	*cur_col = 1;
	ldelete((RSIZE) (llength(lp) + 1));
	*cur_col = saved_col;
}
void
exec_cmd()
{	char	*cp;
	LIST	list[16];
	char	buf[BUFSIZ];
	char	*arg;

	if (argv[1].l_flags == F_NULL) {
		if ((cp = get_arg1("Command: ", buf, sizeof buf)) == NULL)
			return;
		ewputs("", (char *) NULL);
		str_exec(cp);
		return;
		}
	arg = get_str(1);
	if (arg[0] == NULL)
		return;
	if (strchr(arg, ' ')) {
		str_exec(arg);
		return;
		}
	nest_level++;
	list[0] = F_STR;
	LPUT32(list, (long) arg);
	list[5] = F_HALT;
	exec1(list, argv[2].l_list ? argv[2].l_list : &list[5]);
	delete_local_symbols();
}
void
get_parm()
{	LIST *next_atom();
	int	length = 0;
	char	reply[80];
	SYMBOL	*sp = argv[2].l_sym;
	LIST	*lp;
	SPTREE	*saved_level;
	int	i;
	char	*def = "";
	char	numbuf[10];
	LISTV	lpv;
	

	acc_assign_int((long) 0);
	length = argv[4].l_flags == F_NULL ? sizeof reply - 1 : argv[4].l_int;
	if (sp->s_type == F_STR)
		def = get_str(5);
	else
		sprintf(def = numbuf, "%ld", argv[4].l_int);

	if (argv[1].l_flags == F_NULL) {
re_prompt:
		if (argv[3].l_flags == F_NULL)
			return;
		if (edefreply(get_str(3), def, reply, length) != TRUE) {
			acc_assign_int((long) 0);
			return;
			}
		acc_assign_int((long) 1);
		if (sp->s_type == F_STR) {
			reply[length] = NULL;
			str_assign(sp, reply);
			}
		else
			sscanf(reply, "%ld", &sp->s_int);
		return;

		}
	i = (int) argv[1].l_int;
	for (lp = mac_stack[ms_cnt-1].argv; lp && i > 0 && *lp != F_HALT; i--)
		lp = next_atom(lp);
	if (lp == NULL || *lp == F_HALT)
		goto re_prompt;

	saved_level = lsym_tbl[nest_level];
	lsym_tbl[nest_level--] = spinit();
	eval(lp, &lpv);
	spfree(lsym_tbl[++nest_level]);
	lsym_tbl[nest_level] = saved_level;

	if (lpv.l_flags == F_NULL)
		return;
		
	argv[2] = lpv;
	com_equ1(NOOP, sp);
	acc_assign_int((long) 1);
}
void
put_parm()
{	LIST	*lp;
	int	i = (int) argv[1].l_int;
	SYMBOL	*sp;

	acc_assign_int((long) 0);
	for (lp = mac_stack[ms_cnt-1].argv; i-- > 0 && *lp != F_HALT; )
		if (*lp == F_LIST)
			lp += LGET16(lp);
		else
			lp += sizeof_atoms[*lp];
	if (*lp != F_HALT && *lp == F_STR) {
		SPTREE *saved_level = lsym_tbl[nest_level];
		lsym_tbl[nest_level--] = spinit();
		sp = lookup((char *) LGET32(lp));
		spfree(lsym_tbl[++nest_level]);
		lsym_tbl[nest_level] = saved_level;
		if (sp->s_type == F_INT && argv[2].l_flags == F_INT) {
			acc_assign_int((long) 1);
			int_assign(sp, argv[2].l_int);
			}
		else if (sp->s_type == F_STR && 
		      (argv[2].l_flags == F_STR ||
		       argv[2].l_flags == F_RSTR ||
		       argv[2].l_flags == F_LIT)) {
			acc_assign_int((long) 1);
			str_assign(sp, get_str(2));
			}
		}
}
char	*
get_cwd()
{

	if (cwd[0])
		return cwd;
	getcwd(cwd, sizeof cwd);
	backslash_to_fwd(cwd);
# if defined(MONOCASE_FILENAMES)
	lower_case(cwd);
# endif
	return cwd;
}
void
inq_names()
{	char	*cp;
	char	*ext;
	BUFFER	*bp = curbp;
	
	if (argv[4].l_flags == F_INT) {
		bp = numberb((int) argv[4].l_int);
		}
	if (bp == NULL)
		return;
	cp = bp->b_fname;
	ext = strrchr(cp, '.');

	if (argv[1].l_flags != F_NULL)
		str_assign(argv[1].l_sym, cp);
	if (argv[2].l_flags != F_NULL)
		str_assign(argv[2].l_sym, ext ? ext+1 : "");
	if (argv[3].l_flags != F_NULL)
		str_assign(argv[3].l_sym, bname(cp));
		
}
void
goto_line()
{	long	l;
	int	old_line = *cur_line;

	if (get_iarg1("Go to line: ", &l)) {
		acc_assign_int((long) -1);
		return;
		}

	gotoline((int) l);
	acc_assign_int((long) (old_line != *cur_line));
}
void
goto_old_line()
{	long	l;
	register u_int16	i;
	int	line_no = 1;
	register LINE	*lp;
	LINE	*target_lp = NULL;

	if (get_iarg1("Go to old line: ", &l))
		return;
	i = (u_int16) l;
	if (i == 0)
		i = 1;
	acc_assign_int((long) 1);
	for (lp = lforw(curbp->b_linep); lp != curbp->b_linep; lp = lforw(lp)) {
		if (lp->l_lineno == i) {
got_target:
			u_dot();
			win_modify(WFMOVE);
			*cur_line = line_no;
			win_modify(WFMOVE);
			return;
			}
		if (lp->l_lineno > i)
			target_lp = lp;
		line_no++;
		}
	if (target_lp) {
		line_no = target_lp->l_lineno;
		goto got_target;
		}

	gotoline((int) l);
}
void
nothing()
{
}
void
do_do()
{	LISTV	condition;
	LISTV	statement;
	LISTV	result;
	extern int doing_return;

	loop_count++;	
	statement = argv[1];
	condition = argv[2];

	break_flag = FALSE;
	cont_flag = FALSE;
	do {
		if (statement.l_flags != F_NULL)
			eval_expr(&statement);
		if (cont_flag)
			cont_flag = break_flag = FALSE;
		}
	while (!doing_return && !break_flag &&
		eval(condition.l_list, &result) == F_INT && result.l_int);
	loop_count--;
	break_flag = cont_flag = FALSE;
}
void
do_loop(cond_index, post_index, stmt_index)
int	cond_index;
int	post_index;
int	stmt_index;
{	LISTV	condition;
	LISTV	statement;
	LISTV	post_op;
	LISTV	result;
	extern int doing_return;

	loop_count++;

	condition.l_flags = F_NULL;
	post_op.l_flags = F_NULL;
	statement.l_flags = F_NULL;
	if (cond_index)
		condition = argv[cond_index];
	if (post_index)
		post_op = argv[post_index];
	if (stmt_index)
		statement = argv[stmt_index];

	break_flag = FALSE;
	cont_flag = FALSE;
	while (!doing_return && !break_flag) {
		switch (eval(condition.l_list, &result)) {
		  case F_INT:
		  	if (result.l_int == 0)	
				goto end_of_loop;
			break;
		  case F_FLOAT:
		  	if (result.l_float == 0)	
				goto end_of_loop;
			break;
		  default:
		  	goto end_of_loop;
		  }
		if (statement.l_flags != F_NULL) {
			eval_expr(&statement);
			if (doing_return)
				break;
			if (cont_flag)
				;
			else if (break_flag)
				break;
			}
		if (post_op.l_flags != F_NULL)
			eval(post_op.l_list, &result);
		if (cont_flag)
			cont_flag = break_flag = FALSE;
		}
end_of_loop:
	loop_count--;
	break_flag = cont_flag = FALSE;
}
void
do_for()
{
	do_loop(2, 3, 4);
}
void
do_while()
{
	do_loop(1, 0, 2);
}
void
do_break()
{
	if (loop_count <= 0) {
		errorf("break not executed in loop.");
		return;
		}
	break_flag = TRUE;
}
void
breaksw()
{
	if (switch_level <= 0) {
		errorf("breaksw not executed inside switch.");
		return;
		}
	break_flag = TRUE;
	breaksw_flag = TRUE;
}
void
do_continue()
{
	break_flag = TRUE;
	cont_flag = TRUE;
}

/**********************************************************************/
/*   Code to implement the (switch) statement.			      */
/**********************************************************************/
void
do_switch()
{	register LIST	*lp = argv[2].l_list;
	LISTV	result;
	char	*switch_item = get_str(1);
	int	wildcard = FALSE;
	LISTV	arg;
	OPCODE	type;

	arg = argv[1];
	/***********************************************/
	/*   Walk  down  the  switch statement trying  */
	/*   to    find   a   match   or   the   NULL  */
	/*   representing the default case.	       */
	/***********************************************/
	switch_level++;
	for (; lp && *lp != F_HALT; lp = next_atom(lp)) {
		if (wildcard || *lp == F_NULL) {
		    	lp = next_atom(lp);
		    	goto eval_it;
			}

		type = eval(lp, &result);
		if (breaksw_flag) {
			break;
			}
		lp = next_atom(lp);
		switch (arg.l_flags) {
		  case F_INT:
		  	if (type == F_INT) {
				if (arg.l_int != result.l_int)
					continue;
				}
			else if (type == F_FLOAT) {
				if (arg.l_float != result.l_float)
					continue;
				}
			break;
		  case F_FLOAT:
		  	if (type == F_INT) {
				if (arg.l_float != (double) result.l_int)
					continue;
				}
			else if (type == F_FLOAT) {
				if (arg.l_float != result.l_float)
					continue;
				}
			break;
		  case F_STR:
		  case F_LIT:
		  case F_RSTR:
			switch (type) {
			  case F_STR:
			  case F_LIT:
				if (strcmp(result.l_str, switch_item) != 0)
					continue;
				break;
			  case F_RSTR:
				if (strcmp(result.l_ref->r_ptr, switch_item) != 0)
					continue;
				break;
			  default:
			  	continue;
			  }
			break;
		  default:
			/***********************************************/
			/*   No match so we try next case statement.   */
			/***********************************************/
			continue;
		  }
			
		/***********************************************/
		/*   Here  we've  found  a  match  so execute  */
		/*   the  statement  list  if  there  is one)  */
		/*   associated with this case statement.      */
		/***********************************************/
eval_it:
		if (lp && *lp != F_HALT) {
			if (*lp == F_NULL) {
				wildcard = TRUE;
				continue;
				}
			eval(lp, &result);
			}
		break;
		}
	breaksw_flag = break_flag = FALSE;
	switch_level--;
}
void
do_if()
{	LISTV	result;
	LIST	*lp = NULL;
	LIST	*lp_else = argc > 3 ? argv[3].l_list : NULL;
	char	*cp;

	switch (argv[1].l_flags) {
	  case F_INT:
		if (argv[1].l_int)
			lp = argv[2].l_list;
		else
			lp = lp_else;
		break;
	  case F_FLOAT:
		if (argv[1].l_float)
			lp = argv[2].l_list;
		else
			lp = lp_else;
		break;
	  case F_STR:
	  case F_LIT:
	  case F_RSTR:
	  	cp = get_str(1);
		if (*cp)
			lp = argv[2].l_list;
		else
			lp = lp_else;
		break;
	  default:
	  	break;
	  }
	if (lp) {
		if (eval(lp, &result) == F_RSTR) {
			/***********************************************/
			/*   Make  sure  theres  > 1 reference to the  */
			/*   result.  If  theres only one ref, it may  */
			/*   be  the  accumulators  but  eval doesn't  */
			/*   tell  us  how  it derived the result and  */
			/*   we  could  screw  up badly if we dont do  */
			/*   this.				       */
			/***********************************************/
			r_inc(result.l_ref);
			acc_assign_ref(result.l_ref);
			r_dec(result.l_ref);
			}
		}
}
void
do_tabs()
{	int	i;
	u_int16	t;
	int	prompting = argv[1].l_flags == F_NULL;
	int	tab_stop;

	if (prompting) {
		for (argc = 1; argc < NTABS; ) {
			char	buf[80];
			if (ereply("Enter tab stop (return terminates): ", 
			    buf, sizeof buf) != TRUE)
				return;
			if ((argv[argc].l_int = (long) atoi(buf)) == 0)
				break;
			argv[argc].l_flags = F_INT;
			argv[++argc].l_flags = F_NULL;
			}
		}

	curbp->b_tabs[0] = 0;
	for (i = 1, tab_stop = 1; i < argc && tab_stop < NTABS; i++)
		if (argv[i].l_flags != F_NULL && argv[i].l_int != 1) {
			t = (u_int16) argv[i].l_int;
			curbp->b_tabs[tab_stop-1] = t ? t - 1 : 0;
			curbp->b_tabs[tab_stop] = 0;
			tab_stop++;
			}
	win_modify(WFHARD);
}
static int	old_line;
static int	old_col;
void
move_rel()
{	
	win_modify(WFMOVE);
	old_line = *cur_line;
	old_col = *cur_col;
	*cur_line += argv[1].l_int;
	*cur_col += argv[2].l_int;
	check_bounds();
}
void
check_bounds()
{
	if (*cur_col < 1)
		*cur_col = 1;
	if (*cur_line > curbp->b_numlines + (int) nrow - 3)
		*cur_line = curbp->b_numlines + (int) nrow - 3;
	else if (*cur_line < 1)
		*cur_line = 1;
	win_modify(WFMOVE);
	if (*cur_line != old_line || *cur_col != old_col)
		acc_assign_int((long) 1);
	else
		acc_assign_int((long) 0);
}
void
move_abs()
{
	win_modify(WFMOVE);
	old_line = *cur_line;
	old_col = *cur_col;
	if (argv[1].l_int)
		*cur_line = argv[1].l_int;
	if (argv[2].l_int)
		*cur_col = argv[2].l_int;

	check_bounds();
}
void
insert(proc)
int	proc;
{	char	*cp;
	long	n = argv[2].l_flags == F_INT ? argv[2].l_int : 1;
	int	len;
	char	buf;

	/***********************************************/
	/*   If   first   argument  is  numeric  then  */
	/*   insert  that  character  into  the line,  */
	/*   i.e. dont treat '\n' as a newline.	       */
	/***********************************************/
	if (argv[1].l_flags == F_INT) {
		buf = (char) argv[1].l_int;
		while (n-- > 0) {
			llinsert(&buf, 1, FALSE);
			}
		lchange(WFEDIT);
		return;
		}
	cp = get_str(1);
	len = strlen(cp);
	while (n-- > 0)
		if (proc)
			p_write(cp, len);
		else
			linsert(cp, len);
}
void
selfinsert()
{

	if (argv[1].l_flags == F_INT)
		character = argv[1].l_int;
	if (!imode) {
		if (character == '\t') {
			u_dot();
			*cur_col = next_tab_stop(*cur_col) + 1;
			return;
			}
		if (character == '\r' || character == '\n') {
			(void) forwline(1);	
			*cur_col = 1;
			return;
			}
		else {
			LINE	*lp = linep(*cur_line);
			if (!tab_replace())
				if (current_offset(*cur_col, FALSE) < lp->l_used)
					ldelete((RSIZE) 1);
			}
		}
	doing_selfinsert = TRUE;
	if (character == '\t' && (curbp->b_flag & BFTABS) == 0) {
		int t = next_tab_stop(*cur_col) - *cur_col;
		while (t-- >= 0)
			l_insert(' ');
		}
	else if (character == '\r' || character == '\n')
		lnewline();
	else 
		l_insert(character);
	doing_selfinsert = FALSE;
	curbp->b_uchar = character;
	trigger(REG_TYPED);
}
void
do_abort()
{
	ttclose();
	exit(1);
}
void
do_version()
{
	extern	char	*version;
	extern int major_version, minor_version, edit_version;

	if (argv[1].l_flags == F_NULL)
		ewprintf("%s", version);
	else
		int_assign(argv[1].l_sym, (long) major_version);
	argv_assign(2, (long) minor_version);
	argv_assign(3, (long) edit_version);
	acc_assign_int((long) major_version * 100 + minor_version);
}
void
returns(do_returns)
int	do_returns;
{	extern int doing_return;

	if (!do_returns)
		doing_return = TRUE;

	acc_assign_argv(&argv[1]);
}
void
inq_kbd_char()
{
	acc_assign_int((long) typeahead());
}
void
read_char()
{	extern int wait_flag;
	long	n = argv[1].l_flags == F_INT ? argv[1].l_int : 0;
	int	ret;
	
	update();
	/***********************************************/
	/*   Check if user wants a raw keystroke.      */
	/***********************************************/
	if (argv[2].l_flags == F_INT && argv[2].l_int != 0) {
		ret = get_rawkey(n);
		character = (char) ret;
		acc_assign_int(ret ? (long) ret : (long) -1L);
		return;
		}

	ret = getkey(wait_flag ? n : -1L);
	character = (char) ret;
	acc_assign_int((long) ret);
}
void
first_time()
{	MACRO	*mptr;

	if (ms_cnt == 0) {
		ewprintf("first_time: ms_cnt == 0 ?");
		return;
		}
	mptr = lookup_macro(mac_stack[ms_cnt-1].name);
	acc_assign_int((long) mptr->m_ftime);
}
void
do_upper()
{	register char	*cp;

	acc_assign_str(get_str(1), -1);
	cp = acc_get_sval();

	for (; *cp; cp++)
		if (*cp >= 'a' && *cp <= 'z')
			*cp -= 0x20;
}
void
do_lower()
{	register char	*cp;

	acc_assign_str(get_str(1), -1);
	cp = acc_get_sval();

	for (; *cp; cp++)
		if (*cp >= 'A' && *cp <= 'Z')
			*cp += 0x20;
}
void
do_getenv()
{	char	*cp;

	cp = ggetenv(get_str(1));
	acc_assign_str(cp ? cp : "", -1);
}
void
do_read()
{	int	l = 32767;
	char	*cp;
	LINE	*lp = vm_lock_line(*cur_line);
	int	offset = current_offset(*cur_col, FALSE);
	int	len = llength(lp) - offset;
	int	length_to_copy;

	if (argv[1].l_flags != F_NULL)
		l = (int) argv[1].l_int;

	if (l < 0)
		l = 1;
	length_to_copy = l > len ? len : l;
	if (length_to_copy) {
		acc_assign_str((char *) &lgetc(lp, offset), length_to_copy + 1);
		}
	else {
		acc_assign_str("\n", 1);
		}
	cp = acc_get_sval() + length_to_copy;
	vm_unlock(*cur_line);
	l -= length_to_copy;
	if (l > 0)
		*cp++ = '\n';
	*cp = NULL;
}
void
inq_position()
{
	argv_assign(1, (long) *cur_line);
	argv_assign(2, (long) *cur_col);

	if (*cur_line <= curbp->b_numlines)
		acc_assign_int((long) 0L);
	else
		acc_assign_int((long) (*cur_line - curbp->b_numlines));
}

void
do_getwd()
{
	str_assign(argv[2].l_sym, get_cwd());
	acc_assign_int((long) 1L);
}
void
do_cd()
{	
	char	buf[BUFSIZ];
	char *cp;
	int	ret, j;
	char	**files;
	char	*mem = NULL;
	int	free_flag = FALSE;
	char	*strpbrk();

	if (argv[1].l_flags == F_NULL) {
		ewprintf("%s", get_cwd());
		return;
		}
	cp = get_str(1);
	
	/***********************************************/
	/*   Perform     wildcard     and    variable  */
	/*   substitution on directory name.	       */
	/***********************************************/
	if (strpbrk(cp, "~*?[$")) {
		files = shell_expand(cp);
		if (files != NULL) {
			free_flag = TRUE;
			cp = NULL;
			for (j = 0; files[j]; j++) {
				if (cp == NULL && files[j][0])
					mem = cp = files[j];
				else
					chk_free(files[j]);
				}
			chk_free((void *) files);
			}
		}
	acc_assign_int((long) 0);
# if CR_DOS
	/***********************************************/
	/*   Need  to  check if we're changing drives  */
	/*   under DOS & OS/2.			       */
	/***********************************************/
	if (cp[0] && cp[1] == ':')
		set_current_drive(cp[0]);
		
	ret = chdir(cp+2);
# else
	ret = chdir(cp);
# endif
	if (ret) {
		strcpy(buf, cp);
		cp = buf+strlen(buf)-1;

		/***********************************************/
		/*   DOS  &  OS2  dont  like trailing slashes  */
		/*   in directory names.		       */
		/***********************************************/
		while (cp > buf + 1 && (*cp == '/' || *cp == '\\'))
			*cp-- = NULL;
		acc_assign_int((long) (chdir(buf) ? 1 : 0));
		}
	cwd[0] = NULL;
	if (free_flag && mem)
		chk_free((void *) mem);
}
void
redraw()
{
	screen_garbled = TRUE;
	update();
}
void
do_getpid()
{
	acc_assign_int((long) getpid());
}
void
inq_system()
{
	acc_assign_int((long) (curbp->b_flag & BF_SYSBUF));
}
# include	<time.h>
char	*days[] = {
	"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
	"Saturday"};
char	*months[] = {
	"January", "February", "March", "April", "May", "June", 
	"July", "August", "September", "October", "November", "December"
	};

extern long time();
void
do_time()
{	struct tm *tp;
	long	l = time((long *) 0);

	tp = localtime(&l);
	argv_assign(1, (long) tp->tm_hour);
	argv_assign(2, (long) tp->tm_min);
	argv_assign(3, (long) tp->tm_sec);
	acc_assign_int(l);
}
void
do_date()
{	struct tm *tp;
	long	l = time((long *) 0);

	tp = localtime(&l);
	
	argv_assign(1, (long) (tp->tm_year + 1900));
	argv_assign(2, (long) (tp->tm_mon + 1));
	argv_assign(3, (long) tp->tm_mday);
	if (argv[4].l_flags != F_NULL)
		str_assign(argv[4].l_sym, months[tp->tm_mon]);
	if (argv[5].l_flags != F_NULL)
		str_assign(argv[5].l_sym, days[tp->tm_wday]);

}
void
use_tab_char()
{	char	*cp;
	char	buf[32];

	acc_assign_int((long) tab_char);
	if (argv[1].l_flags == F_INT)
		return;
	if ((cp = get_arg1("Fill with tab chars? ", buf, sizeof buf)) == NULL)
		return;
	tab_char = *cp == 'y' || *cp == 'Y';
	if (tab_char)
		curbp->b_flag |= BFTABS;
	else
		curbp->b_flag &= ~BFTABS;
}
void
inq_line_length()
{	int	i, saved_line;
	extern int start_line, end_line;
	int	line_length = 0;
	BUFFER	*bp = argv[1].l_flags == F_INT 
			? numberb((u_int16) argv[1].l_int) 
			: curbp;
	BUFFER	*saved_curbp = curbp;
	int	wline = 0;
	int	woldline = 0;

	/***********************************************/
	/*   Dont   reposition   buffer   in  current  */
	/*   window.				       */
	/***********************************************/
	if (curwp) {
		wline = curwp->w_line;
		woldline = curwp->w_old_line;
		}
	if (bp == NULL) {
		acc_assign_int(-1L);
		return;
		}
	curbp = bp;
	set_hooked();
	if (get_marked_areas((WINDOW *) NULL) == FALSE) {
		start_line = 1;
		end_line = curbp->b_numlines - 1;
		}
	saved_line = *cur_line;

	for (i = start_line; i <= end_line; i++) {
		int	len;
		LINE	*lp = vm_lock_line(i);

		*cur_line = i;
		len = current_col(llength(lp));
		if (len > line_length)
			line_length = len;
		}

	acc_assign_int((long) line_length);
	curbp = saved_curbp;
	set_hooked();
	*cur_line = saved_line;
	
	/***********************************************/
	/*   Make  sure  buffer is repositioned where  */
	/*   it was.				       */
	/***********************************************/
	if (curwp) {
		curwp->w_line = wline;
		curwp->w_old_line = woldline;
		}
}
/**********************************************************************/
/*   Macro called to put CRISP to sleep for a bit.		      */
/**********************************************************************/
void
do_sleep()
{	long	n = argv[1].l_flags == F_INT ? argv[1].l_int : 1;

	if (n == 0)
		return;
	
	getkey(n SECONDS);

}

/**********************************************************************/
/*   Macro  to  return  current  CPU  time  used  by  CRISP. Time is  */
/*   returned in microseconds.					      */
/**********************************************************************/
void
inq_clock()
{
# if !CR_ACORN
	long	clock();
# endif

	acc_assign_int(clock());
}

/**********************************************************************/
/*   Function called for unimplemented macros.			      */
/**********************************************************************/
void
unimp()
{	extern char *command_name;

	ewprintf("*** %s not yet implemented.", command_name);
}

/**********************************************************************/
/*   Function  to  return  a  random  number.  Tries  to be SysV/BSD  */
/*   independent  by  keeping  to 32 bit random numbers. This random  */
/*   number  generator  needs  to  be  tested for passing reasonable  */
/*   random  numbers.  It should be sufficient for games but may not  */
/*   be any good for statistics.				      */
/**********************************************************************/
int	srand_called = FALSE;
void
do_rand()
{
	long	value;

	/***********************************************/
	/*   If  we  haven't  done  an  srand()  then  */
	/*   make  sure  we  have a bit of randomness  */
	/*   between each invocation of CRISP.	       */
	/***********************************************/
	if (!srand_called) {
		srand_called = TRUE;
		srand(time((long *) NULL));
		}
	value = rand() << 21;
	value += rand() << 10;
	value += rand() >> 3;
	value &= 0x7fffffffL;
	acc_assign_int(argv[1].l_flags == F_INT ? value % argv[1].l_int : value);
	
}
/**********************************************************************/
/*   Function to set the seed for the random number generator.	      */
/**********************************************************************/
void
do_srand()
{
	srand_called = TRUE;
	if (argv[1].l_flags == F_INT) {
		srand(argv[1].l_int);
		}
	else
		srand(time((long *) NULL));
}
/*******************************************************************/
/*   input_mode -- enable or disable user from typing in	   */
/*   XON/XOFF or JOB control character.				   */
/*******************************************************************/
void
input_mode()
{
	acc_assign_int((long) 
		sys_enable_char(argv[1].l_int, argv[2].l_int));
}
int
str_icmp(p1, p2)
char	*p1;
char	*p2;
{	int	ch1, ch2;

	while (1) {
		if (*p1 == NULL && *p2 == NULL)
			return 0;
		ch1 = *p1++;
		ch2 = *p2++;
		if (isupper(ch1))
			ch1 += 0x20;
		if (isupper(ch2))
			ch2 += 0x20;
		if (ch1 == ch2)
			continue;
		return -1;
		}
}
