]> git.draconx.ca Git - liblbx.git/blobdiff - src/tools.c
tools: Add some smarter error printing routines.
[liblbx.git] / src / tools.c
index b80e5212966e234ffe54d495ea67d4a0b7c9caba..200cf9c289e023e20f10681576f4fbf8062e5aac 100644 (file)
 
 #include <config.h>
 #include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <inttypes.h>
 #include <assert.h>
 
 #include "tools.h"
@@ -46,3 +52,91 @@ const char *tool_invocation(void)
        assert(argv0 != NULL);
        return argv0;
 }
+
+/* Saturating addition. */
+static size_t add_size(size_t a, size_t b)
+{
+       if (a >= SIZE_MAX - b)
+               return SIZE_MAX;
+       return a + b;
+}
+
+static int vfmsg_internal(FILE *f, int err, const char *fmt, va_list ap)
+{
+       size_t invokelen, fmtlen, errlen, totlen;
+       char *newfmt, *errmsg;
+       int rc;
+
+       invokelen = strlen(tool_invocation());
+       fmtlen = fmt ? strlen(fmt) : 0;
+       errlen = err > 0 ? strlen(errmsg = strerror(err)) : 0;
+
+       totlen = add_size(invokelen, sizeof "\n");
+       if (fmtlen > 0) {
+               totlen = add_size(totlen, strlen(": "));
+               totlen = add_size(totlen, fmtlen);
+       }
+       if (errlen > 0) {
+               totlen = add_size(totlen, strlen(": "));
+               totlen = add_size(totlen, errlen);
+       }
+
+       if (totlen == SIZE_MAX || totlen > INT_MAX)
+               return -1;
+
+       newfmt = malloc(totlen);
+       if (!newfmt)
+               return -1;
+
+       if (errlen && fmtlen)
+               rc = sprintf(newfmt, "%s: %s: %s\n", argv0, fmt, errmsg);
+       else if (errlen)
+               rc = sprintf(newfmt, "%s: %s\n", argv0, errmsg);
+       else if (fmtlen)
+               rc = sprintf(newfmt, "%s: %s\n", argv0, fmt);
+       else
+               rc = sprintf(newfmt, "%s\n", argv0);
+
+       assert(rc < totlen);
+       if (rc < 0)
+               goto out;
+
+       rc = vfprintf(f, newfmt, ap);
+out:
+       free(newfmt);
+       return rc;
+}
+
+int tool_vmsg(const char *fmt, va_list ap)
+{
+       return vfmsg_internal(stdout, -1, fmt, ap);
+}
+
+int tool_verr(int err, const char *fmt, va_list ap)
+{
+       return vfmsg_internal(stderr, err == 0 ? errno : err, fmt, ap);
+}
+
+int tool_msg(const char *fmt, ...)
+{
+       va_list ap;
+       int rc;
+
+       va_start(ap, fmt);
+       rc = tool_vmsg(fmt, ap);
+       va_end(ap);
+
+       return rc;
+}
+
+int tool_err(int err, const char *fmt, ...)
+{
+       va_list ap;
+       int rc;
+
+       va_start(ap, fmt);
+       rc = tool_verr(err, fmt, ap);
+       va_end(ap);
+
+       return rc;
+}