diff --git a/probe.c b/probe.c
index 2b6b01efd7d6f544c269e7f6b3ceec00dd1de67a..52296f49774da442f8b1bf8856912252cf83332f 100644
--- a/probe.c
+++ b/probe.c
@@ -19,8 +19,10 @@ static char *getxmlattr(xmlAttr *a, char *name, char *err)
 
 char *get_xmpchapters(char *xmp)
 {
-	int i, len, maxlen;
-	char *out;
+	int i;
+	char *ptr;
+	size_t size;
+	FILE *stream;
 	xmlDocPtr doc;
 	xmlXPathContextPtr xpath;
 	xmlXPathObjectPtr res;
@@ -41,51 +43,52 @@ char *get_xmpchapters(char *xmp)
 	res = xmlXPathEvalExpression("/x:xmpmeta/rdf:RDF/rdf:Description/xmpDM:Tracks/rdf:Bag/rdf:li/rdf:Description/xmpDM:markers/rdf:Seq/rdf:li/rdf:Description", xpath);
 	if (!res || !res->nodesetval)
 		return "";
-	len = 0; maxlen = res->nodesetval->nodeNr*200;
-	out = malloc(maxlen);
-	len += xsnprintf(out+len, maxlen-len-1, "[");
+	ptr = 0;
+	size = 0;
+	if (!(stream = open_memstream(&ptr, &size)))
+		exit(99);
+	fprintf(stream, "[");
 	for (i = 0; i < res->nodesetval->nodeNr; i ++)
 	{
 		start = atoi(getxmlattr(res->nodesetval->nodeTab[i]->properties, "startTime", "0"))/framerate;
 		if (i)
-			len += xsnprintf(out+len, maxlen-len-1, ",");
-		len += xsnprintf(out+len, maxlen-len-1, "{\"time\": %f, \"text\": \"%s\"}",
-				start, jescape(getxmlattr(res->nodesetval->nodeTab[i]->properties, "name", "")));
+			fprintf(stream, ",");
+		fprintf(stream, "{\"time\": %f, \"text\": \"%s\"}", start,
+				jescape(getxmlattr(res->nodesetval->nodeTab[i]->properties, "name", "")));
 	}
-	len += xsnprintf(out+len, maxlen-len-1, "]");
-	return out;
+	fprintf(stream, "]");
+	fclose(stream);
+	return ptr;
 }
 
 int main(int argc, char *argv[])
 {
-	int jobid;
-	char path[100], *xmp;
+	int err;
+	char *path, *xmp;
 	AVFormatContext *demux, *mux;
 	AVDictionaryEntry *tag;
 	AVDictionary *opts;
 	if (argc != 5)
 		return 1;
 	av_register_all();
-	av_log_set_callback(avlogbuf_callback);
+	init_avlogbuf();
 
 	jobid = atoi(argv[1]);
-	xsnprintf(BL(path), "%s/%s", CONFIG_VIDEOS_RAW,
-			jstr(jlookup(argv[4], "path"), ""));
+	path = mprintf("%s/%s", CONFIG_VIDEOS_RAW, jstr(jlookup(argv[4], "path"), ""));
 	ping_job(jobid, "running", 0);
 
 	opts = 0;
 	av_dict_set_int(&opts, "export_xmp", 1, 0);
 	demux = 0;
-	if (avformat_open_input(&demux, path, 0, &opts))
-		goto fail;
+	if (err = avformat_open_input(&demux, path, 0, &opts))
+		job_failed("Opening input file failed: %s", av_err2str(err));
 	avformat_find_stream_info(demux, 0);
 	if (tag = av_dict_get(demux->metadata, "xmp", 0, 0))
-		ping_job(jobid, "finished", "{\"xmp_chapters\": %s, \"duration\": %f, \"log\": \"%s\"}", get_xmpchapters(tag->value), demux->duration*av_q2d(AV_TIME_BASE_Q), get_avlogbuf());
+		ping_job(jobid, "finished", "{\"xmp_chapters\": %s, \"duration\": %f, \"log\": \"%s\"}",
+				get_xmpchapters(tag->value), demux->duration*av_q2d(AV_TIME_BASE_Q),
+				jescape(get_avlogbuf()));
 	else
-		ping_job(jobid, "finished", "{\"duration\": %f, \"log\": \"%s\"}", demux->duration*av_q2d(AV_TIME_BASE_Q), get_avlogbuf());
+		ping_job(jobid, "finished", "{\"duration\": %f, \"log\": \"%s\"}",
+				demux->duration*av_q2d(AV_TIME_BASE_Q), jescape(get_avlogbuf()));
 	return 0;
-
-fail:
-	ping_job(jobid, "failed", "{\"log\": \"%s\"}", get_avlogbuf());
-	return 1;
 }
