diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2025-08-03 15:41:28 +0300 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2025-08-03 16:09:33 +0300 |
commit | 51bd183f064d0c0ea5e0184d9f6b6b62e5c01e50 (patch) | |
tree | 9820e9671db37fbf5657d1327ef93e3081f8a6ab /src | |
parent | 5a85721a0a1b592287cb67188c5f0c5b55b3e348 (diff) | |
download | sciteco-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.c | 7 | ||||
-rw-r--r-- | src/interface.h | 2 | ||||
-rw-r--r-- | src/main.c | 41 | ||||
-rw-r--r-- | src/view.c | 63 | ||||
-rw-r--r-- | src/view.h | 2 |
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); @@ -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; @@ -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. * @@ -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) \ |