#include #include #include #include #include struct he { struct he *prev; struct he *next; const char *data; }; struct history { uint32_t entries_left; struct he *newest_entry; struct he *oldest_entry; }; static void move_entry_to_front(struct history *h, struct he *e) { if (!e || !h || (e == h->newest_entry)) return; if (e->prev) e->prev->next = e->next; if (e->next) e->next->prev = e->prev; if (e == h->oldest_entry) h->oldest_entry = e->next; e->prev = h->newest_entry; e->next = 0; if (h->newest_entry) h->newest_entry->next = e; h->newest_entry = e; } static void add_to_history(struct history *h, const char *from) { const uint32_t from_size = strlen(from) + 1; char *const copy = get_block(from_size); blockcpy(copy, from, from_size); struct he *entry; if (h->entries_left) { --h->entries_left; entry = get_block(sizeof(struct he)); } else { entry = h->oldest_entry; h->oldest_entry = entry->next; entry->next->prev = 0; free_block(entry->data); } entry->data = copy; entry->prev = h->newest_entry; entry->next = 0; if (h->newest_entry) h->newest_entry->next = entry; else h->oldest_entry = entry; h->newest_entry = entry; } struct history *new_history(uint32_t max_entries) { if (max_entries < 2) return 0; struct history *h = get_block(sizeof(struct history)); if (!h) return 0; h->newest_entry = 0; h->oldest_entry = 0; h->entries_left = max_entries; return h; } uint32_t read_line(char *sz, uint32_t max_length, const char *prompt, struct history *hist) { term_add_sz(prompt); term_add_char(' '); term_paint(); uint32_t l = 0; uint32_t c = 0; struct he *h_on = 0; bool he_modified = false; while (1) { struct key_packet kp = term_get_key_blocking(); switch (kp.key_id) { case KEY_LEFT_ARROW: if (c) { --c; term_cursor_left(); term_paint(); } continue; case KEY_RIGHT_ARROW: if (c != l) { ++c; term_cursor_right(); term_paint(); } continue; case KEY_HOME: while (c) { --c; term_cursor_left(); } term_paint(); continue; case KEY_END: while (c != l) { ++c; term_cursor_right(); } term_paint(); continue; case KEY_DELETE: if (c != l) { ++c; term_cursor_right(); } case KEY_BSPACE: if (!c) continue; he_modified = true; --c; --l; for (uint32_t i = c; i < l; ++i) sz[i] = sz[i + 1]; term_cursor_left(); term_add_sn_no_ww(sz + c, l - c); term_add_char(' '); for (uint32_t i = l + 1; i > c; --i) term_cursor_left(); term_paint(); continue; case KEY_ENTER: while (c != l) { ++c; term_cursor_right(); } sz[l] = '\0'; if (hist) { if (he_modified) add_to_history(hist, sz); else if (h_on) move_entry_to_front(hist, h_on); } term_add_char('\n'); term_paint(); _yield_task(); return l; case KEY_UP_ARROW: if (!hist || !hist->newest_entry || (h_on == hist->oldest_entry)) continue; he_modified = false; h_on = h_on ? h_on->prev : hist->newest_entry; for (uint32_t i = 0; i < c; ++i) term_cursor_left(); for (uint32_t i = 0; i < l; ++i) term_add_char(' '); for (uint32_t i = 0; i < l; ++i) term_cursor_left(); l = strcpy(sz, h_on->data); term_add_sn_no_ww(sz, l); c = l; term_paint(); continue; case KEY_DOWN_ARROW: if (!h_on) continue; he_modified = false; h_on = h_on->next; for (uint32_t i = 0; i < c; ++i) term_cursor_left(); for (uint32_t i = 0; i < l; ++i) term_add_char(' '); for (uint32_t i = 0; i < l; ++i) term_cursor_left(); if (h_on) { l = strcpy(sz, h_on->data); term_add_sn_no_ww(sz, l); } else l = 0; c = l; term_paint(); continue; default: if (l == max_length) continue; char ch = key_to_char(kp); if (!ch) continue; he_modified = true; if (c == l) { ++l; term_add_char(sz[c++] = ch); term_paint(); continue; } if (!(kp.modifiers & INSERT)) { term_add_char(sz[c++] = ch); term_paint(); continue; } for (uint32_t i = l; i > c; --i) sz[i] = sz[i - 1]; sz[c] = ch; ++l; term_add_sn_no_ww(sz + c, l - c); ++c; for (uint32_t i = l; i > c; --i) term_cursor_left(); term_paint(); continue; } } }