diff --git a/remux.c b/remux.c
index 9ada5392d695b12639b2b8f3174f86cf95144896..02723c90e52e608e94f1a82641ef6d086890eb53 100644
--- a/remux.c
+++ b/remux.c
@@ -42,9 +42,9 @@ void parse_chapters(AVFormatContext *ctx, char *s, int duration)
 
 int main(int argc, char *argv[])
 {
-	int jobid, i;
+	int i, err;
 	int *idxmap;
-	char *p, path[100], tmp[100];
+	char *p, *path, *tmp;
 	AVFormatContext *demux, *mux;
 	AVPacket pkt;
 	AVStream *stream;
@@ -52,19 +52,18 @@ int main(int argc, char *argv[])
 	if (argc != 5)
 		return 1;
 	av_register_all();
-	av_log_set_callback(avlogbuf_callback);
+	init_avlogbuf();
 	memset(&pkt, 0, sizeof(pkt));
 	av_init_packet(&pkt);
 
 	jobid = atoi(argv[1]);
-	xsnprintf(BL(path), "%s/%s", CONFIG_VIDEOS_RELEASED,
-			jstr(jlookup(argv[4], "path"), ""));
-	xsnprintf(BL(tmp), "%s/.tmp-%i", CONFIG_VIDEOS_TMP, jobid);
+	path = mprintf("%s/%s", CONFIG_VIDEOS_RELEASED, jstr(jlookup(argv[4], "path"), ""));
+	tmp = mprintf("%s/.tmp-%i", CONFIG_VIDEOS_TMP, jobid);
 	ping_job(jobid, "running", 0);
 
 	demux = 0;
-	if (avformat_open_input(&demux, path, 0, 0))
-		goto fail;
+	if (err = avformat_open_input(&demux, path, 0, 0))
+		job_failed("Opening input file failed: %s", av_err2str(err));
 	avformat_find_stream_info(demux, 0);
 	avformat_alloc_output_context2(&mux, 0, jstr(jlookup(argv[4], "format"), 0), path);
 	av_dict_copy(&mux->metadata, demux->metadata, 0);
@@ -89,8 +88,8 @@ int main(int argc, char *argv[])
 	avio_open(&mux->pb, tmp, AVIO_FLAG_WRITE);
 	muxopts = 0;
 	parse_dict(&muxopts, jlookup(argv[4], "options"));
-	if (avformat_write_header(mux, &muxopts) < 0)
-		goto fail;
+	if ((err = avformat_write_header(mux, &muxopts)) < 0)
+		job_failed("Writing temporary file failed: %s", av_err2str(err));
 	while (!av_read_frame(demux, &pkt))
 	{
 		if (pkt.stream_index >= demux->nb_streams
@@ -104,21 +103,16 @@ int main(int argc, char *argv[])
 		pkt.duration = av_rescale_q(pkt.duration, demux->streams[pkt.stream_index]->time_base,
 				mux->streams[pkt.stream_index]->time_base);
 		pkt.pos = -1;
-		if (av_interleaved_write_frame(mux, &pkt))
-			goto fail;
+		if (err = av_interleaved_write_frame(mux, &pkt))
+			job_failed("Could not write frame: %s", av_err2str(err));
 	}
 	av_interleaved_write_frame(mux, 0);
-	if (av_write_trailer(mux))
-		goto fail;
+	if (err = av_write_trailer(mux))
+		job_failed("Error writing trailer to temporary file", av_err2str(err));
 	avio_closep(&mux->pb);
 	if (rename(tmp, path))
-		goto fail;
+		job_failed("Overwriting output file failed: %s", strerror(errno));
 	unlink(tmp);
-	ping_job(jobid, "finished", "{\"log\": \"%s\"}", get_avlogbuf());
+	ping_job(jobid, "finished", "{\"log\": \"%s\"}", jescape(get_avlogbuf()));
 	return 0;
-
-fail:
-	unlink(tmp);
-	ping_job(jobid, "failed", "{\"log\": \"%s\"}", get_avlogbuf());
-	return 1;
 }
diff --git a/thumbnail.c b/thumbnail.c
index f33ffa8c0e06faa8807a90771fac29bcaec36078..0266bcc9f67f4a4dbeec3039188b613c26f20256 100644
--- a/thumbnail.c
+++ b/thumbnail.c
@@ -24,8 +24,8 @@ AVFrame *scale_frame(AVFrame *frame, enum AVPixelFormat pix_fmt,
 
 int main(int argc, char *argv[])
 {
-	int jobid, vidx;
-	char lectureid[JSTR_SIZE], src[100], tmp[100], dest[100];
+	int err, vidx;
+	char lectureid[JSTR_SIZE], *src, *tmp, *dest;
 	AVFormatContext *demux, *mux;
 	AVCodecContext *dec, *enc;
 	AVPacket pkt;
@@ -33,25 +33,23 @@ int main(int argc, char *argv[])
 	if (argc != 5)
 		return 1;
 	av_register_all();
-	av_log_set_callback(avlogbuf_callback);
+	init_avlogbuf();
 
 	/* Prepare arguments */
 	jobid = atoi(argv[1]);
 	jstrb(jlookup(argv[4], "lectureid"), "-1", lectureid);
-	xsnprintf(BL(src), "%s/%s", CONFIG_VIDEOS_RELEASED,
-			jstr(jlookup(argv[4], "path"), ""));
-	xsnprintf(BL(tmp), "%s/.tmp-%i", CONFIG_VIDEOS_TMP, jobid);
-	xsnprintf(BL(dest), "%s/thumbnail/l_%s.jpg", CONFIG_VIDEOS_RELEASED,
-			lectureid);
+	src = mprintf("%s/%s", CONFIG_VIDEOS_RELEASED, jstr(jlookup(argv[4], "path"), ""));
+	tmp = mprintf("%s/.tmp-%i", CONFIG_VIDEOS_TMP, jobid);
+	dest = mprintf("%s/thumbnail/l_%s.jpg", CONFIG_VIDEOS_RELEASED, lectureid);
 	ping_job(jobid, "running", 0);
 
 	/* Open src */
 	demux = 0;
-	if (avformat_open_input(&demux, src, 0, 0))
-		goto fail;
+	if (err = avformat_open_input(&demux, src, 0, 0))
+		job_failed("Opening input file failed: %s", av_err2str(err));
 	avformat_find_stream_info(demux, 0);
 	if ((vidx = av_find_best_stream(demux, AVMEDIA_TYPE_VIDEO, -1, -1, 0, 0)) < 0)
-		goto fail;
+		job_failed("Could not find video stream in input file");
 	dec = avcodec_alloc_context3(avcodec_find_decoder(demux->streams[vidx]->codecpar->codec_id));
 	avcodec_parameters_to_context(dec, demux->streams[vidx]->codecpar);
 	avcodec_open2(dec, dec->codec, 0);
@@ -71,8 +69,8 @@ int main(int argc, char *argv[])
 	avio_open(&mux->pb, tmp, AVIO_FLAG_WRITE);
 	avcodec_open2(enc, enc->codec, 0);
 	avcodec_parameters_from_context(mux->streams[0]->codecpar, enc);
-	if (avformat_write_header(mux, 0) < 0)
-		goto fail;
+	if ((err = avformat_write_header(mux, 0)) < 0)
+		job_failed("Writing temporary file failed: %s", av_err2str(err));
 
 	/* Create thumbnail */
 	av_seek_frame(demux, -1, (2L*demux->duration)/5L, 0);
@@ -81,8 +79,8 @@ int main(int argc, char *argv[])
 	frame = av_frame_alloc();
 	do
 	{
-		if (av_read_frame(demux, &pkt))
-			goto fail;
+		if (err = av_read_frame(demux, &pkt))
+			job_failed("Reading frame failed: %s", av_err2str(err));
 		if (pkt.stream_index == vidx)
 			avcodec_send_packet(dec, &pkt);
 	}
@@ -97,17 +95,12 @@ int main(int argc, char *argv[])
 		av_interleaved_write_frame(mux, &pkt);
 	}
 	av_interleaved_write_frame(mux, 0);
-	if (av_write_trailer(mux))
-		goto fail;
+	if (err = av_write_trailer(mux))
+		job_failed("Error writing trailer to temporary file: %s", av_err2str(err));
 	avio_closep(&mux->pb);
 	if (rename(tmp, dest))
-		goto fail;
+		job_failed("Overwriting output file failed: %s", strerror(errno));
 	unlink(tmp);
-	ping_job(jobid, "finished", "{\"log\": \"%s\"}", get_avlogbuf());
+	ping_job(jobid, "finished", "{\"log\": \"%s\"}", jescape(get_avlogbuf()));
 	return 0;
-
-fail:
-	unlink(tmp);
-	ping_job(jobid, "failed", "{\"log\": \"%s\"}", get_avlogbuf());
-	return 1;
 }
diff --git a/util.h b/util.h
index 898e5e17afbba86def6771d4c1b58679308b0dc5..1a86f0ffcd538755445120b8017cc36ef5af0c52 100644
--- a/util.h
+++ b/util.h
@@ -4,14 +4,19 @@
 /* Generic */
 #define SL(s) (s), (sizeof(s)-1)
 #define BL(b) (b), (sizeof(b))
-size_t xvsnprintf(char *p, size_t len, const char *fmt, va_list ap);
-size_t xsnprintf(char *p, size_t len, const char *fmt, ...);
+extern int jobid;
 
+char *vmprintf(const char *fmt, va_list ap);
+char *mprintf(const char *fmt, ...);
+
+/* Logging */
+void init_avlogbuf(void);
 void avlogbuf_callback(void *classp, int level, const char *fmt, va_list ap);
 char *get_avlogbuf(void);
 
 /* API */
 int ping_job(int id, char *state, char *status, ...);
+void job_failed(char *msg, ...);
 
 /* JSON parser */
 #define JSTR_SIZE 100
@@ -24,4 +29,6 @@ char *jenter(char *s);
 char *jnext(char *s);
 char *jvalue(char *s);
 char *jlookup(char *s, char *key);
+
+/* JSON util */
 char *jescape(char *s);
diff --git a/util/api.c b/util/api.c
index 07fe0eb4f45512099e4ebe34f31bf6f1453204cd..fe4c769557df72273c1ec78cdc9ad294736271ac 100644
--- a/util/api.c
+++ b/util/api.c
@@ -1,6 +1,7 @@
 #include <curl/curl.h>
 #include <stdio.h>
 #include <stdarg.h>
+#include <stdlib.h>
 #include <unistd.h>
 #include <string.h>
 
@@ -9,7 +10,7 @@
 
 static size_t curl_write_cb(char *ptr, size_t size, size_t nmemb, void *user)
 {
-	return size;
+	return size*nmemb;
 }
 
 int ping_job(int id, char *state, char *status, ...)
@@ -17,39 +18,44 @@ int ping_job(int id, char *state, char *status, ...)
 	int ret;
 	CURL *curl;
 	va_list ap;
-	char *e_host, *e_status, *e_apikey, *e_state;
-	char url[100], buf[500];
+	char *p, *url, *e_host, *e_status, *e_apikey, *e_state;
+	char hostbuf[HOST_NAME_MAX+1];
 	if (!(curl = curl_easy_init()))
 		return -1;
 	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_cb);
 	e_apikey = curl_easy_escape(curl, CONFIG_APIKEY, 0);
 	e_state = curl_easy_escape(curl, state, 0);
