aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/Makefile.am1
-rw-r--r--src/core-commands.c207
-rw-r--r--src/core-commands.h50
-rw-r--r--src/stdio-commands.c197
-rw-r--r--src/stdio-commands.h21
5 files changed, 266 insertions, 210 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index e7a8545..ff2e86b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -45,6 +45,7 @@ libsciteco_base_la_SOURCES = main.c sciteco.h list.h \
parser.c parser.h \
core-commands.c core-commands.h \
move-commands.c move-commands.h \
+ stdio-commands.c stdio-commands.h \
search.c search.h \
spawn.c spawn.h \
glob.c glob.h \
diff --git a/src/core-commands.c b/src/core-commands.c
index 2f6acb8..a1d180c 100644
--- a/src/core-commands.c
+++ b/src/core-commands.c
@@ -43,47 +43,12 @@
#include "memory.h"
#include "eol.h"
#include "qreg.h"
+#include "stdio-commands.h"
#include "qreg-commands.h"
#include "goto-commands.h"
#include "move-commands.h"
#include "core-commands.h"
-gboolean teco_state_command_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx,
- gunichar key, GError **error);
-
-/**
- * @class TECO_DEFINE_STATE_COMMAND
- * @implements TECO_DEFINE_STATE_CASEINSENSITIVE
- * @ingroup states
- *
- * Base state for everything where part of a one or two letter command
- * is accepted.
- */
-#define TECO_DEFINE_STATE_COMMAND(NAME, ...) \
- TECO_DEFINE_STATE_CASEINSENSITIVE(NAME, \
- .process_edit_cmd_cb = (teco_state_process_edit_cmd_cb_t) \
- teco_state_command_process_edit_cmd, \
- .style = SCE_SCITECO_COMMAND, \
- ##__VA_ARGS__ \
- )
-
-/**
- * @class TECO_DEFINE_STATE_START
- * @implements TECO_DEFINE_STATE_COMMAND
- * @ingroup states
- *
- * Base state for everything where a new command can begin
- * (the start state itself and all lookahead states).
- */
-#define TECO_DEFINE_STATE_START(NAME, ...) \
- TECO_DEFINE_STATE_COMMAND(NAME, \
- .end_of_macro_cb = NULL, /* Allowed at the end of a macro! */ \
- .is_start = TRUE, \
- .keymacro_mask = TECO_KEYMACRO_MASK_START | TECO_KEYMACRO_MASK_CASEINSENSITIVE, \
- ##__VA_ARGS__ \
- )
-
-static teco_state_t *teco_state_start_input(teco_machine_main_t *ctx, gunichar chr, GError **error);
static teco_state_t *teco_state_control_input(teco_machine_main_t *ctx, gunichar chr, GError **error);
static teco_state_t *teco_state_ctlc_control_input(teco_machine_main_t *ctx, gunichar chr, GError **error);
@@ -560,174 +525,6 @@ teco_state_start_cmdline_pop(teco_machine_main_t *ctx, GError **error)
g_set_error_literal(error, TECO_ERROR, TECO_ERROR_CMDLINE, "");
}
-/**
- * Print number from stack in the given radix.
- *
- * It must be popped manually, so we can call it multiple times
- * on the same number.
- */
-static gboolean
-teco_print(guint radix, GError **error)
-{
- if (!teco_expressions_eval(FALSE, error))
- return FALSE;
- if (!teco_expressions_args()) {
- teco_error_argexpected_set(error, "=");
- return FALSE;
- }
- /*
- * FIXME: There should be a raw output function,
- * also to allow output without trailing LF.
- * Perhaps repurpose teco_expressions_format().
- */
- const gchar *fmt = "%" TECO_INT_MODIFIER "d";
- switch (radix) {
- case 8: fmt = "%" TECO_INT_MODIFIER "o"; break;
- case 16: fmt = "%" TECO_INT_MODIFIER "X"; break;
- }
- teco_interface_msg(TECO_MSG_USER, fmt, teco_expressions_peek_num(0));
- return TRUE;
-}
-
-/*$ "=" "==" "===" print
- * <n>= -- Print integer as message
- * <n>==
- * <n>===
- *
- * Shows integer <n> as a message in the message line and/or
- * on the console.
- * One \(lq=\(rq formats the integer as a signed decimal number,
- * \(lq==\(rq formats as an unsigned octal number and
- * \(lq===\(rq as an unsigned hexadecimal number.
- * It is logged with the user-message severity.
- * The command fails if <n> is not given.
- *
- * A noteworthy quirk is that \(lq==\(rq and \(lq===\(rq
- * will print 2 or 3 numbers in succession when executed
- * from interactive mode at the end of the command line
- * in order to guarantee immediate feedback.
- *
- * If you want to print multiple values from the stack,
- * you have to put the \(lq=\(rq into a pass-through loop
- * or separate the commands with
- * whitespace (e.g. \(lq^Y= =\(rq).
- */
-/*
- * In order to imitate TECO-11 closely, we apply the lookahead
- * strategy -- `=` and `==` are not executed immediately but only
- * when a non-`=` character is parsed (cf. `$$` and `^C^C`).
- * However, this would be very annoying during interactive
- * execution, therefore we still print the number immediately
- * and perhaps multiple times:
- * Typing `===` prints the number first in decimal,
- * then octal and finally in hexadecimal.
- * This won't happen e.g. in a loop that is closed on the command-line.
- *
- * FIXME: Support colon-modifier to suppress line-break on console.
- */
-TECO_DECLARE_STATE(teco_state_print_decimal);
-TECO_DECLARE_STATE(teco_state_print_octal);
-
-static gboolean
-teco_state_print_decimal_initial(teco_machine_main_t *ctx, GError **error)
-{
- if (ctx->flags.mode > TECO_MODE_NORMAL || !teco_cmdline_is_executing(ctx))
- return TRUE;
- /*
- * Interactive invocation:
- * don't yet pop number as we may have to print it repeatedly
- */
- return teco_print(10, error);
-}
-
-static teco_state_t *
-teco_state_print_decimal_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
-{
- if (chr == '=')
- return &teco_state_print_octal;
-
- if (ctx->flags.mode == TECO_MODE_NORMAL) {
- if (!teco_cmdline_is_executing(ctx) && !teco_print(10, error))
- return NULL;
- teco_expressions_pop_num(0);
- }
- return teco_state_start_input(ctx, chr, error);
-}
-
-/*
- * Due to the deferred nature of `=`,
- * it is valid to end in this state as well.
- */
-static gboolean
-teco_state_print_decimal_end_of_macro(teco_machine_main_t *ctx, GError **error)
-{
- if (teco_cmdline_is_executing(ctx) || ctx->flags.mode > TECO_MODE_NORMAL)
- return TRUE;
- if (!teco_print(10, error))
- return FALSE;
- teco_expressions_pop_num(0);
- return TRUE;
-}
-
-TECO_DEFINE_STATE_START(teco_state_print_decimal,
- .initial_cb = (teco_state_initial_cb_t)teco_state_print_decimal_initial,
- .end_of_macro_cb = (teco_state_end_of_macro_cb_t)
- teco_state_print_decimal_end_of_macro
-);
-
-static gboolean
-teco_state_print_octal_initial(teco_machine_main_t *ctx, GError **error)
-{
- if (ctx->flags.mode > TECO_MODE_NORMAL || !teco_cmdline_is_executing(ctx))
- return TRUE;
- /*
- * Interactive invocation:
- * don't yet pop number as we may have to print it repeatedly
- */
- return teco_print(8, error);
-}
-
-static teco_state_t *
-teco_state_print_octal_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
-{
- if (chr == '=') {
- if (ctx->flags.mode == TECO_MODE_NORMAL) {
- if (!teco_print(16, error))
- return NULL;
- teco_expressions_pop_num(0);
- }
- return &teco_state_start;
- }
-
- if (ctx->flags.mode == TECO_MODE_NORMAL) {
- if (!teco_cmdline_is_executing(ctx) && !teco_print(8, error))
- return NULL;
- teco_expressions_pop_num(0);
- }
- return teco_state_start_input(ctx, chr, error);
-}
-
-/*
- * Due to the deferred nature of `==`,
- * it is valid to end in this state as well.
- */
-static gboolean
-teco_state_print_octal_end_of_macro(teco_machine_main_t *ctx, GError **error)
-{
- if (teco_cmdline_is_executing(ctx) || ctx->flags.mode > TECO_MODE_NORMAL)
- return TRUE;
- if (!teco_print(8, error))
- return FALSE;
- teco_expressions_pop_num(0);
- return TRUE;
-}
-
-TECO_DEFINE_STATE_START(teco_state_print_octal,
- .initial_cb = (teco_state_initial_cb_t)teco_state_print_octal_initial,
- .end_of_macro_cb = (teco_state_end_of_macro_cb_t)
- teco_state_print_octal_end_of_macro
-);
-
/*$ A
* [n]A -> code -- Get character code from buffer
* -A -> code
@@ -768,7 +565,7 @@ teco_state_start_get(teco_machine_main_t *ctx, GError **error)
? -1 : teco_interface_get_character(get_pos, len));
}
-static teco_state_t *
+teco_state_t *
teco_state_start_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
{
static teco_machine_main_transition_t transitions[] = {
diff --git a/src/core-commands.h b/src/core-commands.h
index 9f62da8..9ae508b 100644
--- a/src/core-commands.h
+++ b/src/core-commands.h
@@ -25,11 +25,30 @@
/** non-operational characters in teco_state_start */
#define TECO_NOOPS " \f\r\n\v"
+/* in cmdline.c */
+gboolean teco_state_command_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx,
+ gunichar key, GError **error);
+
+/**
+ * @class TECO_DEFINE_STATE_COMMAND
+ * @implements TECO_DEFINE_STATE_CASEINSENSITIVE
+ * @ingroup states
+ *
+ * Base state for everything where part of a one or two letter command
+ * is accepted.
+ */
+#define TECO_DEFINE_STATE_COMMAND(NAME, ...) \
+ TECO_DEFINE_STATE_CASEINSENSITIVE(NAME, \
+ .process_edit_cmd_cb = (teco_state_process_edit_cmd_cb_t) \
+ teco_state_command_process_edit_cmd, \
+ .style = SCE_SCITECO_COMMAND, \
+ ##__VA_ARGS__ \
+ )
+
/*
* FIXME: Most of these states can probably be private/static
* as they are only referenced from teco_state_start.
*/
-TECO_DECLARE_STATE(teco_state_start);
TECO_DECLARE_STATE(teco_state_fcommand);
void teco_undo_change_dir_to_current(void);
@@ -38,9 +57,6 @@ TECO_DECLARE_STATE(teco_state_changedir);
TECO_DECLARE_STATE(teco_state_condcommand);
TECO_DECLARE_STATE(teco_state_control);
TECO_DECLARE_STATE(teco_state_ascii);
-TECO_DECLARE_STATE(teco_state_escape);
-TECO_DECLARE_STATE(teco_state_ctlc);
-TECO_DECLARE_STATE(teco_state_ctlc_control);
TECO_DECLARE_STATE(teco_state_ecommand);
typedef struct {
@@ -57,7 +73,8 @@ gboolean teco_state_insert_process(teco_machine_main_t *ctx, const teco_string_t
teco_state_t *teco_state_insert_done(teco_machine_main_t *ctx, const teco_string_t *str, GError **error);
/* in cmdline.c */
-gboolean teco_state_insert_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, gunichar chr, GError **error);
+gboolean teco_state_insert_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx,
+ gunichar chr, GError **error);
/**
* @class TECO_DEFINE_STATE_INSERT
@@ -82,3 +99,26 @@ gboolean teco_state_insert_process_edit_cmd(teco_machine_main_t *ctx, teco_machi
TECO_DECLARE_STATE(teco_state_insert_plain);
TECO_DECLARE_STATE(teco_state_insert_indent);
+
+/**
+ * @class TECO_DEFINE_STATE_START
+ * @implements TECO_DEFINE_STATE_COMMAND
+ * @ingroup states
+ *
+ * Base state for everything where a new command can begin
+ * (the start state itself and all lookahead states).
+ */
+#define TECO_DEFINE_STATE_START(NAME, ...) \
+ TECO_DEFINE_STATE_COMMAND(NAME, \
+ .end_of_macro_cb = NULL, /* Allowed at the end of a macro! */ \
+ .is_start = TRUE, \
+ .keymacro_mask = TECO_KEYMACRO_MASK_START | TECO_KEYMACRO_MASK_CASEINSENSITIVE, \
+ ##__VA_ARGS__ \
+ )
+
+teco_state_t *teco_state_start_input(teco_machine_main_t *ctx, gunichar chr, GError **error);
+
+TECO_DECLARE_STATE(teco_state_start);
+TECO_DECLARE_STATE(teco_state_escape);
+TECO_DECLARE_STATE(teco_state_ctlc);
+TECO_DECLARE_STATE(teco_state_ctlc_control);
diff --git a/src/stdio-commands.c b/src/stdio-commands.c
new file mode 100644
index 0000000..263425c
--- /dev/null
+++ b/src/stdio-commands.c
@@ -0,0 +1,197 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib.h>
+
+#include "sciteco.h"
+#include "parser.h"
+#include "error.h"
+#include "expressions.h"
+#include "interface.h"
+#include "cmdline.h"
+#include "core-commands.h"
+#include "stdio-commands.h"
+
+/**
+ * Print number from stack in the given radix.
+ *
+ * It must be popped manually, so we can call it multiple times
+ * on the same number.
+ */
+static gboolean
+teco_print(guint radix, GError **error)
+{
+ if (!teco_expressions_eval(FALSE, error))
+ return FALSE;
+ if (!teco_expressions_args()) {
+ teco_error_argexpected_set(error, "=");
+ return FALSE;
+ }
+ /*
+ * FIXME: There should be a raw output function,
+ * also to allow output without trailing LF.
+ * Perhaps repurpose teco_expressions_format().
+ */
+ const gchar *fmt = "%" TECO_INT_MODIFIER "d";
+ switch (radix) {
+ case 8: fmt = "%" TECO_INT_MODIFIER "o"; break;
+ case 16: fmt = "%" TECO_INT_MODIFIER "X"; break;
+ }
+ teco_interface_msg(TECO_MSG_USER, fmt, teco_expressions_peek_num(0));
+ return TRUE;
+}
+
+/*$ "=" "==" "===" print
+ * <n>= -- Print integer as message
+ * <n>==
+ * <n>===
+ *
+ * Shows integer <n> as a message in the message line and/or
+ * on the console.
+ * One \(lq=\(rq formats the integer as a signed decimal number,
+ * \(lq==\(rq formats as an unsigned octal number and
+ * \(lq===\(rq as an unsigned hexadecimal number.
+ * It is logged with the user-message severity.
+ * The command fails if <n> is not given.
+ *
+ * A noteworthy quirk is that \(lq==\(rq and \(lq===\(rq
+ * will print 2 or 3 numbers in succession when executed
+ * from interactive mode at the end of the command line
+ * in order to guarantee immediate feedback.
+ *
+ * If you want to print multiple values from the stack,
+ * you have to put the \(lq=\(rq into a pass-through loop
+ * or separate the commands with
+ * whitespace (e.g. \(lq^Y= =\(rq).
+ */
+/*
+ * In order to imitate TECO-11 closely, we apply the lookahead
+ * strategy -- `=` and `==` are not executed immediately but only
+ * when a non-`=` character is parsed (cf. `$$` and `^C^C`).
+ * However, this would be very annoying during interactive
+ * execution, therefore we still print the number immediately
+ * and perhaps multiple times:
+ * Typing `===` prints the number first in decimal,
+ * then octal and finally in hexadecimal.
+ * This won't happen e.g. in a loop that is closed on the command-line.
+ *
+ * FIXME: Support colon-modifier to suppress line-break on console.
+ */
+TECO_DECLARE_STATE(teco_state_print_octal);
+
+static gboolean
+teco_state_print_decimal_initial(teco_machine_main_t *ctx, GError **error)
+{
+ if (ctx->flags.mode > TECO_MODE_NORMAL || !teco_cmdline_is_executing(ctx))
+ return TRUE;
+ /*
+ * Interactive invocation:
+ * don't yet pop number as we may have to print it repeatedly
+ */
+ return teco_print(10, error);
+}
+
+static teco_state_t *
+teco_state_print_decimal_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
+{
+ if (chr == '=')
+ return &teco_state_print_octal;
+
+ if (ctx->flags.mode == TECO_MODE_NORMAL) {
+ if (!teco_cmdline_is_executing(ctx) && !teco_print(10, error))
+ return NULL;
+ teco_expressions_pop_num(0);
+ }
+ return teco_state_start_input(ctx, chr, error);
+}
+
+/*
+ * Due to the deferred nature of `=`,
+ * it is valid to end in this state as well.
+ */
+static gboolean
+teco_state_print_decimal_end_of_macro(teco_machine_main_t *ctx, GError **error)
+{
+ if (teco_cmdline_is_executing(ctx) || ctx->flags.mode > TECO_MODE_NORMAL)
+ return TRUE;
+ if (!teco_print(10, error))
+ return FALSE;
+ teco_expressions_pop_num(0);
+ return TRUE;
+}
+
+TECO_DEFINE_STATE_START(teco_state_print_decimal,
+ .initial_cb = (teco_state_initial_cb_t)teco_state_print_decimal_initial,
+ .end_of_macro_cb = (teco_state_end_of_macro_cb_t)
+ teco_state_print_decimal_end_of_macro
+);
+
+static gboolean
+teco_state_print_octal_initial(teco_machine_main_t *ctx, GError **error)
+{
+ if (ctx->flags.mode > TECO_MODE_NORMAL || !teco_cmdline_is_executing(ctx))
+ return TRUE;
+ /*
+ * Interactive invocation:
+ * don't yet pop number as we may have to print it repeatedly
+ */
+ return teco_print(8, error);
+}
+
+static teco_state_t *
+teco_state_print_octal_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
+{
+ if (chr == '=') {
+ if (ctx->flags.mode == TECO_MODE_NORMAL) {
+ if (!teco_print(16, error))
+ return NULL;
+ teco_expressions_pop_num(0);
+ }
+ return &teco_state_start;
+ }
+
+ if (ctx->flags.mode == TECO_MODE_NORMAL) {
+ if (!teco_cmdline_is_executing(ctx) && !teco_print(8, error))
+ return NULL;
+ teco_expressions_pop_num(0);
+ }
+ return teco_state_start_input(ctx, chr, error);
+}
+
+/*
+ * Due to the deferred nature of `==`,
+ * it is valid to end in this state as well.
+ */
+static gboolean
+teco_state_print_octal_end_of_macro(teco_machine_main_t *ctx, GError **error)
+{
+ if (teco_cmdline_is_executing(ctx) || ctx->flags.mode > TECO_MODE_NORMAL)
+ return TRUE;
+ if (!teco_print(8, error))
+ return FALSE;
+ teco_expressions_pop_num(0);
+ return TRUE;
+}
+
+TECO_DEFINE_STATE_START(teco_state_print_octal,
+ .initial_cb = (teco_state_initial_cb_t)teco_state_print_octal_initial,
+ .end_of_macro_cb = (teco_state_end_of_macro_cb_t)
+ teco_state_print_octal_end_of_macro
+);
diff --git a/src/stdio-commands.h b/src/stdio-commands.h
new file mode 100644
index 0000000..b795c08
--- /dev/null
+++ b/src/stdio-commands.h
@@ -0,0 +1,21 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+
+#include "parser.h"
+
+TECO_DECLARE_STATE(teco_state_print_decimal);