/* * Copyright (C) 2012-2025 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 . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "sciteco.h" #include "error.h" #include "string-utils.h" #include "expressions.h" #include "parser.h" #include "lexer.h" #include "core-commands.h" #include "undo.h" #include "interface.h" #include "goto.h" #include "goto-commands.h" TECO_DECLARE_STATE(teco_state_blockcomment); TECO_DECLARE_STATE(teco_state_eolcomment); teco_string_t teco_goto_skip_label = {NULL, 0}; /* * NOTE: The comma is theoretically not allowed in a label * (see syntax), but is accepted anyway since labels * are historically used as comments. * SciTECO has true block and EOL comments, though as well. */ static teco_state_t * teco_state_label_input(teco_machine_main_t *ctx, gunichar chr, GError **error) { if (!ctx->goto_label.len) { switch (chr) { case '*': return &teco_state_blockcomment; /* `!*` */ case '!': return &teco_state_eolcomment; /* `!!` */ } } if (chr == '!') { gssize existing_pc = teco_goto_table_set(&ctx->goto_table, ctx->goto_label.data, ctx->goto_label.len, ctx->macro_pc); if (existing_pc < 0) { /* new label */ if (ctx->parent.must_undo) teco_goto_table_undo_remove(&ctx->goto_table, ctx->goto_label.data, ctx->goto_label.len); if (teco_goto_skip_label.len > 0 && !teco_string_cmp(&ctx->goto_label, teco_goto_skip_label.data, teco_goto_skip_label.len)) { teco_undo_string_own(teco_goto_skip_label); memset(&teco_goto_skip_label, 0, sizeof(teco_goto_skip_label)); if (ctx->parent.must_undo) teco_undo_flags(ctx->flags); ctx->flags.mode = TECO_MODE_NORMAL; } } else if (existing_pc != ctx->macro_pc) { g_autofree gchar *label_printable = teco_string_echo(ctx->goto_label.data, ctx->goto_label.len); teco_interface_msg(TECO_MSG_WARNING, "Ignoring goto label \"%s\" redefinition", label_printable); } if (ctx->parent.must_undo) teco_undo_string_own(ctx->goto_label); else teco_string_clear(&ctx->goto_label); memset(&ctx->goto_label, 0, sizeof(ctx->goto_label)); return &teco_state_start; } /* * The goto label is collected in parse-only mode as well * since we could jump into a currently dead branch later. * * FIXME: Theoretically, we could avoid that at least in TECO_MODE_LEXING. */ if (ctx->parent.must_undo) undo__teco_string_truncate(&ctx->goto_label, ctx->goto_label.len); teco_string_append_wc(&ctx->goto_label, chr); return &teco_state_label; } TECO_DEFINE_STATE(teco_state_label, .style = SCE_SCITECO_LABEL ); static teco_state_t * teco_state_goto_done(teco_machine_main_t *ctx, const teco_string_t *str, GError **error) { if (ctx->flags.mode > TECO_MODE_NORMAL) return &teco_state_start; if (!str->len) { /* you can still write @O/,/, though... */ g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED, "No labels given for "); return NULL; } teco_int_t value; if (!teco_expressions_pop_num_calc(&value, 0, error)) return NULL; /* * Find the comma-separated substring in str indexed by `value`. */ teco_string_t label = {NULL, 0}; while (value >= 0) { label.data = label.data ? label.data+label.len+1 : str->data; const gchar *p = label.data ? memchr(label.data, ',', str->len - (label.data - str->data)) : NULL; label.len = p ? p - label.data : str->len - (label.data - str->data); value--; if (!p) break; } if (value < 0 && label.len > 0) { gssize pc = teco_goto_table_find(&ctx->goto_table, label.data, label.len); if (pc >= 0) { ctx->macro_pc = pc; } else { /* skip till label is defined */ g_assert(teco_goto_skip_label.len == 0); undo__teco_string_truncate(&teco_goto_skip_label, 0); teco_string_init(&teco_goto_skip_label, label.data, label.len); if (ctx->parent.must_undo) teco_undo_flags(ctx->flags); ctx->flags.mode = TECO_MODE_PARSE_ONLY_GOTO; } } return &teco_state_start; } /* in cmdline.c */ gboolean teco_state_goto_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, gunichar chr, GError **error); gboolean teco_state_goto_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, GError **error); /*$ "O" goto * Olabel$ -- Go to label * [n]Olabel0[,label1,...]$ * * Go to