Non-curses pager in C (revision 2)
Making the code portable again
You did a fine job cleaning up last code's bugs. My answer will be fairly short this time.

How can I avoid having to read from /dev/tty? I know that the device
file is available on practically all Unixes, but it won't work on
Windows.

Well, I have some good news and some bad news.
Bad News
The bad news is that I'm pretty sure the only way you aren't going to get that bug where you substitute the file for STDIN is if you continue reading from /dev/tty. After all, you can't possibly read from STDIN because that's where the file is.

You can stop now. As a quick and smart fix, you can drop support for reading from STDIN (I mean, who would use a pager to read something they are typing into STDIN?). Or, if you want you can read on for the good news.

Good News
There's a solution that allows you to keep on reading from /dev/tty and that is to use the amazing Windows API. Luckily, you can do that in C because Windows provides a header file windows.h that gives you full access to the Windows API in your C/C++ code.
Now, you may think to just include the windows.h file in your code and use whatever the API has to read the keyboard. Well, you can't quite do that because if you try compiling that on *nix, you'll get an error saying that the windows.h could not be found.
An easy fix is to use the pre-processor:
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;

#ifdef _WIN32
#include &lt;windows.h&gt;
#endif

This will make sure to only include the windows.h file if the operating system is a Windows one.
Then, in the section where you would normally open and read from the /dev/tty file, you would encase that in:
#ifdef linux
...
#endif

and the Windows code in the respective pre-processor statements.
As for what actually goes inside the Windows code sections, I am unsure. Through some quick searching, I came across ReadConsole in the API index. However, I have not tested it out. If that does not work, feel free to explore the API for other possibilities.