/* Copyright (C) 2008 Emmanuel Varoquaux This file is part of XOS. 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 . */ #include #include "localhist.h" #include "kill.h" #include "edit.h" #include "input.h" #include "output.h" #include "tty.h" #include "terminal.h" #include "line.h" #include "misc.h" #include "xmalloc.h" #include static int command_is_kill; static int command_is_yank; static int last_command_was_kill; static int last_command_was_yank; static int start_of_current_word() { int newpoint; newpoint = point; while (newpoint != 0 && !letter_or_digit(line[newpoint - 1])) newpoint--; while (newpoint != 0 && letter_or_digit(line[newpoint - 1])) newpoint--; return newpoint; } static int end_of_current_word() { int newpoint; newpoint = point; while (newpoint != line_end && !letter_or_digit(line[newpoint])) newpoint++; while (newpoint != line_end && letter_or_digit(line[newpoint])) newpoint++; return newpoint; } static int previous_whitespace() { int newpoint; newpoint = point; while (newpoint != 0 && whitespace(line[newpoint - 1])) newpoint--; while (newpoint != 0 && !whitespace(line[newpoint - 1])) newpoint--; return newpoint; } static void copy_to_kill_ring(const char *start, const char *end, int append) { int len, oldlen, newlen; char *text, *newtext; len = end - start; if (append != 0) { text = kill_ring_get(); oldlen = strlen(text); newlen = oldlen + len; newtext = xmalloc(newlen + 1); if (append > 0) { memcpy(newtext, text, oldlen); memcpy(newtext + oldlen, start, len); } else { memcpy(newtext, start, len); memcpy(newtext + len, text, oldlen); } newtext[newlen] = '\0'; kill_ring_replace(newtext); } else { text = xmalloc(len + 1); memcpy(text, start, len); text[len] = '\0'; kill_ring_add(text); } } static void kill_region(int start, int end) { if (start != end) { copy_to_kill_ring(line + start, line + end, last_command_was_kill ? (mark == start ? 1 : -1) : 0); command_is_kill = 1; } delete_region(start, end); set_mark(start); } static void yank(int start, int end, const char *text) { command_is_yank = 1; replace_region_with_text(start, end, text); set_mark(start); } char __attribute__ ((weak, visibility ("default"))) *readline(const char *prompt) { int insert_mode; int cmd; char c; struct linebuf_struct *linebuf; int newpoint; const char *text; if (!rl_instream) rl_instream = stdin; if (!rl_outstream) rl_outstream = stdout; get_term_info(); setup_tty(); local_history_init(); insert_mode = 1; begin_editing(prompt, local_history_current()); command_is_kill = 0; command_is_yank = 0; while (1) { last_command_was_kill = command_is_kill; last_command_was_yank = command_is_yank; command_is_kill = 0; command_is_yank = 0; cmd = read_cmd(); switch (cmd_get_name(cmd)) { /* Moving */ case CM_MOVE_START: move_to(0); continue; case CM_MOVE_END: move_to(line_end); continue; case CM_MOVE_FW_CHAR: if (point == line_end) goto bad_command; move_to(point + 1); continue; case CM_MOVE_BW_CHAR: if (point == 0) goto bad_command; move_to(point - 1); continue; case CM_MOVE_FW_WORD: move_to(end_of_current_word()); continue; case CM_MOVE_BW_WORD: move_to(start_of_current_word()); continue; case CM_CLEAR_SCREEN: clear_screen(); continue; case CM_REFRESH_LINE: refresh_line(); continue; /* History */ case CM_ACCEPT_LINE: accept_line(); goto end; case CM_PREV_HIST: if (!(linebuf = local_history_previous())) goto bad_command; reset_line(linebuf); continue; case CM_NEXT_HIST: if (!(linebuf = local_history_next())) goto bad_command; reset_line(linebuf); continue; case CM_BEGIN_HIST: if (!(linebuf = local_history_begin())) goto bad_command; reset_line(linebuf); continue; case CM_END_HIST: if (!(linebuf = local_history_end())) goto bad_command; reset_line(linebuf); continue; /* Changing Text */ case CM_DELETE_CHAR: if (point == line_end) goto bad_command; delete_region(point, point + 1); continue; case CM_BW_DELETE_CHAR: if (point == 0) goto bad_command; if (insert_mode || point == line_end) delete_region(point - 1, point); else erase_region(point - 1, point); continue; case CM_INSERT_CHAR: c = cmd_get_char(cmd); if (insert_mode || point == line_end) replace_region_with_char(point, point, c); else replace_region_with_char(point, point + 1, c); continue; case CM_TOOGLE_OVWRT_MODE: insert_mode = !insert_mode; continue; /* Killing */ case CM_KILL_LINE: kill_region(point, line_end); continue; case CM_BW_KILL_LINE: if (point == 0) goto bad_command; kill_region(0, point); continue; case CM_KILL_WORD: kill_region(point, end_of_current_word()); continue; case CM_BW_KILL_WORD: kill_region(start_of_current_word(), point); continue; case CM_BW_KILL_WORD_WSP: if ((newpoint = previous_whitespace()) == point) goto bad_command; kill_region(newpoint, point); continue; case CM_YANK: if (!(text = kill_ring_get())) goto bad_command; yank(point, point, text); continue; case CM_YANK_POP: if (!last_command_was_yank) goto bad_command; text = kill_ring_get(); yank(point - strlen(text), point, kill_ring_rotate()); continue; /* Miscellaneous */ case CM_REVERT_LINE: delete_region(0, line_end); continue; case CM_SET_MARK: set_mark(point); continue; case CM_EXCHANGE_POINT_AND_MARK: if (mark == -1) goto bad_command; if (mark > line_end) { set_mark(-1); goto bad_command; } exchange_point_and_mark(); continue; /* divers */ case CM_INTERRUPT: abort_line(); goto end; case CM_EOF: discard_line(); goto end; } bad_command: ding(); } end: end_editing(); local_history_destroy(); restore_tty(); return line; }