aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorRobin Haberkorn <robin.haberkorn@googlemail.com>2025-08-03 15:41:28 +0300
committerRobin Haberkorn <robin.haberkorn@googlemail.com>2025-08-03 16:09:33 +0300
commit51bd183f064d0c0ea5e0184d9f6b6b62e5c01e50 (patch)
tree9820e9671db37fbf5657d1327ef93e3081f8a6ab /src
parent5a85721a0a1b592287cb67188c5f0c5b55b3e348 (diff)
downloadsciteco-51bd183f064d0c0ea5e0184d9f6b6b62e5c01e50.tar.gz
added --quiet, --stdin and --stdout for easier integration into UNIX pipelines
* In principle --stdin and --stdout could have been done in pure TECO code using the <^T> command. Having built-in command-line arguments however has several advantages: * Significantly faster than reading byte-wise with ^T. * Performs EOL normalization unless specifying --8bit of course. * Significantly shortens command-lines. `sciteco -qio` and `sciteco -qi` can be real replacements for sed and awk. * You can even place SciTECO into the middle of a pipeline while editing interactively: foo | sciteco -qio --no-profile | bar Unfortunately, this will not currently work when munging the profile as command-line parameters are also transmitted via the unnamed buffer. This should be changed to use special Q-registers (FIXME). * --quiet can help to improve the test suite (TODO). Should probably be the default in TE_CHECK(). * --stdin and --stdout allow to simplify many SciTECO scripts, avoiding temporary files, especially for womenpage generation (TODO). * For processing potentially infinite streams, you will still have to read using ^T.
Diffstat (limited to 'src')
-rw-r--r--src/interface.c7
-rw-r--r--src/interface.h2
-rw-r--r--src/main.c41
-rw-r--r--src/view.c63
-rw-r--r--src/view.h2
5 files changed, 114 insertions, 1 deletions
diff --git a/src/interface.c b/src/interface.c
index c3103fd..cfc8279 100644
--- a/src/interface.c
+++ b/src/interface.c
@@ -36,6 +36,9 @@
//#define DEBUG
+/** minimum level of messages to print to stdout/stderr */
+teco_msg_t teco_interface_msg_level = TECO_MSG_USER;
+
teco_view_t *teco_interface_current_view = NULL;
TECO_DEFINE_UNDO_CALL(teco_interface_show_view, teco_view_t *);
@@ -114,6 +117,10 @@ teco_interface_msg(teco_msg_t type, const gchar *fmt, ...)
void
teco_interface_stdio_msg(teco_msg_t type, const gchar *str, gsize len)
{
+ /* "user"-level messages are always printed */
+ if (type != TECO_MSG_USER && type < teco_interface_msg_level)
+ return;
+
switch (type) {
case TECO_MSG_USER:
fwrite(str, 1, len, stdout);
diff --git a/src/interface.h b/src/interface.h
index 02af8a2..c0c41bd 100644
--- a/src/interface.h
+++ b/src/interface.h
@@ -66,6 +66,8 @@ typedef enum {
TECO_MSG_ERROR
} teco_msg_t;
+extern teco_msg_t teco_interface_msg_level;
+
/** @pure */
void teco_interface_msg_literal(teco_msg_t type, const gchar *str, gsize len);
diff --git a/src/main.c b/src/main.c
index 42e1520..2be81ea 100644
--- a/src/main.c
+++ b/src/main.c
@@ -42,6 +42,7 @@
#include "parser.h"
#include "goto.h"
#include "qreg.h"
+#include "view.h"
#include "ring.h"
#include "undo.h"
#include "error.h"
@@ -109,6 +110,9 @@ teco_get_default_config_path(void)
#endif
static gboolean teco_show_version = FALSE;
+static gboolean teco_quiet = FALSE;
+static gboolean teco_stdin = FALSE;
+static gboolean teco_stdout = FALSE;
static gchar *teco_eval_macro = NULL;
static gboolean teco_mung_file = FALSE;
static gboolean teco_mung_profile = TRUE;
@@ -122,6 +126,12 @@ teco_process_options(gchar ***argv)
static const GOptionEntry option_entries[] = {
{"version", 'v', 0, G_OPTION_ARG_NONE, &teco_show_version,
"Show version"},
+ {"quiet", 'q', 0, G_OPTION_ARG_NONE, &teco_quiet,
+ "Don't print any non-user-level messages to stdout"},
+ {"stdin", 'i', 0, G_OPTION_ARG_NONE, &teco_stdin,
+ "Read stdin into the unnamed buffer"},
+ {"stdout", 'o', 0, G_OPTION_ARG_NONE, &teco_stdout,
+ "Print current buffer to stdout before program termination"},
{"eval", 'e', 0, G_OPTION_ARG_STRING, &teco_eval_macro,
"Evaluate macro", "macro"},
{"mung", 'm', 0, G_OPTION_ARG_NONE, &teco_mung_file,
@@ -195,6 +205,10 @@ teco_process_options(gchar ***argv)
exit(EXIT_SUCCESS);
}
+ if (teco_quiet)
+ /* warnings and errors will still be printed to stderr */
+ teco_interface_msg_level = TECO_MSG_WARNING;
+
if ((*argv)[0] && !g_strcmp0((*argv)[1], "-S")) {
/* translate -S to --, this is always passed down */
(*argv)[1][1] = '-';
@@ -453,17 +467,39 @@ main(int argc, char **argv)
}
/*
+ * Load stdin into the unnamed buffer.
+ * This will also perform EOL normalization.
+ * This is not done automatically when isatty(0) == 0
+ * since you might want to read from stdin manually (^T).
+ * Loading stdin also won't work if the stream is infinite.
+ *
+ * NOTE: The profile hasn't run yet, so it cannot guess the
+ * documents encoding. This should therefore be done by the profile
+ * for any preexisting unnamed buffer.
+ *
+ * FIXME: The unnamed buffer is also currently used for
+ * command-line parameters.
+ * Therefore you practically cannot pipe into SciTECO
+ * while using opener.tes.
+ */
+ if (teco_stdin && !teco_view_load_from_stdin(teco_ring_current->view, TRUE, &error))
+ goto cleanup;
+
+ /*
* Add remaining arguments to unnamed buffer.
*
* FIXME: This is not really robust since filenames may contain linefeeds.
* Also, the Unnamed Buffer should be kept empty for piping.
- * Therefore, it would be best to store the arguments in Q-Regs, e.g. $0,$1,$2...
+ * Therefore, it would be best to store the arguments in Q-Regs, e.g. ^A0,^A1,^A2...
*/
for (gint i = 1; argv_utf8[i]; i++) {
teco_interface_ssm(SCI_APPENDTEXT, strlen(argv_utf8[i]), (sptr_t)argv_utf8[i]);
teco_interface_ssm(SCI_APPENDTEXT, 1, (sptr_t)"\n");
}
+ if (teco_interface_ssm(SCI_GETLENGTH, 0, 0) > 0)
+ teco_ring_dirtify();
+
/*
* Execute macro or mung file
*/
@@ -571,6 +607,9 @@ main(int argc, char **argv)
goto cleanup;
cleanup:
+ if (!error && teco_stdout)
+ teco_view_save_to_stdout(teco_ring_current->view, &error);
+
if (error != NULL) {
teco_error_display_full(error);
ret = EXIT_FAILURE;
diff --git a/src/view.c b/src/view.c
index 0fc1986..e44b386 100644
--- a/src/view.c
+++ b/src/view.c
@@ -350,6 +350,44 @@ teco_view_load_from_file(teco_view_t *ctx, const gchar *filename,
return TRUE;
}
+/**
+ * Load stdin until EOF into view's document.
+ *
+ * @param ctx The view to load.
+ * @param clear Whether to completely replace document
+ * (leaving dot at the beginning of the document) or insert at dot
+ * (leaving dot at the end of the insertion).
+ * @param error A GError.
+ * @return FALSE in case of a GError.
+ *
+ * @memberof teco_view_t
+ */
+gboolean
+teco_view_load_from_stdin(teco_view_t *ctx, gboolean clear, GError **error)
+{
+#ifdef G_OS_WIN32
+ g_autoptr(GIOChannel) channel = g_io_channel_win32_new_fd(0);
+#else
+ g_autoptr(GIOChannel) channel = g_io_channel_unix_new(0);
+#endif
+ g_assert(channel != NULL);
+
+ /*
+ * The file loading algorithm does not need buffered
+ * streams, so disabling buffering should increase
+ * performance (slightly).
+ */
+ g_io_channel_set_encoding(channel, NULL, NULL);
+ g_io_channel_set_buffered(channel, FALSE);
+
+ if (!teco_view_load_from_channel(ctx, channel, clear, error)) {
+ g_prefix_error_literal(error, "Error reading stdin: ");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
#if 0
/*
@@ -554,6 +592,31 @@ teco_view_save_to_file(teco_view_t *ctx, const gchar *filename, GError **error)
return TRUE;
}
+/** @memberof teco_view_t */
+gboolean
+teco_view_save_to_stdout(teco_view_t *ctx, GError **error)
+{
+#ifdef G_OS_WIN32
+ g_autoptr(GIOChannel) channel = g_io_channel_win32_new_fd(1);
+#else
+ g_autoptr(GIOChannel) channel = g_io_channel_unix_new(1);
+#endif
+ g_assert(channel != NULL);
+
+ /*
+ * teco_view_save_to_channel() expects a buffered and blocking channel
+ */
+ g_io_channel_set_encoding(channel, NULL, NULL);
+ g_io_channel_set_buffered(channel, TRUE);
+
+ if (!teco_view_save_to_channel(ctx, channel, error)) {
+ g_prefix_error_literal(error, "Error writing to stdout: ");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
/**
* Convert a glyph index to a byte offset as used by Scintilla.
*
diff --git a/src/view.h b/src/view.h
index 4e4b85e..82a5c99 100644
--- a/src/view.h
+++ b/src/view.h
@@ -56,6 +56,7 @@ gboolean teco_view_load_from_channel(teco_view_t *ctx, GIOChannel *channel,
gboolean clear, GError **error);
gboolean teco_view_load_from_file(teco_view_t *ctx, const gchar *filename,
gboolean clear, GError **error);
+gboolean teco_view_load_from_stdin(teco_view_t *ctx, gboolean clear, GError **error);
/** @memberof teco_view_t */
#define teco_view_load(CTX, FROM, CLEAR, ERROR) \
@@ -66,6 +67,7 @@ gboolean teco_view_load_from_file(teco_view_t *ctx, const gchar *filename,
gboolean teco_view_save_to_channel(teco_view_t *ctx, GIOChannel *channel, GError **error);
gboolean teco_view_save_to_file(teco_view_t *ctx, const gchar *filename, GError **error);
+gboolean teco_view_save_to_stdout(teco_view_t *ctx, GError **error);
/** @memberof teco_view_t */
#define teco_view_save(CTX, TO, ERROR) \