/* * Copyright (c) 2018 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include "shell_ops.h" void shell_op_cursor_vert_move(const struct shell *shell, s32_t delta) { if (delta != 0) { shell_raw_fprintf(shell->fprintf_ctx, "\033[%d%c", delta > 0 ? delta : -delta, delta > 0 ? 'A' : 'B'); } } void shell_op_cursor_horiz_move(const struct shell *shell, s32_t delta) { if (delta != 0) { shell_raw_fprintf(shell->fprintf_ctx, "\033[%d%c", delta > 0 ? delta : -delta, delta > 0 ? 'C' : 'D'); } } /* Function returns true if command length is equal to multiplicity of terminal * width. */ static inline bool full_line_cmd(const struct shell *shell) { return ((shell->ctx->cmd_buff_len + shell_strlen(shell->prompt)) % shell->ctx->vt100_ctx.cons.terminal_wid == 0); } /* Function returns true if cursor is at beginning of an empty line. */ bool shell_cursor_in_empty_line(const struct shell *shell) { return ((shell->ctx->cmd_buff_pos + shell_strlen(shell->prompt)) % shell->ctx->vt100_ctx.cons.terminal_wid == 0); } void shell_op_cond_next_line(const struct shell *shell) { if (shell_cursor_in_empty_line(shell) || full_line_cmd(shell)) { cursor_next_line_move(shell); } } void shell_op_cursor_position_synchronize(const struct shell *shell) { struct shell_multiline_cons *cons = &shell->ctx->vt100_ctx.cons; bool last_line; shell_multiline_data_calc(cons, shell->ctx->cmd_buff_pos, shell->ctx->cmd_buff_len); last_line = (cons->cur_y == cons->cur_y_end); /* In case cursor reaches the bottom line of a terminal, it will * be moved to the next line. */ if (full_line_cmd(shell)) { cursor_next_line_move(shell); } if (last_line) { shell_op_cursor_horiz_move(shell, cons->cur_x - cons->cur_x_end); } else { shell_op_cursor_vert_move(shell, cons->cur_y_end - cons->cur_y); shell_op_cursor_horiz_move(shell, cons->cur_x - cons->cur_x_end); } } void shell_op_cursor_move(const struct shell *shell, s16_t val) { struct shell_multiline_cons *cons = &shell->ctx->vt100_ctx.cons; u16_t new_pos = shell->ctx->cmd_buff_pos + val; s32_t row_span; s32_t col_span; shell_multiline_data_calc(cons, shell->ctx->cmd_buff_pos, shell->ctx->cmd_buff_len); /* Calculate the new cursor. */ row_span = row_span_with_buffer_offsets_get(&shell->ctx->vt100_ctx.cons, shell->ctx->cmd_buff_pos, new_pos); col_span = column_span_with_buffer_offsets_get( &shell->ctx->vt100_ctx.cons, shell->ctx->cmd_buff_pos, new_pos); shell_op_cursor_vert_move(shell, -row_span); shell_op_cursor_horiz_move(shell, col_span); shell->ctx->cmd_buff_pos = new_pos; } void shell_op_word_remove(const struct shell *shell) { char *str = &shell->ctx->cmd_buff[shell->ctx->cmd_buff_pos - 1]; char *str_start = &shell->ctx->cmd_buff[0]; u16_t chars_to_delete; /* Line must not be empty and cursor must not be at 0 to continue. */ if ((shell->ctx->cmd_buff_len == 0) || (shell->ctx->cmd_buff_pos == 0)) { return; } /* Start at the current position. */ chars_to_delete = 0; /* Look back for all spaces then for non-spaces. */ while ((str >= str_start) && (*str == ' ')) { ++chars_to_delete; --str; } while ((str >= str_start) && (*str != ' ')) { ++chars_to_delete; --str; } /* Manage the buffer. */ memmove(str + 1, str + 1 + chars_to_delete, shell->ctx->cmd_buff_len - chars_to_delete); shell->ctx->cmd_buff_len -= chars_to_delete; shell->ctx->cmd_buff[shell->ctx->cmd_buff_len] = '\0'; /* Update display. */ shell_op_cursor_move(shell, -chars_to_delete); cursor_save(shell); shell_fprintf(shell, SHELL_NORMAL, "%s", str + 1); clear_eos(shell); cursor_restore(shell); } void shell_op_cursor_home_move(const struct shell *shell) { shell_op_cursor_move(shell, -shell->ctx->cmd_buff_pos); } void shell_op_cursor_end_move(const struct shell *shell) { shell_op_cursor_move(shell, shell->ctx->cmd_buff_len - shell->ctx->cmd_buff_pos); } void shell_op_left_arrow(const struct shell *shell) { if (shell->ctx->cmd_buff_pos > 0) { shell_op_cursor_move(shell, -1); } } void shell_op_right_arrow(const struct shell *shell) { if (shell->ctx->cmd_buff_pos < shell->ctx->cmd_buff_len) { shell_op_cursor_move(shell, 1); } } static void reprint_from_cursor(const struct shell *shell, u16_t diff, bool data_removed) { /* Clear eos is needed only when newly printed command is shorter than * previously printed command. This can happen when delete or backspace * was called. * * Such condition is useful for Bluetooth devices to save number of * bytes transmitted between terminal and device. */ if (data_removed) { clear_eos(shell); } shell_fprintf(shell, SHELL_NORMAL, "%s", &shell->ctx->cmd_buff[shell->ctx->cmd_buff_pos]); shell->ctx->cmd_buff_pos = shell->ctx->cmd_buff_len; if (full_line_cmd(shell)) { if (((data_removed) && (diff > 0)) || (!data_removed)) { cursor_next_line_move(shell); } } shell_op_cursor_move(shell, -diff); } static void data_insert(const struct shell *shell, const char *data, u16_t len) { u16_t after = shell->ctx->cmd_buff_len - shell->ctx->cmd_buff_pos; char *curr_pos = &shell->ctx->cmd_buff[shell->ctx->cmd_buff_pos]; if ((shell->ctx->cmd_buff_len + len) >= CONFIG_SHELL_CMD_BUFF_SIZE) { return; } memmove(curr_pos + len, curr_pos, after); memcpy(curr_pos, data, len); shell->ctx->cmd_buff_len += len; shell->ctx->cmd_buff[shell->ctx->cmd_buff_len] = '\0'; if (!flag_echo_is_set(shell)) { shell->ctx->cmd_buff_pos += len; return; } reprint_from_cursor(shell, after, false); } void char_replace(const struct shell *shell, char data) { shell->ctx->cmd_buff[shell->ctx->cmd_buff_pos++] = data; shell_raw_fprintf(shell->fprintf_ctx, "%c", data); if (shell_cursor_in_empty_line(shell)) { cursor_next_line_move(shell); } } void shell_op_char_insert(const struct shell *shell, char data) { if (shell->ctx->internal.flags.insert_mode && (shell->ctx->cmd_buff_len != shell->ctx->cmd_buff_pos)) { char_replace(shell, data); } else { data_insert(shell, &data, 1); } } void shell_op_char_backspace(const struct shell *shell) { if ((shell->ctx->cmd_buff_len == 0) || (shell->ctx->cmd_buff_pos == 0)) { return; } shell_op_cursor_move(shell, -1); shell_op_char_delete(shell); } void shell_op_char_delete(const struct shell *shell) { u16_t diff = shell->ctx->cmd_buff_len - shell->ctx->cmd_buff_pos; char *str = &shell->ctx->cmd_buff[shell->ctx->cmd_buff_pos]; if (diff == 0) { return; } memmove(str, str + 1, diff); --shell->ctx->cmd_buff_len; reprint_from_cursor(shell, --diff, true); } void shell_op_completion_insert(const struct shell *shell, const char *compl, u16_t compl_len) { data_insert(shell, compl, compl_len); }