-	if (gethostname(BL(buf)))
-		strcpy(buf, "localhost");
-	buf[sizeof(buf)-1] = 0;
-	e_host = curl_easy_escape(curl, buf, 0);
+	if (gethostname(BL(hostbuf)))
+		strcpy(hostbuf, "localhost");
+	e_host = curl_easy_escape(curl, hostbuf, 0);
 	if (status)
 	{
 		va_start(ap, status);
-		xvsnprintf(BL(buf), status, ap);
+		p = vmprintf(status, ap);
 		va_end(ap);
 	}
 	else
-		strcpy(buf, "{}");
-	e_status = curl_easy_escape(curl, buf, 0);
-	xsnprintf(BL(buf), "apikey=%s&state=%s&host=%s&status=%s",
-			e_apikey, e_state, e_host, e_status);
+		p = strdup("{}");
+	e_status = curl_easy_escape(curl, p, 0);
+	free(p);
+	p = mprintf("apikey=%s&state=%s&host=%s&status=%s", e_apikey, e_state,
+			e_host, e_status);
 	curl_free(e_apikey);
 	curl_free(e_state);
 	curl_free(e_host);
 	curl_free(e_status);
-	curl_easy_setopt(curl, CURLOPT_POSTFIELDS, buf);
-	xsnprintf(BL(url), "%s/internal/jobs/api/job/%i/ping", CONFIG_APIBASE, id);
+	curl_easy_setopt(curl, CURLOPT_POSTFIELDS, p);
+	url = mprintf("%s/internal/jobs/api/job/%i/ping", CONFIG_APIBASE, id);
 	curl_easy_setopt(curl, CURLOPT_URL, url);
 	ret = curl_easy_perform(curl);
