Is there any way to recover from a printf()/puts() error?

94 views Asked by At

Whilst interpreting and transpiling Brainfuck, I have this series of calls to printf()/puts():

while (ip < ops->count) {
        Op op = ops->items[ip];

        switch (op.kind) {
            case '+':
                printf("    t.mem[t.head] += %zu;\n", op.operand);
                ++ip;
                break;
            case '-':
                printf("    t.mem[t.head] -= %zu;\n", op.operand);
                ++ip;
                break;
            case '.':
                printf("    xputchar_many(&t, %zu);\n", op.operand);
                ++ip;
                break;
            case ',':
                printf("    xgetchar_many(&t, %zu);{\n", op.operand);
                ++ip;
                break;
            case '>':
                printf("    t.head += %zu;\n\n"
                       "    grow_tape_and_append_many_zeroes(&t);\n", op.operand);
                ++ip;
                break;
            case '<':
                printf("    t.head = t.head < %zu ? t.count - (%zu - t.head) : t.head - %zu;\n",
                       op.operand, op.operand, op.operand);
                ++ip;
                break;
            case '[':
                puts("    while (t.mem[t.head] != 0) {");
                ++ip;
                break;
            case ']':
                puts("    }");
                ++ip;
                break;
        }
    }

The calls to puts()/printf() are generating the code.

I have two questions:

Is there any chance of recovery after printf() fails (i.e. return a negative integer) or puts() fail (i.e. return EOF)? (I am of the opinion no.)

If no, is calling fprintf(stderr, ...) before exiting still useful? (I am of the opinion yes.)

2

There are 2 answers

2
John Bollinger On BEST ANSWER

Is there any chance of recovery after printf() fails (i.e. return a negative integer) or puts() fail (i.e. return EOF)? (I am of the opinion no.)

There is unlikely to be anything that the program can do by itself to recover. As another answer points out, you can clear the destination FILE's error indicator and try again, but there's no particular reason to expect that that will be sufficient (but also no reason suppose that it couldn't be sufficient in any particular instance).

However, you're probably asking the wrong question. You're writing a Brainfuck transpiler. The first two questions you should be asking here are

  • Is it worth trying to recover from I/O errors?

    and

  • If not, then what should be done instead?

I claim that no, for this utility, it is not worth spending time or effort on an error-recovery mechanism for I/O errors. Such errors are unlikely to occur and unlikely to have good recovery options anyway, and the utility's role is not critical or sensitive. A recovery strategy for such errors would very likely be a wasted effort.

Instead, then, the program should probably just attempt to print a diagnostic to stderr and exit with a failure status, or maybe even abort().

You wrote in comments:

The problem is wrongly generated code, or half generated code, without an error indication.

Either exiting with a failure status or terminating because of a signal (which would be the result of calling abort()) is detectable in the environment from which the program is launched, so either of these options provides an error indication that can alert about the generated code being faulty as a result of the kind of error you ask about.

There may be other indications the program can provide too. For example, as @JonathanLeffler noted in comments, if the output were going to a regular file whose path the program knew, then it might attempt to remove that file when terminating because of an I/O error. Whether that or any other such behavior would be desirable is a judgement for you to make.

2
anatolyg On

In principle, yes. The <stdio.h> header file has a dedicated clearerr function to do just that. You could try displaying the error using perror, try to do clearerr and try again.

Possible use-case, suggested by Eric Postpischil:

You are writing logs to some device. It fills, and puts returns an error due to insufficient space. You [...] remove old log files and try again.

The cppreference site gives a slightly convoluted example for an error which looks recoverable to me (I didn't try it):

setlocale(LC_ALL, "en_US.utf8");

errno = 0;
if (fputwc(L'', stdout) == WEOF)
{
    if (errno == EILSEQ)
        puts("Encoding error in fputwc.");
    else
        puts("I/O error in fputwc.");
    return EXIT_FAILURE;
}

If stdout got errors, stderr could still be writable! Redirection is a common example (you could redirect stdout using a shell or freopen, while leaving stderr connected to the console).