/* %Z%%M% %I% %E% */

/*
 * Copyright (c) 1991, 1992, 1993, 1994, 1995 by Wayne C. Gramlich.
 * All rights reserved.
 *
 * Permission to use, copy, modify, distribute, and sell this software
 * for any purpose is hereby granted without fee provided that the above
 * copyright notice and this permission are retained.  The author makes
 * no representations about the suitability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 */

/* This file contains code for doing output: */

#ifndef ERROR_EXPORTS_H
#include "error_exports.h"
#endif

#ifndef LINT_H
#include "lint.h"
#endif

#ifndef OUT_EXPORTS_H
#include "out_exports.h"
#endif

#ifndef ROUTINE_DEFS_H
#include "routine_defs.h"
#endif

#ifndef ROUTINE_EXPORTS_H
#include "routine_exports.h"
#endif

#ifndef STR_EXPORTS_H
#include "str_exports.h"
#endif

#ifndef TABLE_EXPORTS_H
#include "table_exports.h"
#endif

#ifndef TYPE_DEFS_H
#include "type_defs.h"
#endif

#ifndef UNIX_ASSERT_H
#include "unix_assert.h"
#endif

#ifndef UNIX_CTYPE_H
#include "unix_ctype.h"
#endif

#ifndef UNIX_STDARG_H
#include "unix_stdarg.h"
#endif

#ifndef UNIX_UNISTD_H
#include "unix_unistd.h"
#endif

#ifndef VECTOR_EXPORTS_H
#include "vector_exports.h"
#endif

static Table(Str, Str)	c_keywords = (Table(Str, Str))0;

/*
 * out(out_file, format, arg1, ..., argN)
 *	This routine will print "arg1" through "argN" to "out_file" using
 *	"format" to control formatting.  See out_format() for a complete
 *	description of the formating control characters.
 */
#if defined(lint)

/* ARGSUSED */
/* VARARGS2 */
void
out(
	Stdio		out_file,
	Str		format,
	...)
{
	va_list		args;

	args = (va_list)0;
	out_format(out_file, format, args);
}

#else /* defined(lint */

void
out(
	Stdio		out_file,
	Str		format,
	...)
{
	va_list		args;

	va_start(args, format);
	out_format(out_file, format, args);
	va_end(args);
}

#endif /* defined(lint */

/*
 * out_count_read(in_file)
 *	This routine will read and return a count from "in_file".
 */
int
out_count_read(
	Stdio		in_file)
{
	int		count;

	count = getw(in_file);
	if (count > 5000) {
		count = count;		/* Plant breakpoint here! */
		assert_fail();
	}
	return count;
}

/*
 * out_count_write(out_file, count)
 *	This routine will write "count" to "out_file".
 */
void
out_count_write(
	Stdio		out_file,
	int		count)
{
	(void)putw(count, out_file);
}

/*
 * lint has all sorts of pointer alignment problems with va_arg().
 * Make lint shut up by redefining va_arg().
 */
#if defined(lint)
#undef va_arg
#define va_arg(var, type) ((type)(0 + (int)(var)))
#endif /* defined(lint */

/*
 * out_format(out_file, format, args_ptr)
 *	This routine takes the arguments pointed to by "args_ptr" and prints
 *	them to "out_file" usign "format".  Look at the comments inside of
 *	this routine to see what formating is avaiable.
 */
