Я пытаюсь получить ширину терминала с помощью 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
. Отредактирую свой пост, спасибо.
Вероятно, вы печатаете значение неинициализированной переменной. Ваш код не проверяет успешность работы ioctl
, а в случае неудачи оставляет ws
нетронутым.
Исправить:
#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);
@KamilCuk Спасибо, исправлено.
Вы не проверяете, действительно ли
ioctl(STDIN_FILENO, TIOCGWINSZ, &ws);
работает.