/* 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;
}