+	free(p);
+	free(url);
 	curl_easy_cleanup(curl);
 	if (ret)
+	{
+		fprintf(stderr, "API call to \"%s\" failed: %s\n", url, curl_easy_strerror(ret));
 		return -1;
+	}
 	else
 		return 0;
 }
diff --git a/util/avlogbuf.c b/util/avlogbuf.c
index eb422d2ceb3a4f402677bc3e69428f7ee1a50c8d..6ccaa13cc82891b11378f5eebc9314c56f9e8932 100644
--- a/util/avlogbuf.c
+++ b/util/avlogbuf.c
@@ -3,41 +3,38 @@
 
 #include "../util.h"
 
-static char *logbuffer = 0;
-static size_t logoffset = 0, logsize = 0;
-static pthread_mutex_t loglock = PTHREAD_MUTEX_INITIALIZER;;
+static char *logbuffer;
+static size_t logsize;
+static FILE *logstream;
+
+void init_avlogbuf(void)
+{
+	logbuffer = 0;
+	logsize = 0;
+	if (!(logstream = open_memstream(&logbuffer, &logsize)))
+		exit(99);
+	av_log_set_callback(avlogbuf_callback);
+}
 
 void avlogbuf_callback(void *class, int level, const char *fmt, va_list ap)
 {
-	int len, print_prefix;
+	int print_prefix;
+	char buf[200];
 	if (level >= AV_LOG_VERBOSE)
 		return;
-	av_log_default_callback(class, level, fmt, ap);
-	if (pthread_mutex_lock(&loglock))
-		return;
-	if (logsize-logoffset < 1024)
-		logbuffer = realloc(logbuffer, (logsize += 1024));
-	if (!logbuffer)
-		return;
 	print_prefix = 1;
-	len = av_log_format_line2(class, level, fmt, ap, logbuffer+logoffset,
-			logsize-logoffset, &print_prefix);
-	if (len < logsize-logoffset)
-		logoffset += len;
-	else
-		logoffset = logsize-1;
-	pthread_mutex_unlock(&loglock);
+	av_log_format_line(class, level, fmt, ap, BL(buf), &print_prefix);
+	fputs(buf, logstream);
 }
 
 char *get_avlogbuf(void)
 {
-	char *ret, *p;
-	if (!logbuffer || pthread_mutex_lock(&loglock))
-		return strdup("");
-	ret = strdup(logbuffer);
-	pthread_mutex_unlock(&loglock);
-	for (p = ret; *p; p ++)
-		if (*p == '"')
-			*p = '\'';
-	return ret;
+	char *p;
+	if (!logstream)
+		return "";
+	fclose(logstream);
+	p = logbuffer;
+	init_avlogbuf();
+	fputs(p, logstream);
+	return p;
 }
