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