summaryrefslogtreecommitdiffhomepage
path: root/ir/obstack/obstack_printf.c
blob: f472d8993ac33fdf3107ab7bf4e53cd65ce900e1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/*
 * This file is part of libFirm.
 * Copyright (C) 2012 Karlsruhe Institute of Technology.
 */
#include "obstack.h"
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>

int obstack_vprintf(struct obstack *obst, const char *fmt, va_list ap) FIRM_NOTHROW
{
#ifdef _WIN32
	/* win32/C89 has no va_copy function... so we have to use the stupid fixed-length version */
	char buf[16384];
	int len = _vsnprintf(buf, sizeof(buf), fmt, ap);
	obstack_grow(obst, buf, len);
	return len;
#else
	char    buf[128];
	char   *buffer = buf;
	size_t  size   = sizeof(buf);
	int     len;

	for (;;) {
		va_list tap;
		va_copy(tap, ap);
		len = vsnprintf(buffer, size, fmt, tap);
		va_end(tap);

		/* snprintf should return -1 only in the error case, but older glibcs
		 * and probably other systems are buggy in this respect and return -1 if
		 * the buffer was too small. We only abort for LARGE unrealistic buffer
		 * sizes here */
		if (len < 0) {
			if (buffer != buf)
				free(buffer);
			if (size > 65536)
				return -1;
			size *= 2;
		} else if ((size_t)len >= size) {
			/* If we come here more than once, vsnprintf() returned garbage */
			assert(buffer == buf);
			size = (size_t)len + 1;
		} else {
			break;
		}
		buffer = (char*)malloc(size);
	}

	obstack_grow(obst, buffer, len);
	if (buffer != buf)
		free(buffer);

	return len;
#endif
}

int obstack_printf(struct obstack *obst, const char *fmt, ...) FIRM_NOTHROW
{
	va_list ap;
	int     res;

	va_start(ap, fmt);
	res = obstack_vprintf(obst, fmt, ap);
	va_end(ap);

	return res;
}