void
out_format(
	Stdio		out_file,
	Str		format,
	va_list		args)
{
	char		chr;
	Str		comment;
	int		indent;

	if (out_file == (Stdio)0) {
		out_file = stdout;
	}
	while ((chr = *format++) != '\0') {
		if (chr != '%') {
			(void)fputc(chr, out_file);
			continue;
		}
		chr = *format++;
		switch (chr) {
		    case '\0':
			format--;
			break;
		    case '\t': /* Beginning-of-line indentation */
			indent = va_arg(args, int);
			assert(indent < 500);
			while (indent > 1) {
				(void)fputc('\t', out_file);
				indent -= 2;
			}
			if (indent == 1) {
				(void)fputs("    ", out_file);
			}
			break;
		    case '\n': /* End-of-line comment */
			comment = va_arg(args, Str);
			if (comment != (Str)0) {
				(void)fputs("\t/* ", out_file);
				(void)fputs(comment + 1, out_file);
				(void)fputs(" */", out_file);
			}
			(void)fputc('\n', out_file);
			break;
		    case '\'':
			out_quoted_string(out_file, va_arg(args, Str), '\'', 0);
			break;
		    case '"':
			out_quoted_string(out_file, va_arg(args, Str), '"', 0);
			break;
		    case '#':
			out_quoted_string(out_file, va_arg(args, Str), '"', 1);
			break;
		    case '@':
		      {
			Type_ref	type_ref;

			type_ref = va_arg(args, Type_ref);
			if (!strequal(type_ref->name, "global__")) {
				(void)fputc('@', out_file);
				type_ref_print(type_ref, out_file);
			}
			break;
		      }
		    case 'c': /* Character */
			(void)fputc(va_arg(args, int), out_file);
			break;
		    case 'd': /* Decimal */
			(void)fprintf(out_file, "%d", va_arg(args, int));
			break;
		    case 'I': /* Inititial object: */
			(void)fputs(va_arg(args, Str), out_file);
			(void)fputs("___initial", out_file);
			break;
		    case 'M': /* Type reference list mangle */
			type_refs_mangle(va_arg(args, Type_refs), 0, out_file);
			break;
		    case 'm': /* Type reference mangle */
			type_ref_mangle(va_arg(args, Type_ref), out_file);
			break;
		    case 'q':
			(void)fputc('{', out_file);
			type_refs_print(va_arg(args, Type_refs),
					    out_file);
			(void)fputc('}', out_file);
			break;
		    case 'r':
		      {
			Type_ref	type_ref;

			type_ref = va_arg(args, Type_ref);
			type_ref_print(type_ref, out_file);
			break;
		      }
		    case 'R':
		      {			
			Routine_ref	routine_ref;

			routine_ref = va_arg(args, Routine_ref);
			(void)fputs(routine_ref->name, out_file);
			if (routine_ref->type_ref != (Type_ref)0) {
				(void)fputc('@', out_file);
				(void)fputs(routine_ref->type_ref->name,
					    out_file);
			}
			break;
		      }
		    case 's': /* Regular string without keyword conversion: */
		      {
			Str		str;

			assert(c_keywords != (Table(Str, Str))0);
			str = va_arg(args, Str);
			if (str == (Str)0) {
				str = "(null)";
			}
			(void)fputs(str, out_file);
			break;
		      }
		    case 'S': /* Regular string with keyword conversion: */
		      {
			Str		str;
			Str		new_str;

			assert(c_keywords != (Table(Str, Str))0);
			str = va_arg(args, Str);
			if (str == (Str)0) {
				str = "(null)";
			} else {
				new_str = table_lookup(Str, Str,
						       c_keywords, str);
				if (new_str != (Str)0) {
					str = new_str;
				}
			}
			(void)fputs(str, out_file);
			break;
		      }
		    case 'T': /* Type string: */
			(void)fputs(va_arg(args, Str), out_file);
			(void)fputs("___type", out_file);
			break;
		    case 'X': /* Hexadecimal */
			(void)fprintf(out_file, "0x%x", va_arg(args, int));
			break;
		    default:
			error_abort("Bad format character %c (=%d)", chr, chr);
			/* NOTREACHED */
		}
	}
}

/*
 * out_initialize(heap)
 *	Will initialize the C keyword table table.
 */
