@@ -231,7 +231,20 @@ int get_flags(const char **fmt)
231
231
} while (1 );
232
232
}
233
233
234
- int vsprintf (char * buf , const char * fmt , va_list args )
234
+ static
235
+ int get_int (const char * * fmt , va_list * ap )
236
+ {
237
+ if (isdigit (* * fmt ))
238
+ return skip_atoi (fmt );
239
+ if (* * fmt == '*' ) {
240
+ ++ (* fmt );
241
+ /* it's the next argument */
242
+ return va_arg (* ap , int );
243
+ }
244
+ return 0 ;
245
+ }
246
+
247
+ int vsprintf (char * buf , const char * fmt , va_list ap )
235
248
{
236
249
int len ;
237
250
unsigned long long num ;
@@ -246,6 +259,24 @@ int vsprintf(char *buf, const char *fmt, va_list args)
246
259
number of chars for from string */
247
260
int qualifier ; /* 'h', 'hh', 'l' or 'll' for integer fields */
248
261
262
+ va_list args ;
263
+
264
+ /*
265
+ * We want to pass our input va_list to helper functions by reference,
266
+ * but there's an annoying edge case. If va_list was originally passed
267
+ * to us by value, we could just pass &ap down to the helpers. This is
268
+ * the case on, for example, X86_32.
269
+ * However, on X86_64 (and possibly others), va_list is actually a
270
+ * size-1 array containing a structure. Our function parameter ap has
271
+ * decayed from T[1] to T*, and &ap has type T** rather than T(*)[1],
272
+ * which is what will be expected by a function taking a va_list *
273
+ * parameter.
274
+ * One standard way to solve this mess is by creating a copy in a local
275
+ * variable of type va_list and then passing a pointer to that local
276
+ * copy instead, which is what we do here.
277
+ */
278
+ va_copy (args , ap );
279
+
249
280
for (str = buf ; * fmt ; ++ fmt ) {
250
281
if (* fmt != '%' || * ++ fmt == '%' ) {
251
282
* str ++ = * fmt ;
@@ -256,32 +287,17 @@ int vsprintf(char *buf, const char *fmt, va_list args)
256
287
flags = get_flags (& fmt );
257
288
258
289
/* get field width */
259
- field_width = -1 ;
260
- if (isdigit (* fmt )) {
261
- field_width = skip_atoi (& fmt );
262
- } else if (* fmt == '*' ) {
263
- ++ fmt ;
264
- /* it's the next argument */
265
- field_width = va_arg (args , int );
266
- if (field_width < 0 ) {
267
- field_width = - field_width ;
268
- flags |= LEFT ;
269
- }
290
+ field_width = get_int (& fmt , & args );
291
+ if (field_width < 0 ) {
292
+ field_width = - field_width ;
293
+ flags |= LEFT ;
270
294
}
271
295
272
296
/* get the precision */
273
297
precision = -1 ;
274
298
if (* fmt == '.' ) {
275
299
++ fmt ;
276
- if (isdigit (* fmt )) {
277
- precision = skip_atoi (& fmt );
278
- } else if (* fmt == '*' ) {
279
- ++ fmt ;
280
- /* it's the next argument */
281
- precision = va_arg (args , int );
282
- } else {
283
- precision = 0 ;
284
- }
300
+ precision = get_int (& fmt , & args );
285
301
if (precision >= 0 )
286
302
flags &= ~ZEROPAD ;
287
303
}
@@ -392,6 +408,9 @@ int vsprintf(char *buf, const char *fmt, va_list args)
392
408
str = number (str , num , base , field_width , precision , flags );
393
409
}
394
410
* str = '\0' ;
411
+
412
+ va_end (args );
413
+
395
414
return str - buf ;
396
415
}
397
416
0 commit comments