// 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

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

#ifndef REPORT_PREFIX_INFO
#define REPORT_PREFIX_INFO  "Info: "
#endif
#ifndef REPORT_PREFIX_ERROR
#define REPORT_PREFIX_ERROR "Error: "
#endif
#ifndef REPORT_PREFIX_FATAL
#define REPORT_PREFIX_FATAL "Fatal: "
#endif

#define report_info( ...) report(REPORT_STREAM_INFO,  REPORT_PREFIX_INFO,  0, __VA_ARGS__)
#define report_error(...) report(REPORT_STREAM_ERROR, REPORT_PREFIX_ERROR, 0, __VA_ARGS__)
#define report_fatal(...) report(REPORT_STREAM_FATAL, REPORT_PREFIX_FATAL, 1, __VA_ARGS__)


static int report(
    FILE * stream,
    char const * prefix,
    int fatal,
    int report_errno,
    char const * format,
    ...
) {
    fprintf(stream, "%s", prefix);
    {
        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 (fatal)
        exit(EXIT_FAILURE);
    return EXIT_FAILURE;
}