/* $RCSfile: utils.c,v $  $Revision: 2.0 $ */

#include <stdio.h>
#include "gdefs.h"
#include "externals.h"
#include "protos.h"
extern FILE *ifile,*ofile;

static int begin_or_end = FALSE;	/* Variable used between 
                                       print_char and print_chars */

void err_exit(int x)
{
	printf(err_msg[x]);
	exit(x);
}

void print_hex(int i)
{
	if ((i>=0) && (i<=9)) {
		fputc(i+'0',ofile);
	}
	else if ((i>=10) && (i<=15)) {
		fputc(i+'A'-10,ofile);
	}
	else printf("\nERROR: i= %d\n",i);
}

void output_byte(int c)
{
	int i,j,k;
	i=c;
	j=i / 16;
	k=i % 16;
	print_hex(j);
	print_hex(k);
}

void indent(int x)
{
	int i;

	/* Default is to indent 4 spaces */
	if (x != 0) {
		for (i=0; i<x; i++) {
			/* Insert favorite tabbing style */
			fprintf(ofile,"    ");
		}
	}
}

/* ROUTINE: print_esc_string
 *
 *    This routine prints the entire escape sequence string.  If the
 *    hex option is used, a hex printout of the string is also given.
 */
void print_esc_string(void)
{

	fprintf(ofile,"\n<Esc>%s\n", &esc_string[1]);
	if (opt_x) {
		int i,j;
		/* Print the characters in hex */
		for (i=0; i <= esc_count; i++) {
			j = 0xff & esc_string[i];
			output_byte(j);
			fprintf(ofile," ");
		}
		fprintf(ofile,"\n");
	}
}

/* ROUTINE: print_sub_string
 *
 *    This routine prints part of the escape sequence string.
 *    This consists of the "header" which is the first header characters
 *    after the escape.  Then the string between start and stop is printed.
 */
void print_sub_string(int header, int start, int stop)
{
	int i;
	char s[255];
	char	*ptr_c;

	ptr_c = s;
	for (i=1; i<=header; i++) {
		*ptr_c++ = esc_string[i];
	}
	for (i=start; i<=stop; i++) {
		*ptr_c++ = esc_string[i];
	}
	*ptr_c = 0;
	indent(1);
	fprintf(ofile,"<Esc>%-6s ", s);
}

/* ROUTINE: get_num
 *
 *    Convert an alpha number sequence to an actual int.  
 *
 * Variables used:
 *    stop_pos  -- *stop_pos contains the starting point on 
 *                 entry and is updated to the new "stop point"
 *                 before exiting.
 *    num       -- global variable used to return the integer value.
 *    fraction  -- global string used to return fractional part (if exists)
 *    is_float  -- global flag to say that fractional part exists.
 *    plus      -- global flag to say number started with a '+' sign.
 *    minus     -- global flag to say number started with a '-' sign.
 *    
 * NOTES:
 *    Pcl assumes a zero if no number is given.  Thus, this routine returns
 *    a zero if '0' is encountered or if no digits are encountered.
 *    
 *    If the fractional part of a floating point number is zero, the
 *    number will be treated like an int.
 */
void get_num(int *stop_pos, int sum)
{
	char c;
	int  done;
	int  i,j;

	i = *stop_pos;
	c = esc_string[i];
	plus = minus = is_float = FALSE;
	if (c == '-') {
		minus = TRUE;
		i++;
	}
	else if (c == '+') {
		i++;
		plus = TRUE;
	}
	done = FALSE;
	while (done == FALSE) {
		c = esc_string[i];
		if ((c >= '0') && (c <= '9')) {
			sum *= 10;
			sum += c - '0';
			i++;
		}
		else done = TRUE;
	}
	if (c == '.') {
		i++;
		done = FALSE;
		j = 0;
		while (done == FALSE) {
			c = esc_string[i];
			if ((c >= '0') && (c <= '9')) {
				fraction[j] = c;
				i++;
				j++;
			}
			else done = TRUE;
		}
		/* Remove trailing 0's and see if anything's left */
		if (j > 0) {
			done = FALSE;
			while (done == FALSE) {
				fraction[j] = 0;
				if ((j == 0) || (fraction[j-1] != '0')) {
					done = TRUE;
				}
				else {
					j--;
				}
			}
			if (j != 0) {
				is_float = TRUE;
			}
		}
	}
	*stop_pos = i;
	if (minus) {
		sum = -sum;
	}
	num = sum;
}

