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>
556 lines
16 KiB
Plaintext
556 lines
16 KiB
Plaintext
/*
|
|
** BRIEF -- Basic Reconfigurable Interactive Editing Facility
|
|
**
|
|
** Written by Dave Nanian and Michael Strickman.
|
|
*/
|
|
|
|
/*
|
|
** compile.cb:
|
|
**
|
|
** This file contains all of the standard BRIEF macros for compiling
|
|
** files.
|
|
*/
|
|
|
|
string add_to_path (string path, string name);
|
|
string escape_re (string original, ~string);
|
|
int next_error (~int action);
|
|
int cc (string pass_string, ~string legal_extension, ~int check_warnings, ~int background, ~int continuation);
|
|
void _cc_complete (int ret_code, string full_name, int check_warnings, string pass_string);
|
|
int cm (...);
|
|
int cb (...);
|
|
|
|
extern int _check_warnings, // Check for warnings in the compiler output?
|
|
_background; // Run the compiler in the background?
|
|
|
|
/*
|
|
** compile_it:
|
|
**
|
|
** This function automatically compiles the file in the current
|
|
** buffer. It uses the "BC<extension>" environment variable to
|
|
** determine what to do with any given file. If no "BC<extension>"
|
|
** environment variable exists for the specific file extension being
|
|
** compiled, compile_it checks to see if it's ".c", ".m" or ".asm". If
|
|
** it's a macro file the "cm" macro is executed; if it's a C file, a
|
|
** generic "cc" command is used, and if it's an ASM file, the Macro
|
|
** Assembler is invoked.
|
|
**
|
|
** Other compilers can be supported very easily. Simply set a
|
|
** "BC<extension>" environment variable to the "pass string" you
|
|
** want to use. For example, if you wanted to call the (fictitious)
|
|
** UnderWare C compiler, which has two passes called "under" and "ware",
|
|
** you'd use the command:
|
|
**
|
|
** set bcc="under %s;ware %s"
|
|
**
|
|
** You must place the pass string in quotes. If you don't, compile_it
|
|
** calls a macro named whatever is in the pass string. So, for example,
|
|
** if you accidently set your pass string using the command:
|
|
**
|
|
** set bcc=under %s;ware %s
|
|
**
|
|
** compile_it would call a macro named "under %s;ware %s". This probably
|
|
** isn't what you want -- but this feature can be useful if you want to
|
|
** run some sort of custom macro for the file extension (compile_it does
|
|
** this when compiling cm files).
|
|
**
|
|
** Also note that DOS requires you to double any % characters that
|
|
** appear in a batch file. So if you were setting the BCC variable in
|
|
** your autoexec, you would use the line:
|
|
**
|
|
** set bcc="under %%s;ware %%s"
|
|
**
|
|
** Each pass begins with the name of the executable program that does
|
|
** that compilation pass. That is followed by the a space, the special
|
|
** string "%s", which is replaced by the filename (with NO extension),
|
|
** and the multiple pass separation character ";". If you want to put
|
|
** a ";" in your pass string, use "\;".
|
|
**
|
|
** These special characters are very important -- don't forget them!
|
|
** Remember that the special "%s" string is only replaced by the filename,
|
|
** not the filename and the extension. Up to seven of these can be specified
|
|
** in any given pass. If your compiler requires the extension as well,
|
|
** place it after this string (e.g. "cc -c %s.c").
|
|
**
|
|
** If you want to pass options to your compiler, you can place them
|
|
** either before or after the "%s". Placing them before puts the option
|
|
** before the filename, and vice versa.
|
|
**
|
|
** If your compiler doesn't return an error code, put an exclamation
|
|
** point in front of the first pass string (either inside or outside the
|
|
** quotes); this will override the current warnings_only setting and
|
|
** automatically check for errors in the compiler output.
|
|
*/
|
|
|
|
int compile_it (void)
|
|
{
|
|
string extension, // File extension of the current file.
|
|
command; // Command to be used to compile file.
|
|
|
|
inq_names (NULL, extension);
|
|
command = trim (ltrim (inq_environment ("BC" + upper (extension))));
|
|
|
|
if (command != "")
|
|
{
|
|
if (index (command, "\""))
|
|
{
|
|
int loc,
|
|
background;
|
|
|
|
background = _background;
|
|
|
|
while (loc = index (command, "\""))
|
|
command = substr (command, 1, loc - 1) + substr (command, loc + 1);
|
|
|
|
if (substr (command, strlen (command), 1) == "&")
|
|
{
|
|
background = 1;
|
|
command = trim (substr (command, 1, strlen (command) - 1));
|
|
}
|
|
if ("!" == substr (command, 1, 1))
|
|
returns (cc (substr (command, 2), extension, 1, background));
|
|
else
|
|
returns (cc (command, extension, NULL, background));
|
|
}
|
|
else
|
|
{
|
|
int check_warnings = _check_warnings;
|
|
|
|
if (substr (command, 1, 1) == "!")
|
|
{
|
|
check_warnings = 1;
|
|
command = substr (command, 2);
|
|
}
|
|
returns (execute_macro (command, command, check_warnings));
|
|
}
|
|
}
|
|
else
|
|
switch (extension)
|
|
{
|
|
case "m":
|
|
returns (cm (_check_warnings));
|
|
|
|
case "asm":
|
|
returns (cc ("masm %s\\;", extension));
|
|
|
|
case "c":
|
|
returns (cc ("cc -c %s.c", extension));
|
|
|
|
case "cb":
|
|
returns (cb (_check_warnings));
|
|
|
|
default:
|
|
{
|
|
error ("Can't compile: no BC%s environment variable.", upper (extension));
|
|
returns (-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
** warnings_only:
|
|
**
|
|
** This macro toggles whether or not errors are searched for when a
|
|
** compile is done and the compiler returns "no errors". Note that its
|
|
** value is saved in the state file.
|
|
*/
|
|
|
|
int warnings_only (~int)
|
|
{
|
|
int ret_code,
|
|
previous_value;
|
|
|
|
previous_value = _check_warnings;
|
|
|
|
if (!(ret_code = get_parm (0, _check_warnings)))
|
|
_check_warnings = !_check_warnings;
|
|
|
|
if (!ret_code || inq_called () == "")
|
|
message ("Compile warning detection %s.", _check_warnings ? "on" : "off");
|
|
|
|
returns (previous_value);
|
|
}
|
|
|
|
/*
|
|
** bgd_compilation:
|
|
**
|
|
** This macro toggles whether or not compilation is done in the
|
|
** background under operating systems that support it. Note that this
|
|
** value is saved in the state file.
|
|
*/
|
|
|
|
int bgd_compilation (~int)
|
|
{
|
|
int ret_code,
|
|
previous_value;
|
|
|
|
previous_value = _background;
|
|
|
|
if (!(ret_code = get_parm (0, _background)))
|
|
_background = !_background;
|
|
|
|
if (!ret_code || inq_called () == "")
|
|
message ("Background compilation %s.", _background ? "on" : "off");
|
|
|
|
returns (previous_value);
|
|
}
|
|
|
|
/*
|
|
** cc:
|
|
**
|
|
** This routine compiles the file in the current buffer using the
|
|
** passed "pass string" and the BRIEF DOS command. It needs a lot
|
|
** of memory to run -- either turn swapping on or start with at least
|
|
** 256K and -M20. Of course, this may vary depending with the size
|
|
** and memory requirements of the specific compiler you're using.
|
|
**
|
|
** The "pass string" passed should be of the form:
|
|
**
|
|
** pass_1 %s;pass_2 %s;...pass_n %s
|
|
**
|
|
** The optional second parameter is an extended file type -- this is
|
|
** used by the "cm" and "cb" macros, and to compile other types of files
|
|
** (e.g. .asm).
|
|
**
|
|
** If no pass string is specified, it defaults to a generic "cc"
|
|
** command. If no extension is specified, it defaults to "c".
|
|
*/
|
|
|
|
int cc (string pass_string, ~string, ~int, ~int, ~int continuation)
|
|
{
|
|
string file_name, // The name of the file we're compiling.
|
|
legal_extension, // The extension of files we can compile.
|
|
path, // The path of the file we're compiling.
|
|
extension, // The extension of the file we're compiling.
|
|
command_line, // The compile command line.
|
|
old_path, // The original path we were on.
|
|
error_file; // The file to put error information in.
|
|
|
|
int loc, // Generic index place holder.
|
|
length, // Generic length place holder.
|
|
ret_code = 1, // Return code from DOS.
|
|
buffer_id, // Buffer ID of error buffer.
|
|
check_warnings, // Examine result of compile for errors?
|
|
background; // Do compilation in background?
|
|
|
|
/*
|
|
** We get the name of the file from inq_names and check to see
|
|
** if it's a legal extension.
|
|
*/
|
|
|
|
if (!get_parm (1, legal_extension))
|
|
legal_extension = "c";
|
|
|
|
inq_names (path, extension, file_name);
|
|
|
|
if (extension != legal_extension)
|
|
error ("Current buffer is not a .%s file.", legal_extension);
|
|
else
|
|
{
|
|
/*
|
|
** If the file has been modified, we want to make sure the
|
|
** current version gets compiled, so we write it to disk.
|
|
**
|
|
** Note that if the user does not specify a pass string, it
|
|
** defaults to a generic "cc" command.
|
|
*/
|
|
|
|
if (pass_string == "")
|
|
pass_string = "cc -c %s.c";
|
|
|
|
if (!get_parm (2, check_warnings))
|
|
check_warnings = _check_warnings;
|
|
|
|
if (!get_parm (3, background))
|
|
background = _background;
|
|
|
|
version (old_path);
|
|
|
|
if (old_path != "OS/2")
|
|
background = 0;
|
|
|
|
if (!continuation && inq_modified ())
|
|
{
|
|
int old_msg_level;
|
|
|
|
old_msg_level = inq_msg_level ();
|
|
set_msg_level (0);
|
|
ret_code = write_buffer ();
|
|
set_msg_level (old_msg_level);
|
|
}
|
|
|
|
if (ret_code > 0)
|
|
{
|
|
/*
|
|
** Now we parse the filename off the path string,
|
|
** making sure to handle the possible presence of forward
|
|
** and backward slash characters. We then replace the
|
|
** file_name's ".c" with ".err" for redirection purposes.
|
|
*/
|
|
|
|
path = substr (path, 1, rindex (path, substr (path, 3, 1)));
|
|
|
|
if (strlen (path) > 3)
|
|
path = substr (path, 1, strlen (path) - 1);
|
|
|
|
file_name = substr (file_name, 1, index (file_name, ".") - 1);
|
|
error_file = file_name + ".err";
|
|
ret_code = 0;
|
|
|
|
/*
|
|
** We want the .obj file to end up in the file's
|
|
** directory, so we change to the directory where the
|
|
** file is, saving the current directory. We also make
|
|
** the file's drive the default drive.
|
|
*/
|
|
|
|
getwd ("", command_line);
|
|
getwd (path, old_path);
|
|
old_path = substr (command_line, 1, 1) + substr (old_path, 2);
|
|
cd (path);
|
|
cd (substr (path, 1, 2));
|
|
|
|
/*
|
|
** This loop goes through each pass of the compiler,
|
|
** checks to see if the return code was OK, and, if so,
|
|
** continues along. If an error occurs, the loop exits
|
|
** immediately.
|
|
*/
|
|
|
|
while (!ret_code && strlen (pass_string))
|
|
{
|
|
if (loc = search_string ("[~\\\\];", pass_string))
|
|
{
|
|
command_line = substr (pass_string, 1, loc);
|
|
pass_string = substr (pass_string, loc + 2);
|
|
}
|
|
else
|
|
{
|
|
command_line = pass_string;
|
|
pass_string = "";
|
|
}
|
|
command_line += " >&" + error_file;
|
|
|
|
while (loc = index (command_line, "\\;"))
|
|
command_line = substr (command_line, 1, loc - 1) + substr (command_line, loc + 1);
|
|
|
|
/*
|
|
** Since environment variables don't allow "=" characters to
|
|
** appear in them, we use "--" as an alias.
|
|
*/
|
|
|
|
while (loc = index (command_line, "--"))
|
|
command_line = substr (command_line, 1, loc - 1) + "=" + substr (command_line, loc + 2);
|
|
|
|
sprintf (command_line, command_line, file_name, file_name, file_name, file_name, file_name, file_name, file_name);
|
|
message ("%s", background ? command_line + " &" : command_line);
|
|
|
|
if (background)
|
|
{
|
|
string completion_rtn;
|
|
|
|
inq_names (file_name);
|
|
sprintf (completion_rtn, "_cc_complete \"%s\" %d \"%s\"", file_name, check_warnings, escape_re (pass_string, "\""));
|
|
dos (command_line, 0, completion_rtn);
|
|
break;
|
|
}
|
|
else if ((ret_code = dos (command_line, 0)) > 0)
|
|
next_error ();
|
|
else if (ret_code == 0 && check_warnings)
|
|
ret_code = next_error (2);
|
|
}
|
|
/*
|
|
** Finally, we restore the old directory. If the
|
|
** compilation did not succeed, the next_error macro
|
|
** was called to place the cursor under the error.
|
|
**
|
|
** Otherwise, the temporary file is deleted and a
|
|
** message is printed.
|
|
*/
|
|
|
|
if (!background && ret_code <= 0)
|
|
{
|
|
del (error_file);
|
|
|
|
if (ret_code == 0)
|
|
message ("Compilation successful.");
|
|
}
|
|
cd (substr (old_path, 3));
|
|
cd (substr (old_path, 1, 2));
|
|
}
|
|
}
|
|
returns (ret_code);
|
|
}
|
|
|
|
/*
|
|
** _cc_complete:
|
|
**
|
|
** This is the completion routine for background compilation.
|
|
*/
|
|
|
|
void _cc_complete (int ret_code, string full_name, int check_warnings, string pass_string)
|
|
{
|
|
int in_memory,
|
|
old_buf_id = inq_buffer (),
|
|
buf_id,
|
|
loc;
|
|
|
|
string file_name,
|
|
extension;
|
|
|
|
/*
|
|
** First, we check to see if the buffer being compiled
|
|
** is in the buffer list.
|
|
*/
|
|
|
|
in_memory = (inq_buffer (full_name) != 0);
|
|
|
|
/*
|
|
** If in_memory is true then the current buffer is set
|
|
** to the buffer we're compiling. Otherwise, we have to
|
|
** read it into memory to prepare for a possible next_error.
|
|
*/
|
|
|
|
loc = strlen (full_name);
|
|
|
|
while (!index ("/\\", substr (full_name, loc, 1)))
|
|
--loc;
|
|
|
|
file_name = substr (full_name, loc + 1);
|
|
|
|
if (!in_memory)
|
|
if (buf_id = create_buffer (file_name, full_name))
|
|
set_buffer (buf_id);
|
|
else
|
|
error ("_cc_complete: fatal error, can't edit %s.", file_name);
|
|
|
|
inq_names (NULL, extension);
|
|
|
|
if (!ret_code)
|
|
{
|
|
if (check_warnings)
|
|
ret_code = next_error (3);
|
|
|
|
if (!ret_code)
|
|
if (pass_string == "")
|
|
{
|
|
message ("Compilation of %s successful.", file_name);
|
|
del (substr (full_name, 1, rindex (full_name, ".")) + "err");
|
|
}
|
|
else
|
|
cc (pass_string, extension, check_warnings, 1, 1);
|
|
}
|
|
/*
|
|
** At this point, we know we can clean up. First, we set
|
|
** the current buffer to the one we started with. Then, if we
|
|
** temporarily edited the buffer we were compiling, we delete
|
|
** it.
|
|
*/
|
|
|
|
set_buffer (old_buf_id);
|
|
|
|
if (!in_memory)
|
|
delete_buffer (buf_id);
|
|
|
|
if (ret_code)
|
|
{
|
|
beep ();
|
|
|
|
if (buf_id == 0 || old_buf_id == buf_id)
|
|
error ("Error compiling %s; press %s.", file_name, inq_assignment ("next_error", 1));
|
|
else
|
|
error ("Error compiling %s.", file_name, inq_assignment ("next_error", 1));
|
|
}
|
|
}
|
|
|
|
/*
|
|
** cm:
|
|
**
|
|
** This macro compiles the macro in the current buffer (if there is one).
|
|
** If the compilation was successful, the macro is re-loaded (or loaded, as
|
|
** the case may be). If the compilation failed, the errorfix macro
|
|
** is used to locate the problem in the .m file.
|
|
**
|
|
** Note that the "cc" macro is used to do most of the work. This macro
|
|
** merely calls "cc" with the parameters required for compilation of a
|
|
** macro file, then loads the file if the compilation was successful.
|
|
*/
|
|
|
|
int cm (...)
|
|
{
|
|
int check_warnings,
|
|
curr_parm,
|
|
ret_code;
|
|
|
|
string command_line = "cm";
|
|
|
|
for (curr_parm = 1 ; curr_parm >= 0 && !get_parm (curr_parm, check_warnings) ; curr_parm--);
|
|
|
|
if (curr_parm > 0)
|
|
get_parm (0, command_line);
|
|
|
|
command_line += " %s";
|
|
|
|
if (!(ret_code = cc (command_line, "m", check_warnings, 0)))
|
|
{
|
|
int old_msg_level;
|
|
|
|
string name;
|
|
|
|
inq_names (name);
|
|
name = substr (name, 1, rindex (name, ".") - 1);
|
|
old_msg_level = inq_msg_level ();
|
|
set_msg_level (3);
|
|
delete_macro (name);
|
|
set_msg_level (old_msg_level);
|
|
load_macro (name);
|
|
message ("Macro compiled and loaded.");
|
|
}
|
|
returns (ret_code);
|
|
}
|
|
|
|
/*
|
|
** cb:
|
|
**
|
|
** This macro compiles the CBRIEF macro in the current buffer (if
|
|
** there is one). If the compilation was successful, the macro is
|
|
** re-loaded (or loaded, as the case may be). If the compilation failed,
|
|
** the errorfix macro is used to locate the problem in the .m file.
|
|
**
|
|
** Note that the "cc" macro is used to do most of the work. This macro
|
|
** merely calls "cc" with the parameters required for compilation of a
|
|
** CBRIEF file, then loads the file if the compilation was successful.
|
|
*/
|
|
|
|
int cb (...)
|
|
{
|
|
int check_warnings,
|
|
curr_parm,
|
|
ret_code;
|
|
|
|
string command_line = "cb";
|
|
|
|
for (curr_parm = 1 ; curr_parm >= 0 && !get_parm (curr_parm, check_warnings) ; curr_parm--);
|
|
|
|
if (curr_parm > 0)
|
|
get_parm (0, command_line);
|
|
|
|
command_line += " %s";
|
|
|
|
if (!(ret_code = cc (command_line, "cb", check_warnings, 0)))
|
|
{
|
|
int old_msg_level;
|
|
|
|
string name;
|
|
|
|
inq_names (name);
|
|
name = substr (name, 1, rindex (name, ".") - 1);
|
|
old_msg_level = inq_msg_level ();
|
|
set_msg_level (3);
|
|
delete_macro (name);
|
|
set_msg_level (old_msg_level);
|
|
load_macro (name);
|
|
message ("Macro compiled and loaded.");
|
|
}
|
|
returns (ret_code);
|
|
}
|