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


/**********************************************************************/
/*   Following  define  the  initial  size of the temporary printf()  */
/*   buffer  and  the  increment  size  to use for large objects. (A  */
/*   negative increment means multiply by the absolute value).	      */
/**********************************************************************/
# define	PRINTF_SIZE	512
# define	PRINTF_INCR	(-2)

/**********************************************************************/
/*   Buffer  used  for  printf()s. We expand the buffer as needed so  */
/*   that  we  don't  place  any  restriction  on  the  size  of the  */
/*   formatted output.						      */
/**********************************************************************/
static char	*buf;
static int	buf_size = PRINTF_SIZE;

# define	CHECK_BUFFER(len) { if (obp - buf + len >= buf_size - 4) \
					expand_buffer(&obp); \
					}
int	p_on_error = 0;
extern	int	pcolor;
int	msg_error();
char	*do__prntf PROTO((int));
static void	expand_buffer PROTO((char **));

/**********************************************************************/
/*   Function  to  implement the printf() style processing, but also  */
/*   to support the CRISP style extras.				      */
/**********************************************************************/
char	*
do__prntf(offset)
int	offset;
{	char	*fmt;
	char	tmpbuf[512];
	char	fmtbuf[64];
	char	*fp;
	int	arg = offset + 2;
	char	*cp;
	char	*obp;
	int	field_width;
	int	seen_neg;
	int	len;
	int	fill;
	int	type;
	int	long_flag = FALSE;

	if (buf == NULL)
		buf = (char *) chk_alloc(PRINTF_SIZE);

	fmt = get_str(offset + 1);

	obp = buf;
	while (*fmt) {
		if (*fmt != '%') {
			CHECK_BUFFER(1);
			*obp++ = *fmt++;
			continue;
			}
		/***********************************************/
		/*   Keep   a   copy   of  the  users  '%...'  */
		/*   specifier  so  we  can  use it ourselves  */
		/*   for the complicated ones.		       */
		/***********************************************/
		fp = fmtbuf;
		*fp++ = *fmt++;
		if (*fmt == '-') {
			seen_neg = TRUE;
			*fp++ = *fmt++;
			}
		else {
			seen_neg = FALSE;
			}
		field_width = 0;
		fill = ' ';
		if (*fmt == '0')
			fill = '0';
		while (isdigit(*fmt)) {
			field_width = 10 * field_width + *fmt - '0';
			*fp++ = *fmt++;
			}
		if (*fmt == '.') {
			*fp++ = *fmt++;
			while (isdigit(*fmt)) {
				*fp++ = *fmt++;
				}
			}
		if (seen_neg)
			field_width = -field_width;
		if (*fmt == 'l') {
			*fp++ = *fmt++;
			long_flag = TRUE;
			}
		cp = tmpbuf;
		type = *fmt++;
		*fp++ = type;
		*fp = NULL;
		/***********************************************/
		/*   The  %t  format  specifier means use the  */
		/*   natural type of the operand.	       */
		/***********************************************/
		if (type == 't') {
			if (argv[arg].l_flags == F_INT)
				type = 'd';
			else if (argv[arg].l_flags == F_FLOAT) {
				type = 'f';
				long_flag = TRUE;
				}
			else
				type = 's';
			fp[-1] = type;
			}
		switch (type) {
		  case 's':
		  	switch (argv[arg].l_flags) {
			  case F_STR:
			  case F_RSTR:
			  case F_LIT:
			  case F_LIST:
				cp = get_str(arg);
				break;
			  default:
				cp = "<bad-string>";
			  }
			arg++;
			fill = ' ';
copy_field:
			len = strlen(cp);
			if (field_width > 0) {
				field_width -= len;
				CHECK_BUFFER(field_width);
				while (field_width-- > 0) {
					*obp++ = fill;
					}
				field_width = 0;
				}
			else
				field_width += len;
			CHECK_BUFFER(len);
			memcpy(obp, cp, len);
			obp += len;
			CHECK_BUFFER(-field_width);
			while (field_width++ < 0)
				*obp++ = ' ';
			break;
		  case 'c':
		  	tmpbuf[0] = argv[arg++].l_int;
			tmpbuf[1] = NULL;
			fill = ' ';
			goto copy_field;
		  case 'd':
		  	sprintf(tmpbuf, "%ld", argv[arg++].l_int);
			goto copy_field;
		  case 'x':
		  	sprintf(tmpbuf, "%lx", argv[arg++].l_int);
			goto copy_field;
		  case 'o':
		  	sprintf(tmpbuf, "%lo", argv[arg++].l_int);
			goto copy_field;
		  case 'e':
		  case 'f':
		  case 'g':
		  	sprintf(tmpbuf, fmtbuf, argv[arg++].l_float);
			fill = ' ';
			field_width = 0;
			goto copy_field;
		  case NULL:
		  	goto end_of_function;
		  case 'p':
			/***********************************************/
			/*   p  means  do a percentage style message.  */
			/*   If  a  second  hasn't  passed  since the  */
			/*   last message then ignore this entry.      */
			/***********************************************/
			if (!second_passed())
				return NULL;
			break;
		  default:
			CHECK_BUFFER(1);
		  	*obp++ = fmt[-1];
			break;
		  }
		}
end_of_function:
	*obp = NULL;
	return buf;

}
static void
expand_buffer(bp)
char	**bp;
{	int	offset = *bp - buf;

	if (PRINTF_INCR < 0)
		buf_size *= -PRINTF_INCR;
	else
		buf_size += PRINTF_INCR;
	buf = (char *) chk_realloc(buf, buf_size);
	*bp = buf + offset;
}
/*----------------------------------------------------------------
 *
 *   (message format_string [value1] ...)
 *
 *   Function:
 *
 *	This macro is used to display a message on the status line.
 *      It takes a variable number of paramaters, the first of which
 *	is a printf-like string. The message is displayed on the status
 *      line.
 *
 *      All parameters are evaluated, even if they are not needed.
 *
 *   Examples:
 *
 *	(message "The value of variable i = %d"  i)
 *
 *      (message "File: %s"  current_file_name)
 *
 *----------------------------------------------------------------*/
