#include "diagnostics.h"
#include "stuff/errors.h"
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static int diagnostics_state = -1;
struct diagnostics_info {
const char* log_file;
const char* main_file;
const char* tool;
const char* args;
};
struct diagnostic {
const char* level;
char* message;
};
static struct diagnostic* diagnostics;
static int ndiagnostic;
static struct diagnostics_info info;
static void diagnostics_atexit(void);
static const char* diagnostic_level_name(enum diagnostic_level level)
{
const char* names[] = {
"warning",
"error",
"fatal error",
};
int nname = sizeof(names) / sizeof(*names);
if (level >= 0 && level < nname)
return names[level];
return names[ERROR];
}
void diagnostics_enable(enum bool enable)
{
if (diagnostics_state == -1) {
atexit(diagnostics_atexit);
}
diagnostics_state = enable ? 1 : 0;
}
void diagnostics_output(const char* logfile)
{
if (info.log_file)
free((void*)info.log_file);
info.log_file = logfile ? strdup(logfile) : NULL;
}
enum bool diagnostics_enabled(void)
{
return diagnostics_state == 1 ? TRUE : FALSE;
}
void diagnostics_log_args(int argc, char** argv)
{
char* buf;
size_t len;
FILE* stream;
if (argc > 0)
info.tool = argv[0];
if (info.args) {
free((void*)info.args);
info.args = NULL;
}
stream = open_memstream(&buf, &len);
if (stream) {
for (int i = 1; i < argc; ++i) {
fprintf(stream, "%s%s", i == 1 ? "" : " ", argv[i]);
}
fclose(stream);
info.args = strdup(buf);
free(buf);
}
}
void diagnostics_log_msg(enum diagnostic_level level, const char* message)
{
diagnostics = reallocf(diagnostics, sizeof(*diagnostics) * (ndiagnostic+1));
struct diagnostic* d = &diagnostics[ndiagnostic++];
d->level = diagnostic_level_name(level);
d->message = strdup(message);
}
void diagnostics_write(void)
{
char* buf;
size_t len;
FILE* stream;
int fd;
if (ndiagnostic == 0)
return;
stream = open_memstream(&buf, &len);
if (!stream)
return;
fprintf(stream, "<dict>\n");
if (info.tool) {
fprintf(stream, " <key>tool</key>\n");
fprintf(stream, " <string>%s</string>\n", info.tool);
}
else if (progname) {
fprintf(stream, " <key>tool</key>\n");
fprintf(stream, " <string>%s</string>\n", progname);
}
if (info.args) {
fprintf(stream, " <key>args</key>\n");
fprintf(stream, " <string>%s</string>\n", info.args);
}
fprintf(stream, " <key>diagnostics</key>\n");
fprintf(stream, " <array>\n");
for (int i = 0; i < ndiagnostic; ++i) {
struct diagnostic* d = &diagnostics[i];
fprintf(stream, " <dict>\n");
fprintf(stream, " <key>level</key>\n");
fprintf(stream, " <string>%s</string>\n", d->level);
if (d->message) {
fprintf(stream, " <key>message</key>\n");
fprintf(stream, " <string>%s</string>\n", d->message);
}
fprintf(stream, " </dict>\n");
}
fprintf(stream, " </array>\n");
fprintf(stream, "</dict>\n");
fclose(stream);
if (info.log_file) {
fd = open(info.log_file, O_WRONLY | O_APPEND | O_CREAT, 0644);
if (-1 == fd) {
fprintf(stderr, "error: cannot open file at %s: %s\n",
info.log_file, strerror(errno));
free(buf);
return;
}
}
else {
fd = STDERR_FILENO;
}
write(fd, buf, len);
if (fd != STDERR_FILENO)
close(fd);
free(buf);
for (int i = 0; i < ndiagnostic; ++i) {
struct diagnostic* d = &diagnostics[i];
free(d->message);
}
free(diagnostics);
diagnostics = NULL;
ndiagnostic = 0;
}
void diagnostics_atexit(void)
{
if (diagnostics_enabled())
diagnostics_write();
}