C/C++ Arena

Step 7 of 7

Challenge: a real command-line tool

Command-line tools are glued together by scripts and other programs, and that only works because they all follow a few conventions. Writing tools that follow them is part of writing professional C.

stdout and stderr

A program has two output streams. stdout is for the program's actual results; stderr is for errors and diagnostics. They're kept separate so that ./tool > results.txt saves only results, while errors still appear on the screen. Write errors with fprintf(stderr, ...).

Exit codes

The value main returns (or passes to exit()) is the exit code. 0 means success; anything else means failure, and different numbers can signal different problems (by convention, 2 often means "used incorrectly"). Shell scripts check it: if ./tool; then ..., and CI systems fail a build when a step exits nonzero. <stdlib.h> names the basics EXIT_SUCCESS and EXIT_FAILURE.

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

int check_all(const int *values, int n) {
    int status = EXIT_SUCCESS;
    for (int i = 0; i < n; i++) {
        if (values[i] < 0) {
            fprintf(stderr, "check: value %d is negative\n", i);
            status = EXIT_FAILURE;
            continue;
        }
        printf("value %d ok: %d\n", i, values[i]);
    }
    return status;
}

int main(void) {
    int v[] = {3, 8, 5};
    int status = check_all(v, 3);
    printf("done\n");
    return status;
}
value 0 ok: 3
value 1 ok: 8
value 2 ok: 5
done

Keep going, but remember the failure

A good tool processes everything it can: one missing file shouldn't stop the others from being counted. It reports each problem on stderr as it happens, remembers that something failed (the status variable), and returns the failure code at the end. Early exit is right only when continuing makes no sense, such as no arguments at all, where you print a usage message to stderr.

The tests on this site check stdout and the exit code separately, just like a script would.

Your turn: write lines FILE..., a tool that prints COUNT NAME for each file and a final COUNT total line. A file that can't be opened prints lines: cannot open NAME to stderr, is skipped, and makes the program exit with code 1 at the end (after the total). With no arguments at all, print usage: lines FILE... to stderr and exit with code 2.

Previous: Variadic functions