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

/*
 * Copyright (c) 1991-2006 by Wayne C. Gramlich.
 * All rights reserved.
 */

/*
 * This file is used to output error messages with a file name and
 * line number.
 */

#ifndef FLAGS_DEFS_H
#include "flags_defs.h"
#endif

#ifndef HEAP_EXPORTS_H
#include "heap_exports.h"
#endif

#ifndef INT_EXPORTS_H
#include "int_exports.h"
#endif

#ifndef LIBC_EXPORTS_H
#include "libc_exports.h"
#endif

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

#ifndef MSG_DEFS_H
#include "msg_defs.h"
#endif

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

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

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

#ifndef UNIX_STDIO_H
#include "unix_stdio.h"
#endif

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

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

LOCAL int	msg_int_compare(int, int, int);

/*
 * msg_count_get(Msg)
 *	This routine will return the number of error messages output.
 */
int
msg_count_get(
	Msg		msg)
{
	return msg->count;
}

/*
 * msg_create(in_file, heap)
 *	This routine will create and return a new Msg object for outputing
 *	error messages for "flags->in_file" allocated from "heap".
 */
Msg
msg_create(
	Flags		flags,
	Heap		heap)
{
	char		buffer[2000];
	Flags_file	in_file;
	Msg		msg;
	char		working_dir[2000];

	assert(getcwd(working_dir, sizeof(working_dir)) != (Str)0);
	in_file = flags->in_file;
	msg = heap_allocate(heap, Msg);
	msg->count = 0;
	msg->error_file = stderr;
	if (strequal(in_file->path, working_dir)) {
		(void)sprintf(buffer, "%s%s", in_file->base, in_file->suffix);
		msg->file_name = strdupl(buffer, heap);
	} else {
		msg->file_name = in_file->full;
	}
	msg->flags = flags;
	msg->position_split = -1;
	msg->positions = vec_create(int, heap);
	return msg;
}

/*
 * msg_int_compare(int1, int2, zilch)
 *	This routine will return <0, ==0, >0 depending upon whether "int1"
 *	is less than, equal to, or greater than 0.
 */
/* ARGSUSED */
LOCAL int
msg_int_compare(
	int		int1,
	int		int2,
	int		zilch)
{
	return int_compare(int1, int2);
}

/*
 * msg_line_append(msg, position)
 *	This routine will append "position" to the position table inside of
 *	"msg".
 */
void
msg_line_append(
	Msg		msg,
	int		position)
{
	vec_append(int, msg->positions, position);
}

/*
 * msg_line_get(msg, position)
 *	This routine will return the line number associated with "position".
 */
int
msg_line_get(
	Msg		msg,
	int		position)
{
	return vec_search_binary(int, msg->positions,
				 position, msg_int_compare, 0) + 1;
}


/*
 * msg_out(msg, position, format, arg1, ..., argN)
 *	This routine will output "arg1" through "argN" as an error message
 *	using "format".  "position" is used to compute line number.
 */
void
msg_out(
	Msg		msg,
	int		position,
	Str		format,
	...)
{
	va_list		args;
	int		line_number;
	Stdio		error_file;
	int		temp;

	error_file = msg->error_file;
	va_start(args, format);
	line_number = vec_search_binary(int, msg->positions, position,
					msg_int_compare, 0) + 1;
	if ((msg->position_split >= 0) && (position >= msg->position_split)) {
		temp = vec_search_binary(int, msg->positions,
					 msg->position_split,
					 msg_int_compare, 0) + 1;
		out(error_file, "%\", line %d: ",
		    msg->flags->gen_file->full,
		    line_number - temp);
	} else {
		out(error_file, "%\", line %d: ",
		    msg->file_name, line_number);
	}
	out_format(error_file, format, args);
	out(error_file, "\n");
	msg->count++;
	va_end(args);
}

/*
 * msg_trace(msg, position, format, arg1, ..., argN)
 *	This routine will output "arg1" through "argN" as a trace message
 *	using "format".  "position" is used to compute line number.
 */
void
msg_trace(
	Msg		msg,
	int		position,
	Str		format,
	...)
{
	va_list		args;
	int		line_number;
	Stdio		error_file;
	int		temp;

	error_file = msg->error_file;
	va_start(args, format);
	line_number = vec_search_binary(int, msg->positions, position,
					msg_int_compare, 0) + 1;
	if ((msg->position_split >= 0) && (position >= msg->position_split)) {
		temp = vec_search_binary(int, msg->positions,
					 msg->position_split,
					 msg_int_compare, 0) + 1;
		out(error_file, "%\", line %d: ",
		    msg->flags->gen_file->full,
		    line_number - temp);
	} else {
		out(error_file, "%\", line %d: ",
		    msg->file_name, line_number);
	}
	out_format(error_file, format, args);
	out(error_file, "\n");
	va_end(args);
}
