/* ** BRIEF -- Basic Reconfigurable Interactive Editing Facility ** ** Written by Dave Nanian and Michael Strickman. */ /* ** search.cb: ** ** This file contains all of the standard BRIEF macros for search ** and translate. */ /* ** search: ** ** This macro defines the global variables needed by the search and ** translate macros. */ #define FORWARD 1 // Value used to search FORWARD. #define BACKWARD 0 // Value used to search BACKWARD. #define TRUE 1 // Standard definitions of TRUE & FALSE. #define FALSE 0 #define MARKSRCH_TIMEOUT 5 // Timeout for marked location. extern int _dir, // Current/last search direction. _t_dir, // Current/last translate direction. _reg_exp, // Regular expressions on/off. _block_search, // Block search on/off. _package_buf; // Parsed language package information. extern string _s_pat, // Current/last search pattern. _t_pat, // Current/last translate pattern. _r_pat; // Current/last replacement pattern. string _nw_macro, // Current next_word macro. _pw_macro, // Current previous_word macro. _ew_macro, // Current begin_word macro. _bw_macro; // Current end_word macro. string _evaluate_package (string extension, string macro_name); void _marksrch (int match_length, ~string); int _marksrch_active; // Flag to tell _invalid_key we're active. /* ** _on: ** ** This simple generic package resets the _nw_macro and _pw_macro variables ** when we switch buffers and BPACKAGE parsing is active. */ string _on () { returns (_nw_macro = _pw_macro = _ew_macro = _bw_macro = ""); } /* ** next_word, _next_word: ** ** Locates the next word in the file using a regular expression. If ** there are no following words in the file, the cursor does not move. */ int next_word () { if (_nw_macro != "") returns (execute_macro (_nw_macro)); else { string extension; inq_names (NULL, extension); if (!_package_buf) { extension = "." + extension + "_next_word"; returns (execute_macro (inq_macro (extension) ? extension : "_next_word")); } else returns (execute_macro (_nw_macro = _evaluate_package (extension, "_next_word"))); } } int _next_word () { returns (search_fwd ("[ \t\x0c\n]\\c[~ \t\x0c\n]")); } /* ** previous_word, _previous_word: ** ** Locates the previous word in the file using a regular expression. If ** there are no preceding words in the file, the cursor does not move. */ int previous_word () { if (_pw_macro != "") returns (execute_macro (_pw_macro)); else { string extension; inq_names (NULL, extension); if (!_package_buf) { extension = "." + extension + "_previous_word"; returns (execute_macro (inq_macro (extension) ? extension : "_previous_word")); } else returns (execute_macro (_pw_macro = _evaluate_package (extension, "_previous_word"))); } } int _previous_word () { if (prev_char ()) returns (search_back ("{[~ \t\x0c\n]+}|{[ \t\x0c\n]+}[ \t\x0c\n]@", -2)); else returns (0); } /* ** .c_next_word, .c_previous_word, .cb_next_word, .cb_previous_word, ** .h_next_word, .h_previous_word, .inc_next_word, .inc_previous_word, ** .m_next_word, .m_previous_word, .asm_next_word, .asm_previous_word: ** ** These definitions are used as "language sensitive" word patterns ** for C, Assembler and the BRIEF macro languages. */ int .c_next_word () { int length; save_position (); if (length = search_fwd ("{[a-zA-Z0-9_]+}|{[~ \t\x0c\na-zA-Z0-9_]+}|{[ \t\x0c\n]+}[ \t\x0c\n]@", -2)) { int at_end_of_line; next_char (--length); at_end_of_line = (read (1) == "\n"); restore_position (at_end_of_line); returns (!at_end_of_line); } else { restore_position (); returns (0); } } int .c_previous_word () { if (prev_char ()) returns (search_back ("{[a-zA-Z0-9_]+}|{[~ \t\x0c\na-zA-Z0-9_]+}[ \t\x0c\n]@", -2)); else returns (0); } int .cb_next_word () { returns (.c_next_word ()); } int .cb_previous_word () { returns (.c_previous_word ()); } int .h_next_word () { returns (.c_next_word ()); } int .h_previous_word () { returns (.c_previous_word ()); } int .inc_next_word () { returns (.c_next_word ()); } int .inc_previous_word () { returns (.c_previous_word ()); } int .m_next_word () { returns (.c_next_word ()); } int .m_previous_word () { returns (.c_previous_word ()); } int .asm_next_word () { returns (.c_next_word ()); } int .asm_previous_word () { returns (.c_previous_word ()); } /* ** search_fwd: ** ** This macro is used to search forward in the buffer from the ** keyboard level. If there is a previously defined pattern, it is ** displayed on the command line. Typing any character makes this pattern ** vanish and the new one will take its place. If Esc is typed, the old ** pattern is retained. */ replacement int search_fwd (~string, ~int, ~int, ~int) { /* ** If we're not being called from the keyboard, we don't want ** to extend the search command at all. */ if (inq_called () != "") returns (search_fwd ()); else { /* ** We set the message level to 0 so that the user can see ** the messages that the search command displays. */ int old_msg_level = set_msg_level (0), ret_code; string prompt; /* ** It's nice to be able to see the setting of the regular ** expression toggle in the search string. We check to see ** what it is, and change the search prompt accordingly. */ sprintf (prompt, " Search %sfor: ", _reg_exp ? "" : "(RE off) "); if (get_parm (0, _s_pat, prompt, NULL, _s_pat)) { _dir = FORWARD; /* ** Finally, we search for the specified pattern, using ** the current regular expression setting. Since we've ** given the pattern in the function call, the user is ** not prompted. */ if ((ret_code = search_fwd (_s_pat, _reg_exp, NULL, _block_search)) > 0) _marksrch (ret_code, "search_fwd"); } else ret_code = -1; set_msg_level (old_msg_level); returns (ret_code); } } /* ** search_back: ** ** This macro is used to search backward in the buffer from the ** keyboard level. If there is a previously defined pattern, it is ** displayed on the command line. Typing any character makes this pattern ** vanish and the new one will take its place. If Esc is typed, the old ** pattern is retained. */ replacement int search_back (~string, ~int, ~int, ~int) { /* ** If we're not being called from the keyboard, we don't want ** to extend the search command at all. */ if (inq_called () != "") returns (search_back ()); else { /* ** We set the message level to 0 so that the user can see ** the messages that the search command displays. */ int old_msg_level = set_msg_level (0), ret_code; string prompt; /* ** It's nice to be able to see the setting of the regular ** expression toggle in the search string. We check to see ** what it is, and change the search prompt accordingly. */ sprintf (prompt, " Search %sfor: ", _reg_exp ? "" : "(RE off) "); /* ** Here we retrieve the search pattern for the user. Note ** that we pass the previous search pattern (which is stored in ** the global variable _s_pat) to _get_default as a default prompt ** response. */ if (get_parm (0, _s_pat, prompt, NULL, _s_pat)) { _dir = BACKWARD; /* ** Finally, we search for the specified pattern, using ** the current regular expression setting. Since we've ** given the pattern in the function call, the user is ** not prompted. */ if ((ret_code = search_back (_s_pat, _reg_exp, NULL, _block_search)) > 0) _marksrch (ret_code, "search_back"); } else ret_code = -1; set_msg_level (old_msg_level); returns (ret_code); } } /* ** search_again: ** ** This macro uses the information provided by search_fwd and search_back ** to search for the same pattern again, in the same direction. */ int search_again () { /* ** We set the message level to 0 so that the user can see ** the messages that the search commands display. */ int ret_code, old_msg_level = set_msg_level (0); /* ** We check to see if there is a previous search pattern -- ** if not, we can't search for it. */ if (strlen (_s_pat)) { /* ** We're searching again, so we have to move before ** we call the appropriate search routine. We do this ** to ensure we don't find the same pattern a second ** time. The current cursor location is saved on the ** position stack because of this: if the search fails, ** we want to put the cursor back where it was before the ** movement. */ save_position (); if (_dir == BACKWARD && prev_char ()) ret_code = search_back (_s_pat, _reg_exp, NULL, _block_search); else if (_dir == FORWARD && next_char ()) ret_code = search_fwd (_s_pat, _reg_exp, NULL, _block_search); else message ("Pattern not found."); if (ret_code > 0) _marksrch (ret_code, "search_again"); /* ** Restore the cursor position if the search failed. */ restore_position (!ret_code); } else { error ("No previous search pattern."); beep (); } set_msg_level (old_msg_level); returns (ret_code); } /* Define a mouse event handler to exit from the process if a mouse ** event occurs. Save the parameters to pass on to the current handler ** after the process exits. */ void idle_mouse(); int do_mouse_event, parm1, parm2, parm3, parm4; /* ** _marksrch: ** ** This routine accepts the return code from the search routines and ** does marking based on that return code. It can be replaced by the ** user to modify the marking action. */ void _marksrch (int match_length, ~string) { if (!inq_kbd_char ()) { drop_anchor (); next_char (match_length - 1 - (match_length != 1)); swap_anchor (); keyboard_push (); _marksrch_active++; refresh (); set_mouse_action( "idle_mouse" ); do_mouse_event = 0; register_macro (4, "_marksrch_idle"); process (); unregister_macro (4, "_marksrch_idle"); _marksrch_active--; keyboard_pop (); raise_anchor (); if (do_mouse_event) { string mse_handler; sprintf(mse_handler, "%s %d %d %d %d", inq_mouse_action(), parm1, parm2, parm3, parm4); execute_macro(mse_handler); } } } /* This function is called if a mouse event occurs while a pattern is ** marked after a search. */ void idle_mouse(int p1, int p2, int p3, int p4) { parm1 = p1; parm2 = p2; parm3 = p3; parm4 = p4; do_mouse_event = 1; exit(); } void _marksrch_idle () { if (MARKSRCH_TIMEOUT != 0 && inq_idle_time () >= MARKSRCH_TIMEOUT) exit (); } /* ** _invalid_key: ** ** This routine is called when an "invalid" key is pressed during normal ** macro processing. If the _marksrch_active variable isn't set, we just ** call down to the default _invalid_key handler. Otherwise, we exit the ** shell process we're in, and allow the keystroke to be processed again. */ void _invalid_key (void) { if (_marksrch_active) exit (); else _invalid_key (); } /* ** translate_back: ** ** This routine handles calls to translate back from the keyboard level. */ int translate_back (~string, ~string, ~int, ~int, ~int, ~int, ~int) { returns (translate ()); } /* ** translate: ** ** This routine handles calls to translate from the keyboard level, ** making sure that the call heeds the value of the regular expression ** toggle. As a special bonus, BRIEF will use the value of the last ** thing searched for as the translation pattern if no pattern is ** entered. Once a translate pattern is specified, the search and ** translate patterns separate -- a pattern is maintained for each ** operation. */ replacement int translate (~string, ~string, ~int, ~int, ~int, ~int, ~int) { int old_msg_level, ret_code, dir; string pattern, default_pat, prompt; /* ** If we're not being called from the keyboard, we don't want ** to extend the search command at all. */ if (inq_called () != "") if (inq_called () != "translate_back") return (translate ()); else dir = BACKWARD; else dir = FORWARD; old_msg_level = set_msg_level (0); /* ** It's nice to be able to see the setting of the regular ** expression toggle in the search string. We check to see ** what it is, and change the translate prompt accordingly. */ sprintf (prompt, "%c Pattern%s: ", dir ? '' : '', _reg_exp ? "" : " (RE off)"); /* ** If there haven't been any previous translate patterns, ** we use the last search pattern as the default response to ** the Pattern: prompt. Otherwise, we use the previous ** translate pattern. */ if (!strlen (_t_pat)) default_pat = _s_pat; else default_pat = _t_pat; if (get_parm (0, pattern, prompt, NULL, default_pat) && get_parm (1, _r_pat, "Replacement: ", NULL, _r_pat)) { _t_pat = pattern; _t_dir = dir; ret_code = translate (_t_pat, _r_pat, NULL, _reg_exp, NULL, inq_marked (), _t_dir); } else ret_code = -1; set_msg_level (old_msg_level); returns (ret_code); } /* ** translate_again: ** ** This call implements the translate_again command -- it uses the values ** of the various global translate variables to perform the translation. */ int translate_again () { int ret_code, old_msg_level = set_msg_level (0); if (strlen (_t_pat) || strlen (_r_pat)) ret_code = translate (_t_pat, _r_pat, NULL, _reg_exp, NULL, inq_marked (), _t_dir); else { error ("No previous translate pattern."); beep (); } set_msg_level (old_msg_level); returns (ret_code); } /* ** toggle_re: ** ** This macro toggles the state of the regular expression global ** variable, which controls whether regular expressions are used during ** search and translate. */ int toggle_re (~int) { int ret_code, previous_value = _reg_exp; if (!(ret_code = get_parm (0, _reg_exp))) _reg_exp = !_reg_exp; if (!ret_code || inq_called () == "") /* ** This displays a message on the status line showing the ** new regular expression setting. Note the use of (if) -- ** this is the equivalent of the C ?: operator. If the ** expression is true, the statement evaluates to "off"; ** otherwise, it evaluates to "on". ** ** Also note that the message is not displayed unless ** the macro is called from the keyboard or the command is ** used as a toggle. */ message ("Regular expressions %s.", _reg_exp ? "on" : "off"); returns (previous_value); } /* ** block_search: ** ** This macro toggles the state of block searching. */ int block_search (~int) { int ret_code, previous_value = _block_search; if (!(ret_code = get_parm (0, _block_search))) _block_search = !_block_search; if (!ret_code || inq_called () == "") message ("Block searching %s.", _block_search ? "on" : "off"); returns (previous_value); } /* ** i_search: ** ** This macro implements a simple incremental search. It does not ** allow regular expressions to be specified. The Esc key terminates ** the search. ** ** The backspace key moves you to the previously matched pattern. */ void i_search () { int char, level; string pattern, character, assignment; /* ** We use the save_position/restore_position commands to keep ** track of the previously matched location. Searching back for ** the identical pattern would not work, since it would match the ** current occurrence. In addition, prev_char then search_back ** would not match if the current occurrence were the correct one. */ save_position (); while (TRUE) { /* ** Since we want the cursor to remain on the screen in the ** proper place, we create a "pseudo-cursor" with an IBM ** graphics character. */ message ("I-search for: %sÜ", pattern); while ((char = read_char ()) == -1); if (char == key_to_int ("")) break; sprintf (character, "%s#%d", character, char); assignment = inq_assignment (character); if (assignment == "self_insert" || assignment == "nothing") { if (char == key_to_int ("")) { if (strlen (pattern) > 1) pattern = substr (pattern, 1, strlen (pattern) - 1); else pattern = ""; restore_position (); if (!--level) { save_position (); level = 1; } } else { /* ** Now we have a character that we can search for. ** We add it to the string of characters, and use ** the search_fwd command to locate it. While we're ** searching, we place an ellipsis after the ** string, so the user know's we're doing something. */ sprintf (character, "%c", char & 0xff); pattern += character; save_position (); message ("I-search for: %sÜ...", pattern); if (!search_fwd (pattern, FALSE)) { beep (); restore_position (); pattern = substr (pattern, 1, strlen (pattern) - 1); } else ++level; } refresh (); character = ""; } else if (assignment != "ambiguous") { character = ""; if (assignment == "search_again") { message ("I-search for: %sÜ...", pattern); save_position (); next_char (); if (!search_fwd (pattern, FALSE)) { beep (); restore_position (); } else { refresh (); restore_position (FALSE); } } else beep (); } } /* ** When finished, we pop all of the saved positions off the ** stack. Note the use of the "pop, don't move" parameter with ** */ while (--level >= 0) restore_position (FALSE); message ("I-search ended."); } /* ** end_word: ** _end_word: ** ** Locates the end of the current word in the files using a regular ** expression. If there is no current word in the file, the cursor does not ** move. */ int end_word () { if (_ew_macro != "") returns (execute_macro (_ew_macro)); else { string extension; inq_names (NULL, extension); if (!_package_buf) { extension = "." + extension + "_end_word"; returns (execute_macro (inq_macro (extension) ? extension : "_end_word")); } else returns (execute_macro (_ew_macro = _evaluate_package (extension, "_end_word"))); } } int _end_word () { returns (search_fwd ("{[~ \t\x0c\n]\\c[ \t\x0c\n]}|>")); } /* ** begin_word: ** _begin_word: ** ** Locates the beginning of the current word. */ int begin_word () { if (_bw_macro != "") returns (execute_macro (_bw_macro)); else { string extension; inq_names (NULL, extension); if (!_package_buf) { extension = "." + extension + "_begin_word"; returns (execute_macro (inq_macro (extension) ? extension : "_begin_word")); } else returns (execute_macro (_bw_macro = _evaluate_package (extension, "_begin_word"))); } } int _begin_word () { returns (search_back ("{[ \t\x0c]}|<[ \t\x0c]@\\c[~ \t\x0c]")); } /* ** .c_end_word, .c_begin_word, .cb_end_word, .cb_begin_word, ** .h_end_word, .h_begin_word, .inc_end_word, .inc_begin_word, ** .m_end_word, .m_begin_word, .asm_end_word, .asm_begin_word: ** ** These definitions are used as "language sensitive" word patterns ** for C, Assembler and the BRIEF macro languages. */ int .c_end_word () { returns( search_fwd ("{[a-zA-Z0-9_]+}|{[~ \t\x0c\na-zA-Z0-9_]+}|{[ \t\x0c\n]+>}\\c", -2) ); } int .c_begin_word () { returns (search_back ("{[a-zA-Z0-9_]+}|{[~ \t\x0c\na-zA-Z0-9_]+}", -2)); } int .cb_end_word () { returns (.c_end_word ()); } int .cb_begin_word () { returns (.c_begin_word ()); } int .h_end_word () { returns (.c_end_word ()); } int .h_begin_word () { returns (.c_begin_word ()); } int .inc_end_word () { returns (.c_end_word ()); } int .inc_begin_word () { returns (.c_begin_word ()); } int .m_end_word () { returns (.c_end_word ()); } int .m_begin_word () { returns (.c_begin_word ()); } int .asm_end_word () { returns (.c_end_word ()); } int .asm_begin_word () { returns (.c_begin_word ()); }