Почему термины не работают перед системным вызовом `write`?

Почему вывод этой программы не подчеркивается

int main() {
  tgetent(NULL, getenv("TERM"));
  tputs(tgetstr("us", NULL), 1, &putchar);
  write(1, "Hello world!\n", 13);
  tputs(tgetstr("ue", NULL), 1, &putchar);
}

но это есть?

int main() {
  tgetent(NULL, getenv("TERM"));
  tputs(tgetstr("us", NULL), 1, &putchar);
  puts("Hello world!");
  tputs(tgetstr("ue", NULL), 1, &putchar);
}

ИЗМЕНИТЬ

Проблема действительно в управлении буфером! Если я добавлю fflush, строка будет правильно подчеркнута

int main() {
  tgetent(NULL, getenv("TERM"));
  tputs(tgetstr("us", NULL), 1, &putchar);
  fflush(stdout);
  write(1, "Hello world!\n", 13);
  tputs(tgetstr("ue", NULL), 1, &putchar);
}

person GingerBadger    schedule 28.09.2020    source источник
comment
Поскольку termcap/terminfo предполагает, что он полностью контролирует отображение, а write() подрывает это. Вам нужно будет refresh() разместить информацию о termcap/terminfo. Тогда, возможно, если вам повезет, write() сработает. Но вы не должны смешивать функции termcap/terminfo с низкоуровневыми функциями write() (или read()).   -  person Jonathan Leffler    schedule 28.09.2020
comment
@JonathanLeffler Спасибо! Но чем write() отличается от функций более высокого уровня, таких как putchar() и puts()? Помимо управления буфером?   -  person GingerBadger    schedule 28.09.2020
comment
Хммм… Мне хотелось сказать, что все дело в управлении буфером, но это предполагает curses, а не только termcap или terminfo. Потом мне снились кошмары.... Затем я посмотрел на справочную страницу (man 3 tputs на Mac), и они, в конце концов, являются частью curses — и поэтому вполне вероятно, что все связано с управлением буфером. Вы не показали MCVE (минимальный, полный, проверяемый пример — или MRE или любое другое имя, которое сейчас использует SO) или SSCCE ( Короткий, автономный, правильный пример). Что еще есть в программе?   -  person Jonathan Leffler    schedule 28.09.2020
comment
Проблема действительно была в управлении буфером! Спасибо за вашу помощь :)   -  person GingerBadger    schedule 28.09.2020
comment
The issue is, indeed пожалуйста, опубликуйте это как ответ на ваш вопрос.   -  person KamilCuk    schedule 28.09.2020


Ответы (1)


Причина различия в том, что putchar и puts буферизуются, а write — нет. В этом примере

int main() {
  tgetent(NULL, getenv("TERM"));
  tputs(tgetstr("us", NULL), 1, &putchar);
  write(1, "Hello world!\n", 13);
  tputs(tgetstr("ue", NULL), 1, &putchar);
}

персонажи, написанные

write(1, "Hello world!\n", 13);

скорее всего, попадут на экран первыми, потому что (в отличие от вызовов tputs) они записываются сразу. Символы, записанные вызовами tputs, сохраняются в буфере (который обычно намного больше, чем строки, возвращаемые tgetstr), и, поскольку вы не предоставили другого способа очистки буфера, они записываются очистка среды выполнения при ее выходе.

Без явного оператора возврата стандарт C гарантирует, что он имеет тот же эффект, что и вызов exit (который выполняет fflush для каждого открытого файла, включая такие потоки, как stdout).

Хотя в принципе вы могли бы создать termcap с очень длинными строками, предполагается, что описания termcap ограничены 1023 байтами (и даже описания terminfo обычно ограничены 4096 байтами), в то время как стандартный размер буфера ввода-вывода обычно в несколько раз больше. limit, чтобы вы не увидели, что эти tputs вызовы записаны без много работы (т. е. без изменения среды выполнения...).

person Thomas Dickey    schedule 08.10.2020