diff --git a/util/jescape.c b/util/jescape.c
new file mode 100644
index 0000000000000000000000000000000000000000..a2e8decb2c89832f3f04b596719911b672b6b7d9
--- /dev/null
+++ b/util/jescape.c
@@ -0,0 +1,39 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "../util.h"
+
+char *jescape(char *s)
+{
+	int i;
+	char *ptr;
+	size_t size;
+	FILE *stream;
+	if (!(stream = open_memstream(&ptr, &size)))
+		exit(99);
+	for (; *s; s ++)
+		switch (*s)
+		{
+			case '"':
+				fputs("\\\"", stream); break;
+			case '\\':
+				fputs("\\\\", stream); break;
+			case '\b':
+				fputs("\\b", stream); break;
+			case '\f':
+				fputs("\\f", stream); break;
+			case '\n':
+				fputs("\\n", stream); break;
+			case '\r':
+				fputs("\\r", stream); break;
+			case '\t':
+				fputs("\\t", stream); break;
+			default:
+				if (*s <= 0x1f)
+					fprintf(stream, "\\u%04x", *s);
+				else
+					fputc(*s, stream);
+		}
+	fclose(stream);
+	return ptr;
+}
diff --git a/util/job_error.c b/util/job_error.c
new file mode 100644
index 0000000000000000000000000000000000000000..8ddd3d5c93ea50e491843b6de702e1d7d74203e4
--- /dev/null
+++ b/util/job_error.c
@@ -0,0 +1,16 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "../util.h"
+
+int jobid;
+
+void job_failed(char *msg, ...)
+{
+	va_list ap;
+	va_start(ap, msg);
+	ping_job(jobid, "failed", "{\"reason\": \"%s\", \"log\": \"%s\"}",
+			jescape(vmprintf(msg, ap)), jescape(get_avlogbuf()));
+	va_end(ap);
+	exit(2);
+}
diff --git a/util/json.c b/util/json.c
index b9fa3dcf12bbeee6ee183f4a4eebf914dc663403..9fb84e276846e97683168730b10c548b62bc96a9 100644
--- a/util/json.c
+++ b/util/json.c
@@ -5,9 +5,6 @@
 
 #include "../util.h"
 
