/* * Copyright (c) 2018 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include #include /* * History must store strings (commands) and allow traversing them and adding * new string. When new item is added then first it is compared if it is not * the same as the last one (then it is not stored). If there is no room in the * buffer to store the new item, oldest one is removed until there is a room. */ struct shell_history_item { sys_dnode_t dnode; uint16_t len; char data[]; }; void z_shell_history_mode_exit(struct shell_history *history) { history->current = NULL; } bool z_shell_history_get(struct shell_history *history, bool up, uint8_t *dst, uint16_t *len) { struct shell_history_item *h_item; /* history item */ sys_dnode_t *l_item; /* list item */ if (up) { /* button up */ l_item = (history->current == NULL) ? sys_dlist_peek_head(&history->list) : sys_dlist_peek_next_no_check(&history->list, history->current); } else { /* button down */ l_item = sys_dlist_peek_prev(&history->list, history->current); } history->current = l_item; if (l_item == NULL) { /* Reached the end of history. */ *len = 0U; return false; } h_item = CONTAINER_OF(l_item, struct shell_history_item, dnode); memcpy(dst, h_item->data, h_item->len); *len = h_item->len; dst[*len] = '\0'; return true; } /* Returns true if element was removed. */ static bool remove_from_tail(struct shell_history *history) { sys_dnode_t *node; if (sys_dlist_is_empty(&history->list)) { return false; } node = sys_dlist_peek_tail(&history->list); sys_dlist_remove(node); k_heap_free(history->heap, CONTAINER_OF(node, struct shell_history_item, dnode)); return true; } void z_shell_history_put(struct shell_history *history, uint8_t *line, size_t len) { sys_dnode_t *node; struct shell_history_item *new, *h_prev_item; uint32_t total_len = len + offsetof(struct shell_history_item, data); z_shell_history_mode_exit(history); if (len == 0) { return; } node = sys_dlist_peek_head(&history->list); h_prev_item = CONTAINER_OF(node, struct shell_history_item, dnode); if (node && (h_prev_item->len == len) && (memcmp(h_prev_item->data, line, len) == 0)) { /* Same command as before, do not store */ return; } for (;;) { new = k_heap_alloc(history->heap, total_len, K_NO_WAIT); if (new) { /* Got memory, add new item */ break; } else if (!remove_from_tail(history)) { /* Nothing to remove, cannot allocate memory. */ return; } } new->len = len; memcpy(new->data, line, len); sys_dlist_prepend(&history->list, &new->dnode); } void z_shell_history_purge(struct shell_history *history) { while (remove_from_tail(history)) { } history->current = NULL; }