/*
 * ROUTINE: decode_esc_string
 *
 *   This routine provides the first level of decoding for an escape
 *   string.  If it is a simple 2 character escape sequence, it is
 *   handled here.  Otherwise, the appropriate lower level routine
 *   is called.
 */
void decode_esc_string(void)
{
	char c;

	c = esc_string[1];
	if (esc_count == 1) {
		indent(1);
		fprintf(ofile,"<Esc>%-6s ", &esc_string[1]);
		switch (c) {
			case '9':
				fprintf(ofile,
					"Reset Left and Right Margins to default settings.\n");
				break;
			case '=':
				fprintf(ofile,"Half Line Feed.  Move down one-half line.\n");
				break;
			case 'E':
				fprintf(ofile,"Reset to user defaults.\n");
				indent(4);
				fprintf(ofile,
					"Print buffered page.  Delete temporary fonts and macros.\n");
				break;
			case 'Y':
				fprintf(ofile,"Display Functions ON.\n");
				break; 
			case 'Z':
				fprintf(ofile,"Display Functions OFF.\n");
				break;
			case 'z':
				fprintf(ofile,"Print Self Test.\n");
				break;
			default:
				fprintf(ofile,"%s",bad_esc);
				break;
		}
	}
	else {
		switch (c) {
			case '(':
			case ')':
				decode_paren();
				break;
			case '&':
				decode_ampersand();
				break;
			case '*':
				decode_asterisk();
				break;
			default:
				fprintf(ofile,"%s",bad_esc);
				break;
		}
	}
}

/* ROUTINE: add_char
 *
 *    This routine places a character into a buffer when we are in
 *    character mode.  The idea is to store the first (typically 10)
 *    characters and last (again typically 10) characters.  Thus, if
 *    we encounter a big text section, we don't have to print the whole
 *    thing.  The first characters are placed in the char_start
 *    array.  The last characters are placed in the char_end array which
 *    is a circular buffer.
 */
void add_char(int c)
{
	if (opt_e == FALSE) {
		if (char_count <= CHAR_START_LEN + CHAR_END_LEN - 1) {
			char_start[char_count] = c;
		}
		if (char_count >= CHAR_START_LEN) {
			char_end[(char_count - CHAR_START_LEN) % CHAR_END_LEN] = c;
		}
		char_count++;
	}
}

/* ROUTINE: print_extra_rasters
 *
 *    Raster images are printed a line at a time.  Rather than print
 *    each raster escape sequence, the first is simply printed, and
 *    all others that follow of the same size are counted, and the
 *    additional count is printed with this routine.
 */
void print_extra_rasters(void)
{
	if ((raster_mode != NO_RASTER) && (raster_count > 0)) {
		indent(1);
		fprintf(ofile,"%d same size raster commands follow.\n",raster_count);
	}
	raster_mode = NO_RASTER;
	raster_count = 0;
}

/* ROUTINE: print_char
 *
 *    This routine prints a character that was encountered in character
 *    mode.  Some special characters are expressed mnemonically between
 *    brackets.  Other (typically) non-printing characters are represented
 *    by their hex character codes in brackets.  The space character gets 
 *    some extra special treatment.  In cases at the beginning or end of 
 *    strings, where it might not be visually obvious, a <SP> is printed
 *    instead.  The begin_or_end variable is used between this routine 
 *    and print_chars to convey this information and is always cleared
 *    on exit from this routine.
 */
