;**
;**		BRIEF -- Basic Reconfigurable Interactive Editing Facility
;**
;**		Written by Dave Nanian and Michael Strickman.
;**
;**		Revision history:
;**		-----------------
;**		13 December 1985		Added support for MASM v4.0.
;**		5 December 1985		Simplified and corrected search pattern.
;**									Changed highlight logic to look for "Warning"
;**									rather than "Error".
;**		6 October 1985			Added support for the Microsoft C compiler.
;**

;**		errorfix.m
;**
;**		This file contains a number of macros that locate errors in various
;**	files, using the error messages produced by the Computer Innovations
;**	CI-C86, Wizard C, Lattice C, Microsoft C, MASM, or BRIEF CM compilers.
;**
;**		To locate errors in a program, you should redirect the output
;**	from the compiler to a file called "<filename>.err".  You then edit
;**	the original file and invoke the "next_error" macro.  The "next_error"
;**	macro is assigned to the Ctrl-n key after the "errorfix" macro is run.
;**
;**		Sample wcc.bat, c86cc.bat, mcc.bat and lcc.bat files are provided
;**	in the /brief/macros directory to illustrate the use of this macro
;**	package.  In addition, the "cc" macro in extended.m uses this to
;**	locate its errors after an unsucessful compilation.
;**

#define	TRUE			1
#define	FALSE			0

(macro errorfix
	(
		;**
		;**		If we're being invoked for the first time, we declare our
		;**	global variables and assign our keys.
		;**

		(if (first_time)
			(
				(string		error_text)

				(global		error_text)

				(= error_text "No previous error.")
				(assign_to_key "^n" "next_error")
				(assign_to_key "^p" "previous_error")
				(message "Error handler initialized.")
			)
		)
	)
)

;**
;**		next_error:
;**
;**		This routine does the majority of the work in this file.  It searches
;**	for the string that identifies the error message in the routine (in the
;**	case of these compilers, the line number is followed by a colon).  We
;**	then parse off the line number, and move the cursor to the appropriate
;**	line of the old file using the goto_old_line routine.
;**

(macro next_error
	(
		(string		error_extension
						error_file
		)
		(int			error_line
						line
						done
						old_buffer
						buffer_ptr
		)
		;**
		;**		We make sure that the errorfix system is initialized by calling
		;**	the errorfix macro -- it will do nothing if it is not being called
		;**	for the first time.
		;**

		(errorfix)

		;**
		;**		First, we get the full name of the buffer so we can
		;**	check if the current error message applies.  We then
		;**	create the appropriate buffer (if it's already created,
		;**	create_buffer will return its ID) and make it current.
		;**

		(inq_names error_file NULL error_extension)
		(= error_file (+ (substr error_file 1 (rindex error_file ".")) "err"))
		(= old_buffer (inq_buffer))
		(set_buffer (= buffer_ptr (create_buffer "Errors" error_file TRUE)))

		;**
		;**		Now, we look for the telltale error message string.
		;**
		;**		This pattern makes the following assumptions:
		;**
		;**		Error lines contain the filename, and place this name BEFORE
		;**		the error number.
		;**
		;**		The error number is the first number present on the line.
		;**
		;**		The line number is preceded by a space or tab, the word "line",
		;**		or is surrounded by parentheses.
		;**

		(if (search_fwd "<*.[cChHmM]|{asm}|{ASM}[ \\t,(:][ \\t]@{line }@\\c[0-9]")
			(
				;**
				;**		If we find it (and if we got here, we did find it),
				;**	we convert it to a real number, read in the error message,
				;**	and check to see if it applies to the current file.
				;**

				(= error_line (atoi (read)))
				(save_position)
				(beginning_of_line)
				(= error_text (read))
				(restore_position)
				(= error_text (substr error_text 1 (- (strlen error_text) 1)))

				(if (index (lower error_text) error_extension)
					(
						(= error_extension error_text)
						(= error_text (substr error_text (search_string "[ \\t(:][0-9][0-9]@*\\c[`\"a-zA-Z]" error_text)))

						;**
						;**		If the compiler tells us if it is an error, we put
						;**	errors in bright colors, and warnings in normal
						;**	normal colors.
						;**

						(if (! (search_string "[Ww]arning" error_extension))
							(error error_text)
						;else							
							(message error_text)
						)
						(= line error_line)
				
						;**
						;**		Now, we skip all other messages that pertain to the
						;**	same line, assuming that they are spurious messages
						;**	generated by the compiler because of the first error.
						;**

						(while (== line error_line)
							(
								(if (search_fwd "<*.[cChHmM]|{asm}|{ASM}[ \\t,(:][ \\t]@{line }@\\c[0-9]")
									(
										(= line (atoi (read)))

										(if (!= line error_line)
											(beginning_of_line)
										)
									)
								;else
									(= line 0)
								)
							)
						)
						(set_buffer old_buffer)
						(goto_old_line error_line)
						(beginning_of_line)
						(search_fwd "[~ \t]")
					)
				;else
					(
						(set_buffer old_buffer)
						(message error_text)
					)
				)
			)
		;else
			(
				(message "No more errors.")
				(delete_buffer buffer_ptr)
				(set_buffer old_buffer)
			)
		)
	)
)

;**
;**		previous_error:
;**
;**		This just displays the text of the last error so it can be re-read.
;**

(macro previous_error
	(
		(errorfix)
		(message error_text)
	)
)
