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>
888 lines
18 KiB
C++
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);
|
|
}
|