// TODO: Document that `perror(3)` is standard but does not take a printf-style
// format argument. The ones that do are nonstandard, `err(3)` (BSD), `error`
// (GNU).
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>


#define REPORT_INFO  0
#define REPORT_ERROR 1
#define REPORT_FATAL 2

#define report_info( ...) report(REPORT_INFO,  __VA_ARGS__)
#define report_error(...) report(REPORT_ERROR, __VA_ARGS__)
#define report_fatal(...) report(REPORT_FATAL, __VA_ARGS__)

#ifndef REPORT_STREAM_INFO
#define REPORT_STREAM_INFO  stderr
#endif
#ifndef REPORT_STREAM_ERROR
#define REPORT_STREAM_ERROR stderr
#endif

#ifndef REPORT_FORMAT_PREFIX_INFO
#define REPORT_FORMAT_PREFIX_INFO  "Info: "
#endif
#ifndef REPORT_FORMAT_PREFIX_ERROR
#define REPORT_FORMAT_PREFIX_ERROR "Error: "
#endif
#ifndef REPORT_FORMAT_PREFIX_FATAL
#define REPORT_FORMAT_PREFIX_FATAL "Fatal: "
#endif


static int report(int report, int report_errno, char const * format, ...) {
    FILE * stream = REPORT_STREAM_INFO;
    if (report >= REPORT_ERROR)
        stream = REPORT_STREAM_ERROR;
    fprintf(stream, "%s",
        report == REPORT_INFO  ? REPORT_FORMAT_PREFIX_INFO  :
        report == REPORT_ERROR ? REPORT_FORMAT_PREFIX_ERROR :
        report == REPORT_FATAL ? REPORT_FORMAT_PREFIX_FATAL :
        ""
    );
    {
        va_list ap;
        va_start(ap, format);
        vfprintf(stream, format, ap);
        va_end(ap);
    }
    if (report_errno)
        fprintf(stream, ": %s", strerror(report_errno));
    fprintf(stream, "\n");
    if (report >= REPORT_FATAL)
        exit(EXIT_FAILURE);
    return report;
}