1 /*
2 * Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana
3 * University Research and Technology
4 * Corporation. All rights reserved.
5 * Copyright (c) 2004-2013 The University of Tennessee and The University
6 * of Tennessee Research Foundation. All rights
7 * reserved.
8 * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart,
9 * University of Stuttgart. All rights reserved.
10 * Copyright (c) 2004-2005 The Regents of the University of California.
11 * All rights reserved.
12 * Copyright (c) 2007 Cisco Systems, Inc. All rights reserved.
13 * Copyright (c) 2015 Intel, Inc. All rights reserved.
14 * $COPYRIGHT$
15 *
16 * Additional copyrights may follow
17 *
18 * $HEADER$
19 */
20
21 /*
22 * Buffer safe printf functions for portability to archaic platforms.
23 */
24
25 #include <src/include/pmix_config.h>
26
27
28 #include <errno.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include "src/util/printf.h"
34 #include "src/util/output.h"
35
36 /*
37 * Make a good guess about how long a printf-style varargs formatted
38 * string will be once all the % escapes are filled in. We don't
39 * handle every % escape here, but we handle enough, and then add a
40 * fudge factor in at the end.
41 */
42 static int guess_strlen(const char *fmt, va_list ap)
43 {
44 char *sarg, carg;
45 double darg;
46 float farg;
47 size_t i;
48 int iarg;
49 int len;
50 long larg;
51
52 /* Start off with a fudge factor of 128 to handle the % escapes that
53 we aren't calculating here */
54
55 len = (int)strlen(fmt) + 128;
56 for (i = 0; i < strlen(fmt); ++i) {
57 if ('%' == fmt[i] && i + 1 < strlen(fmt)
58 && '%' != fmt[i + 1]) {
59 ++i;
60 switch (fmt[i]) {
61 case 'c':
62 carg = va_arg(ap, int);
63 len += 1; /* let's suppose it's a printable char */
64 (void)carg; /* prevent compiler from complaining about set but not used variables */
65 break;
66 case 's':
67 sarg = va_arg(ap, char *);
68
69 /* If there's an arg, get the strlen, otherwise we'll
70 * use (null) */
71
72 if (NULL != sarg) {
73 len += (int)strlen(sarg);
74 } else {
75 #if PMIX_ENABLE_DEBUG
76 pmix_output(0, "PMIX DEBUG WARNING: Got a NULL argument to pmix_vasprintf!\n");
77 #endif
78 len += 5;
79 }
80 break;
81
82 case 'd':
83 case 'i':
84 iarg = va_arg(ap, int);
85 /* Alloc for minus sign */
86 if (iarg < 0)
87 ++len;
88 /* Now get the log10 */
89 do {
90 ++len;
91 iarg /= 10;
92 } while (0 != iarg);
93 break;
94
95 case 'x':
96 case 'X':
97 iarg = va_arg(ap, int);
98 /* Now get the log16 */
99 do {
100 ++len;
101 iarg /= 16;
102 } while (0 != iarg);
103 break;
104
105 case 'f':
106 farg = (float)va_arg(ap, int);
107 /* Alloc for minus sign */
108 if (farg < 0) {
109 ++len;
110 farg = -farg;
111 }
112 /* Alloc for 3 decimal places + '.' */
113 len += 4;
114 /* Now get the log10 */
115 do {
116 ++len;
117 farg /= 10.0;
118 } while (0 != farg);
119 break;
120
121 case 'g':
122 darg = va_arg(ap, int);
123 /* Alloc for minus sign */
124 if (darg < 0) {
125 ++len;
126 darg = -darg;
127 }
128 /* Alloc for 3 decimal places + '.' */
129 len += 4;
130 /* Now get the log10 */
131 do {
132 ++len;
133 darg /= 10.0;
134 } while (0 != darg);
135 break;
136
137 case 'l':
138 /* Get %ld %lx %lX %lf */
139 if (i + 1 < strlen(fmt)) {
140 ++i;
141 switch (fmt[i]) {
142 case 'x':
143 case 'X':
144 larg = va_arg(ap, int);
145 /* Now get the log16 */
146 do {
147 ++len;
148 larg /= 16;
149 } while (0 != larg);
150 break;
151
152 case 'f':
153 darg = va_arg(ap, int);
154 /* Alloc for minus sign */
155 if (darg < 0) {
156 ++len;
157 darg = -darg;
158 }
159 /* Alloc for 3 decimal places + '.' */
160 len += 4;
161 /* Now get the log10 */
162 do {
163 ++len;
164 darg /= 10.0;
165 } while (0 != darg);
166 break;
167
168 case 'd':
169 default:
170 larg = va_arg(ap, int);
171 /* Now get the log10 */
172 do {
173 ++len;
174 larg /= 10;
175 } while (0 != larg);
176 break;
177 }
178 }
179
180 default:
181 break;
182 }
183 }
184 }
185
186 return len;
187 }
188
189
190 int pmix_asprintf(char **ptr, const char *fmt, ...)
191 {
192 int length;
193 va_list ap;
194
195 va_start(ap, fmt);
196 length = pmix_vasprintf(ptr, fmt, ap);
197 va_end(ap);
198
199 return length;
200 }
201
202
203 int pmix_vasprintf(char **ptr, const char *fmt, va_list ap)
204 {
205 int length;
206 va_list ap2;
207
208 /* va_list might have pointer to internal state and using
209 it twice is a bad idea. So make a copy for the second
210 use. Copy order taken from Autoconf docs. */
211 #if PMIX_HAVE_VA_COPY
212 va_copy(ap2, ap);
213 #elif PMIX_HAVE_UNDERSCORE_VA_COPY
214 __va_copy(ap2, ap);
215 #else
216 memcpy (&ap2, &ap, sizeof(va_list));
217 #endif
218
219 /* guess the size */
220 length = guess_strlen(fmt, ap);
221
222 /* allocate a buffer */
223 *ptr = (char *) malloc((size_t) length + 1);
224 if (NULL == *ptr) {
225 errno = ENOMEM;
226 va_end(ap2);
227 return -1;
228 }
229
230 /* fill the buffer */
231 length = vsprintf(*ptr, fmt, ap2);
232 #if PMIX_HAVE_VA_COPY || PMIX_HAVE_UNDERSCORE_VA_COPY
233 va_end(ap2);
234 #endif /* PMIX_HAVE_VA_COPY || PMIX_HAVE_UNDERSCORE_VA_COPY */
235
236 /* realloc */
237 *ptr = (char*) realloc(*ptr, (size_t) length + 1);
238 if (NULL == *ptr) {
239 errno = ENOMEM;
240 return -1;
241 }
242
243 return length;
244 }
245
246
247 int pmix_snprintf(char *str, size_t size, const char *fmt, ...)
248 {
249 int length;
250 va_list ap;
251
252 va_start(ap, fmt);
253 length = pmix_vsnprintf(str, size, fmt, ap);
254 va_end(ap);
255
256 return length;
257 }
258
259
260 int pmix_vsnprintf(char *str, size_t size, const char *fmt, va_list ap)
261 {
262 int length;
263 char *buf;
264
265 length = pmix_vasprintf(&buf, fmt, ap);
266 if (length < 0) {
267 return length;
268 }
269
270 /* return the length when given a null buffer (C99) */
271 if (str) {
272 if ((size_t) length < size) {
273 strcpy(str, buf);
274 } else {
275 memcpy(str, buf, size - 1);
276 str[size] = '\0';
277 }
278 }
279
280 /* free allocated buffer */
281 free(buf);
282
283 return length;
284 }
285
286
287 #ifdef TEST
288
289 int main(int argc, char *argv[])
290 {
291 char a[10];
292 char b[173];
293 char *s;
294 int length;
295
296 puts("test for NULL buffer in snprintf:");
297 length = pmix_snprintf(NULL, 0, "this is a string %d", 1004);
298 printf("length = %d\n", length);
299
300 puts("test of snprintf to an undersize buffer:");
301 length = pmix_snprintf(a, sizeof(a), "this is a string %d", 1004);
302 printf("string = %s\n", a);
303 printf("length = %d\n", length);
304 printf("strlen = %d\n", (int) strlen(a));
305
306 puts("test of snprintf to an oversize buffer:");
307 length = pmix_snprintf(b, sizeof(b), "this is a string %d", 1004);
308 printf("string = %s\n", b);
309 printf("length = %d\n", length);
310 printf("strlen = %d\n", (int) strlen(b));
311
312 puts("test of asprintf:");
313 length = pmix_asprintf(&s, "this is a string %d", 1004);
314 printf("string = %s\n", s);
315 printf("length = %d\n", length);
316 printf("strlen = %d\n", (int) strlen(s));
317
318 free(s);
319
320 return 0;
321 }
322
323 #endif