void print_char(char c)
{
	char s[2];

	switch (c) {
		case CARRIAGE_RETURN:
			fprintf(ofile,"<CR>");
			break;
		case LINE_FEED:
			fprintf(ofile,"<LF>");
			break;
		case BACK_SPACE:
			fprintf(ofile,"<BS>");
			break;
		case TAB:
			fprintf(ofile,"<TAB>");
			break;
		case ' ':
			if (begin_or_end) {
				fprintf(ofile,"<SP>");
			}
			else {
				fprintf(ofile," ");
			}
			break;
		default:
			if (((c >= 32) && (c <= 127)) || ((c >= 161) && (c <= 254))) {
				s[0]=c;
				s[1]=0;
				fprintf(ofile,"%s",s);
			}
			else {
				/* Non-printing characters in Roman-8 character set */
				/* Hopefully one won't see much of these */
				fprintf(ofile,"<0x%x>",0xff & c);
			}
			break;
	}
	begin_or_end = FALSE;
}

/*
 * ROUTINE: print_chars
 *     Prints the first (10) characters and last (10) characters of text 
 *
 * NOTES:
 *     Uses the following forms:
 *        <=20 chars "123456789ab"  
 *         >20 chars "123456789a ... 123456789a"  
 *     Will also print additional line info when lines have been skipped.
 *     Special characters will come out between <>'s.  If you are unsure
 *     if <>'s were in your text, count the number of characters printed.
 *     Spaces on "ends" of text will come out as <SP>.
 *     The actual number of characters printed depends on CHAR_START_LEN
 *     and CHAR_END_LEN.
 *     Will print the hex equivalents if -x option used.
 */
void print_chars(void)
{
	int i,j,k;

	print_extra_rasters();
	if (opt_e == FALSE) {
		if (char_count > 0) {
			fprintf(ofile,"\n");
			if (char_count <= CHAR_START_LEN + CHAR_END_LEN) {
				/* Only print (some of the) start characters */
				begin_or_end = TRUE;
				for (i=0; i<char_count; i++) {
					if (i == char_count-1) {
						begin_or_end = TRUE;
					}
					print_char(char_start[i]);
				}
				if (opt_x) {
					fprintf(ofile,"\n");
					for (i=0; i<char_count; i++) {
						j = 0xff & char_start[i];
						output_byte(j);
						fprintf(ofile," ");
					}
				}
			}
			else { 
				/* "End" array did "wrap around" */
				/* Move the "end" characters to end of "start" array */
				j = k = (char_count - CHAR_START_LEN) % CHAR_END_LEN;
				for (i=CHAR_START_LEN; k<CHAR_END_LEN; i++, k++) {
					char_start[i] = char_end[k];
				}
				for (k=0; k<j; i++, k++) {
					char_start[i] = char_end[k];
				}
				begin_or_end = TRUE;
				/* Print the first set */
				for (i=0; i<CHAR_START_LEN; i++) {
					if (i == CHAR_START_LEN-1) {
						begin_or_end = TRUE;
					}
					print_char(char_start[i]);
				}
				fprintf(ofile," ... ");
				begin_or_end = TRUE;
				/* Print the second set */
				for (i=CHAR_START_LEN; i<CHAR_START_LEN+CHAR_END_LEN; i++) {
					if (i == CHAR_START_LEN+CHAR_END_LEN-1) {
						begin_or_end = TRUE;
					}
					print_char(char_start[i]);
				}
				if (opt_x) {
					fprintf(ofile,"\n");
					for (i=0; i<CHAR_START_LEN; i++) {
						j = 0xff & char_start[i];
						output_byte(j);
						fprintf(ofile," ");
					}
					fprintf(ofile," ... ");
					for (i=CHAR_START_LEN; i<CHAR_START_LEN+CHAR_END_LEN; i++) {
						j = 0xff & char_start[i];
						output_byte(j);
						fprintf(ofile," ");
					}
				}
			}
			fprintf(ofile,"\n");
			if (num_lines > 0) {
				indent(1);
				fprintf(ofile,"Added %d more lines.\n", num_lines);
			}
		}
	}
	num_lines = char_count = 0;
}