void
out_initialize(
	Heap		heap)
{
	c_keywords = table_create(Str, Str,
				  50, strequal, strhash, (Str)0, heap);
	(void)table_insert(Str, Str, c_keywords,
			   "auto", "out__keyword");
	(void)table_insert(Str, Str, c_keywords,
			   "case", "case__keyword");
	(void)table_insert(Str, Str, c_keywords,
			   "char", "char__keyword");
	(void)table_insert(Str, Str, c_keywords,
			   "double", "double__keyword");
	(void)table_insert(Str, Str, c_keywords,
			   "else", "else__keyword");
	(void)table_insert(Str, Str, c_keywords,
			   "enum", "else__keyword");
	(void)table_insert(Str, Str, c_keywords,
			   "extern", "extern__keyword");
	(void)table_insert(Str, Str, c_keywords,
			   "float", "float__keyword");
	(void)table_insert(Str, Str, c_keywords,
			   "goto", "goto__keyword");
	(void)table_insert(Str, Str, c_keywords,
			   "if", "if__keyword");
	(void)table_insert(Str, Str, c_keywords,
			   "int", "int__keyword");
	(void)table_insert(Str, Str, c_keywords,
			   "long", "long__keyword");
	(void)table_insert(Str, Str, c_keywords,
			   "short", "short__keyword");
	(void)table_insert(Str, Str, c_keywords,
			   "signed", "signed__keyword");
	(void)table_insert(Str, Str, c_keywords,
			   "static", "static__keyword");
	(void)table_insert(Str, Str, c_keywords,
			   "struct", "static__keyword");
	(void)table_insert(Str, Str, c_keywords,
			   "switch", "switch__keyword");
	(void)table_insert(Str, Str, c_keywords,
			   "typedef", "typedef__keyword");
	(void)table_insert(Str, Str, c_keywords,
			   "union", "unsigned__keyword");
	(void)table_insert(Str, Str, c_keywords,
			   "unsigned", "unsigned__keyword");
	(void)table_insert(Str, Str, c_keywords,
			   "void", "void__keyword");
	(void)table_insert(Str, Str, c_keywords,
			   "while", "while__keyword");
}

/*
 * out_marker_read(in_file, marker)
 *	This routine will verify that "marker" is next in "in_file".
 */
void
out_marker_read(
	Stdio		in_file,
	Str		marker)
{
	int		temp;
	static int	marker_count = 0;
	static int	marker_match = 0;

	temp = getw(in_file);
	marker_count++;
	if (marker_count == marker_match) {
		temp = temp;		/* Plant breakpoint here! */
	}
	if (strhash(marker) != temp) {
		temp = temp;		/* Plant breakpoint here! */
		assert_fail();
	}
}

/*
 * out_marker_write(out_file, marker)
 *	This routine will write "marker" to "out_file".
 */
void
out_marker_write(
	Stdio		out_file,
	Str		marker)
{
	(void)putw(strhash(marker), out_file);
}

/*
 * out_quoted_string(out_file, str, quote, prepend_length)
 *	This routine will print "str" to "out_file" enclosed in "quote".
 */
void
out_quoted_string(
	Stdio		out_file,
	Str		str,
	char		quote,
	int		prepend_length)
{
	char		chr;
	int		size;
	Str		text;

	if (prepend_length) {
		size = strlen(str);
		assert(size < 255);
		(void)fprintf(out_file, "%c\\%03o", quote, size);
	} else {
		(void)fputc(quote, out_file);
	}
	for (text = str, size = strlen(str), chr = *text++;
	     size-- > 0; chr = *text++) {
		if (isprint(chr)) {
			if ((chr == quote) || (chr == '\\')) {
				(void)fputc('\\', out_file);
			}
			(void)fputc(chr, out_file);
		} else {
			(void)fputc('\\', out_file);
			switch (chr) {
			    case '\n':
				(void)fputc('n', out_file);
				break;
			    case '\t':
				(void)fputc('t', out_file);
				break;
			    default:
				(void)fprintf(out_file, "%03o", chr);
			}
		}
	}
	(void)fputc(quote, out_file);
}

