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

888 lines
18 KiB
C++

/*
** BRIEF -- Basic Reconfigurable Interactive Editing Facility
**
** Written by Dave Nanian and Michael Strickman.
*/
/*
** misc.h:
**
** This file contains a number of miscellaneous BRIEF functions.
*/
#define REG_MARK 1
#define COL_MARK 2
#define LINE_MARK 3
#define NI_MARK 4
#define NOTHING 0
#define REPLACE 1
#define REMOVE 2
string add_to_path (string path, string name);
void _col_cut (~int append);
void _col_copy (~int append);
void _col_paste ();
void _col_delete ();
int next_word ();
int previous_word ();
int _dir, // Direction of last search.
_t_dir, // Direction of last translate.
_reg_exp, // Use regular expressions?
_block_search, // Restrict search to blocks?
_check_warnings, // Recognize warning-only compiles?
_background, // Perform compilations in the background?
_package_buf; // Parsed language package information.
string _s_pat, // Value of last search pattern.
_t_pat, // Value of last translate pattern.
_r_pat; // Value of last replacement pattern.
/*
** _init:
**
** This macro defines the global variables used by the various BRIEF
** packages that may be restored by the restore macro. Placing the
** variables here ensures the macro isn't autoloaded when its variables
** are referenced.
*/
void _init ()
{
_reg_exp = _dir = _t_dir = 1;
}
/*
** delete_char:
**
** This macro implements block-sensitive delete.
*/
replacement delete_char ()
{
int block_type;
if (inq_called () != "" || !(block_type = inq_marked ()))
return (delete_char ());
else if (block_type == COL_MARK)
_col_delete ();
else
{
int old_msg_level = inq_msg_level ();
set_msg_level (0);
delete_block ();
set_msg_level (old_msg_level);
}
}
/*
** delete_next_word, delete_previous_word:
**
** These functions use the language-sensitive next_word and previous_word
** functions to perform word deletion.
*/
int delete_next_word ()
{
int line;
string line_end = trim (read ());
save_position ();
drop_anchor (4);
inq_position (line);
if (next_word ())
{
int after_line;
inq_position (after_line);
if (after_line != line && line_end != "")
{
restore_position ();
raise_anchor ();
delete_to_eol ();
return (1);
}
else if (inq_marked ())
{
delete_block ();
return (1);
}
}
restore_position ();
raise_anchor ();
returns (0);
}
int delete_previous_word ()
{
save_position ();
drop_anchor (4);
if (previous_word () && inq_marked ())
{
delete_block ();
restore_position (0);
returns (1);
}
else
{
restore_position ();
raise_anchor ();
returns (0);
}
}
/*
** delete_to_bol:
**
** This routine deletes to the beginning of the line.
*/
int delete_to_bol (void)
{
drop_anchor (4);
if (beginning_of_line ())
{
delete_block ();
returns (1);
}
else
{
raise_anchor ();
returns (0);
}
}
/*
** write_buffer:
**
** This replacement macro decides what to do when Alt-w is pressed.
** If a block is marked, then the "write_block" primitive is called.
** Otherwise, write_buffer is called to write out the entire buffer.
*/
replacement int write_buffer (...)
{
if (inq_called () != "")
return (write_buffer ());
else
{
int old_msg_level = inq_msg_level (),
ret_code;
set_msg_level (0);
if (inq_marked ())
ret_code = write_block ();
else
ret_code = write_buffer ();
set_msg_level (old_msg_level);
return (ret_code);
}
}
/*
** write_and_exit:
**
** This command writes all buffers and exits BRIEF.
*/
void write_and_exit (void)
{
/*
** We set the message level to ensure that the informational
** messages displayed by exit ("w") can be read by the user.
*/
set_msg_level (0);
exit ("w");
}
/*
** load_keystroke_macro:
**
** This routine acts as a front-end to the playback function, prompting
** for the name of the keystrke macro to load.
*/
int load_keystroke_macro (~string)
{
int ret_code,
old_msg_level = set_msg_level (0);
string file_name;
if (inq_keystroke_macro ())
ret_code = playback ();
else if ((ret_code = get_parm (0, file_name, "Keystroke macro file: ")) > 0)
if (file_name != "")
ret_code = playback (file_name, NULL, 0);
set_msg_level (old_msg_level);
returns (ret_code);
}
/*
** save_keystroke_macro:
**
** This replacement for the base save_keystroke_macro displays the path
** that the macro is going to be saved to. Rather than save to the current
** directory as the built-in version does, the replacement cycles through
** the BPATH.
*/
replacement int save_keystroke_macro (~string)
{
if (inq_called () != "")
returns (save_keystroke_macro ());
else
{
int old_msg_level = set_msg_level (0),
num_keys,
ret_code;
/*
** First, we determine whether or not we should bother prompting.
** The normal save_keystroke_macro command won't even bother prompting
** if we're remembering, playing back, or if there's no macro. We do
** the same, but let the base function display the error message.
*/
if (inq_keystroke_macro (num_keys) || num_keys == 0)
ret_code = save_keystroke_macro ();
else
{
string prompt,
bpath,
file_name,
curr_dir;
int start_loc = 1,
loc,
len,
max_width;
/*
** Here, we display a prompt that should help people save
** keystroke macros. We loop through the BPATH environment
** variable, displaying the next directory every time <Enter>
** is pressed on a blank line. This directory is the "base"
** directory to save to.
*/
inq_screen_size (NULL, max_width);
max_width -= 32 + 23 + 4;
if ((bpath = trim (ltrim (compress (inq_environment ("BPATH"))))) == "")
bpath = ";/brief/macros";
do
{
if (loc = index (substr (bpath, start_loc), ";"))
{
curr_dir = trim (ltrim (substr (substr (bpath, start_loc), 1, loc - 1)));
start_loc += loc;
}
else
{
curr_dir = trim (ltrim (substr (bpath, start_loc)));
start_loc = 1;
}
if (curr_dir == "" || curr_dir == ".")
getwd ("", curr_dir);
if ((len = strlen (curr_dir)) <= max_width)
sprintf (prompt, "Save keystrokes as [%s]: ", curr_dir);
else
{
int over = len - max_width,
mid = len / 2;
sprintf (prompt, "Save keystrokes as [%s...%s]: ",
substr (curr_dir, 1, mid - (over / 2) - 3 - (over % 2)),
substr (curr_dir, mid + (over / 2) + 1));
}
}
while ((ret_code = get_parm (0, file_name, prompt)) && !strlen (file_name));
/*
** At this point, we take the "base" directory (from the BPATH)
** and the file name that the user specified, and put them together
** in an intelligent way. If a drive letter is specified, or if
** the path specified is absolute, we ignore the base and just use
** what the user specified.
*/
if (ret_code > 0)
if (index (file_name, ":") || index ("/\\", substr (file_name, 1, 1)))
ret_code = save_keystroke_macro (file_name);
else
ret_code = save_keystroke_macro (add_to_path (curr_dir, file_name));
}
returns (ret_code);
}
}
/*
** display_file_name:
**
** Displays the current buffer's file name on the status line. If the
** name is too large, as much of the end as will fit is displayed, with an
** ellipsis in the middle.
*/
void display_file_name ()
{
#define FILE_PROMPT "File: "
string file_name;
int len,
max_width;
inq_names (file_name, NULL);
inq_screen_size (NULL, max_width);
if (inq_modified ())
file_name += "*";
len = strlen (file_name) + strlen( FILE_PROMPT ); // Length of string
if ( len <= max_width)
message ("%s%s", FILE_PROMPT, file_name);
else
{
int over = len - max_width,
mid = len / 2;
message ("%s%s...%s", FILE_PROMPT,
substr (file_name, 1, mid - (over / 2) - 3 - (over % 2)),
substr (file_name, mid + (over / 2) + 1));
}
}
/*
** open_line:
**
** Adds a blank line after the current line, placing the cursor on the
** first column of the new line. The normal macro for the Enter key is
** used to perform the function after the end of line.
*/
void open_line ()
{
end_of_line ();
if (inq_assignment ("<Enter>") == "self_insert" || inq_assignment ("<Enter>") == "open_line")
insert ("\n");
else
execute_macro (inq_assignment ("<Enter>"));
}
/*
** back_tab:
**
** Moves back to the previous tab stop. Deals with the appropriate
** special cases as well.
*/
void back_tab ()
{
int col,
start_col,
start_dist = distance_to_tab ();
inq_position (NULL, start_col);
/*
** If we were on a tab stop to start with, we back up two
** characters. Otherwise, just one.
*/
move_rel (0, -1);
if (distance_to_tab () < start_dist)
move_rel (0, -1);
while (distance_to_tab () != 1 && move_rel (0, -1));
/*
** If the last place we were was the first column, we just stay
** there. Also, if there were any one space tabs, we know that
** we should not move back to the original tab stop.
*/
inq_position (NULL, col);
if (col != 1 && start_col - col != 1)
move_rel (0, 1);
}
/*
** quote:
**
** This macro inserts the next character from the keyboard as-is.
** Non-ASCII keys, such as the Alt keys, are pushed back and executed
** as commands.
*/
void quote ()
{
int value;
while ((value = read_char ()) == -1);
if (value & 0xff && ((value & 0xff) <= 127 || !(value & 0xff00)))
insert ("%c", value);
else
push_back (value);
}
/*
** copy:
**
** This macro checks to see if a block is marked when the "copy" key
** is pressed. If so, the block is copied. If not, a single line is
** copied.
*/
replacement int copy (~int)
{
if (inq_called () != "")
returns (copy ());
else
{
int block_type;
if (block_type = inq_marked ())
{
if (block_type == COL_MARK)
{
int append;
get_parm (0, append);
_col_copy (append);
}
else
{
int old_msg_level = inq_msg_level ();
set_msg_level (0);
copy ();
set_msg_level (old_msg_level);
}
}
else
{
drop_anchor (LINE_MARK);
copy ();
message ("Line copied to scrap.");
}
}
}
/*
** cut:
**
** This macro checks to see if a block is marked when the "cut" key
** is pressed. If so, the block is cut. If not, a single line is cut.
*/
replacement int cut (~int)
{
if (inq_called () != "")
returns (cut ());
else
{
int block_type;
if (block_type = inq_marked ())
{
if (block_type == COL_MARK)
{
int append;
get_parm (0, append);
_col_cut (append);
}
else
{
int old_msg_level = inq_msg_level ();
set_msg_level (0);
cut ();
set_msg_level (old_msg_level);
}
}
else
{
drop_anchor (LINE_MARK);
cut ();
message ("Line cut to scrap.");
}
}
}
/*
** paste:
**
** This replacement macro checks to see if the current scrap is a column
** scrap buffer. If so, it calls the _col_paste routine.
*/
replacement int paste ()
{
if (inq_called () != "")
returns (paste ());
else
{
int scrap_type;
inq_scrap (NULL, scrap_type);
if (scrap_type == COL_MARK)
_col_paste ();
else
{
int old_msg_level = inq_msg_level ();
set_msg_level (0);
paste ();
set_msg_level (old_msg_level);
}
}
}
/*
** _home/new_home:
**
** These two macros implement "triple-click" Home logic:
**
** The first time Home is pressed, it moves to the beginning of the
** line.
**
** The second time Home is pressed, it moves to the top of the screen.
**
** The third time Home is pressed, it moves to the top of the file.
**
** If any keys are pressed in between presses of Home, this sequence
** starts back at the beginning.
*/
void _home ()
{
global int _new_home;
if (inq_command () != "new_home")
{
_new_home = 0;
beginning_of_line ();
assign_to_key ("<Home>", "new_home");
push_back (key_to_int ("<Home>"));
}
else if (++_new_home == 1)
top_of_window ();
else
{
top_of_buffer ();
set_top_left (1, 1);
}
}
void new_home ()
{
assign_to_key ("<Home>", "_home");
}
/*
** _end/new_end:
**
** These two macros implement "triple-click" End logic:
**
** The first time End is pressed, it moves to the end of the line.
**
** The second time End is pressed, it moves to the end of the last line
** on the screen.
**
** The third time End is pressed, it moves to the end of the file.
**
** If any keys are pressed in between presses of End, this sequence
** starts back at the beginning.
*/
void _end ()
{
global int _new_end;
if (inq_command () != "new_end")
{
_new_end = 0;
end_of_line ();
assign_to_key ("<End>", "new_end");
push_back (key_to_int ("<End>"));
}
else
{
int col,
win_cols,
shift;
inq_window_size (NULL, win_cols, shift);
if (++_new_end == 1)
{
end_of_window ();
end_of_line ();
}
else
end_of_buffer ();
inq_position (NULL, col);
if (shift && col <= win_cols)
{
beginning_of_line ();
refresh ();
end_of_line ();
}
else if (_new_end == 1)
refresh ();
}
}
void new_end ()
{
assign_to_key ("<End>", "_end");
}
/*
** add_to_path:
**
** This simple routine adds a filename to a path and returns the
** result.
*/
string add_to_path (string path, string name)
{
if (strlen (path) && !index ("/\\:", substr (path, strlen (path))))
path += "/";
path += name;
returns (path);
}
/*
** escape_re:
**
** This function changes a string with regular expression characters in
** it to one without, and returns the result. Optionally, a different group
** of "regular expression" characters can be passed.
*/
string escape_re (string original, ~string)
{
int curr_pos,
original_len;
string bad_chars,
escaped,
curr_char;
if (!get_parm (1, bad_chars))
bad_chars = "?*@+\\|%${}[]<>";
original_len = strlen (original);
while (original_len--)
escaped += (index (bad_chars, curr_char = substr (original, ++curr_pos, 1)) ? "\\" : "") + curr_char;
returns (escaped);
}
/*
** search_path:
**
** This routine searches a path, such as the PATH or BPATH environment
** variable, for the specified file. It returns the null string if the
** file wasn't found, or the full path name if it was.
*/
string search_path (string path, string filename)
{
int semi_loc;
string curr_name;
/*
** Get the directory list to check and append a semicolon to it.
** This means we will always check the current directory last
** unless we are told to check it sooner.
*/
path += ";";
/*
** Search all the directories for the given file name. As soon
** as we find the file, append the name of the file to the
** given directory and return the full name.
*/
while (semi_loc = index (path, ";"))
{
curr_name = add_to_path (substr (path, 1, semi_loc - 1), filename);
/*
** If the file exists in the given directory, we return
** the full name without bothering to parse anything else.
*/
if (exist (curr_name))
return (curr_name);
path = substr (path, semi_loc + 1);
}
returns ("");
}
/*
** gen_block:
**
** This routine goes through the current block and passes the information
** from the block to a given routine. That routine performs an operation
** on the string, and then tells gen_block to replace, remove or ignore the
** data.
**
** Note that no line boundaries are ever removed by this routine. Also,
** the anchor is left as is.
*/
void gen_block (string macro_name, string msg)
{
string before,
after;
int start_line,
start_col,
end_line,
end_col,
curr_line,
num_lines,
num_chars,
do_upper,
lines_per_step,
curr_step,
scale_factor,
type,
action;
message (msg + "...");
save_position ();
if (!inq_marked ())
mark (LINE_MARK);
type = inq_marked (start_line, start_col, end_line, end_col);
num_lines = (end_line - start_line) + 1;
curr_line = start_line;
if (above (num_lines, 100))
{
if (above (num_lines, 32767))
scale_factor = 1;
else
scale_factor = 32767 / num_lines;
lines_per_step = num_lines / 100;
}
else
{
scale_factor = 100;
lines_per_step = 1;
}
move_abs (start_line, start_col);
while (below_eq (curr_line, end_line))
{
save_position ();
drop_anchor ();
move_abs (0, curr_line == end_line || type == COL_MARK ? end_col : 2 * inq_line_length ());
num_chars = inq_mark_size ();
raise_anchor ();
restore_position ();
after = before = read (num_chars);
action = execute_macro (macro_name, after);
if (action == REMOVE || after != before)
{
int test1_col,
test2_col;
inq_position (NULL, test1_col);
if (action == REPLACE && prev_char ())
{
next_char ();
inq_position (NULL, test2_col);
if (test2_col > test1_col)
prev_char ();
}
if (index (after, "\n"))
{
after = substr (after, 1, strlen (after) - 1);
delete_to_eol ();
}
else
{
drop_anchor ();
move_abs (0, end_col);
delete_block ();
}
if (action == REPLACE)
insert (after);
}
move_abs (++curr_line, type == COL_MARK ? start_col : 1);
if (++curr_step == lines_per_step)
{
curr_step = 0;
message ("%s, %d%% complete...", msg, (100 * (((curr_line - start_line) * scale_factor) / num_lines)) / scale_factor);
}
}
restore_position ();
}
void toupper ()
{
gen_block ("_do_upper", "Uppercasing block");
raise_anchor ();
message ("Uppercasing complete.");
}
int _do_upper (string line)
{
put_parm (0, upper (line));
returns (REPLACE);
}
void tolower ()
{
gen_block ("_do_lower", "Lowercasing block");
raise_anchor ();
message ("Lowercasing complete.");
}
int _do_lower (string line)
{
put_parm (0, lower (line));
returns (REPLACE);
}