diff options
Diffstat (limited to 'src/search.cpp')
-rw-r--r-- | src/search.cpp | 946 |
1 files changed, 0 insertions, 946 deletions
diff --git a/src/search.cpp b/src/search.cpp deleted file mode 100644 index 63c59e9..0000000 --- a/src/search.cpp +++ /dev/null @@ -1,946 +0,0 @@ -/* - * Copyright (C) 2012-2017 Robin Haberkorn - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <string.h> - -#include <glib.h> -#include <glib/gprintf.h> - -#include "sciteco.h" -#include "string-utils.h" -#include "expressions.h" -#include "undo.h" -#include "qregisters.h" -#include "ring.h" -#include "parser.h" -#include "search.h" -#include "error.h" - -namespace SciTECO { - -namespace States { - StateSearch search; - StateSearchAll searchall; - StateSearchKill searchkill; - StateSearchDelete searchdelete; - - StateReplace replace; - StateReplace_insert replace_insert; - StateReplace_ignore replace_ignore; - - StateReplaceDefault replacedefault; - StateReplaceDefault_insert replacedefault_insert; - StateReplaceDefault_ignore replacedefault_ignore; -} - -/* - * Command states - */ - -/*$ S search pattern - * S[pattern]$ -- Search for pattern - * [n]S[pattern]$ - * -S[pattern]$ - * from,toS[pattern]$ - * :S[pattern]$ -> Success|Failure - * [n]:S[pattern]$ -> Success|Failure - * -:S[pattern]$ -> Success|Failure - * from,to:S[pattern]$ -> Success|Failure - * - * Search for <pattern> in the current buffer/Q-Register. - * Search order and range depends on the arguments given. - * By default without any arguments, S will search forward - * from dot till file end. - * The optional single argument specifies the occurrence - * to search (1 is the first occurrence, 2 the second, etc.). - * Negative values for <n> perform backward searches. - * If missing, the sign prefix is implied for <n>. - * Therefore \(lq-S\(rq will search for the first occurrence - * of <pattern> before dot. - * - * If two arguments are specified on the command, - * search will be bounded in the character range <from> up to - * <to>, and only the first occurrence will be searched. - * <from> might be larger than <to> in which case a backward - * search is performed in the selected range. - * - * After performing the search, the search <pattern> is saved - * in the global search Q-Register \(lq_\(rq. - * A success/failure condition boolean is saved in that - * register's integer part. - * <pattern> may be omitted in which case the pattern of - * the last search or search and replace command will be - * implied by using the contents of register \(lq_\(rq - * (this could of course also be manually set). - * - * After a successful search, the pointer is positioned after - * the found text in the buffer. - * An unsuccessful search will display an error message but - * not actually yield an error. - * The message displaying is suppressed when executed from loops - * and register \(lq_\(rq is the implied argument for break-commands - * so that a search-break idiom can be implemented as follows: - * .EX - * <Sfoo$; ...> - * .EE - * Alternatively, S may be colon-modified in which case it returns - * a condition boolean that may be directly evaluated by a - * conditional or break-command. - * - * In interactive mode, searching will be performed immediately - * (\(lqsearch as you type\(rq) highlighting matched text - * on the fly. - * Changing the <pattern> results in the search being reperformed - * from the beginning. - */ -void -StateSearch::initial(void) -{ - tecoInt v1, v2; - - undo.push_var(parameters); - - parameters.dot = interface.ssm(SCI_GETCURRENTPOS); - - v2 = expressions.pop_num_calc(); - if (expressions.args()) { - /* TODO: optional count argument? */ - v1 = expressions.pop_num_calc(); - if (v1 <= v2) { - parameters.count = 1; - parameters.from = (gint)v1; - parameters.to = (gint)v2; - } else { - parameters.count = -1; - parameters.from = (gint)v2; - parameters.to = (gint)v1; - } - - if (!Validate::pos(parameters.from) || - !Validate::pos(parameters.to)) - throw RangeError("S"); - } else { - parameters.count = (gint)v2; - if (v2 >= 0) { - parameters.from = parameters.dot; - parameters.to = interface.ssm(SCI_GETLENGTH); - } else { - parameters.from = 0; - parameters.to = parameters.dot; - } - } - - parameters.from_buffer = QRegisters::current ? NULL : ring.current; - parameters.to_buffer = NULL; -} - -static inline const gchar * -regexp_escape_chr(gchar chr) -{ - static gchar escaped[] = {'\\', '\0', '\0'}; - - escaped[1] = chr; - return g_ascii_isalnum(chr) ? escaped + 1 : escaped; -} - -/** - * Convert a SciTECO pattern character class to a regular - * expression character class. - * It will throw an error for wrong class specs (like invalid - * Q-Registers) but not incomplete ones. - * - * @param state Initial pattern converter state. - * May be modified on return, e.g. when ^E has been - * scanned without a valid class. - * @param pattern The character class definition to convert. - * This may point into a longer pattern. - * The pointer is modified and always left after - * the last character used, so it may point to the - * terminating null byte after the call. - * @param escape_default Whether to treat single characters - * as classes or not. - * @return A regular expression string or NULL for incomplete - * class specifications. When a string is returned, the - * state has always been reset to STATE_START. - * Must be freed with g_free(). - */ -gchar * -StateSearch::class2regexp(MatchState &state, const gchar *&pattern, - bool escape_default) -{ - while (*pattern) { - QRegister *reg; - gchar *temp, *temp2; - - switch (state) { - case STATE_START: - switch (*pattern) { - case CTL_KEY('S'): - pattern++; - return g_strdup("[:^alnum:]"); - case CTL_KEY('E'): - state = STATE_CTL_E; - break; - default: - /* - * Either a single character "class" or - * not a valid class at all. - */ - return escape_default ? g_strdup(regexp_escape_chr(*pattern++)) - : NULL; - } - break; - - case STATE_CTL_E: - switch (String::toupper(*pattern)) { - case 'A': - pattern++; - state = STATE_START; - return g_strdup("[:alpha:]"); - /* same as <CTRL/S> */ - case 'B': - pattern++; - state = STATE_START; - return g_strdup("[:^alnum:]"); - case 'C': - pattern++; - state = STATE_START; - return g_strdup("[:alnum:].$"); - case 'D': - pattern++; - state = STATE_START; - return g_strdup("[:digit:]"); - case 'G': - state = STATE_ANYQ; - break; - case 'L': - pattern++; - state = STATE_START; - return g_strdup("\r\n\v\f"); - case 'R': - pattern++; - state = STATE_START; - return g_strdup("[:alnum:]"); - case 'V': - pattern++; - state = STATE_START; - return g_strdup("[:lower:]"); - case 'W': - pattern++; - state = STATE_START; - return g_strdup("[:upper:]"); - default: - /* - * Not a valid ^E class, but could still - * be a higher-level ^E pattern. - */ - return NULL; - } - break; - - case STATE_ANYQ: - /* will throw an error for invalid specs */ - if (!qreg_machine.input(*pattern, reg)) - /* incomplete, but consume byte */ - break; - qreg_machine.reset(); - - temp = reg->get_string(); - temp2 = g_regex_escape_string(temp, -1); - g_free(temp); - - pattern++; - state = STATE_START; - return temp2; - - default: - /* - * Not a class, but could still be any other - * high-level pattern. - */ - return NULL; - } - - pattern++; - } - - /* - * End of string. May happen for empty strings but also - * incomplete ^E or ^EG classes. - */ - return NULL; -} - -/** - * Convert SciTECO pattern to regular expression. - * It will throw an error for definitely wrong pattern constructs - * but not for incomplete patterns (a necessity of search-as-you-type). - * - * @bug Incomplete patterns after a pattern has been closed (i.e. its - * string argument) are currently not reported as errors. - * - * @param pattern The pattern to scan through. - * Modifies the pointer to point after the last - * successfully scanned character, so it can be - * called recursively. It may also point to the - * terminating null byte after the call. - * @param single_expr Whether to scan a single pattern - * expression or an arbitrary sequence. - * @return The regular expression string or NULL. - * Must be freed with g_free(). - */ -gchar * -StateSearch::pattern2regexp(const gchar *&pattern, - bool single_expr) -{ - MatchState state = STATE_START; - gchar *re = NULL; - - do { - gchar *temp; - - /* - * First check whether it is a class. - * This will not treat individual characters - * as classes, so we do not convert them to regexp - * classes unnecessarily. - * NOTE: This might throw an error. - */ - try { - temp = class2regexp(state, pattern); - } catch (...) { - g_free(re); - throw; - } - if (temp) { - g_assert(state == STATE_START); - - String::append(re, "["); - String::append(re, temp); - String::append(re, "]"); - g_free(temp); - - /* class2regexp() already consumed all the bytes */ - continue; - } - - if (!*pattern) - /* end of pattern */ - break; - - switch (state) { - case STATE_START: - switch (*pattern) { - case CTL_KEY('X'): String::append(re, "."); break; - case CTL_KEY('N'): state = STATE_NOT; break; - default: - String::append(re, regexp_escape_chr(*pattern)); - } - break; - - case STATE_NOT: - state = STATE_START; - try { - temp = class2regexp(state, pattern, true); - } catch (...) { - g_free(re); - throw; - } - if (!temp) - /* a complete class is strictly required */ - goto incomplete; - g_assert(state == STATE_START); - - String::append(re, "[^"); - String::append(re, temp); - String::append(re, "]"); - g_free(temp); - - /* class2regexp() already consumed all the bytes */ - continue; - - case STATE_CTL_E: - state = STATE_START; - switch (String::toupper(*pattern)) { - case 'M': state = STATE_MANY; break; - case 'S': String::append(re, "\\s+"); break; - /* same as <CTRL/X> */ - case 'X': String::append(re, "."); break; - /* TODO: ASCII octal code!? */ - case '[': - String::append(re, "("); - state = STATE_ALT; - break; - default: - g_free(re); - throw SyntaxError(*pattern); - } - break; - - case STATE_MANY: - /* consume exactly one pattern element */ - try { - temp = pattern2regexp(pattern, true); - } catch (...) { - g_free(re); - throw; - } - if (!temp) - /* a complete expression is strictly required */ - goto incomplete; - - String::append(re, "("); - String::append(re, temp); - String::append(re, ")+"); - g_free(temp); - state = STATE_START; - - /* pattern2regexp() already consumed all the bytes */ - continue; - - case STATE_ALT: - switch (*pattern) { - case ',': - String::append(re, "|"); - break; - case ']': - String::append(re, ")"); - state = STATE_START; - break; - default: - try { - temp = pattern2regexp(pattern, true); - } catch (...) { - g_free(re); - throw; - } - if (!temp) - /* a complete expression is strictly required */ - goto incomplete; - - String::append(re, temp); - g_free(temp); - - /* pattern2regexp() already consumed all the bytes */ - continue; - } - break; - - default: - /* shouldn't happen */ - g_assert_not_reached(); - } - - pattern++; - } while (!single_expr || state != STATE_START); - - /* - * Complete open alternative. - * This could be handled like an incomplete expression, - * but closing it automatically improved search-as-you-type. - */ - if (state == STATE_ALT) - String::append(re, ")"); - - return re; - -incomplete: - g_free(re); - return NULL; -} - -void -StateSearch::do_search(GRegex *re, gint from, gint to, gint &count) -{ - GMatchInfo *info; - const gchar *buffer; - - gint matched_from = -1, matched_to = -1; - - buffer = (const gchar *)interface.ssm(SCI_GETCHARACTERPOINTER); - g_regex_match_full(re, buffer, (gssize)to, from, - (GRegexMatchFlags)0, &info, NULL); - - if (count >= 0) { - while (g_match_info_matches(info) && --count) - g_match_info_next(info, NULL); - - if (!count) - /* successful */ - g_match_info_fetch_pos(info, 0, - &matched_from, &matched_to); - } else { - /* only keep the last `count' matches, in a circular stack */ - struct Range { - gint from, to; - }; - Range *matched = new Range[-count]; - gint matched_total = 0, i = 0; - - while (g_match_info_matches(info)) { - g_match_info_fetch_pos(info, 0, - &matched[i].from, - &matched[i].to); - - g_match_info_next(info, NULL); - i = ++matched_total % -count; - } - - count = MIN(count + matched_total, 0); - if (!count) { - /* successful, i points to stack bottom */ - matched_from = matched[i].from; - matched_to = matched[i].to; - } - - delete[] matched; - } - - g_match_info_free(info); - - if (matched_from >= 0 && matched_to >= 0) - /* match success */ - interface.ssm(SCI_SETSEL, matched_from, matched_to); -} - -void -StateSearch::process(const gchar *str, gint new_chars) -{ - static const gint flags = G_REGEX_CASELESS | G_REGEX_MULTILINE | - G_REGEX_DOTALL | G_REGEX_RAW; - - QRegister *search_reg = QRegisters::globals["_"]; - - gchar *re_pattern; - GRegex *re; - - gint count = parameters.count; - - if (current_doc_must_undo()) - interface.undo_ssm(SCI_SETSEL, - interface.ssm(SCI_GETANCHOR), - interface.ssm(SCI_GETCURRENTPOS)); - - search_reg->undo_set_integer(); - search_reg->set_integer(FAILURE); - - /* - * NOTE: pattern2regexp() modifies str pointer and may throw - */ - re_pattern = pattern2regexp(str); - qreg_machine.reset(); -#ifdef DEBUG - g_printf("REGEXP: %s\n", re_pattern); -#endif - if (!re_pattern) - goto failure; - re = g_regex_new(re_pattern, (GRegexCompileFlags)flags, - (GRegexMatchFlags)0, NULL); - g_free(re_pattern); - if (!re) - goto failure; - - if (!QRegisters::current && - ring.current != parameters.from_buffer) { - ring.undo_edit(); - parameters.from_buffer->edit(); - } - - do_search(re, parameters.from, parameters.to, count); - - if (parameters.to_buffer && count) { - Buffer *buffer = parameters.from_buffer; - - if (ring.current == buffer) - ring.undo_edit(); - - if (count > 0) { - do { - buffer = buffer->next() ? : ring.first(); - buffer->edit(); - - if (buffer == parameters.to_buffer) { - do_search(re, 0, parameters.dot, count); - break; - } - - do_search(re, 0, interface.ssm(SCI_GETLENGTH), - count); - } while (count); - } else /* count < 0 */ { - do { - buffer = buffer->prev() ? : ring.last(); - buffer->edit(); - - if (buffer == parameters.to_buffer) { - do_search(re, parameters.dot, - interface.ssm(SCI_GETLENGTH), - count); - break; - } - - do_search(re, 0, interface.ssm(SCI_GETLENGTH), - count); - } while (count); - } - - ring.current = buffer; - } - - search_reg->set_integer(TECO_BOOL(!count)); - - g_regex_unref(re); - - if (!count) - return; - -failure: - interface.ssm(SCI_GOTOPOS, parameters.dot); -} - -State * -StateSearch::done(const gchar *str) -{ - BEGIN_EXEC(&States::start); - - QRegister *search_reg = QRegisters::globals["_"]; - - if (*str) { - /* workaround: preserve selection (also on rubout) */ - gint anchor = interface.ssm(SCI_GETANCHOR); - if (current_doc_must_undo()) - interface.undo_ssm(SCI_SETANCHOR, anchor); - - search_reg->undo_set_string(); - search_reg->set_string(str); - - interface.ssm(SCI_SETANCHOR, anchor); - } else { - gchar *search_str = search_reg->get_string(); - process(search_str, 0 /* unused */); - g_free(search_str); - } - - if (eval_colon()) - expressions.push(search_reg->get_integer()); - else if (IS_FAILURE(search_reg->get_integer()) && - !loop_stack.items() /* not in loop */) - interface.msg(InterfaceCurrent::MSG_ERROR, "Search string not found!"); - - return &States::start; -} - -/*$ N - * [n]N[pattern]$ -- Search over buffer-boundaries - * -N[pattern]$ - * from,toN[pattern]$ - * [n]:N[pattern]$ -> Success|Failure - * -:N[pattern]$ -> Success|Failure - * from,to:N[pattern]$ -> Success|Failure - * - * Search for <pattern> over buffer boundaries. - * This command is similar to the regular search command - * (S) but will continue to search for occurrences of - * pattern when the end or beginning of the current buffer - * is reached. - * Occurrences of <pattern> spanning over buffer boundaries - * will not be found. - * When searching forward N will start in the current buffer - * at dot, continue with the next buffer in the ring searching - * the entire buffer until it reaches the end of the buffer - * ring, continue with the first buffer in the ring until - * reaching the current file again where it searched from the - * beginning of the buffer up to its current dot. - * Searching backwards does the reverse. - * - * N also differs from S in the interpretation of two arguments. - * Using two arguments the search will be bounded between the - * buffer with number <from>, up to the buffer with number - * <to>. - * When specifying buffer ranges, the entire buffers are searched - * from beginning to end. - * <from> may be greater than <to> in which case, searching starts - * at the end of buffer <from> and continues backwards until the - * beginning of buffer <to> has been reached. - * Furthermore as with all buffer numbers, the buffer ring - * is considered a circular structure and it is possible - * to search over buffer ring boundaries by specifying - * buffer numbers greater than the number of buffers in the - * ring. - */ -void -StateSearchAll::initial(void) -{ - tecoInt v1, v2; - - undo.push_var(parameters); - - parameters.dot = interface.ssm(SCI_GETCURRENTPOS); - - v2 = expressions.pop_num_calc(); - if (expressions.args()) { - /* TODO: optional count argument? */ - v1 = expressions.pop_num_calc(); - if (v1 <= v2) { - parameters.count = 1; - parameters.from_buffer = ring.find(v1); - parameters.to_buffer = ring.find(v2); - } else { - parameters.count = -1; - parameters.from_buffer = ring.find(v2); - parameters.to_buffer = ring.find(v1); - } - - if (!parameters.from_buffer || !parameters.to_buffer) - throw RangeError("N"); - } else { - parameters.count = (gint)v2; - /* NOTE: on Q-Registers, behave like "S" */ - if (QRegisters::current) - parameters.from_buffer = parameters.to_buffer = NULL; - else - parameters.from_buffer = parameters.to_buffer = ring.current; - } - - if (parameters.count >= 0) { - parameters.from = parameters.dot; - parameters.to = interface.ssm(SCI_GETLENGTH); - } else { - parameters.from = 0; - parameters.to = parameters.dot; - } -} - -State * -StateSearchAll::done(const gchar *str) -{ - BEGIN_EXEC(&States::start); - - StateSearch::done(str); - QRegisters::hook(QRegisters::HOOK_EDIT); - - return &States::start; -} - -/*$ FK - * FK[pattern]$ -- Delete up to occurrence of pattern - * [n]FK[pattern]$ - * -FK[pattern]$ - * from,toFK[pattern]$ - * :FK[pattern]$ -> Success|Failure - * [n]:FK[pattern]$ -> Success|Failure - * -:FK[pattern]$ -> Success|Failure - * from,to:FK[pattern]$ -> Success|Failure - * - * FK searches for <pattern> just like the regular search - * command (S) but when found deletes all text from dot - * up to but not including the found text instance. - * When searching backwards the characters beginning after - * the occurrence of <pattern> up to dot are deleted. - * - * In interactive mode, deletion is not performed - * as-you-type but only on command termination. - */ -State * -StateSearchKill::done(const gchar *str) -{ - gint dot; - - BEGIN_EXEC(&States::start); - - QRegister *search_reg = QRegisters::globals["_"]; - - StateSearch::done(str); - - if (IS_FAILURE(search_reg->get_integer())) - return &States::start; - - dot = interface.ssm(SCI_GETCURRENTPOS); - - interface.ssm(SCI_BEGINUNDOACTION); - if (parameters.dot < dot) { - /* kill forwards */ - gint anchor = interface.ssm(SCI_GETANCHOR); - - if (current_doc_must_undo()) - interface.undo_ssm(SCI_GOTOPOS, dot); - interface.ssm(SCI_GOTOPOS, anchor); - - interface.ssm(SCI_DELETERANGE, - parameters.dot, anchor - parameters.dot); - } else { - /* kill backwards */ - interface.ssm(SCI_DELETERANGE, dot, parameters.dot - dot); - } - interface.ssm(SCI_ENDUNDOACTION); - ring.dirtify(); - - if (current_doc_must_undo()) - interface.undo_ssm(SCI_UNDO); - - return &States::start; -} - -/*$ FD - * FD[pattern]$ -- Delete occurrence of pattern - * [n]FD[pattern]$ - * -FD[pattern]$ - * from,toFD[pattern]$ - * :FD[pattern]$ -> Success|Failure - * [n]:FD[pattern]$ -> Success|Failure - * -:FD[pattern]$ -> Success|Failure - * from,to:FD[pattern]$ -> Success|Failure - * - * Searches for <pattern> just like the regular search command - * (S) but when found deletes the entire occurrence. - */ -State * -StateSearchDelete::done(const gchar *str) -{ - BEGIN_EXEC(&States::start); - - QRegister *search_reg = QRegisters::globals["_"]; - - StateSearch::done(str); - - if (IS_SUCCESS(search_reg->get_integer())) { - interface.ssm(SCI_BEGINUNDOACTION); - interface.ssm(SCI_REPLACESEL, 0, (sptr_t)""); - interface.ssm(SCI_ENDUNDOACTION); - ring.dirtify(); - - if (current_doc_must_undo()) - interface.undo_ssm(SCI_UNDO); - } - - return &States::start; -} - -/*$ FS - * FS[pattern]$[string]$ -- Search and replace - * [n]FS[pattern]$[string]$ - * -FS[pattern]$[string]$ - * from,toFS[pattern]$[string]$ - * :FS[pattern]$[string]$ -> Success|Failure - * [n]:FS[pattern]$[string]$ -> Success|Failure - * -:FS[pattern]$[string]$ -> Success|Failure - * from,to:FS[pattern]$[string]$ -> Success|Failure - * - * Search for <pattern> just like the regular search command - * (S) does but replace it with <string> if found. - * If <string> is empty, the occurrence will always be - * deleted so \(lqFS[pattern]$$\(rq is similar to - * \(lqFD[pattern]$\(rq. - * The global replace register is \fBnot\fP touched - * by the FS command. - * - * In interactive mode, the replacement will be performed - * immediately and interactively. - */ -State * -StateReplace::done(const gchar *str) -{ - BEGIN_EXEC(&States::replace_ignore); - - QRegister *search_reg = QRegisters::globals["_"]; - - StateSearchDelete::done(str); - - return IS_SUCCESS(search_reg->get_integer()) - ? (State *)&States::replace_insert - : (State *)&States::replace_ignore; -} - -State * -StateReplace_ignore::done(const gchar *str) -{ - return &States::start; -} - -/*$ FR - * FR[pattern]$[string]$ -- Search and replace with default - * [n]FR[pattern]$[string]$ - * -FR[pattern]$[string]$ - * from,toFR[pattern]$[string]$ - * :FR[pattern]$[string]$ -> Success|Failure - * [n]:FR[pattern]$[string]$ -> Success|Failure - * -:FR[pattern]$[string]$ -> Success|Failure - * from,to:FR[pattern]$[string]$ -> Success|Failure - * - * The FR command is similar to the FS command. - * It searches for <pattern> just like the regular search - * command (S) and replaces the occurrence with <string> - * similar to what FS does. - * It differs from FS in the fact that the replacement - * string is saved in the global replacement register - * \(lq-\(rq. - * If <string> is empty the string in the global replacement - * register is implied instead. - */ -State * -StateReplaceDefault::done(const gchar *str) -{ - BEGIN_EXEC(&States::replacedefault_ignore); - - QRegister *search_reg = QRegisters::globals["_"]; - - StateSearchDelete::done(str); - - return IS_SUCCESS(search_reg->get_integer()) - ? (State *)&States::replacedefault_insert - : (State *)&States::replacedefault_ignore; -} - -State * -StateReplaceDefault_insert::done(const gchar *str) -{ - BEGIN_EXEC(&States::start); - - QRegister *replace_reg = QRegisters::globals["-"]; - - if (*str) { - replace_reg->undo_set_string(); - replace_reg->set_string(str); - } else { - gchar *replace_str = replace_reg->get_string(); - StateInsert::process(replace_str, strlen(replace_str)); - g_free(replace_str); - } - - return &States::start; -} - -State * -StateReplaceDefault_ignore::done(const gchar *str) -{ - BEGIN_EXEC(&States::start); - - if (*str) { - QRegister *replace_reg = QRegisters::globals["-"]; - - replace_reg->undo_set_string(); - replace_reg->set_string(str); - } - - return &States::start; -} - -} /* namespace SciTECO */ |