Files
CydandClaude Fable 5 db7745fcd0 sda4: commit the Glaze developer hard-drive dump
Un-ignored: the dev drive is the ground truth the restoration and
emulator work constantly reference (DPL3/LIBDPL + VRENDER i860 renderer
source, BT/RP live+dev game trees, VGL_LABS pod boot, scene/audio
content). Kept in-repo for the pod-owner community.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 19:41:15 -05:00

830 lines
22 KiB
Plaintext

/*
** BRIEF -- Basic Reconfigurable Interactive Editing Facility
**
** Written by Dave Nanian and Michael Strickman.
*/
/* Revision history:
** -----------------
** 1) IO 29/01/90
** . Added 0-9 to some of the searches so that they cope with
** numbers in file extensions (which is legal)
*/
/*
** errorfix.cb:
**
** This file contains the main driver and support macros for BRIEF's
** error location facility.
*/
#define TRUE 1
#define FALSE 0
#define WARNING 1
#define ERROR 2
#define DISPLAY_NEXT_ERROR 0
#define POP_UP_ERROR_WINDOW 1
#define PROBE_AND_DISPLAY 2
#define JUST_PROBE 3
string add_to_path (string path, string file_name);
string escape_re (string original, ~string);
void center_line (void);
void _exit ();
int get_error_file (string full_name);
int find_error_file (string full_name);
int delete_error_file (string full_name, int delete_buffer_too);
void add_error_file (string full_name);
int next_error (~int action);
int _call_next (string error_extension, string file_name);
int _call_prev (string error_extension, string file_name);
int _next_error (~string extension);
int _prev_error (~string extension);
int _error_info (string error_file, int, int, string, string file_name);
int _error_buf_id, // Holds the error buffer.
_cancel_errorfix; // Flag to show whether or not Cancel done from menu.
/*
** _init:
**
** This macro gets run when the macro is loaded. It creates the
** error buffer, and sets _error_buf_id to the error buffer id.
*/
void _init (void)
{
_error_buf_id = create_buffer ("Error Stack", NULL, 1);
}
/*
** compile_it
**
** This is a replacement compile macro. It first deletes the error
** file, if one exists. The file name is also removed from the error
** stack, and then the actual compilation is executed.
**
** Returns:
**
** Result of the original compile macro.
*/
replacement compile_it ()
{
string file_name; // Name of the file that's being compiled.
inq_names (file_name);
/*
** Before compiling the file, we ensure that its error file
** has been removed from the error stack and the buffer has been
** deleted from memory. This ensures the file can be written
** by the compiler, and is up to date.
*/
delete_error_file (file_name, TRUE);
returns (compile_it ());
}
/*
** get_error_file:
**
** This routine obtains the most recent error file name, which is
** located at the top of the error stack file. It returns the name
** in the passed parameter.
**
** Returns:
**
** TRUE if a file was obtained from the error stack
** FALSE if a file could not be obtained from the error stack
*/
int get_error_file (string full_name)
{
int old_buf_id = set_buffer (_error_buf_id),
loc;
string line;
move_abs (1, 1);
if ((full_name = read ()) != "\n")
{
/*
** Trim is used to remove the newline.
*/
full_name = trim (full_name);
}
else
full_name = "";
put_parm (0, full_name);
set_buffer (old_buf_id);
returns (full_name != "");
}
/*
** find_error_file:
**
** This routine finds the location of the given file and path
** in the error stack file. The current line in the error stack
** is left at the found line.
**
**
** Returns:
**
** 0 if the file name and path are not found.
** Otherwise, the line number of the file path in the error stack file.
*/
int find_error_file (string full_name)
{
int old_buf_id = set_buffer (_error_buf_id),
error_file_line;
top_of_buffer ();
/*
** Since the full_name includes the drive letter, we don't need
** to worry about embedded paths (i.e., accidentally finding
** "\foo\foo\foo.c" instead of "\foo\foo.c").
*/
if (search_fwd (full_name + "\n", 0))
inq_position (error_file_line);
set_buffer (old_buf_id);
returns (error_file_line);
}
/*
** delete_error_file:
**
** This routine finds and if found, deletes, the given file and path
** from the error stack file.
*/
int delete_error_file (string full_name, int delete_buffer_too)
{
int ret_code,
old_buf_id = set_buffer (_error_buf_id),
buffer_id,
loc;
if (ret_code = find_error_file (full_name))
delete_line ();
/*
** If there is already a buffer for the error file, we
** "create" it (create_buffer returns the ID of a buffer
** that already existed) and then delete it immediately.
** Note that under some very obscure circumstances, the
** create_buffer call could fail. If it does, it'll
** return 0, which is an invalid buffer id. We check for
** this case since delete_buffer does not.
*/
loc = rindex (full_name, substr (full_name, 3, 1));
if (rindex (substr (full_name, loc + 1), "."))
full_name = substr (full_name, 1, rindex (full_name, ".")) + "err";
else
full_name += ".err";
if (delete_buffer_too
&& (buffer_id = create_buffer ("Error File", full_name, 1))
&& !inq_views (buffer_id))
delete_buffer (buffer_id);
set_buffer (old_buf_id);
returns (ret_code);
}
/*
** add_error_file:
**
** This routine adds an error file to the error stack file.
** The given file name (and its path) is inserted at the top of the
** error stack file. A call is made to delete_error_file, to find
** and delete a previous occurrence of this file name in the error
** stack file (if it exists).
*/
void add_error_file (string full_name)
{
int old_buf_id = set_buffer (_error_buf_id);
delete_error_file (full_name, FALSE);
top_of_buffer ();
insert (full_name + "\n");
set_buffer (old_buf_id);
}
/*
** fix_error_name:
**
** This routine constructs the necessary names from the given file_name,
** and checks to see if we've already attached the error file to another
** file. If so, we return that file's name rather than the name passed.
** This ensures that, when you've got an include file and a source file with
** the same name, you keep the error file attached to the source.
**
*/
void fix_error_name (string file_name, string error_file, string error_extension)
{
int loc,
old_buf_id = set_buffer (_error_buf_id);
/*
** This loop'll go through once for an unaliased error file, and twice
** if we find it attached to another buffer. In essence, it's only a
** loop to save having to repeat the parsing code.
*/
for (;;)
{
top_of_buffer ();
loc = rindex (file_name, substr (file_name, 3, 1));
if (rindex (substr (file_name, loc + 1), "."))
{
error_extension = substr (file_name, loc = rindex (file_name, "."));
error_file = substr (file_name, 1, loc - 1);
}
else
{
error_file = file_name;
error_extension = ".";
}
if (!search_fwd (file_name + "\n", 0) && search_fwd ("<" + escape_re (error_file) + "[~/\\\\\n]@>"))
file_name = trim (read ());
else
break;
}
put_parm (0, file_name);
put_parm (1, error_file + ".err");
put_parm (2, error_extension);
}
/*
** next_error:
**
** This routine is the engine for BRIEF's error location facility. It
** reads in the error file and calls the appropriate routines to display
** either an individual error message or a window full of error message
** information.
**
** If no parameter (or zero) is passed to next_error, the error file is
** read into a buffer and searched for error messages. If the buffer is
** empty, the message "No errors." is displayed and the error buffer is
** removed. If the buffer is not empty, and a message can be located, the
** text of the message is displayed on the message line, the cursor is placed
** on the first non-blank character of the line containing the error, and that
** line is centered in the window. If no messages can be located, the error
** file is presented in a pop-up window.
**
** If a "1" is passed to next_error, the error information is displayed in
** a window, with the current message highlighted. If no messages could be
** recognised, the highlight is not displayed, and the cursor can be moved
** anywhere within the file.
**
** If a "2" is passed to next_error, the error file is tested to see if
** any messages can be recognised. If so, the same action is taken as with
** next_error 0. If not, no message is displayed and the error file buffer
** is deleted.
**
** If a "3" is passed, a test is done and a return code is returned, but
** no message is displayed in either case.
**
** In all cases, next_error returns 0 if no error was found, and non-zero
** if there was an error.
**
** If an error file is not found for the current file, then the error
** stack is accessed to obtain the error file accessed most recently.
*/
int next_error (~int action)
{
int error_buf,
old_buf_id = inq_buffer (),
line,
col,
loc,
attempts,
error_type,
more_errors,
prev_errors,
errors_exist,
total_error_lines,
windowed;
string error_msg,
error_file,
error_file_name,
curr_file_name,
buf_name,
parms,
error_extension;
_cancel_errorfix = FALSE;
inq_names (error_file_name, NULL, buf_name);
curr_file_name = error_file_name;
/*
** Find the error file for the current buffer. If one does
** not exist, then get the most recently accessed error file
** from the error stack.
*/
while (TRUE)
{
/*
** From the current error_file_name, construct the error_file and
** error_extension (which is either the error_file_name's extension
** or "." if there is no extension). If there's a similar file
** already in the error list, use it instead.
*/
fix_error_name (error_file_name, error_file, error_extension);
if (!(error_buf = create_buffer ("Error File", error_file, TRUE)))
{
/*
** Note that we could have gone through this list a number
** of times before this failure happens, so we must ensure the
** current buffer is reset.
*/
set_buffer (old_buf_id);
return (FALSE);
}
set_buffer (error_buf);
/*
** errors_exist is set to TRUE if there is data in the file.
** We assume that an empty file means no file, and that a file
** with data in it is worth looking into.
*/
save_position ();
top_of_buffer ();
errors_exist = end_of_buffer ();
inq_position (total_error_lines);
restore_position ();
if (!errors_exist)
{
/*
** If there aren't any errors in the file, we need to pop the
** buffer off the error stack (if it's there), and check the one
** at the top of the stack. If there isn't one, we're done.
**
** The attempts variable basically keeps track of the number
** of buffers we've examined that were actually on the error
** stack. This lets us distinguish between the "No errors"
** and the "No more errors" cases.
*/
attempts += delete_error_file (error_file_name, TRUE);
/*
** If we're just supposed to check whether or not there are
** errors in the current file, we stop here.
*/
if (action >= PROBE_AND_DISPLAY)
break;
}
else
{
/*
** Now that we know the file isn't empty, we check to
** see if there are "real" errors in the file. This is done
** with the next/prev error routine appropriate to the file's
** extension. We set the more_errors and prev_errors variables
** based on this test, and reset the errors_exist variable based
** on both of them.
*/
add_error_file (error_file_name);
parms = error_extension + " " + buf_name;
windowed = (action == POP_UP_ERROR_WINDOW);
save_position ();
more_errors = execute_macro ("_call_next " + parms);
if (more_errors && windowed)
execute_macro ("_call_prev " + parms);
else
{
down ();
save_position ();
prev_errors = execute_macro ("_call_prev " + parms);
restore_position ();
}
restore_position ();
errors_exist = (more_errors || prev_errors);
break;
}
/*
** If there are any more error files on the error stack, we check
** them out the same way.
*/
if (!get_error_file (error_file_name))
{
if (attempts == 0)
{
set_buffer (old_buf_id);
message ("No errors.");
return (0);
}
break;
}
more_errors = prev_errors = FALSE;
}
/*
** If we're supposed to pop up a window, or if the file isn't
** empty and we don't recognize any error messages, we pop up an
** error window. In the latter case, we do it so the user can
** see what's going on.
*/
if (windowed = (action < PROBE_AND_DISPLAY && (windowed || !errors_exist)))
{
int lines,
cols;
keyboard_push ();
assign_to_key ("<Enter>", "_exit");
assign_to_key ("<Esc>", "_error_cancel");
assign_to_key ("<Left>", "left");
assign_to_key ("<Right>", "right");
assign_to_key ("<Home>", "beginning_of_line");
assign_to_key ("<End>", "end_of_line");
/*
** If there are recognizable errors in the file, we assign the
** up and down keys to the error search functions. Otherwise, we
** just move up and down normally. This allows the user to browse
** the file even though we don't know what's going on.
*/
if (errors_exist)
{
save_position ();
drop_anchor (3);
assign_to_key ("<Up>", "_call_prev " + parms);
assign_to_key ("<Down>", "_call_next " + parms);
}
else
{
assign_to_key ("<Up>", "up");
assign_to_key ("<Down>", "down");
}
beginning_of_line ();
inq_screen_size (lines, cols);
create_window (5, lines - 4, cols - 5, 3, " or  to move, <Enter> to select, <Esc> to exit");
attach_buffer (error_buf);
refresh ();
process ();
if (errors_exist)
{
restore_position ();
raise_anchor ();
}
delete_window ();
/*
** Note that we set the current buffer back before doing the
** keyboard_pop: this ensures that the local keyboard (if any)
** is restored properly.
*/
set_buffer (old_buf_id);
keyboard_pop ();
set_buffer (error_buf);
}
/*
** At this point the cursor is pointing at the requested error
** message. If the user didn't cancel, we display the chosen message
** and move the cursor to the appropriate line.
*/
if (!_cancel_errorfix && action != JUST_PROBE)
{
string macro_to_call = error_extension + "_error_info";
if (!inq_macro (macro_to_call))
macro_to_call = "_error_info";
if ((windowed && errors_exist || more_errors)
&& (error_type = execute_macro (macro_to_call, buf_name, line, col, error_msg, curr_file_name, error_file_name)))
{
int error_file_buf = inq_buffer (curr_file_name);
if (error_file_buf != old_buf_id)
{
set_buffer (old_buf_id);
/*
** If the buffer is in the system, and it's being viewed in
** a window, we switch to it. Otherwise, we use edit_file to
** read it in (if necessary) and attach it to the current window.
*/
if (error_file_buf != 0 && inq_views (error_file_buf))
{
int curr_window = inq_window (),
window_buf_id;
do
{
curr_window = next_window (curr_window);
inq_window_info (curr_window, window_buf_id);
}
while (window_buf_id != error_file_buf);
set_window (curr_window);
old_buf_id = error_file_buf;
}
else if (edit_file (curr_file_name) > 0)
old_buf_id = inq_buffer ();
}
else
set_buffer (old_buf_id);
if (error_type < 0)
error_type = -error_type;
if (error_type >= 3)
goto_line (line);
else
goto_old_line (line);
center_line ();
if (col)
move_abs (0, col);
else
{
beginning_of_line ();
next_char (strlen (trim (read ())) - strlen (ltrim (trim (read ()))));
}
if (error_type == ERROR || error_type == -ERROR)
error (error_msg);
else
message (error_msg);
}
else if (windowed)
message (trim (ltrim (read ())));
else
{
if (errors_exist)
message ("No more errors.");
delete_error_file (error_file_name, TRUE);
}
}
else if (_cancel_errorfix)
message ("");
set_buffer (old_buf_id);
returns (more_errors);
}
/*
** _call_next:
**
** This macro is used to call the _next_error function. It checks to
** see if there is a file extension specific function first. If not, it
** calls the generic function.
*/
int _call_next (string error_extension, string file_name)
{
string macro_name = "_next_error";
int ret_code,
mark_type,
col;
if (mark_type = inq_marked ())
raise_anchor ();
if (inq_macro (error_extension + macro_name))
macro_name = error_extension + macro_name;
inq_position (NULL, col);
restore_position ();
ret_code = execute_macro (macro_name + " " + error_extension + " " + file_name);
save_position ();
move_abs (0, col);
if (mark_type)
drop_anchor (mark_type);
returns (ret_code);
}
/*
** _call_prev:
**
** This macro is used to call the _prev_error function. It checks to
** see if there is a file extension specific function first. If not, it
** calls the generic function.
*/
int _call_prev (string error_extension, string file_name)
{
string macro_name = "_prev_error";
int ret_code,
mark_type,
col;
if (mark_type = inq_marked ())
raise_anchor ();
inq_position (NULL, col);
restore_position ();
save_position ();
if (up ())
end_of_line ();
if (inq_macro (error_extension + macro_name))
macro_name = error_extension + macro_name;
restore_position (!(ret_code = execute_macro (macro_name + " " + error_extension + " " + file_name)));
save_position ();
move_abs (0, col);
if (mark_type)
drop_anchor (mark_type);
returns (ret_code);
}
void _error_cancel ()
{
_cancel_errorfix = 1;
_exit ();
}
/*
** _next_error, _prev_error, _error_info:
**
** These routines are the default routines for locating error
** information. They can be enhanced with file extension specific
** error location functions that are written for a specific compiler.
**
** The _next_error and _prev_error macros move the cursor to the
** next and previous error message, respectively. They return TRUE
** if another error message is found, and FALSE otherwise.
**
** _error_info takes a given line in the error buffer (located by
** the _next and _previous error routines) and returns the information
** needed by BRIEF to locate the error message. It returns 0 if there
** is no error message, 1 if the error is a warning, and 2 if it's an
** error. It also returns the line and column in the file the error
** corresponds to, as well as the message that should be displayed at
** the bottom of the window.
*/
/*
** _next_error, _prev_error:
**
** Both of these routines are passed the extension of the file that
** has been compiled and the full file name. Although the generic
** routines don't make use of this information, extension-specific
** routines could.
*/
int _next_error (~string)
{
returns (search_fwd ("<[\\\\0-9a-zA-Z]+.[0-9a-zA-Z][~ \t\n]@[ \t,(:][ \t]@{line }@\\c[0-9]", 1, 0));
}
int _prev_error (~string)
{
returns (search_back ("<[\\\\0-9a-zA-Z]+.[0-9a-zA-Z][~ \t\n]@[ \t,(:][ \t]@{line }@\\c[0-9]", 1, 0));
}
/*
** _error_info:
**
** This routine parses a given error line and returns information
** about it. It is passed the following parameters:
**
** Parameter Description
** --------- -----------
** 0 The name of the file the error was in.
**
** The following are write-only parameters passed back to the calling
** function:
**
** Parameter Description
** --------- -----------
** 1 The line of the file the error was on.
** 2 The column of the file the error was on.
** If 0 is passed back, the first non-blank
** character is assumed.
** 3 The actual error message, minus any useless
** information.
**
** Finally, this last parameter is the buffer identifier of the source
** buffer.
**
** Parameter Description
** --------- -----------
** 4 The full file name of the file the errors are
** in. This macro may reset this name if the
** errors are found in a different buffer.
**
** 5 The file that "owns" the errors -- could be
** different than parameter 4 if we're in an
** include file, and the error file is owned
** by a source file.
**
** _error_info returns an integer corresponding to the level of the
** error encountered.
**
** Error Code Meaning
** ---------- -------
** 0 No error
** 1 Warning error in source file
** -1 Warning error in another file
** 2 "Normal" error in source file
** -2 "Normal" error in another file
** 3 Actual line (not original) error in source file.
** -3 Actual line (not original) error in another file.
** 4 Actual line (not original) warning in source file.
** -4 Actual line (not original) warning in another file.
**
** This information is used by the next_error driver to determine
** how to display the message.
*/
int _error_info (string error_file, int, int, string, string file_name, string base_name)
{
int length,
position,
error_level,
error_line;
string error_text;
if (!(error_line = atoi (read (10))))
if (read (1) == "0")
error_line = 1;
else
return (FALSE);
save_position ();
beginning_of_line ();
error_text = trim (ltrim (read ()));
restore_position ();
error_level = WARNING + !index (lower (error_text), "warning");
/*
** If the error file name isn't found in the error text, we have to do
** more work...
*/
if (!index (lower (error_text), lower (error_file)))
{
position = search_string ("<*\\c[~ \t]+.[0-9a-zA-Z][~ \t\n]@[ \t,(:]", error_text, length);
error_file = substr (error_text, position, length - 1);
if (!index ("/\\", substr (error_file, 1, 1)) && substr (error_file, 2, 1) != ":")
file_name = add_to_path (substr (base_name, 1, rindex (base_name, substr (base_name, 3, 1)) - 1), error_file);
else
file_name = error_file;
error_level *= -1;
}
error_text = trim (ltrim (read ()));
error_text = substr (error_text, search_string ("['`\"a-zA-Z]", error_text));
put_parm (1, error_line);
put_parm (2, 0);
put_parm (3, error_text);
put_parm (4, file_name);
returns (error_level);
}