@@ -295,16 +295,25 @@ int fseek(FILE *stream, long offset, int whence)
295295 * - %[l*]{d,u,c,x,p}
296296 * - %s
297297 * - unknown modifiers are ignored.
298+ *
299+ * Called by vfprintf() and snprintf() to do the actual formatting.
300+ * The callers provide a callback function to save the formatted data.
301+ * The callback function is called multiple times:
302+ * - for each group of literal characters in the format string.
303+ * - for field padding.
304+ * - for each conversion specifier.
305+ * - with (NULL, 0) at the end of the __nolibc_printf.
306+ * If the callback returns non-zero __nolibc_printf() immediately returns -1.
298307 */
299- typedef int (* __nolibc_printf_cb )(intptr_t state , const char * buf , size_t size );
308+ typedef int (* __nolibc_printf_cb )(void * state , const char * buf , size_t size );
300309
301- static __attribute__((unused , format (printf , 4 , 0 )))
302- int __nolibc_printf (__nolibc_printf_cb cb , intptr_t state , size_t n , const char * fmt , va_list args )
310+ static __attribute__((unused , format (printf , 3 , 0 )))
311+ int __nolibc_printf (__nolibc_printf_cb cb , void * state , const char * fmt , va_list args )
303312{
304313 char escape , lpref , ch ;
305314 unsigned long long v ;
306315 unsigned int written , width ;
307- size_t len , ofs , w ;
316+ size_t len , ofs ;
308317 char outbuf [21 ];
309318 const char * outstr ;
310319
@@ -406,17 +415,13 @@ int __nolibc_printf(__nolibc_printf_cb cb, intptr_t state, size_t n, const char
406415 outstr = fmt ;
407416 len = ofs - 1 ;
408417 flush_str :
409- if (n ) {
410- w = len < n ? len : n ;
411- n -= w ;
412- while (width -- > w ) {
413- if (cb (state , " " , 1 ) != 0 )
414- return -1 ;
415- written += 1 ;
416- }
417- if (cb (state , outstr , w ) != 0 )
418+ while (width -- > len ) {
419+ if (cb (state , " " , 1 ) != 0 )
418420 return -1 ;
421+ written += 1 ;
419422 }
423+ if (cb (state , outstr , len ) != 0 )
424+ return -1 ;
420425
421426 written += len ;
422427 do_escape :
@@ -429,18 +434,25 @@ int __nolibc_printf(__nolibc_printf_cb cb, intptr_t state, size_t n, const char
429434
430435 /* literal char, just queue it */
431436 }
437+
438+ /* Request a final '\0' be added to the snprintf() output.
439+ * This may be the only call of the cb() function.
440+ */
441+ if (cb (state , NULL , 0 ) != 0 )
442+ return -1 ;
443+
432444 return written ;
433445}
434446
435- static int __nolibc_fprintf_cb (intptr_t state , const char * buf , size_t size )
447+ static int __nolibc_fprintf_cb (void * stream , const char * buf , size_t size )
436448{
437- return _fwrite (buf , size , ( FILE * ) state );
449+ return _fwrite (buf , size , stream );
438450}
439451
440452static __attribute__((unused , format (printf , 2 , 0 )))
441453int vfprintf (FILE * stream , const char * fmt , va_list args )
442454{
443- return __nolibc_printf (__nolibc_fprintf_cb , ( intptr_t ) stream , SIZE_MAX , fmt , args );
455+ return __nolibc_printf (__nolibc_fprintf_cb , stream , fmt , args );
444456}
445457
446458static __attribute__((unused , format (printf , 1 , 0 )))
@@ -498,26 +510,54 @@ int dprintf(int fd, const char *fmt, ...)
498510 return ret ;
499511}
500512
501- static int __nolibc_sprintf_cb (intptr_t _state , const char * buf , size_t size )
513+ struct __nolibc_sprintf_cb_state {
514+ char * buf ;
515+ size_t space ;
516+ };
517+
518+ static int __nolibc_sprintf_cb (void * v_state , const char * buf , size_t size )
502519{
503- char * * state = (char * * )_state ;
520+ struct __nolibc_sprintf_cb_state * state = v_state ;
521+ size_t space = state -> space ;
522+ char * tgt ;
523+
524+ /* Truncate the request to fit in the output buffer space.
525+ * The last byte is reserved for the terminating '\0'.
526+ * state->space can only be zero for snprintf(NULL, 0, fmt, args)
527+ * so this normally lets through calls with 'size == 0'.
528+ */
529+ if (size >= space ) {
530+ if (space <= 1 )
531+ return 0 ;
532+ size = space - 1 ;
533+ }
534+ tgt = state -> buf ;
535+
536+ /* __nolibc_printf() ends with cb(state, NULL, 0) to request the output
537+ * buffer be '\0' terminated.
538+ * That will be the only cb() call for, eg, snprintf(buf, sz, "").
539+ * Zero lengths can occur at other times (eg "%s" for an empty string).
540+ * Unconditionally write the '\0' byte to reduce code size, it is
541+ * normally overwritten by the data being output.
542+ * There is no point adding a '\0' after copied data - there is always
543+ * another call.
544+ */
545+ * tgt = '\0' ;
546+ if (size ) {
547+ state -> space = space - size ;
548+ state -> buf = tgt + size ;
549+ memcpy (tgt , buf , size );
550+ }
504551
505- memcpy (* state , buf , size );
506- * state += size ;
507552 return 0 ;
508553}
509554
510555static __attribute__((unused , format (printf , 3 , 0 )))
511556int vsnprintf (char * buf , size_t size , const char * fmt , va_list args )
512557{
513- char * state = buf ;
514- int ret ;
558+ struct __nolibc_sprintf_cb_state state = { .buf = buf , .space = size };
515559
516- ret = __nolibc_printf (__nolibc_sprintf_cb , (intptr_t )& state , size , fmt , args );
517- if (ret < 0 )
518- return ret ;
519- buf [(size_t )ret < size ? (size_t )ret : size - 1 ] = '\0' ;
520- return ret ;
560+ return __nolibc_printf (__nolibc_sprintf_cb , & state , fmt , args );
521561}
522562
523563static __attribute__((unused , format (printf , 3 , 4 )))
0 commit comments