0

Why is the output of this program not getting underlined

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

but this is?

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

EDIT

The issue is, indeed, about buffer management! If I add fflush, the string is properly underlined

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);
}
GingerBadger
  • 312
  • 3
  • 12
  • 1
    Because termcap/terminfo assumes it is in complete control of the display and the `write()` subverts that. You'd have to `refresh()` to get the termcap/terminfo information placed. Then maybe if you're luck the `write()` will work. But you should not be mixing termcap/terminfo functions with low-level `write()` (or `read()`) functions. – Jonathan Leffler Sep 28 '20 at 04:20
  • @JonathanLeffler Thanks! But how is `write()` different from "higher-level" functions like `putchar()` and `puts()`? Aside from buffer management? – GingerBadger Sep 28 '20 at 04:38
  • 1
    Hmmm…I was tempted to say "it's all about buffer management", but that assumes `curses` rather than just `termcap` or `terminfo`. Then I had nightmares…. Then I looked at the man page (`man 3 tputs` on a Mac) and they are part of `curses` after all — and therefore it is likely that it _is_ all about buffer management. You've not shown an MCVE ([Minimal, Complete, Verifiable Example](https://stackoverflow.com/help/mcve) — or MRE or whatever name SO now uses) or an SSCCE ([Short, Self-Contained, Correct Example](http://sscce.org/)). What else is there in the program? – Jonathan Leffler Sep 28 '20 at 04:43
  • The issue was indeed about buffer management! Thanks for your help :) – GingerBadger Sep 28 '20 at 04:48
  • `The issue is, indeed` please post that as an answer to your question. – KamilCuk Sep 28 '20 at 07:25

1 Answers1

0

The reason for the difference is that putchar and puts are buffered, while write is not. In this example

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

the characters written by

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

are likely to get to the screen first, because (unlike the tputs calls) they are written immediately. The characters written by the tputs calls are stored in a buffer (which generally is much larger than the strings returned from tgetstr), and since you provided no other way to flush the buffer, those are written by the runtime's cleanup as it exits.

Without an explicit return statement, the C standard guarantees that it has the same effect as calling exit (which does an fflush on each open file—including streams such as stdout).

While you could in principle construct a termcap which had extremely long strings, termcap descriptions are supposed to be limited to 1023 bytes (and even terminfo descriptions are generally limited to 4096 bytes), while the standard I/O buffer size is generally several times that limit, so you wouldn't see those tputs calls written out without a lot of work (i.e., changing the runtime...).

Thomas Dickey
  • 51,086
  • 7
  • 70
  • 105