@@ -244,16 +244,25 @@ char *fgets(char *s, int size, FILE *stream)
244244 * - %[l*]{d,u,c,x,p}
245245 * - %s
246246 * - unknown modifiers are ignored.
247+ *
248+ * Called by vfprintf() and snprintf() to do the actual formatting.
249+ * The callers provide a callback function to save the formatted data.
250+ * The callback function is called multiple times:
251+ * - for each group of literal characters in the format string.
252+ * - for field padding.
253+ * - for each conversion specifier.
254+ * - with (NULL, 0) at the end of the __nolibc_printf.
255+ * If the callback returns non-zero __nolibc_printf() immediately returns -1.
247256 */
248- typedef int (* __nolibc_printf_cb )(intptr_t state , const char * buf , size_t size );
257+ typedef int (* __nolibc_printf_cb )(void * state , const char * buf , size_t size );
249258
250- static __attribute__((unused , format (printf , 4 , 0 )))
251- int __nolibc_printf (__nolibc_printf_cb cb , intptr_t state , size_t n , const char * fmt , va_list args )
259+ static __attribute__((unused , format (printf , 3 , 0 )))
260+ int __nolibc_printf (__nolibc_printf_cb cb , void * state , const char * fmt , va_list args )
252261{
253262 char escape , lpref , ch ;
254263 unsigned long long v ;
255264 unsigned int written , width ;
256- size_t len , ofs , w ;
265+ size_t len , ofs ;
257266 char outbuf [21 ];
258267 const char * outstr ;
259268
@@ -355,17 +364,13 @@ int __nolibc_printf(__nolibc_printf_cb cb, intptr_t state, size_t n, const char
355364 outstr = fmt ;
356365 len = ofs - 1 ;
357366 flush_str :
358- if (n ) {
359- w = len < n ? len : n ;
360- n -= w ;
361- while (width -- > w ) {
362- if (cb (state , " " , 1 ) != 0 )
363- return -1 ;
364- written += 1 ;
365- }
366- if (cb (state , outstr , w ) != 0 )
367+ while (width -- > len ) {
368+ if (cb (state , " " , 1 ) != 0 )
367369 return -1 ;
370+ written += 1 ;
368371 }
372+ if (cb (state , outstr , len ) != 0 )
373+ return -1 ;
369374
370375 written += len ;
371376 do_escape :
@@ -378,18 +383,25 @@ int __nolibc_printf(__nolibc_printf_cb cb, intptr_t state, size_t n, const char
378383
379384 /* literal char, just queue it */
380385 }
386+
387+ /* Request a final '\0' be added to the snprintf() output.
388+ * This may be the only call of the cb() function.
389+ */
390+ if (cb (state , NULL , 0 ) != 0 )
391+ return -1 ;
392+
381393 return written ;
382394}
383395
384- static int __nolibc_fprintf_cb (intptr_t state , const char * buf , size_t size )
396+ static int __nolibc_fprintf_cb (void * stream , const char * buf , size_t size )
385397{
386- return _fwrite (buf , size , ( FILE * ) state );
398+ return _fwrite (buf , size , stream );
387399}
388400
389401static __attribute__((unused , format (printf , 2 , 0 )))
390402int vfprintf (FILE * stream , const char * fmt , va_list args )
391403{
392- return __nolibc_printf (__nolibc_fprintf_cb , ( intptr_t ) stream , SIZE_MAX , fmt , args );
404+ return __nolibc_printf (__nolibc_fprintf_cb , stream , fmt , args );
393405}
394406
395407static __attribute__((unused , format (printf , 1 , 0 )))
@@ -447,26 +459,54 @@ int dprintf(int fd, const char *fmt, ...)
447459 return ret ;
448460}
449461
450- static int __nolibc_sprintf_cb (intptr_t _state , const char * buf , size_t size )
462+ struct __nolibc_sprintf_cb_state {
463+ char * buf ;
464+ size_t space ;
465+ };
466+
467+ static int __nolibc_sprintf_cb (void * v_state , const char * buf , size_t size )
451468{
452- char * * state = (char * * )_state ;
469+ struct __nolibc_sprintf_cb_state * state = v_state ;
470+ size_t space = state -> space ;
471+ char * tgt ;
472+
473+ /* Truncate the request to fit in the output buffer space.
474+ * The last byte is reserved for the terminating '\0'.
475+ * state->space can only be zero for snprintf(NULL, 0, fmt, args)
476+ * so this normally lets through calls with 'size == 0'.
477+ */
478+ if (size >= space ) {
479+ if (space <= 1 )
480+ return 0 ;
481+ size = space - 1 ;
482+ }
483+ tgt = state -> buf ;
484+
485+ /* __nolibc_printf() ends with cb(state, NULL, 0) to request the output
486+ * buffer be '\0' terminated.
487+ * That will be the only cb() call for, eg, snprintf(buf, sz, "").
488+ * Zero lengths can occur at other times (eg "%s" for an empty string).
489+ * Unconditionally write the '\0' byte to reduce code size, it is
490+ * normally overwritten by the data being output.
491+ * There is no point adding a '\0' after copied data - there is always
492+ * another call.
493+ */
494+ * tgt = '\0' ;
495+ if (size ) {
496+ state -> space = space - size ;
497+ state -> buf = tgt + size ;
498+ memcpy (tgt , buf , size );
499+ }
453500
454- memcpy (* state , buf , size );
455- * state += size ;
456501 return 0 ;
457502}
458503
459504static __attribute__((unused , format (printf , 3 , 0 )))
460505int vsnprintf (char * buf , size_t size , const char * fmt , va_list args )
461506{
462- char * state = buf ;
463- int ret ;
507+ struct __nolibc_sprintf_cb_state state = { .buf = buf , .space = size };
464508
465- ret = __nolibc_printf (__nolibc_sprintf_cb , (intptr_t )& state , size , fmt , args );
466- if (ret < 0 )
467- return ret ;
468- buf [(size_t )ret < size ? (size_t )ret : size - 1 ] = '\0' ;
469- return ret ;
509+ return __nolibc_printf (__nolibc_sprintf_cb , & state , fmt , args );
470510}
471511
472512static __attribute__((unused , format (printf , 3 , 4 )))
0 commit comments