HiveBrain v1.2.0
Get Started
← Back to all entries
patterncMinor

Non-curses pager in C (revision 2)

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
pagercursesrevisionnon

Problem

In this revision (follow-up of this post) I've fixed reading from stdin, and also tried to minimise the damage caused by typing additional characters. You might want to see my original post on this.

#include 
#include 

/*
 * A very SIMPLE pager that does not require curses. If your curses library
 * is broken, this is practically the only portable one you can easily get.
 */

int
main(
    int argc,
    char *argv[])
{
    FILE *fp;
    FILE *tty;
    int ch;

    if (argc < 2) {
        fp = stdin;
    } else if ((fp = fopen(argv[1], "r")) == NULL) {
        perror(argv[1]);
        return EXIT_FAILURE;
    }

    if ((tty = fopen("/dev/tty", "r")) == NULL) {
        perror("/dev/tty");
        return EXIT_FAILURE;
    }

    while ((ch = fgetc(fp)) != EOF) {
        if (ch == '\n') {
            while (fgetc(tty) != '\n');
        } else {
            putchar(ch);
        }
    }

    if (ferror(fp)) {
        perror(argv[1]);
        return EXIT_FAILURE;
    }

    fclose(fp);
    return EXIT_SUCCESS;
}


Some questions:

  • 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.



  • Also, I want to avoid dependency on any library except standard C.

Solution

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 
#include 

#ifdef _WIN32
#include 
#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.

Code Snippets

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

#ifdef _WIN32
#include <windows.h>
#endif
#ifdef linux
...
#endif

Context

StackExchange Code Review Q#113229, answer score: 2

Revisions (0)

No revisions yet.