использование ioctl для заполнения структуры winsize при передаче в stdin - PullRequest
0 голосов
/ 13 сентября 2018

Я пытаюсь получить ширину терминала, используя ioctl(), но он не работает при конвейерной передаче или перенаправлении на стандартный ввод.

Мне удалось обойти проблему, проанализировав результатtput cols, но использование внешней команды кажется грязным.Кроме того, я предполагаю, что это делает его менее портативным, поскольку Windows не использует bourne-совместимую оболочку?

main.c

// tput method
char res[10];

FILE cmd = popen("tput cols", "r");
fgets(res, 10 - 1, cmd);
pclose(cmd);

unsigned short term_cols = atoi(res);
printf("Term width (tput): %d\n", term_cols);

// ioctl method
struct winsize ws;
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0)
{
  printf("Term width (ioctl): %d\n", ws.ws_col);
}
else
{
  printf("Failed to retrieve term width from ioctl()");
}

вывод

$ bin/main  
Term width (tput): 84  
Term width (ioctl): 84
$ echo "test" | bin/main  
Term width (tput): 84  
Failed to retrieve term width from ioctl()

Я пытался fflush(stdin); в начале моего кода, но это не такне имеет значения.Это просто ограничение ioctl() или есть способ обойти это?

1 Ответ

0 голосов
/ 13 сентября 2018

Возможно, вы печатаете значение неинициализированной переменной. Ваш код не проверяет, успешно ли ioctl, а если нет, он оставляет нетронутым ws.

Fix:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

...
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) {
    fprintf(stderr, "can't get the window size of stdin: %s\n", strerror(errno));
    exit(EXIT_FAILURE);
}

Когда вы передаете что-то в свою программу, stdin ссылается не на терминал, а на канал. Трубы не имеют размера окна. Вот почему TIOCGWINSZ терпит неудачу здесь.

Портативное решение выглядит так:

const char *term = ctermid(NULL);
if (!term[0]) {
    fprintf(stderr, "can't get the name of my controlling terminal\n");
    exit(EXIT_FAILURE);
}
int fd = open(term, O_RDONLY);
if (fd == -1) {
    fprintf(stderr, "can't open my terminal at %s: %s\n", term, strerror(errno));
    exit(EXIT_FAILURE);
}
if (ioctl(fd, TIOCGWINSZ, &ws) == -1) {
    fprintf(stderr, "can't get the window size of %s: %s\n", term, strerror(errno));
    exit(EXIT_FAILURE);
}
close(fd);
...