/* * 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 "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 * = -- Print integer as message * == * === * * Shows integer 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 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 );