1234370Sjasone#define	assert(e) do {							\
2234370Sjasone	if (config_debug && !(e)) {					\
3234370Sjasone		malloc_write("<jemalloc>: Failed assertion\n");		\
4234370Sjasone		abort();						\
5234370Sjasone	}								\
6234370Sjasone} while (0)
7234370Sjasone
8234370Sjasone#define	not_reached() do {						\
9234370Sjasone	if (config_debug) {						\
10234370Sjasone		malloc_write("<jemalloc>: Unreachable code reached\n");	\
11234370Sjasone		abort();						\
12234370Sjasone	}								\
13234370Sjasone} while (0)
14234370Sjasone
15234370Sjasone#define	not_implemented() do {						\
16234370Sjasone	if (config_debug) {						\
17234370Sjasone		malloc_write("<jemalloc>: Not implemented\n");		\
18234370Sjasone		abort();						\
19234370Sjasone	}								\
20234370Sjasone} while (0)
21234370Sjasone
22234370Sjasone#define	JEMALLOC_UTIL_C_
23234370Sjasone#include "jemalloc/internal/jemalloc_internal.h"
24234370Sjasone
25234370Sjasone/******************************************************************************/
26234370Sjasone/* Function prototypes for non-inline static functions. */
27234370Sjasone
28234370Sjasonestatic void	wrtmessage(void *cbopaque, const char *s);
29234370Sjasone#define	U2S_BUFSIZE	((1U << (LG_SIZEOF_INTMAX_T + 3)) + 1)
30234370Sjasonestatic char	*u2s(uintmax_t x, unsigned base, bool uppercase, char *s,
31234370Sjasone    size_t *slen_p);
32234370Sjasone#define	D2S_BUFSIZE	(1 + U2S_BUFSIZE)
33234370Sjasonestatic char	*d2s(intmax_t x, char sign, char *s, size_t *slen_p);
34234370Sjasone#define	O2S_BUFSIZE	(1 + U2S_BUFSIZE)
35234370Sjasonestatic char	*o2s(uintmax_t x, bool alt_form, char *s, size_t *slen_p);
36234370Sjasone#define	X2S_BUFSIZE	(2 + U2S_BUFSIZE)
37234370Sjasonestatic char	*x2s(uintmax_t x, bool alt_form, bool uppercase, char *s,
38234370Sjasone    size_t *slen_p);
39234370Sjasone
40234370Sjasone/******************************************************************************/
41234370Sjasone
42234370Sjasone/* malloc_message() setup. */
43235238Sjasonestatic void
44234370Sjasonewrtmessage(void *cbopaque, const char *s)
45234370Sjasone{
46234370Sjasone
47234370Sjasone#ifdef SYS_write
48234370Sjasone	/*
49234370Sjasone	 * Use syscall(2) rather than write(2) when possible in order to avoid
50234370Sjasone	 * the possibility of memory allocation within libc.  This is necessary
51234370Sjasone	 * on FreeBSD; most operating systems do not have this problem though.
52234370Sjasone	 */
53234370Sjasone	UNUSED int result = syscall(SYS_write, STDERR_FILENO, s, strlen(s));
54234370Sjasone#else
55234370Sjasone	UNUSED int result = write(STDERR_FILENO, s, strlen(s));
56234370Sjasone#endif
57234370Sjasone}
58234370Sjasone
59235238SjasoneJEMALLOC_EXPORT void	(*je_malloc_message)(void *, const char *s);
60234370Sjasone
61235238SjasoneJEMALLOC_ATTR(visibility("hidden"))
62234370Sjasonevoid
63234370Sjasonewrtmessage_1_0(const char *s1, const char *s2, const char *s3,
64234370Sjasone    const char *s4)
65234370Sjasone{
66234370Sjasone
67234370Sjasone	wrtmessage(NULL, s1);
68234370Sjasone	wrtmessage(NULL, s2);
69234370Sjasone	wrtmessage(NULL, s3);
70234370Sjasone	wrtmessage(NULL, s4);
71234370Sjasone}
72234370Sjasone
73234370Sjasonevoid	(*__malloc_message_1_0)(const char *s1, const char *s2, const char *s3,
74234370Sjasone    const char *s4) = wrtmessage_1_0;
75234370Sjasone__sym_compat(_malloc_message, __malloc_message_1_0, FBSD_1.0);
76234370Sjasone
77234370Sjasone/*
78235238Sjasone * Wrapper around malloc_message() that avoids the need for
79235238Sjasone * je_malloc_message(...) throughout the code.
80235238Sjasone */
81235238Sjasonevoid
82235238Sjasonemalloc_write(const char *s)
83235238Sjasone{
84235238Sjasone
85235238Sjasone	if (je_malloc_message != NULL)
86235238Sjasone		je_malloc_message(NULL, s);
87235238Sjasone	else
88235238Sjasone		wrtmessage(NULL, s);
89235238Sjasone}
90235238Sjasone
91235238Sjasone/*
92234370Sjasone * glibc provides a non-standard strerror_r() when _GNU_SOURCE is defined, so
93234370Sjasone * provide a wrapper.
94234370Sjasone */
95234370Sjasoneint
96235238Sjasonebuferror(char *buf, size_t buflen)
97234370Sjasone{
98235238Sjasone
99235238Sjasone#ifdef _WIN32
100235238Sjasone	FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0,
101235238Sjasone	    (LPSTR)buf, buflen, NULL);
102235238Sjasone	return (0);
103235238Sjasone#elif defined(_GNU_SOURCE)
104234370Sjasone	char *b = strerror_r(errno, buf, buflen);
105234370Sjasone	if (b != buf) {
106234370Sjasone		strncpy(buf, b, buflen);
107234370Sjasone		buf[buflen-1] = '\0';
108234370Sjasone	}
109234370Sjasone	return (0);
110234370Sjasone#else
111234370Sjasone	return (strerror_r(errno, buf, buflen));
112234370Sjasone#endif
113234370Sjasone}
114234370Sjasone
115234370Sjasoneuintmax_t
116234370Sjasonemalloc_strtoumax(const char *nptr, char **endptr, int base)
117234370Sjasone{
118234370Sjasone	uintmax_t ret, digit;
119234370Sjasone	int b;
120234370Sjasone	bool neg;
121234370Sjasone	const char *p, *ns;
122234370Sjasone
123234370Sjasone	if (base < 0 || base == 1 || base > 36) {
124235238Sjasone		set_errno(EINVAL);
125234370Sjasone		return (UINTMAX_MAX);
126234370Sjasone	}
127234370Sjasone	b = base;
128234370Sjasone
129234370Sjasone	/* Swallow leading whitespace and get sign, if any. */
130234370Sjasone	neg = false;
131234370Sjasone	p = nptr;
132234370Sjasone	while (true) {
133234370Sjasone		switch (*p) {
134234370Sjasone		case '\t': case '\n': case '\v': case '\f': case '\r': case ' ':
135234370Sjasone			p++;
136234370Sjasone			break;
137234370Sjasone		case '-':
138234370Sjasone			neg = true;
139234370Sjasone			/* Fall through. */
140234370Sjasone		case '+':
141234370Sjasone			p++;
142234370Sjasone			/* Fall through. */
143234370Sjasone		default:
144234370Sjasone			goto label_prefix;
145234370Sjasone		}
146234370Sjasone	}
147234370Sjasone
148234370Sjasone	/* Get prefix, if any. */
149234370Sjasone	label_prefix:
150234370Sjasone	/*
151234370Sjasone	 * Note where the first non-whitespace/sign character is so that it is
152234370Sjasone	 * possible to tell whether any digits are consumed (e.g., "  0" vs.
153234370Sjasone	 * "  -x").
154234370Sjasone	 */
155234370Sjasone	ns = p;
156234370Sjasone	if (*p == '0') {
157234370Sjasone		switch (p[1]) {
158234370Sjasone		case '0': case '1': case '2': case '3': case '4': case '5':
159234370Sjasone		case '6': case '7':
160234370Sjasone			if (b == 0)
161234370Sjasone				b = 8;
162234370Sjasone			if (b == 8)
163234370Sjasone				p++;
164234370Sjasone			break;
165234370Sjasone		case 'x':
166234370Sjasone			switch (p[2]) {
167234370Sjasone			case '0': case '1': case '2': case '3': case '4':
168234370Sjasone			case '5': case '6': case '7': case '8': case '9':
169234370Sjasone			case 'A': case 'B': case 'C': case 'D': case 'E':
170234370Sjasone			case 'F':
171234370Sjasone			case 'a': case 'b': case 'c': case 'd': case 'e':
172234370Sjasone			case 'f':
173234370Sjasone				if (b == 0)
174234370Sjasone					b = 16;
175234370Sjasone				if (b == 16)
176234370Sjasone					p += 2;
177234370Sjasone				break;
178234370Sjasone			default:
179234370Sjasone				break;
180234370Sjasone			}
181234370Sjasone			break;
182234370Sjasone		default:
183234370Sjasone			break;
184234370Sjasone		}
185234370Sjasone	}
186234370Sjasone	if (b == 0)
187234370Sjasone		b = 10;
188234370Sjasone
189234370Sjasone	/* Convert. */
190234370Sjasone	ret = 0;
191234370Sjasone	while ((*p >= '0' && *p <= '9' && (digit = *p - '0') < b)
192234370Sjasone	    || (*p >= 'A' && *p <= 'Z' && (digit = 10 + *p - 'A') < b)
193234370Sjasone	    || (*p >= 'a' && *p <= 'z' && (digit = 10 + *p - 'a') < b)) {
194234370Sjasone		uintmax_t pret = ret;
195234370Sjasone		ret *= b;
196234370Sjasone		ret += digit;
197234370Sjasone		if (ret < pret) {
198234370Sjasone			/* Overflow. */
199235238Sjasone			set_errno(ERANGE);
200234370Sjasone			return (UINTMAX_MAX);
201234370Sjasone		}
202234370Sjasone		p++;
203234370Sjasone	}
204234370Sjasone	if (neg)
205234370Sjasone		ret = -ret;
206234370Sjasone
207234370Sjasone	if (endptr != NULL) {
208234370Sjasone		if (p == ns) {
209234370Sjasone			/* No characters were converted. */
210234370Sjasone			*endptr = (char *)nptr;
211234370Sjasone		} else
212234370Sjasone			*endptr = (char *)p;
213234370Sjasone	}
214234370Sjasone
215234370Sjasone	return (ret);
216234370Sjasone}
217234370Sjasone
218234370Sjasonestatic char *
219234370Sjasoneu2s(uintmax_t x, unsigned base, bool uppercase, char *s, size_t *slen_p)
220234370Sjasone{
221234370Sjasone	unsigned i;
222234370Sjasone
223234370Sjasone	i = U2S_BUFSIZE - 1;
224234370Sjasone	s[i] = '\0';
225234370Sjasone	switch (base) {
226234370Sjasone	case 10:
227234370Sjasone		do {
228234370Sjasone			i--;
229234370Sjasone			s[i] = "0123456789"[x % (uint64_t)10];
230234370Sjasone			x /= (uint64_t)10;
231234370Sjasone		} while (x > 0);
232234370Sjasone		break;
233234370Sjasone	case 16: {
234234370Sjasone		const char *digits = (uppercase)
235234370Sjasone		    ? "0123456789ABCDEF"
236234370Sjasone		    : "0123456789abcdef";
237234370Sjasone
238234370Sjasone		do {
239234370Sjasone			i--;
240234370Sjasone			s[i] = digits[x & 0xf];
241234370Sjasone			x >>= 4;
242234370Sjasone		} while (x > 0);
243234370Sjasone		break;
244234370Sjasone	} default: {
245234370Sjasone		const char *digits = (uppercase)
246234370Sjasone		    ? "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
247234370Sjasone		    : "0123456789abcdefghijklmnopqrstuvwxyz";
248234370Sjasone
249234370Sjasone		assert(base >= 2 && base <= 36);
250234370Sjasone		do {
251234370Sjasone			i--;
252234370Sjasone			s[i] = digits[x % (uint64_t)base];
253234370Sjasone			x /= (uint64_t)base;
254234370Sjasone		} while (x > 0);
255234370Sjasone	}}
256234370Sjasone
257234370Sjasone	*slen_p = U2S_BUFSIZE - 1 - i;
258234370Sjasone	return (&s[i]);
259234370Sjasone}
260234370Sjasone
261234370Sjasonestatic char *
262234370Sjasoned2s(intmax_t x, char sign, char *s, size_t *slen_p)
263234370Sjasone{
264234370Sjasone	bool neg;
265234370Sjasone
266234370Sjasone	if ((neg = (x < 0)))
267234370Sjasone		x = -x;
268234370Sjasone	s = u2s(x, 10, false, s, slen_p);
269234370Sjasone	if (neg)
270234370Sjasone		sign = '-';
271234370Sjasone	switch (sign) {
272234370Sjasone	case '-':
273234370Sjasone		if (neg == false)
274234370Sjasone			break;
275234370Sjasone		/* Fall through. */
276234370Sjasone	case ' ':
277234370Sjasone	case '+':
278234370Sjasone		s--;
279234370Sjasone		(*slen_p)++;
280234370Sjasone		*s = sign;
281234370Sjasone		break;
282234370Sjasone	default: not_reached();
283234370Sjasone	}
284234370Sjasone	return (s);
285234370Sjasone}
286234370Sjasone
287234370Sjasonestatic char *
288234370Sjasoneo2s(uintmax_t x, bool alt_form, char *s, size_t *slen_p)
289234370Sjasone{
290234370Sjasone
291234370Sjasone	s = u2s(x, 8, false, s, slen_p);
292234370Sjasone	if (alt_form && *s != '0') {
293234370Sjasone		s--;
294234370Sjasone		(*slen_p)++;
295234370Sjasone		*s = '0';
296234370Sjasone	}
297234370Sjasone	return (s);
298234370Sjasone}
299234370Sjasone
300234370Sjasonestatic char *
301234370Sjasonex2s(uintmax_t x, bool alt_form, bool uppercase, char *s, size_t *slen_p)
302234370Sjasone{
303234370Sjasone
304234370Sjasone	s = u2s(x, 16, uppercase, s, slen_p);
305234370Sjasone	if (alt_form) {
306234370Sjasone		s -= 2;
307234370Sjasone		(*slen_p) += 2;
308234370Sjasone		memcpy(s, uppercase ? "0X" : "0x", 2);
309234370Sjasone	}
310234370Sjasone	return (s);
311234370Sjasone}
312234370Sjasone
313234370Sjasoneint
314234370Sjasonemalloc_vsnprintf(char *str, size_t size, const char *format, va_list ap)
315234370Sjasone{
316234370Sjasone	int ret;
317234370Sjasone	size_t i;
318234370Sjasone	const char *f;
319234370Sjasone
320234370Sjasone#define	APPEND_C(c) do {						\
321234370Sjasone	if (i < size)							\
322234370Sjasone		str[i] = (c);						\
323234370Sjasone	i++;								\
324234370Sjasone} while (0)
325234370Sjasone#define	APPEND_S(s, slen) do {						\
326234370Sjasone	if (i < size) {							\
327234370Sjasone		size_t cpylen = (slen <= size - i) ? slen : size - i;	\
328234370Sjasone		memcpy(&str[i], s, cpylen);				\
329234370Sjasone	}								\
330234370Sjasone	i += slen;							\
331234370Sjasone} while (0)
332234370Sjasone#define	APPEND_PADDED_S(s, slen, width, left_justify) do {		\
333234370Sjasone	/* Left padding. */						\
334234370Sjasone	size_t pad_len = (width == -1) ? 0 : ((slen < (size_t)width) ?	\
335234370Sjasone	    (size_t)width - slen : 0);					\
336234370Sjasone	if (left_justify == false && pad_len != 0) {			\
337234370Sjasone		size_t j;						\
338234370Sjasone		for (j = 0; j < pad_len; j++)				\
339234370Sjasone			APPEND_C(' ');					\
340234370Sjasone	}								\
341234370Sjasone	/* Value. */							\
342234370Sjasone	APPEND_S(s, slen);						\
343234370Sjasone	/* Right padding. */						\
344234370Sjasone	if (left_justify && pad_len != 0) {				\
345234370Sjasone		size_t j;						\
346234370Sjasone		for (j = 0; j < pad_len; j++)				\
347234370Sjasone			APPEND_C(' ');					\
348234370Sjasone	}								\
349234370Sjasone} while (0)
350234370Sjasone#define GET_ARG_NUMERIC(val, len) do {					\
351234370Sjasone	switch (len) {							\
352234370Sjasone	case '?':							\
353234370Sjasone		val = va_arg(ap, int);					\
354234370Sjasone		break;							\
355234543Sjasone	case '?' | 0x80:						\
356234543Sjasone		val = va_arg(ap, unsigned int);				\
357234543Sjasone		break;							\
358234370Sjasone	case 'l':							\
359234370Sjasone		val = va_arg(ap, long);					\
360234370Sjasone		break;							\
361234543Sjasone	case 'l' | 0x80:						\
362234543Sjasone		val = va_arg(ap, unsigned long);			\
363234543Sjasone		break;							\
364234370Sjasone	case 'q':							\
365234370Sjasone		val = va_arg(ap, long long);				\
366234370Sjasone		break;							\
367234543Sjasone	case 'q' | 0x80:						\
368234543Sjasone		val = va_arg(ap, unsigned long long);			\
369234543Sjasone		break;							\
370234370Sjasone	case 'j':							\
371234370Sjasone		val = va_arg(ap, intmax_t);				\
372234370Sjasone		break;							\
373234370Sjasone	case 't':							\
374234370Sjasone		val = va_arg(ap, ptrdiff_t);				\
375234370Sjasone		break;							\
376234370Sjasone	case 'z':							\
377234370Sjasone		val = va_arg(ap, ssize_t);				\
378234370Sjasone		break;							\
379234543Sjasone	case 'z' | 0x80:						\
380234543Sjasone		val = va_arg(ap, size_t);				\
381234543Sjasone		break;							\
382234370Sjasone	case 'p': /* Synthetic; used for %p. */				\
383234370Sjasone		val = va_arg(ap, uintptr_t);				\
384234370Sjasone		break;							\
385234370Sjasone	default: not_reached();						\
386234370Sjasone	}								\
387234370Sjasone} while (0)
388234370Sjasone
389234370Sjasone	i = 0;
390234370Sjasone	f = format;
391234370Sjasone	while (true) {
392234370Sjasone		switch (*f) {
393234370Sjasone		case '\0': goto label_out;
394234370Sjasone		case '%': {
395234370Sjasone			bool alt_form = false;
396234370Sjasone			bool left_justify = false;
397234370Sjasone			bool plus_space = false;
398234370Sjasone			bool plus_plus = false;
399234370Sjasone			int prec = -1;
400234370Sjasone			int width = -1;
401234543Sjasone			unsigned char len = '?';
402234370Sjasone
403234370Sjasone			f++;
404234370Sjasone			if (*f == '%') {
405234370Sjasone				/* %% */
406234370Sjasone				APPEND_C(*f);
407234370Sjasone				break;
408234370Sjasone			}
409234370Sjasone			/* Flags. */
410234370Sjasone			while (true) {
411234370Sjasone				switch (*f) {
412234370Sjasone				case '#':
413234370Sjasone					assert(alt_form == false);
414234370Sjasone					alt_form = true;
415234370Sjasone					break;
416234370Sjasone				case '-':
417234370Sjasone					assert(left_justify == false);
418234370Sjasone					left_justify = true;
419234370Sjasone					break;
420234370Sjasone				case ' ':
421234370Sjasone					assert(plus_space == false);
422234370Sjasone					plus_space = true;
423234370Sjasone					break;
424234370Sjasone				case '+':
425234370Sjasone					assert(plus_plus == false);
426234370Sjasone					plus_plus = true;
427234370Sjasone					break;
428234370Sjasone				default: goto label_width;
429234370Sjasone				}
430234370Sjasone				f++;
431234370Sjasone			}
432234370Sjasone			/* Width. */
433234370Sjasone			label_width:
434234370Sjasone			switch (*f) {
435234370Sjasone			case '*':
436234370Sjasone				width = va_arg(ap, int);
437234370Sjasone				f++;
438234370Sjasone				break;
439234370Sjasone			case '0': case '1': case '2': case '3': case '4':
440234370Sjasone			case '5': case '6': case '7': case '8': case '9': {
441234370Sjasone				uintmax_t uwidth;
442235238Sjasone				set_errno(0);
443234370Sjasone				uwidth = malloc_strtoumax(f, (char **)&f, 10);
444235238Sjasone				assert(uwidth != UINTMAX_MAX || get_errno() !=
445234370Sjasone				    ERANGE);
446234370Sjasone				width = (int)uwidth;
447234370Sjasone				if (*f == '.') {
448234370Sjasone					f++;
449234370Sjasone					goto label_precision;
450234370Sjasone				} else
451234370Sjasone					goto label_length;
452234370Sjasone				break;
453234370Sjasone			} case '.':
454234370Sjasone				f++;
455234370Sjasone				goto label_precision;
456234370Sjasone			default: goto label_length;
457234370Sjasone			}
458234370Sjasone			/* Precision. */
459234370Sjasone			label_precision:
460234370Sjasone			switch (*f) {
461234370Sjasone			case '*':
462234370Sjasone				prec = va_arg(ap, int);
463234370Sjasone				f++;
464234370Sjasone				break;
465234370Sjasone			case '0': case '1': case '2': case '3': case '4':
466234370Sjasone			case '5': case '6': case '7': case '8': case '9': {
467234370Sjasone				uintmax_t uprec;
468235238Sjasone				set_errno(0);
469234370Sjasone				uprec = malloc_strtoumax(f, (char **)&f, 10);
470235238Sjasone				assert(uprec != UINTMAX_MAX || get_errno() !=
471235238Sjasone				    ERANGE);
472234370Sjasone				prec = (int)uprec;
473234370Sjasone				break;
474234370Sjasone			}
475234370Sjasone			default: break;
476234370Sjasone			}
477234370Sjasone			/* Length. */
478234370Sjasone			label_length:
479234370Sjasone			switch (*f) {
480234370Sjasone			case 'l':
481234370Sjasone				f++;
482234370Sjasone				if (*f == 'l') {
483234370Sjasone					len = 'q';
484234370Sjasone					f++;
485234370Sjasone				} else
486234370Sjasone					len = 'l';
487234370Sjasone				break;
488234370Sjasone			case 'j':
489234370Sjasone				len = 'j';
490234370Sjasone				f++;
491234370Sjasone				break;
492234370Sjasone			case 't':
493234370Sjasone				len = 't';
494234370Sjasone				f++;
495234370Sjasone				break;
496234370Sjasone			case 'z':
497234370Sjasone				len = 'z';
498234370Sjasone				f++;
499234370Sjasone				break;
500234370Sjasone			default: break;
501234370Sjasone			}
502234370Sjasone			/* Conversion specifier. */
503234370Sjasone			switch (*f) {
504234370Sjasone				char *s;
505234370Sjasone				size_t slen;
506234370Sjasone			case 'd': case 'i': {
507234370Sjasone				intmax_t val JEMALLOC_CC_SILENCE_INIT(0);
508234370Sjasone				char buf[D2S_BUFSIZE];
509234370Sjasone
510234370Sjasone				GET_ARG_NUMERIC(val, len);
511234370Sjasone				s = d2s(val, (plus_plus ? '+' : (plus_space ?
512234370Sjasone				    ' ' : '-')), buf, &slen);
513234370Sjasone				APPEND_PADDED_S(s, slen, width, left_justify);
514234370Sjasone				f++;
515234370Sjasone				break;
516234370Sjasone			} case 'o': {
517234370Sjasone				uintmax_t val JEMALLOC_CC_SILENCE_INIT(0);
518234370Sjasone				char buf[O2S_BUFSIZE];
519234370Sjasone
520234543Sjasone				GET_ARG_NUMERIC(val, len | 0x80);
521234370Sjasone				s = o2s(val, alt_form, buf, &slen);
522234370Sjasone				APPEND_PADDED_S(s, slen, width, left_justify);
523234370Sjasone				f++;
524234370Sjasone				break;
525234370Sjasone			} case 'u': {
526234370Sjasone				uintmax_t val JEMALLOC_CC_SILENCE_INIT(0);
527234370Sjasone				char buf[U2S_BUFSIZE];
528234370Sjasone
529234543Sjasone				GET_ARG_NUMERIC(val, len | 0x80);
530234370Sjasone				s = u2s(val, 10, false, buf, &slen);
531234370Sjasone				APPEND_PADDED_S(s, slen, width, left_justify);
532234370Sjasone				f++;
533234370Sjasone				break;
534234370Sjasone			} case 'x': case 'X': {
535234370Sjasone				uintmax_t val JEMALLOC_CC_SILENCE_INIT(0);
536234370Sjasone				char buf[X2S_BUFSIZE];
537234370Sjasone
538234543Sjasone				GET_ARG_NUMERIC(val, len | 0x80);
539234370Sjasone				s = x2s(val, alt_form, *f == 'X', buf, &slen);
540234370Sjasone				APPEND_PADDED_S(s, slen, width, left_justify);
541234370Sjasone				f++;
542234370Sjasone				break;
543234370Sjasone			} case 'c': {
544234370Sjasone				unsigned char val;
545234370Sjasone				char buf[2];
546234370Sjasone
547234370Sjasone				assert(len == '?' || len == 'l');
548234370Sjasone				assert_not_implemented(len != 'l');
549234370Sjasone				val = va_arg(ap, int);
550234370Sjasone				buf[0] = val;
551234370Sjasone				buf[1] = '\0';
552234370Sjasone				APPEND_PADDED_S(buf, 1, width, left_justify);
553234370Sjasone				f++;
554234370Sjasone				break;
555234370Sjasone			} case 's':
556234370Sjasone				assert(len == '?' || len == 'l');
557234370Sjasone				assert_not_implemented(len != 'l');
558234370Sjasone				s = va_arg(ap, char *);
559234370Sjasone				slen = (prec == -1) ? strlen(s) : prec;
560234370Sjasone				APPEND_PADDED_S(s, slen, width, left_justify);
561234370Sjasone				f++;
562234370Sjasone				break;
563234370Sjasone			case 'p': {
564234370Sjasone				uintmax_t val;
565234370Sjasone				char buf[X2S_BUFSIZE];
566234370Sjasone
567234370Sjasone				GET_ARG_NUMERIC(val, 'p');
568234370Sjasone				s = x2s(val, true, false, buf, &slen);
569234370Sjasone				APPEND_PADDED_S(s, slen, width, left_justify);
570234370Sjasone				f++;
571234370Sjasone				break;
572234370Sjasone			}
573234370Sjasone			default: not_implemented();
574234370Sjasone			}
575234370Sjasone			break;
576234370Sjasone		} default: {
577234370Sjasone			APPEND_C(*f);
578234370Sjasone			f++;
579234370Sjasone			break;
580234370Sjasone		}}
581234370Sjasone	}
582234370Sjasone	label_out:
583234370Sjasone	if (i < size)
584234370Sjasone		str[i] = '\0';
585234370Sjasone	else
586234370Sjasone		str[size - 1] = '\0';
587234370Sjasone	ret = i;
588234370Sjasone
589234370Sjasone#undef APPEND_C
590234370Sjasone#undef APPEND_S
591234370Sjasone#undef APPEND_PADDED_S
592234370Sjasone#undef GET_ARG_NUMERIC
593234370Sjasone	return (ret);
594234370Sjasone}
595234370Sjasone
596234370SjasoneJEMALLOC_ATTR(format(printf, 3, 4))
597234370Sjasoneint
598234370Sjasonemalloc_snprintf(char *str, size_t size, const char *format, ...)
599234370Sjasone{
600234370Sjasone	int ret;
601234370Sjasone	va_list ap;
602234370Sjasone
603234370Sjasone	va_start(ap, format);
604234370Sjasone	ret = malloc_vsnprintf(str, size, format, ap);
605234370Sjasone	va_end(ap);
606234370Sjasone
607234370Sjasone	return (ret);
608234370Sjasone}
609234370Sjasone
610234370Sjasonevoid
611234370Sjasonemalloc_vcprintf(void (*write_cb)(void *, const char *), void *cbopaque,
612234370Sjasone    const char *format, va_list ap)
613234370Sjasone{
614234370Sjasone	char buf[MALLOC_PRINTF_BUFSIZE];
615234370Sjasone
616234370Sjasone	if (write_cb == NULL) {
617234370Sjasone		/*
618234370Sjasone		 * The caller did not provide an alternate write_cb callback
619234370Sjasone		 * function, so use the default one.  malloc_write() is an
620234370Sjasone		 * inline function, so use malloc_message() directly here.
621234370Sjasone		 */
622235238Sjasone		write_cb = (je_malloc_message != NULL) ? je_malloc_message :
623235238Sjasone		    wrtmessage;
624234370Sjasone		cbopaque = NULL;
625234370Sjasone	}
626234370Sjasone
627234370Sjasone	malloc_vsnprintf(buf, sizeof(buf), format, ap);
628234370Sjasone	write_cb(cbopaque, buf);
629234370Sjasone}
630234370Sjasone
631234370Sjasone/*
632234370Sjasone * Print to a callback function in such a way as to (hopefully) avoid memory
633234370Sjasone * allocation.
634234370Sjasone */
635234370SjasoneJEMALLOC_ATTR(format(printf, 3, 4))
636234370Sjasonevoid
637234370Sjasonemalloc_cprintf(void (*write_cb)(void *, const char *), void *cbopaque,
638234370Sjasone    const char *format, ...)
639234370Sjasone{
640234370Sjasone	va_list ap;
641234370Sjasone
642234370Sjasone	va_start(ap, format);
643234370Sjasone	malloc_vcprintf(write_cb, cbopaque, format, ap);
644234370Sjasone	va_end(ap);
645234370Sjasone}
646234370Sjasone
647234370Sjasone/* Print to stderr in such a way as to avoid memory allocation. */
648234370SjasoneJEMALLOC_ATTR(format(printf, 1, 2))
649234370Sjasonevoid
650234370Sjasonemalloc_printf(const char *format, ...)
651234370Sjasone{
652234370Sjasone	va_list ap;
653234370Sjasone
654234370Sjasone	va_start(ap, format);
655234370Sjasone	malloc_vcprintf(NULL, NULL, format, ap);
656234370Sjasone	va_end(ap);
657234370Sjasone}
658