int
message()
{
	msg_error();
	return 0;
}
int
do_error()
{
	return msg_error();
}
int
msg_error()
{
	char	*cp = do__prntf(0);


	/***********************************************/
	/*   May  get  a  NULL pointer back if %p was  */
	/*   used   to  indicate  that  timer  hasn't  */
	/*   expired.				       */
	/***********************************************/
	if (cp == NULL) {
		return -1;
		}
	/***********************************************/
	/*   If  pause  on error is set, then tack on  */
	/*   a  '..'  and  wait for a keypress before  */
	/*   continuing.			       */
	/***********************************************/
	if (p_on_error) {
		strcat(cp, "..");
		ewputs(cp, (char *) NULL);
		getkey((long) 0);
		return 0;
		}
	/***********************************************/
	/*   Just print message.		       */
	/***********************************************/
	ewputs(cp, (char *) NULL);
	return 0;
}
/*----------------------------------------------------------------
 *
 *   (printf format_string [value1] ...)
 *   Macro:		printf
 *
 *   Function:
 *
 *	This macro is used to print debugging messages on stdout.
 *      format_string is a C printf() string, containing the following
 *	set of %-sequences. [value1] ... are the optional parameters
 *      used to satisfy the %-escapes.
 *
 *      All parameters are evaluated, even if they are not needed.
 *
 *	%c	Print parameter as an ASCII character.
 *	%d	Print parameter in decimal.
 *	%x	Print parameter in hex.
 *	%s	Print argument as a string.
 *
 *   Examples:
 *
 *	(printf "Current directory %s"  (get_current_directory))
 *
 *      (printf "Performed %02d iterations"  count)
 *
 *----------------------------------------------------------------*/
int
do_printf()
{
	char *cp = do__prntf(0);
	if (cp) {
		printf("%s", cp);
		fflush(stdout);
		return 0;
		}
	return -1;
}
void
do_sprintf()
{
	char *cp;
	SYMBOL	*sp = argv[1].l_sym;
	cp = do__prntf(1);
	if (cp)
		str_assign(sp, cp);
}
int
pause_on_error()
{
	p_on_error = argv[1].l_flags == F_INT ? argv[1].l_int : !p_on_error;
	ewprintf("Pausing errors %s.", p_on_error ? "on" : "off");
	return 0;
}
static void
pf(str, a, b, c, d, e, f)
char	*str;
char	*a, *b, *c, *d, *e, *f;
{	char	buf[BUFSIZ];

	sprintf(buf, str, a, b, c, d, e, f);
	if (p_on_error) {
		strcat(buf, "..");
		ewputs(buf, (char *) NULL);
		(void) getkey((long) 0);
		}
	else
		ewputs(buf, (char *) NULL);
}
/* VARARGS1 */
void
infof(str, a, b, c, d, e, f)
char	*str;
char	*a, *b, *c, *d, *e, *f;
{
	if (msg_level == 0)
		ewprintf(str, a, b, c, d, e, f);
}
/* VARARGS1 */
void
errorf(str, a, b, c, d, e, f)
char	*str;
char	*a, *b, *c, *d, *e, *f;
{
	if (msg_level <= 2) {
		pcolor = CERROR;
		pf(str, a, b, c, d, e, f);
		pcolor = CMESSAGE;
		}
}
/**********************************************************************/
/*   Implement a sscanf like function.				      */
/**********************************************************************/
void
do_sscanf()
{
}
