Стрелка навигационного ввода в C
Я создаю интерфейс оболочки для программы, сначала я использовал getline
в сочетании с strtok
разделять запись пользователя, но это не лучший выбор, потому что, если передана строка в кавычках, она будет разбита на несколько аргументов, или, если в строке в кавычках есть пробелы, они будут проигнорированы. Кроме того, я хотел бы реализовать некоторые основные функции оболочки, такие как ярлык Ctrl-L
для очистки оболочки.
Поэтому я решил пользователя termios
и создать свою собственную функцию для ввода в оболочке.
На данный момент код выглядит
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
char *read_cmdline() {
struct termios oldt;
struct termios newt;
tcgetattr(STDIN_FILENO, &oldt); /*store old settings */
newt = oldt; /* copy old settings to new settings */
newt.c_lflag &= ~(ICANON | ECHO); /* make one change to old settings in new settings */
tcsetattr(STDIN_FILENO, TCSANOW, &newt); /*apply the new settings immediatly */
char *line = malloc(128);
int end_of_line = 0;
char c, y, z;
unsigned lus = 0;
unsigned size = 128;
while (!end_of_line) {
if (lus+1 == size) {
size *= 2;
line = realloc(line, size);
}
c = getc(stdin);
switch (c)
{
case 27:
y = getchar();
switch (y)
{
case 91:
z = getchar();
switch (z)
{
case 65:
printf("up arrow key pressed\n");
continue;
case 66:
printf("down arrow key pressed\n");
continue;
case 67:
printf("right arrow key pressed\n");
continue;
case 68:
printf("left arrow key pressed\n");
continue;
}
break;
}
case 12:
system("clear");
break;
}
if (c >= 32 && c <= 126) {
putchar(c);
line[lus++] = c;
}
else if (c == 13)
end_of_line = 1;
}
tcsetattr(STDIN_FILENO, TCSANOW, &oldt); /*reapply the old settings */
return line;
}
Синтаксический анализ пока не реализован, только базовое чтение строки с ярлыком Ctrl-L
реализовано также.
Как сделать навигацию влево / вправо в строке?
Редактировать: у меня есть реализация, которая теперь может перемещаться по одной строке, но несколько строк не работают с этим кодом:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <termios.h>
#define MIN_SIZE 128
char *line = NULL;
static unsigned pos = 0;
static int size;
static int b_size;
static unsigned length = 0;
char *buff = NULL;
static unsigned b_pos = 0;
static unsigned b_length = 0;
void insert(char c)
{
if (pos == length) {
++length;
if (length == size) {
size *= 2;
line = realloc(line, size);
}
line[pos++] = c;
}
else {
++length;
if (length == b_size) {
b_size = size;
size *= 2;
buff = realloc(buff, size);
}
strncpy(buff, line, pos);
strncpy(&buff[pos+1], &line[pos], length-pos);
buff[pos++] = c;
char *temp = buff;
buff = line;
line = temp;
}
}
int move_cursor(int delta) {
int pos2 = pos + delta;
if (pos2 >= 0 && pos2 <= length) {
pos = pos2;
return 1;
}
else
return 0;
}
int backspace() {
if (pos == 0)
return 0;
else {
strncpy(&line[pos-1], &line[pos], length - pos);
--pos;
--length;
return 1;
}
}
int suppr() {
if (pos == length)
return 0;
else {
strncpy(&line[pos], &line[pos+1], length-pos);
--length;
}
}
char *read_cmdline() {
if (line == NULL)
line = malloc(MIN_SIZE);
if (buff == NULL)
buff = malloc(MIN_SIZE);
struct termios oldt;
struct termios newt;
tcgetattr(STDIN_FILENO, &oldt); /*store old settings */
newt = oldt; /* copy old settings to new settings */
newt.c_lflag &= ~(ICANON | ECHO); /* make one change to old settings in new settings */
tcsetattr(STDIN_FILENO, TCSANOW, &newt); /*apply the new settings immediatly */
int end_of_line = 0;
char c, y, z;
while (!end_of_line) {
c = getc(stdin);
switch (c)
{
case 127:
backspace();
putchar('\b');
case 27:
y = getchar();
switch (y)
{
case 91:
z = getchar();
switch (z)
{
case 65:
printf("up arrow key pressed\n");
continue;
case 66:
printf("down arrow key pressed\n");
continue;
case 67:
printf("right arrow key pressed\n");
continue;
case 68:
printf("\033[1D");
move_cursor(-1);
continue;
}
break;
}
case 12:
system("clear");
break;
}
if (c >= 32 && c <= 126) {
insert(c);
printf("\033[%dD", length);
printf("%s", line);
if (pos < length)
printf("\033[%dD", length-pos);
}
else if (c == 13)
end_of_line = 1;
}
tcsetattr(STDIN_FILENO, TCSANOW, &oldt); /*reapply the old settings */
return line;
}
Проблема в том, как я реализовал печать в цикле while, мне нужно восстановить размер терминала и печатать в соответствии с этим размером... Это сложная проблема! Я не упомянул, что не хочу добавлять зависимости в свой проект, поэтому ncurses не разрешен, но это проект UNIX, поэтому нет необходимости в совместимости платформы. (проклятия стандартны?)