-static char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
-	'A', 'B', 'C', 'D', 'E', 'F'};
-
 static char *skip_ws(char *s)
 {
 	for (; *s == ' ' || *s == '\t' || *s == '\n' || *s == '\r'; s ++);
@@ -213,41 +210,3 @@ char *jlookup(char *s, char *key)
 			return jvalue(s);
 	return 0;
 }
-
-char *jescape(char *s)
-{
-	int i;
-	static __thread char buf[JSTR_SIZE];
-	for (i = 0; *s && i+7 < sizeof(buf); s ++)
-		switch (*s)
-		{
-			case '"':
-				buf[i ++] = '\\'; buf[i ++] = '"'; break;
-			case '\\':
-				buf[i ++] = '\\'; buf[i ++] = '\\'; break;
-			case '\b':
-				buf[i ++] = '\\'; buf[i ++] = 'b'; break;
-			case '\f':
-				buf[i ++] = '\\'; buf[i ++] = 'f'; break;
-			case '\n':
-				buf[i ++] = '\\'; buf[i ++] = 'n'; break;
-			case '\r':
-				buf[i ++] = '\\'; buf[i ++] = 'r'; break;
-			case '\t':
-				buf[i ++] = '\\'; buf[i ++] = 't'; break;
-			default:
-				if (*s <= 0x1f)
-				{
-					buf[i ++] = '\\';
-					buf[i ++] = 'u';
-					buf[i ++] = '0';
-					buf[i ++] = '0';
-					buf[i ++] = hex[*s >> 4];
-					buf[i ++] = hex[*s & 0xf];
-				}
-				else
-					buf[i ++] = *s;
-		}
-	buf[i] = 0;
-	return buf;
-}
diff --git a/util/mprintf.c b/util/mprintf.c
new file mode 100644
index 0000000000000000000000000000000000000000..fd850611dfbcce431230eb8b8caed89db7077c0f
--- /dev/null
+++ b/util/mprintf.c
@@ -0,0 +1,30 @@
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include "../util.h"
+
+char *vmprintf(const char *fmt, va_list ap)
+{
+	char *ptr;
+	size_t size;
+	FILE *stream;
+	ptr = 0;
+	size = 0;
+	if (!(stream = open_memstream(&ptr, &size)))
+		exit(99);
+	if (vfprintf(stream, fmt, ap) < 0)
+		exit(99);
+	fclose(stream);
+	return ptr;
+}
+
+char *mprintf(const char *fmt, ...)
+{
+	char *ret;
+	va_list ap;
+	va_start(ap, fmt);
+	ret = vmprintf(fmt, ap);
+	va_end(ap);
+	return ret;
+}
diff --git a/util/xprintf.c b/util/xprintf.c
deleted file mode 100644
index d20cb2a1966c74eb2b540cb3a8d76c50b18a1baf..0000000000000000000000000000000000000000
--- a/util/xprintf.c
+++ /dev/null
@@ -1,25 +0,0 @@
-#include <stdio.h>
-#include <stdarg.h>
-
-#include "../util.h"
-
-size_t xvsnprintf(char *p, size_t len, const char *fmt, va_list ap)
-{
-	int ret;
-	if ((ret = vsnprintf(p, len, fmt, ap)) >= len)
-	{
-		p[len-1] = 0;
-		return len;
-	}
-	return ret;
-}
-
-size_t xsnprintf(char *p, size_t len, const char *fmt, ...)
-{
-	size_t ret;
-	va_list ap;
-	va_start(ap, fmt);
-	ret = vsnprintf(p, len, fmt, ap);
-	va_end(ap);
-	return ret;
-}