/* * BRIEF -- Basic Reconfigurable Interactive Editing Facility * * Written by Dave Nanian and Michael Strickman. * * 1-14-90 Nat added code to translate environment variables * which may be passed in on compile command line */ /* * compile.cb: * * This file contains all of the standard BRIEF macros for compiling files. */ string trans_envar (string cmdln); // NAT - translates out env var's 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" environment variable to * determine what to do with any given file. If no "BC" * 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" 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 = trans_envar( inq_environment( "BC" + upper (extension) ) ); if( !get_parm( 0, command, "Compile with: ", 80, command ) ) return( 0 ); 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 legal_extension, ~int, ~int, ~int continuation) { string file_name, // The name of the file we're compiling. 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 (legal_extension == "") legal_extension = "c"; inq_names (path, extension, file_name); if (extension != legal_extension) error ("Current buffer is not a .%s file.", pass_string); 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); /* * After the above loop has completed, 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, 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 (extension, pass_string, 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 compiline, 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 ("CBRIEF macro compiled and loaded."); } returns (ret_code); }