1 /*
2  * Copyright (c) 2003-2012 Hypertriton, Inc. <http://hypertriton.com/>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
18  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
23  * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 /*
26  * Copyright (c) 1987 Regents of the University of California.
27  * All rights reserved.
28  *
29  * Redistribution and use in source and binary forms are permitted
30  * provided that the above copyright notice and this paragraph are
31  * duplicated in all such forms and that any documentation,
32  * advertising materials, and other materials related to such
33  * distribution and use acknowledge that the software was developed
34  * by the University of California, Berkeley.  The name of the
35  * University may not be used to endorse or promote products derived
36  * from this software without specific prior written permission.
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
38  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
39  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
40  */
41 /*
42  * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
43  *
44  * Permission to use, copy, modify, and distribute this software for any
45  * purpose with or without fee is hereby granted, provided that the above
46  * copyright notice and this permission notice appear in all copies.
47  *
48  * THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL
49  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
50  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE
51  * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
52  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
53  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
54  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
55  */
56 
57 /*
58  * C-string related routines.
59  */
60 
61 #include <agar/core/core.h>
62 
63 #include <string.h>
64 #include <errno.h>
65 #include <stdio.h>
66 #include <ctype.h>
67 
68 #include <agar/config/have_iconv.h>
69 #ifdef HAVE_ICONV
70 # include <agar/config/have_iconv_const.h>
71 # include <iconv.h>
72 #endif
73 
74 #include "string_strcasecmp.h"
75 
76 /* AG_Printf() buffers */
77 static char        *agPrintBuf[AG_STRING_BUFFERS_MAX];
78 #ifdef AG_THREADS
79 static AG_ThreadKey agPrintBufKey[AG_STRING_BUFFERS_MAX];
80 #endif
81 
82 /* Formatting engine extensions */
83 static AG_FmtStringExt *agFmtExtensions = NULL;
84 static Uint             agFmtExtensionCount = 0;
85 #ifdef AG_THREADS
86 static AG_Mutex         agFmtExtensionsLock;
87 #endif
88 
89 size_t AG_DoPrintf(char *, size_t, const char *, va_list);
90 
91 /*
92  * Built-in extended format specifiers.
93  */
94 static size_t
PrintU8(AG_FmtString * fs,char * dst,size_t dstSize)95 PrintU8(AG_FmtString *fs, char *dst, size_t dstSize)
96 {
97 	Uint8 *val = AG_FMTSTRING_ARG(fs);
98 	return StrlcpyUint(dst, (Uint)*val, dstSize);
99 }
100 static size_t
PrintS8(AG_FmtString * fs,char * dst,size_t dstSize)101 PrintS8(AG_FmtString *fs, char *dst, size_t dstSize)
102 {
103 	Sint8 *val = AG_FMTSTRING_ARG(fs);
104 	return StrlcpyInt(dst, (int)*val, dstSize);
105 }
106 static size_t
PrintU16(AG_FmtString * fs,char * dst,size_t dstSize)107 PrintU16(AG_FmtString *fs, char *dst, size_t dstSize)
108 {
109 	Uint16 *val = AG_FMTSTRING_ARG(fs);
110 	return StrlcpyUint(dst, (Uint)*val, dstSize);
111 }
112 static size_t
PrintS16(AG_FmtString * fs,char * dst,size_t dstSize)113 PrintS16(AG_FmtString *fs, char *dst, size_t dstSize)
114 {
115 	Sint16 *val = AG_FMTSTRING_ARG(fs);
116 	return StrlcpyInt(dst, (int)*val, dstSize);
117 }
118 static size_t
PrintU32(AG_FmtString * fs,char * dst,size_t dstSize)119 PrintU32(AG_FmtString *fs, char *dst, size_t dstSize)
120 {
121 	Uint32 *val = AG_FMTSTRING_ARG(fs);
122 	return StrlcpyUint(dst, (Uint)*val, dstSize);
123 }
124 static size_t
PrintS32(AG_FmtString * fs,char * dst,size_t dstSize)125 PrintS32(AG_FmtString *fs, char *dst, size_t dstSize)
126 {
127 	Sint32 *val = AG_FMTSTRING_ARG(fs);
128 	return StrlcpyInt(dst, (int)*val, dstSize);
129 }
130 
131 #ifdef HAVE_64BIT
132 static size_t
PrintU64(AG_FmtString * fs,char * dst,size_t dstSize)133 PrintU64(AG_FmtString *fs, char *dst, size_t dstSize)
134 {
135 	Uint64 *val = AG_FMTSTRING_ARG(fs);
136 	return Snprintf(dst, dstSize, "%llu", (unsigned long long)*val);
137 }
138 static size_t
PrintS64(AG_FmtString * fs,char * dst,size_t dstSize)139 PrintS64(AG_FmtString *fs, char *dst, size_t dstSize)
140 {
141 	Sint64 *val = AG_FMTSTRING_ARG(fs);
142 	return Snprintf(dst, dstSize, "%lld", (long long)*val);
143 }
144 #endif /* HAVE_64BIT */
145 
146 static size_t
PrintOBJNAME(AG_FmtString * fs,char * dst,size_t dstSize)147 PrintOBJNAME(AG_FmtString *fs, char *dst, size_t dstSize)
148 {
149 	AG_Object *ob = AG_FMTSTRING_ARG(fs);
150 	return Strlcpy(dst, (ob != NULL) ? ob->name : "(null)", dstSize);
151 }
152 static size_t
PrintOBJTYPE(AG_FmtString * fs,char * dst,size_t dstSize)153 PrintOBJTYPE(AG_FmtString *fs, char *dst, size_t dstSize)
154 {
155 	AG_Object *ob = AG_FMTSTRING_ARG(fs);
156 	return Strlcpy(dst, (ob != NULL) ? ob->cls->name : "(null)", dstSize);
157 }
158 
159 /* Register a new extended format specifier. */
160 void
AG_RegisterFmtStringExt(const char * fmt,AG_FmtStringExtFn fn)161 AG_RegisterFmtStringExt(const char *fmt, AG_FmtStringExtFn fn)
162 {
163 	AG_FmtStringExt *fs;
164 
165 	AG_MutexLock(&agFmtExtensionsLock);
166 	agFmtExtensions = Realloc(agFmtExtensions,
167 	    (agFmtExtensionCount+1)*sizeof(AG_FmtStringExt));
168 	fs = &agFmtExtensions[agFmtExtensionCount++];
169 	fs->fmt = Strdup(fmt);
170 	fs->fmtLen = strlen(fmt);
171 	fs->fn = fn;
172 	AG_MutexUnlock(&agFmtExtensionsLock);
173 }
174 
175 /* Unregister an extended format specifier. */
176 void
AG_UnregisterFmtStringExt(const char * fmt)177 AG_UnregisterFmtStringExt(const char *fmt)
178 {
179 	Uint i;
180 
181 	AG_MutexLock(&agFmtExtensionsLock);
182 	for (i = 0; i < agFmtExtensionCount; i++) {
183 		if (strcmp(agFmtExtensions[i].fmt, fmt) == 0)
184 			break;
185 	}
186 	if (i < agFmtExtensionCount) {
187 		free(agFmtExtensions[i].fmt);
188 		if (i < agFmtExtensionCount-1) {
189 			memmove(&agFmtExtensions[i], &agFmtExtensions[i+1],
190 			    (agFmtExtensionCount-i-1)*sizeof(AG_FmtStringExt));
191 		}
192 		agFmtExtensionCount--;
193 	}
194 	AG_MutexUnlock(&agFmtExtensionsLock);
195 }
196 
197 /* Release all resources allocated by a format string. */
198 void
AG_FreeFmtString(AG_FmtString * fs)199 AG_FreeFmtString(AG_FmtString *fs)
200 {
201 	Free(fs->s);
202 	free(fs);
203 }
204 
205 /*
206  * Implementation of AG_ProcessFmtString().
207  */
208 
209 #undef FSARG
210 #define FSARG(fs,_type) (*(_type *)(fs)->p[fs->curArg++])
211 
212 #ifdef HAVE_64BIT
213 static size_t
ProcessFmtString64(AG_FmtString * fs,const char * f,char * dst,size_t dstSize)214 ProcessFmtString64(AG_FmtString *fs, const char *f, char *dst, size_t dstSize)
215 {
216 	switch (*f) {
217 # ifdef HAVE_LONG_DOUBLE
218 	case 'f':
219 		return Snprintf(dst, dstSize, "%.2Lf", FSARG(fs,long double));
220 	case 'g':
221 		return Snprintf(dst, dstSize, "%.2Lg", FSARG(fs,long double));
222 # endif
223 	case 'd':
224 	case 'i':
225 		return Snprintf(dst, dstSize, "%lld", (long long)FSARG(fs,Sint64));
226 	case 'o':
227 		return Snprintf(dst, dstSize, "%llo", (unsigned long long)FSARG(fs,Uint64));
228 	case 'u':
229 		return Snprintf(dst, dstSize, "%llu", (unsigned long long)FSARG(fs,Uint64));
230 	case 'x':
231 		return Snprintf(dst, dstSize, "%llx", (unsigned long long)FSARG(fs,Uint64));
232 	case 'X':
233 		return Snprintf(dst, dstSize, "%llX", (unsigned long long)FSARG(fs,Uint64));
234 	}
235 	return (0);
236 }
237 #endif /* HAVE_64BIT */
238 
239 /*
240  * Construct a string from the given AG_FmtString. The arguments are
241  * dereferenced, and the resulting string is written to a fixed-size
242  * buffer dst of dstSize bytes. Returns the number of characters that
243  * would have been copied were dstSize unlimited.
244  */
245 size_t
AG_ProcessFmtString(AG_FmtString * fs,char * dst,size_t dstSize)246 AG_ProcessFmtString(AG_FmtString *fs, char *dst, size_t dstSize)
247 {
248 	char *pDst = &dst[0];
249 	char *pEnd = &dst[dstSize-1];
250 	char *f;
251 	size_t rv;
252 	Uint i;
253 
254 	fs->curArg = 0;
255 
256 	if (dstSize < 1) {
257 		return (0);
258 	}
259 	*pDst = '\0';
260 
261 	for (f = &fs->s[0]; *f != '\0'; f++) {
262 		if (f[0] != '%' || f[1] == '\0') {
263 			*pDst = *f;
264 			if (++pDst >= pEnd) {
265 				*pEnd = '\0';			/* Truncate */
266 				goto out;
267 			}
268 			continue;
269 		}
270 		rv = 0;
271 		switch (f[1]) {
272 		case '[':
273 			for (i = 0; i < agFmtExtensionCount; i++) {
274 				AG_FmtStringExt *fExt = &agFmtExtensions[i];
275 
276 				if (strncmp(fExt->fmt, &f[2], fExt->fmtLen) != 0) {
277 					continue;
278 				}
279 				rv = fExt->fn(fs, pDst, (pEnd-pDst));
280 				f += fExt->fmtLen + 1;	/* Closing "]" */
281 				break;
282 			}
283 			break;
284 		case 'l':
285 			switch (f[2]) {
286 			case 'f':
287 				rv = Snprintf(pDst, (pEnd-pDst), "%.2f", FSARG(fs,double));
288 				f++;
289 				break;
290 			case 'g':
291 				rv = Snprintf(pDst, (pEnd-pDst), "%g", FSARG(fs,double));
292 				f++;
293 				break;
294 #ifdef HAVE_64BIT
295 			case 'l':
296 				rv = ProcessFmtString64(fs, &f[3], pDst, (pEnd-pDst));
297 				f+=2;
298 				break;
299 #endif
300 			}
301 			break;
302 		case 'd':
303 		case 'i':
304 			rv = StrlcpyInt(pDst, FSARG(fs,int), (pEnd-pDst));
305 			break;
306 		case 'u':
307 			rv = StrlcpyUint(pDst, FSARG(fs,Uint), (pEnd-pDst));
308 			break;
309 		case 'f':
310 			rv = Snprintf(pDst, (pEnd-pDst), "%.2f", FSARG(fs,float));
311 			break;
312 		case 'g':
313 			rv = Snprintf(pDst, (pEnd-pDst), "%g", FSARG(fs,float));
314 			break;
315 		case 's':
316 			rv = Strlcpy(pDst, &FSARG(fs,char), (pEnd-pDst));
317 			break;
318 		case 'o':
319 			rv = Snprintf(pDst, (pEnd-pDst), "%o", FSARG(fs,Uint));
320 			break;
321 		case 'x':
322 			rv = Snprintf(pDst, (pEnd-pDst), "%x", FSARG(fs,Uint));
323 			break;
324 		case 'X':
325 			rv = Snprintf(pDst, (pEnd-pDst), "%X", FSARG(fs,Uint));
326 			break;
327 		case 'c':
328 			*pDst = FSARG(fs,char);
329 			rv = 1;
330 			break;
331 		case '%':
332 			*pDst = '%';
333 			rv = 1;
334 			break;
335 		}
336 		if ((pDst += rv) > pEnd) {
337 			*pEnd = '\0';				/* Truncate */
338 			goto out;
339 		}
340 		f++;
341 	}
342 out:
343 	if (pDst < pEnd) {
344 		*pDst = '\0';
345 	} else {
346 		*pEnd = '\0';
347 	}
348 	return (pDst - dst);
349 }
350 
351 #undef FSARG
352 
353 #undef CAT_SPEC
354 #define CAT_SPEC(c) \
355 	if ((pSpec+2) >= pSpecEnd) { AG_FatalError("Format"); } \
356 	pSpec[0] = (c); \
357 	pSpec[1] = '\0'; \
358 	pSpec++;
359 
360 /*
361  * Build a C string from a format string (see AG_String(3) for details on
362  * the format). AG_DoPrintf() writes the formatted output to a fixed-size
363  * buffer (if the buffer is too small, the string is truncated and the
364  * function returns the number of characters that would have been copied
365  * were dstSize unlimited).
366  */
367 size_t
AG_DoPrintf(char * dst,size_t dstSize,const char * fmt,va_list ap)368 AG_DoPrintf(char *dst, size_t dstSize, const char *fmt, va_list ap)
369 {
370 	char spec[32], *pSpec, *pSpecEnd = &spec[32];
371 	AG_FmtString fs;
372 	char *pDst = &dst[0];
373 	char *pEnd = &dst[dstSize-1];
374 	const char *f;
375 	size_t rv;
376 	Uint i;
377 
378 	if (dstSize < 1) {
379 		return (1);
380 	}
381 	*pDst = '\0';
382 
383 	fs.s = NULL;
384 
385 	for (f = &fmt[0]; *f != '\0'; f++) {
386 		if (f[0] != '%' || f[1] == '\0') {
387 			*pDst = *f;
388 			if (++pDst >= pEnd) {
389 				*pEnd = '\0';			/* Truncate */
390 				goto out;
391 			}
392 			continue;
393 		}
394 		spec[0] = '%';
395 		spec[1] = f[1];
396 		spec[2] = '\0';
397 		pSpec = &spec[1];
398 next_char:
399 		rv = 0;
400 		switch (f[1]) {
401 		case '[':
402 			for (i = 0; i < agFmtExtensionCount; i++) {
403 				AG_FmtStringExt *fExt = &agFmtExtensions[i];
404 
405 				if (strncmp(fExt->fmt, &f[2], fExt->fmtLen) != 0) {
406 					continue;
407 				}
408 				fs.curArg = 0;
409 				fs.p[0] = va_arg(ap, void *);
410 				rv = fExt->fn(&fs, pDst, (pEnd-pDst));
411 				f += fExt->fmtLen + 1;	/* Closing "]" */
412 				break;
413 			}
414 			break;
415 		case 'd':
416 		case 'i':
417 			CAT_SPEC(f[1]);
418 			if (pSpec == &spec[2]) {	/* Optimized (%d) */
419 				rv = StrlcpyInt(pDst, va_arg(ap,int), (pEnd-pDst));
420 			} else {
421 				rv = Snprintf(pDst, (pEnd-pDst), spec,
422 				    va_arg(ap,Uint));
423 			}
424 			break;
425 		case 'u':
426 			CAT_SPEC(f[1]);
427 			if (pSpec == &spec[2]) {	/* Optimized (%u) */
428 				rv = StrlcpyUint(pDst, va_arg(ap,Uint), (pEnd-pDst));
429 			} else {
430 				rv = Snprintf(pDst, (pEnd-pDst), spec,
431 				    va_arg(ap,Uint));
432 			}
433 			break;
434 		case 'o':
435 		case 'x':
436 		case 'X':
437 			CAT_SPEC(f[1]);
438 			rv = Snprintf(pDst, (pEnd-pDst), spec, va_arg(ap,Uint));
439 			break;
440 		case 'c':
441 			CAT_SPEC(f[1]);
442 			if (pSpec == &spec[2]) {	/* Optimized (%c) */
443 				if ((pDst+1) > pEnd) {
444 					*pEnd = '\0';		/* Truncate */
445 					goto out;
446 				}
447 				*pDst = (char)va_arg(ap,int);
448 				rv = 1;
449 			} else {
450 				rv = Snprintf(pDst, (pEnd-pDst), spec,
451 				    (char)va_arg(ap,int));
452 			}
453 			break;
454 		case 'f':
455 		case 'g':
456 			CAT_SPEC(f[1]);
457 			rv = Snprintf(pDst, (pEnd-pDst), spec, va_arg(ap,double));
458 			break;
459 		case 's':
460 			CAT_SPEC(f[1]);
461 			if (pSpec == &spec[2]) {	/* Optimized (%s) */
462 				rv = Strlcpy(pDst, va_arg(ap,char *), (pEnd-pDst));
463 			} else {
464 				rv = Snprintf(pDst, (pEnd-pDst), spec,
465 				    va_arg(ap,char *));
466 			}
467 			break;
468 		case 'l':
469 			CAT_SPEC(f[1]);
470 			switch (f[2]) {
471 			case 'd':
472 			case 'i':
473 				CAT_SPEC(f[2]);
474 				rv = Snprintf(pDst, (pEnd-pDst), spec,
475 				    va_arg(ap,long));
476 				break;
477 			case 'o':
478 			case 'u':
479 			case 'x':
480 			case 'X':
481 				CAT_SPEC(f[2]);
482 				rv = Snprintf(pDst, (pEnd-pDst), spec,
483 				    va_arg(ap,Ulong));
484 				break;
485 			case 'f':
486 			case 'g':
487 				CAT_SPEC(f[2]);
488 				rv = Snprintf(pDst, (pEnd-pDst), spec,
489 				    va_arg(ap,double));
490 				break;
491 #ifdef HAVE_64BIT
492 			case 'l':
493 				CAT_SPEC(f[2]);
494 				switch (f[3]) {
495 # ifdef HAVE_LONG_DOUBLE
496 				case 'f':
497 				case 'g':
498 					CAT_SPEC(f[3]);
499 					rv = Snprintf(pDst, (pEnd-pDst), spec,
500 					    va_arg(ap,long double));
501 					break;
502 # endif
503 				case 'd':
504 				case 'i':
505 					CAT_SPEC(f[3]);
506 					rv = Snprintf(pDst, (pEnd-pDst), spec,
507 					    (long long)va_arg(ap,Sint64));
508 					break;
509 				case 'o':
510 				case 'u':
511 				case 'x':
512 				case 'X':
513 					CAT_SPEC(f[3]);
514 					rv = Snprintf(pDst, (pEnd-pDst), spec,
515 					    (unsigned long long)va_arg(ap,Uint64));
516 					break;
517 				}
518 				f++;
519 #endif /* HAVE_64BIT */
520 			}
521 			f++;
522 			break;
523 		case '#':
524 		case '0':
525 		case '-':
526 		case ' ':
527 		case '+':
528 		case '\'':
529 		case '.':
530 		case '1':
531 		case '2':
532 		case '3':
533 		case '4':
534 		case '5':
535 		case '6':
536 		case '7':
537 		case '8':
538 		case '9':
539 			CAT_SPEC(f[1]);
540 			f++;
541 			goto next_char;
542 		case '%':
543 			if ((pDst+1) > pEnd) {
544 				*pEnd = '\0';			/* Truncate */
545 				goto out;
546 			}
547 			*pDst = '%';
548 			rv = 1;
549 			break;
550 		}
551 		if ((pDst += rv) > pEnd) {
552 			*pEnd = '\0';				/* Truncate */
553 			goto out;
554 		}
555 		f++;
556 	}
557 out:
558 	if (pDst < pEnd) {
559 		*pDst = '\0';
560 	} else {
561 		*pEnd = '\0';
562 	}
563 	return (pDst - dst);
564 }
565 
566 #undef CAT_SPEC
567 
568 /*
569  * AG_Printf() performs formatted output conversion and returns a pointer
570  * to an internally-managed buffer (the caller must never free() this buffer).
571  * The AG_PrintfN() variant allows a buffer index to be specified.
572  *
573  * In multi-threaded mode, the AG_Printf() buffers are allocated as
574  * thread-local storage. They will remain valid until the application
575  * or thread exits (or a subsequent AG_Printf() call is made, which
576  * will overwrite it).
577  */
578 char *
AG_Printf(const char * fmt,...)579 AG_Printf(const char *fmt, ...)
580 {
581 	va_list ap;
582 	char *dst, *dstNew;
583 	size_t dstSize = AG_FMTSTRING_BUFFER_INIT, rv;
584 
585 	dst = Malloc(dstSize);
586 restart:
587 	dst[0] = '\0';
588 	va_start(ap, fmt);
589 	if ((rv = AG_DoPrintf(dst, dstSize, fmt, ap)) >= dstSize) {
590 		va_end(ap);
591 		dstNew = TryRealloc(dst, (rv+AG_FMTSTRING_BUFFER_GROW));
592 		if (dstNew == NULL) {
593 			AG_FatalError("Out of memory for AG_Printf");
594 			return (NULL);
595 		}
596 		dst = dstNew;
597 		dstSize = (rv+AG_FMTSTRING_BUFFER_GROW);
598 		goto restart;
599 	}
600 	va_end(ap);
601 
602 #ifdef AG_THREADS
603 	if ((agPrintBuf[0] = (char *)AG_ThreadKeyGet(agPrintBufKey[0])) != NULL) {
604 		free(agPrintBuf[0]);
605 	}
606 	agPrintBuf[0] = dst;
607 	AG_ThreadKeySet(agPrintBufKey[0], agPrintBuf[0]);
608 #else
609 	Free(agPrintBuf[0]);
610 	agPrintBuf[0] = dst;
611 #endif
612 	return (dst);
613 }
614 char *
AG_PrintfN(Uint idx,const char * fmt,...)615 AG_PrintfN(Uint idx, const char *fmt, ...)
616 {
617 	va_list ap;
618 	char *dst, *dstNew;
619 	size_t dstSize = AG_FMTSTRING_BUFFER_INIT, rv;
620 
621 	dst = Malloc(dstSize);
622 restart:
623 	dst[0] = '\0';
624 	va_start(ap, fmt);
625 	if ((rv = AG_DoPrintf(dst, dstSize, fmt, ap)) >= dstSize) {
626 		va_end(ap);
627 		dstNew = TryRealloc(dst, (rv+AG_FMTSTRING_BUFFER_GROW));
628 		if (dstNew == NULL) {
629 			AG_FatalError("Out of memory for AG_Printf");
630 			return (NULL);
631 		}
632 		dst = dstNew;
633 		dstSize = (rv+AG_FMTSTRING_BUFFER_GROW);
634 		goto restart;
635 	}
636 	va_end(ap);
637 
638 #ifdef AG_THREADS
639 	if ((agPrintBuf[idx] = (char *)AG_ThreadKeyGet(agPrintBufKey[idx])) != NULL) {
640 		free(agPrintBuf[idx]);
641 	}
642 	agPrintBuf[idx] = dst;
643 	AG_ThreadKeySet(agPrintBufKey[idx], agPrintBuf[idx]);
644 #else
645 	Free(agPrintBuf[idx]);
646 	agPrintBuf[idx] = dst;
647 #endif
648 	return (dst);
649 }
650 
651 /*
652  * Allocate and return a new AG_FmtString (which describes a string built
653  * from arguments to be later accessed, as the string is printed).
654  *
655  * Optionally, mutexes may be associated with arguments like so:
656  *
657  * 	AG_PrintfP("My string: %m<%s>", myMutex, myString);
658  *
659  * This instructs the formatting engine to acquire myMutex before
660  * accessing the contents of myString.
661  */
662 AG_FmtString *
AG_PrintfP(const char * fmt,...)663 AG_PrintfP(const char *fmt, ...)
664 {
665 	const char *p;
666 	AG_FmtString *fs;
667 	va_list ap;
668 	AG_Mutex *mu = NULL;
669 
670 	if ((fs = TryMalloc(sizeof(AG_FmtString))) == NULL) {
671 		AG_FatalError(NULL);
672 	}
673 	fs->s = Strdup(fmt);
674 	fs->n = 0;
675 
676 	va_start(ap, fmt);
677 	for (p = fmt; *p != '\0'; p++) {
678 		if (*p != '%') {
679 			continue;
680 		}
681 		switch (p[1]) {
682 		case '%':
683 		case ' ':
684 			p++;
685 			break;
686 		case 'm':
687 			mu = (AG_Mutex *)va_arg(ap, void *);
688 			break;
689 		case '>':
690 			mu = NULL;
691 			break;
692 		case '\0':
693 			break;
694 		default:
695 			if (fs->n+1 >= AG_STRING_POINTERS_MAX) {
696 				AG_FatalError("Too many arguments");
697 			}
698 			fs->p[fs->n] = va_arg(ap, void *);
699 			fs->mu[fs->n] = mu;
700 			fs->n++;
701 			mu = NULL;
702 			break;
703 		}
704 	}
705 	va_end(ap);
706 	return (fs);
707 }
708 
709 /*
710  * Copy src to string dst of size siz.  At most siz-1 characters
711  * will be copied.  Always NUL terminates (unless siz == 0).
712  * Returns strlen(src); if retval >= siz, truncation occurred.
713  */
714 size_t
AG_Strlcpy(char * dst,const char * src,size_t siz)715 AG_Strlcpy(char *dst, const char *src, size_t siz)
716 {
717 	char *d = dst;
718 	const char *s = src;
719 	size_t n = siz;
720 
721 	/* Copy as many bytes as will fit */
722 	if (n != 0 && --n != 0) {
723 		do {
724 			if ((*d++ = *s++) == 0) {
725 				break;
726 			}
727 		} while (--n != 0);
728 	}
729 
730 	/* Not enough room in dst, add NUL and traverse rest of src */
731 	if (n == 0) {
732 		if (siz != 0)
733 			*d = '\0';		/* NUL-terminate dst */
734 		while (*s++)
735 			;
736 	}
737 
738 	return (s - src - 1);	/* count does not include NUL */
739 }
740 
741 /*
742  * Appends src to string dst of size siz (unlike strncat, siz is the
743  * full size of dst, not space left).  At most siz-1 characters
744  * will be copied.  Always NUL terminates (unless siz <= strlen(dst)).
745  * Returns strlen(src) + MIN(siz, strlen(initial dst)).
746  * If retval >= siz, truncation occurred.
747  */
748 size_t
AG_Strlcat(char * dst,const char * src,size_t siz)749 AG_Strlcat(char *dst, const char *src, size_t siz)
750 {
751 	char *d = dst;
752 	const char *s = src;
753 	size_t dlen, n = siz;
754 
755 	/* Find the end of dst and adjust bytes left but don't go past end */
756 	while (n-- != 0 && *d != '\0') {
757 		d++;
758 	}
759 	dlen = d - dst;
760 	n = siz - dlen;
761 
762 	if (n == 0) {
763 		return (dlen + strlen(s));
764 	}
765 	while (*s != '\0') {
766 		if (n != 1) {
767 			*d++ = *s;
768 			n--;
769 		}
770 		s++;
771 	}
772 	*d = '\0';
773 
774 	return (dlen + (s - src));	/* count does not include NUL */
775 }
776 
777 /*
778  * Get next token from string *stringp, where tokens are possibly-empty
779  * strings separated by characters from delim.
780  *
781  * Writes NULs into the string at *stringp to end tokens.
782  * delim need not remain constant from call to call.
783  * On return, *stringp points past the last NUL written (if there might
784  * be further tokens), or is NULL (if there are definitely no more tokens).
785  *
786  * If *stringp is NULL, AG_Strsep returns NULL.
787  */
788 char *
AG_Strsep(char ** stringp,const char * delim)789 AG_Strsep(char **stringp, const char *delim)
790 {
791 	char *s;
792 	const char *spanp;
793 	int c, sc;
794 	char *tok;
795 
796 	if ((s = *stringp) == NULL) {
797 		return (NULL);
798 	}
799 	for (tok = s;;) {
800 		c = *s++;
801 		spanp = delim;
802 		do {
803 			if ((sc = *spanp++) == c) {
804 				if (c == 0) {
805 					s = NULL;
806 				} else {
807 					s[-1] = 0;
808 				}
809 				*stringp = s;
810 				return (tok);
811 			}
812 		} while (sc != 0);
813 	}
814 }
815 
816 /* Duplicate a string. */
817 char *
AG_Strdup(const char * s)818 AG_Strdup(const char *s)
819 {
820 	size_t buflen;
821 	char *ns;
822 
823 	buflen = strlen(s)+1;
824 	ns = Malloc(buflen);
825 	memcpy(ns, s, buflen);
826 	return (ns);
827 }
828 
829 /* Duplicate a string or fail. */
830 char *
AG_TryStrdup(const char * s)831 AG_TryStrdup(const char *s)
832 {
833 	size_t buflen;
834 	char *ns;
835 
836 	buflen = strlen(s)+1;
837 	if ((ns = TryMalloc(buflen)) == NULL) {
838 		return (NULL);
839 	}
840 	memcpy(ns, s, buflen);
841 	return (ns);
842 }
843 
844 /*
845  * Locate a substring ignoring case.
846  */
847 const char *
AG_Strcasestr(const char * s,const char * find)848 AG_Strcasestr(const char *s, const char *find)
849 {
850 	char c, sc;
851 	size_t len;
852 
853 	if ((c = *find++) != 0) {
854 		c = (char)tolower((unsigned char)c);
855 		len = strlen(find);
856 		do {
857 			do {
858 				if ((sc = *s++) == 0)
859 					return (NULL);
860 			} while ((char)tolower((unsigned char)sc) != c);
861 		} while (Strncasecmp(s, find, len) != 0);
862 		s--;
863 	}
864 	return (s);
865 }
866 
867 #ifdef HAVE_ICONV
868 
869 static Uint32 *
ImportUnicodeICONV(const char * encoding,const char * s,size_t sLen,size_t * pOutLen,size_t * pOutSize)870 ImportUnicodeICONV(const char *encoding, const char *s, size_t sLen,
871     size_t *pOutLen, size_t *pOutSize)
872 {
873 	Uint32 *ucs, *ucsNew;
874 	const char *inPtr = s;
875 	char *wrPtr;
876 	size_t outSize = (sLen+1)*sizeof(Uint32);
877 	iconv_t cd;
878 
879 	if ((ucs = TryMalloc(outSize)) == NULL) {
880 		return (NULL);
881 	}
882 	if ((cd = iconv_open("UCS-4-INTERNAL", encoding)) == (iconv_t)-1) {
883 		AG_SetError("iconv_open: %s", strerror(errno));
884 		goto fail;
885 	}
886 	wrPtr = (char *)ucs;
887 
888 #ifdef HAVE_ICONV_CONST
889 	if (iconv(cd, &inPtr, &sLen, &wrPtr, &outSize) == (size_t)-1) {
890 		AG_SetError("iconv: %s", strerror(errno));
891 		iconv_close(cd);
892 		goto fail;
893 	}
894 #else
895 	{
896 		char *tmpBuf;
897 		if ((tmpBuf = AG_TryStrdup(inPtr)) == NULL) {
898 			iconv_close(cd);
899 			goto fail;
900 		}
901 		if (iconv(cd, &tmpBuf, &sLen, &wrPtr, &outSize) == (size_t)-1) {
902 			AG_SetError("iconv: %s", strerror(errno));
903 			iconv_close(cd);
904 			free(tmpBuf);
905 			goto fail;
906 		}
907 		free(tmpBuf);
908 	}
909 #endif /* !HAVE_ICONV_CONST */
910 
911 	iconv_close(cd);
912 
913 	outSize = (wrPtr - (char *)ucs)/sizeof(Uint32);
914 	if (pOutLen != NULL) { *pOutLen = outSize; }
915 
916 	/* Shrink the buffer down to the actual string length. */
917 	ucsNew = TryRealloc(ucs, (outSize+1)*sizeof(Uint32));
918 	if (ucsNew == NULL) {
919 		goto fail;
920 	}
921 	ucs = ucsNew;
922 	ucs[outSize] = '\0';
923 	if (pOutSize != NULL) { *pOutSize = (outSize+1)*sizeof(Uint32); }
924 	return (ucs);
925 fail:
926 	Free(ucs);
927 	return (NULL);
928 }
929 
930 #endif /* HAVE_ICONV */
931 
932 /*
933  * Return an internal UCS-4 buffer from the given string and specified
934  * encoding. Optionally returns number of characters converted in
935  * pOutLen, and allocated buffer size in pOutSize.
936  */
937 Uint32 *
AG_ImportUnicode(const char * encoding,const char * s,size_t * pOutLen,size_t * pOutSize)938 AG_ImportUnicode(const char *encoding, const char *s, size_t *pOutLen,
939     size_t *pOutSize)
940 {
941 	Uint32 *ucs;
942 	size_t i, j;
943 	size_t sLen = strlen(s);
944 	size_t bufLen, utf8len;
945 
946 	if (strcmp(encoding, "UTF-8") == 0) {
947 		if (AG_LengthUTF8(s, &utf8len) == -1) {
948 			return (NULL);
949 		}
950 		bufLen = (utf8len + 1)*sizeof(Uint32);
951 		if ((ucs = TryMalloc(bufLen)) == NULL) {
952 			return (NULL);
953 		}
954 		for (i = 0, j = 0; i < sLen; i++, j++) {
955 			switch (AG_CharLengthUTF8(s[i])) {
956 			case 1:
957 				ucs[j] = (Uint32)s[i];
958 				break;
959 			case 2:
960 				ucs[j]  = (Uint32)(s[i]   & 0x1f) << 6;
961 				ucs[j] |= (Uint32)(s[++i] & 0x3f);
962 				break;
963 			case 3:
964 				ucs[j]  = (Uint32)(s[i]   & 0x0f) << 12;
965 				ucs[j] |= (Uint32)(s[++i] & 0x3f) << 6;
966 				ucs[j] |= (Uint32)(s[++i] & 0x3f);
967 				break;
968 			case 4:
969 				ucs[j]  = (Uint32)(s[i]   & 0x07) << 18;
970 				ucs[j] |= (Uint32)(s[++i] & 0x3f) << 12;
971 				ucs[j] |= (Uint32)(s[++i] & 0x3f) << 6;
972 				ucs[j] |= (Uint32)(s[++i] & 0x3f);
973 				break;
974 			case 5:
975 				ucs[j]  = (Uint32)(s[i]   & 0x03) << 24;
976 				ucs[j] |= (Uint32)(s[++i] & 0x3f) << 18;
977 				ucs[j] |= (Uint32)(s[++i] & 0x3f) << 12;
978 				ucs[j] |= (Uint32)(s[++i] & 0x3f) << 6;
979 				ucs[j] |= (Uint32)(s[++i] & 0x3f);
980 				break;
981 			case 6:
982 				ucs[j]  = (Uint32)(s[i]   & 0x01) << 30;
983 				ucs[j] |= (Uint32)(s[++i] & 0x3f) << 24;
984 				ucs[j] |= (Uint32)(s[++i] & 0x3f) << 18;
985 				ucs[j] |= (Uint32)(s[++i] & 0x3f) << 12;
986 				ucs[j] |= (Uint32)(s[++i] & 0x3f) << 6;
987 				ucs[j] |= (Uint32)(s[++i] & 0x3f);
988 				break;
989 			case -1:
990 				Free(ucs);
991 				return (NULL);
992 			}
993 		}
994 		ucs[j] = '\0';
995 		if (pOutLen != NULL) { *pOutLen = j; }
996 		if (pOutSize != NULL) { *pOutSize = bufLen; }
997 	} else if (strcmp(encoding, "US-ASCII") == 0) {
998 		bufLen = (sLen + 1)*sizeof(Uint32);
999 		if ((ucs = TryMalloc(bufLen)) == NULL) {
1000 			return (NULL);
1001 		}
1002 		for (i = 0; i < sLen; i++) {
1003 			ucs[i] = ((const unsigned char *)s)[i];
1004 		}
1005 		ucs[i] = '\0';
1006 		if (pOutLen != NULL) { *pOutLen = i; }
1007 		if (pOutSize != NULL) { *pOutSize = bufLen; }
1008 	} else {
1009 #ifdef HAVE_ICONV
1010 		ucs = ImportUnicodeICONV(encoding, s, sLen, pOutLen, pOutSize);
1011 #else
1012 		AG_SetError("Unknown encoding: %s (no iconv support)", encoding);
1013 		return (NULL);
1014 #endif
1015 	}
1016 	return (ucs);
1017 }
1018 
1019 #ifdef HAVE_ICONV
1020 
1021 static int
ExportUnicodeICONV(const char * encoding,char * dst,const Uint32 * ucs,size_t dstSize)1022 ExportUnicodeICONV(const char *encoding, char *dst, const Uint32 *ucs,
1023     size_t dstSize)
1024 {
1025 	const char *inPtr = (const char *)ucs;
1026 	size_t inSize = AG_LengthUCS4(ucs)*sizeof(Uint32);
1027 	char *wrPtr = dst;
1028 	size_t outSize = dstSize;
1029 	iconv_t cd;
1030 
1031 	if ((cd = iconv_open(encoding, "UCS-4-INTERNAL")) == (iconv_t)-1) {
1032 		AG_SetError("iconv_open: %s", strerror(errno));
1033 		return (-1);
1034 	}
1035 
1036 #ifdef HAVE_ICONV_CONST
1037 	if (iconv(cd, &inPtr, &inSize, &wrPtr, &outSize) == (size_t)-1) {
1038 		AG_SetError("iconv: %s", strerror(errno));
1039 		iconv_close(cd);
1040 		return (-1);
1041 	}
1042 #else
1043 	{
1044 		char *tmpBuf;
1045 		if ((tmpBuf = AG_TryStrdup(inPtr)) == NULL) {
1046 			iconv_close(cd);
1047 			return (-1);
1048 		}
1049 		if (iconv(cd, &tmpBuf, &inSize, &wrPtr, &outSize) == (size_t)-1) {
1050 			AG_SetError("iconv: %s", strerror(errno));
1051 			iconv_close(cd);
1052 			free(tmpBuf);
1053 			return (-1);
1054 		}
1055 		free(tmpBuf);
1056 	}
1057 #endif
1058 
1059 	iconv_close(cd);
1060 
1061 	if (outSize >= sizeof(char)) {
1062 		outSize = wrPtr - dst;
1063 		dst[outSize] = '\0';
1064 	} else {
1065 		AG_SetError("iconv: Out of space for NUL");
1066 		return (-1);
1067 	}
1068 	return (0);
1069 }
1070 
1071 #endif /* HAVE_ICONV */
1072 
1073 /*
1074  * Convert an internal UCS-4 string to a fixed-size buffer using the specified
1075  * encoding. At most dstSize-1 bytes will be copied. The string is always
1076  * NUL-terminated.
1077  */
1078 int
AG_ExportUnicode(const char * encoding,char * dst,const Uint32 * ucs,size_t dstSize)1079 AG_ExportUnicode(const char *encoding, char *dst, const Uint32 *ucs,
1080     size_t dstSize)
1081 {
1082 	size_t len;
1083 
1084 	if (strcmp(encoding, "UTF-8") == 0) {
1085 		for (len = 0; *ucs != '\0' && len < dstSize; ucs++) {
1086 			Uint32 uch = *ucs;
1087 			int chlen, ch1, i;
1088 
1089 			if (uch < 0x80) {
1090 				chlen = 1;
1091 				ch1 = 0;
1092 			} else if (uch < 0x800) {
1093 				chlen = 2;
1094 				ch1 = 0xc0;
1095 			} else if (uch < 0x10000) {
1096 				chlen = 3;
1097 				ch1 = 0xe0;
1098 			} else if (uch < 0x200000) {
1099 				chlen = 4;
1100 				ch1 = 0xf0;
1101 			} else if (uch < 0x4000000) {
1102 				chlen = 5;
1103 				ch1 = 0xf8;
1104 			} else if (uch <= 0x7fffffff) {
1105 				chlen = 6;
1106 				ch1 = 0xfc;
1107 			} else {
1108 				AG_SetError("Bad UTF-8 sequence");
1109 				return (-1);
1110 			}
1111 			if (len+chlen+1 > dstSize) {
1112 				AG_SetError("Out of space");
1113 				return (-1);
1114 			}
1115 			for (i = chlen - 1; i > 0; i--) {
1116 				dst[i] = (uch & 0x3f) | 0x80;
1117 				uch >>= 6;
1118 			}
1119 			dst[0] = uch | ch1;
1120 			dst += chlen;
1121 			len += chlen;
1122 		}
1123 		*dst = '\0';
1124 		return (0);
1125 	} else if (strcmp(encoding, "US-ASCII") == 0) {
1126 		for (len = 0; *ucs != '\0' && len < dstSize; ucs++) {
1127 			if (!isascii((int)*ucs)) {
1128 				AG_SetError("Bad ASCII character");
1129 				return (-1);
1130 			}
1131 			*dst = (char)*ucs;
1132 			dst++;
1133 			len++;
1134 		}
1135 		*dst = '\0';
1136 		return (0);
1137 	} else {
1138 #ifdef HAVE_ICONV
1139 		return ExportUnicodeICONV(encoding, dst, ucs, dstSize);
1140 #else
1141 		AG_SetError("Unknown encoding: %s (no iconv support)", encoding);
1142 		return (-1);
1143 #endif
1144 	}
1145 }
1146 
1147 /* Reverse the characters of a string. */
1148 void
AG_StrReverse(char * s)1149 AG_StrReverse(char *s)
1150 {
1151 	char *p, *q, c;
1152 
1153 	if (*s == '\0') {
1154 		return;
1155 	}
1156 	q = s;
1157 	while (*(++q) != '\0')
1158 		;;
1159 	for (p = s; p < --q; p++) {
1160 		c = *p;
1161 		*p = *q;
1162 		*q = c;
1163 	}
1164 }
1165 
1166 /*
1167  * Format an int (base 10) into a fixed-size buffer.
1168  * XXX on truncation, this returns the size of the buffer + 1 byte
1169  * (we should return the total length required instead).
1170  */
1171 size_t
AG_StrlcpyInt(char * s,int val,size_t size)1172 AG_StrlcpyInt(char *s, int val, size_t size)
1173 {
1174 	static const char *digits = "0123456789";
1175 	int sign = (val < 0);
1176 	int i = 0;
1177 
1178 	if (size < 1) {
1179 		return (1);
1180 	}
1181 	val = (val < 0) ? -val : val;
1182 	do {
1183 		if (i+1 > size) {
1184 			goto trunc;
1185 		}
1186 		s[i++] = digits[val % 10];
1187 	} while ((val /= 10) > 0);
1188 
1189 	if (sign) {
1190 		if (i+1 > size) {
1191 			goto trunc;
1192 		}
1193 		s[i++] = '-';
1194 	}
1195 	if (i+1 > size) {
1196 		goto trunc;
1197 	}
1198 	s[i] = '\0';
1199 	AG_StrReverse(s);
1200 	return (i);
1201 trunc:
1202 	s[size-1] = '\0';
1203 	AG_StrReverse(s);
1204 	return (i+1);
1205 }
1206 
1207 /*
1208  * Format an unsigned int (base 10) into a fixed-size buffer.
1209  * XXX on truncation, this returns the size of the buffer + 1 byte
1210  * (we should return the total length required instead).
1211  */
1212 size_t
AG_StrlcpyUint(char * s,Uint val,size_t size)1213 AG_StrlcpyUint(char *s, Uint val, size_t size)
1214 {
1215 	static const char *digits = "0123456789";
1216 	int i = 0;
1217 
1218 	if (size < 1) {
1219 		return (1);
1220 	}
1221 	do {
1222 		if (i+1 > size) {
1223 			goto trunc;
1224 		}
1225 		s[i++] = digits[val % 10];
1226 	} while ((val /= 10) > 0);
1227 
1228 	if (i+1 > size) {
1229 		goto trunc;
1230 	}
1231 	s[i] = '\0';
1232 	AG_StrReverse(s);
1233 	return (i);
1234 trunc:
1235 	s[size-1] = '\0';
1236 	AG_StrReverse(s);
1237 	return (i+1);
1238 }
1239 
1240 /*
1241  * Format an int (base 10) into a fixed-size buffer (concatenate).
1242  * XXX on truncation, this returns the size of the buffer + 1 byte
1243  * (we should return the total length required instead).
1244  */
1245 size_t
AG_StrlcatInt(char * s,int val,size_t size)1246 AG_StrlcatInt(char *s, int val, size_t size)
1247 {
1248 	static const char *digits = "0123456789";
1249 	int sign = (val < 0);
1250 	int i = strlen(s);
1251 	int iStart = i;
1252 
1253 	if (size < 1) {
1254 		return (1);
1255 	}
1256 	val = (val < 0) ? -val : val;
1257 	do {
1258 		if (i+1 > size) {
1259 			goto trunc;
1260 		}
1261 		s[i++] = digits[val % 10];
1262 	} while ((val /= 10) > 0);
1263 
1264 	if (sign) {
1265 		if (i+1 > size) {
1266 			goto trunc;
1267 		}
1268 		s[i++] = '-';
1269 	}
1270 	if (i+1 > size) {
1271 		goto trunc;
1272 	}
1273 	s[i] = '\0';
1274 	AG_StrReverse(&s[iStart]);
1275 	return (i);
1276 trunc:
1277 	s[size-1] = '\0';
1278 	AG_StrReverse(&s[iStart]);
1279 	return (i+1);
1280 }
1281 
1282 /*
1283  * Format an unsigned int (base 10) into a fixed-size buffer (concatenate).
1284  * XXX on truncation, this returns the size of the buffer + 1 byte
1285  * (we should return the total length required instead).
1286  */
1287 size_t
AG_StrlcatUint(char * s,Uint val,size_t size)1288 AG_StrlcatUint(char *s, Uint val, size_t size)
1289 {
1290 	static const char *digits = "0123456789";
1291 	int i = strlen(s);
1292 	int iStart = i;
1293 
1294 	if (size < 1) {
1295 		return (1);
1296 	}
1297 	do {
1298 		if (i+1 > size) {
1299 			goto trunc;
1300 		}
1301 		s[i++] = digits[val % 10];
1302 	} while ((val /= 10) > 0);
1303 
1304 	if (i+1 > size) {
1305 		goto trunc;
1306 	}
1307 	s[i] = '\0';
1308 	AG_StrReverse(&s[iStart]);
1309 	return (i);
1310 trunc:
1311 	s[size-1] = '\0';
1312 	AG_StrReverse(&s[iStart]);
1313 	return (i+1);
1314 }
1315 
1316 #ifdef AG_THREADS
DestroyPrintBuffer(void * buf)1317 static void DestroyPrintBuffer(void *buf) { Free(buf); }
1318 #endif /* AG_THREADS */
1319 
1320 int
AG_InitStringSubsystem(void)1321 AG_InitStringSubsystem(void)
1322 {
1323 	Uint i;
1324 
1325 	/* Initialize the AG_Printf() buffers. */
1326 	for (i = 0; i < AG_STRING_BUFFERS_MAX; i++) {
1327 		agPrintBuf[i] = NULL;
1328 #ifdef AG_THREADS
1329 		if (AG_ThreadKeyTryCreate(&agPrintBufKey[i], DestroyPrintBuffer) == -1) {
1330 			return (-1);
1331 		}
1332 		AG_ThreadKeySet(agPrintBufKey[i], NULL);
1333 #endif
1334 	}
1335 
1336 	/* Initialize the formatting engine extensions. */
1337 	AG_MutexInit(&agFmtExtensionsLock);
1338 	AG_RegisterFmtStringExt("u8", PrintU8);
1339 	AG_RegisterFmtStringExt("s8", PrintS8);
1340 	AG_RegisterFmtStringExt("u16", PrintU16);
1341 	AG_RegisterFmtStringExt("s16", PrintS16);
1342 	AG_RegisterFmtStringExt("u32", PrintU32);
1343 	AG_RegisterFmtStringExt("s32", PrintS32);
1344 #ifdef HAVE_64BIT
1345 	AG_RegisterFmtStringExt("u64", PrintU64);
1346 	AG_RegisterFmtStringExt("s64", PrintS64);
1347 #endif
1348 	AG_RegisterFmtStringExt("objName", PrintOBJNAME);
1349 	AG_RegisterFmtStringExt("objType", PrintOBJTYPE);
1350 
1351 	return (0);
1352 }
1353 
1354 void
AG_DestroyStringSubsystem(void)1355 AG_DestroyStringSubsystem(void)
1356 {
1357 	Uint i;
1358 
1359 	/* Free the AG_Printf() buffers. */
1360 	for (i = 0; i < AG_STRING_BUFFERS_MAX; i++) {
1361 #ifdef AG_THREADS
1362 		if ((agPrintBuf[i] = (char *)AG_ThreadKeyGet(agPrintBufKey[i]))
1363 		    != NULL) {
1364 			free(agPrintBuf[i]);
1365 		}
1366 		AG_ThreadKeyDelete(agPrintBufKey[i]);
1367 #else
1368 		Free(agPrintBuf[i]);
1369 #endif
1370 		agPrintBuf[i] = NULL;
1371 	}
1372 
1373 	/* Free the formatting engine extensions. */
1374 	for (i = 0; i < agFmtExtensionCount; i++) {
1375 		Free(agFmtExtensions[i].fmt);
1376 	}
1377 	Free(agFmtExtensions);
1378 	agFmtExtensions = NULL;
1379 	agFmtExtensionCount = 0;
1380 
1381 	AG_MutexDestroy(&agFmtExtensionsLock);
1382 }
1383