2006-01-14 07:04:06 +03:00
/*
* Copyright ( C ) Paul Mackerras 1997.
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*/
# include <stdarg.h>
# include <stddef.h>
# include "string.h"
# include "stdio.h"
2006-09-19 08:05:08 +04:00
# include "ops.h"
2006-01-14 07:04:06 +03:00
size_t strnlen ( const char * s , size_t count )
{
const char * sc ;
for ( sc = s ; count - - & & * sc ! = ' \0 ' ; + + sc )
/* nothing */ ;
return sc - s ;
}
2018-03-01 17:57:40 +03:00
char * strrchr ( const char * s , int c )
{
const char * last = NULL ;
do {
if ( * s = = ( char ) c )
last = s ;
} while ( * s + + ) ;
return ( char * ) last ;
}
2014-04-24 11:23:25 +04:00
# ifdef __powerpc64__
# define do_div(n, base) ({ \
unsigned int __base = ( base ) ; \
unsigned int __rem ; \
__rem = ( ( unsigned long long ) ( n ) ) % __base ; \
( n ) = ( ( unsigned long long ) ( n ) ) / __base ; \
__rem ; \
} )
# else
2006-01-14 07:04:06 +03:00
extern unsigned int __div64_32 ( unsigned long long * dividend ,
unsigned int divisor ) ;
/* The unnecessary pointer compare is there
* to check for type safety ( n must be 64 bit )
*/
# define do_div(n,base) ({ \
unsigned int __base = ( base ) ; \
unsigned int __rem ; \
( void ) ( ( ( typeof ( ( n ) ) * ) 0 ) = = ( ( unsigned long long * ) 0 ) ) ; \
if ( ( ( n ) > > 32 ) = = 0 ) { \
__rem = ( unsigned int ) ( n ) % __base ; \
( n ) = ( unsigned int ) ( n ) / __base ; \
} else \
__rem = __div64_32 ( & ( n ) , __base ) ; \
__rem ; \
} )
2014-04-24 11:23:25 +04:00
# endif /* __powerpc64__ */
2006-01-14 07:04:06 +03:00
static int skip_atoi ( const char * * s )
{
int i , c ;
for ( i = 0 ; ' 0 ' < = ( c = * * s ) & & c < = ' 9 ' ; + + * s )
i = i * 10 + c - ' 0 ' ;
return i ;
}
# define ZEROPAD 1 /* pad with zero */
# define SIGN 2 /* unsigned/signed long */
# define PLUS 4 /* show plus */
# define SPACE 8 /* space if plus */
# define LEFT 16 /* left justified */
# define SPECIAL 32 /* 0x */
# define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */
static char * number ( char * str , unsigned long long num , int base , int size , int precision , int type )
{
char c , sign , tmp [ 66 ] ;
const char * digits = " 0123456789abcdefghijklmnopqrstuvwxyz " ;
int i ;
if ( type & LARGE )
digits = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ " ;
if ( type & LEFT )
type & = ~ ZEROPAD ;
if ( base < 2 | | base > 36 )
return 0 ;
c = ( type & ZEROPAD ) ? ' 0 ' : ' ' ;
sign = 0 ;
if ( type & SIGN ) {
if ( ( signed long long ) num < 0 ) {
sign = ' - ' ;
num = - ( signed long long ) num ;
size - - ;
} else if ( type & PLUS ) {
sign = ' + ' ;
size - - ;
} else if ( type & SPACE ) {
sign = ' ' ;
size - - ;
}
}
if ( type & SPECIAL ) {
if ( base = = 16 )
size - = 2 ;
else if ( base = = 8 )
size - - ;
}
i = 0 ;
if ( num = = 0 )
tmp [ i + + ] = ' 0 ' ;
else while ( num ! = 0 ) {
tmp [ i + + ] = digits [ do_div ( num , base ) ] ;
}
if ( i > precision )
precision = i ;
size - = precision ;
if ( ! ( type & ( ZEROPAD + LEFT ) ) )
while ( size - - > 0 )
* str + + = ' ' ;
if ( sign )
* str + + = sign ;
if ( type & SPECIAL ) {
if ( base = = 8 )
* str + + = ' 0 ' ;
else if ( base = = 16 ) {
* str + + = ' 0 ' ;
* str + + = digits [ 33 ] ;
}
}
if ( ! ( type & LEFT ) )
while ( size - - > 0 )
* str + + = c ;
while ( i < precision - - )
* str + + = ' 0 ' ;
while ( i - - > 0 )
* str + + = tmp [ i ] ;
while ( size - - > 0 )
* str + + = ' ' ;
return str ;
}
int vsprintf ( char * buf , const char * fmt , va_list args )
{
int len ;
unsigned long long num ;
int i , base ;
char * str ;
const char * s ;
int flags ; /* flags to number() */
int field_width ; /* width of output field */
int precision ; /* min. # of digits for integers; max
number of chars for from string */
int qualifier ; /* 'h', 'l', or 'L' for integer fields */
/* 'z' support added 23/7/1999 S.H. */
/* 'z' changed to 'Z' --davidm 1/25/99 */
for ( str = buf ; * fmt ; + + fmt ) {
if ( * fmt ! = ' % ' ) {
* str + + = * fmt ;
continue ;
}
/* process flags */
flags = 0 ;
repeat :
+ + fmt ; /* this also skips first '%' */
switch ( * fmt ) {
case ' - ' : flags | = LEFT ; goto repeat ;
case ' + ' : flags | = PLUS ; goto repeat ;
case ' ' : flags | = SPACE ; goto repeat ;
case ' # ' : flags | = SPECIAL ; goto repeat ;
case ' 0 ' : flags | = ZEROPAD ; goto repeat ;
}
/* get field width */
field_width = - 1 ;
if ( ' 0 ' < = * fmt & & * fmt < = ' 9 ' )
field_width = skip_atoi ( & fmt ) ;
else if ( * fmt = = ' * ' ) {
+ + fmt ;
/* it's the next argument */
field_width = va_arg ( args , int ) ;
if ( field_width < 0 ) {
field_width = - field_width ;
flags | = LEFT ;
}
}
/* get the precision */
precision = - 1 ;
if ( * fmt = = ' . ' ) {
+ + fmt ;
if ( ' 0 ' < = * fmt & & * fmt < = ' 9 ' )
precision = skip_atoi ( & fmt ) ;
else if ( * fmt = = ' * ' ) {
+ + fmt ;
/* it's the next argument */
precision = va_arg ( args , int ) ;
}
if ( precision < 0 )
precision = 0 ;
}
/* get the conversion qualifier */
qualifier = - 1 ;
2007-06-16 02:06:36 +04:00
if ( * fmt = = ' l ' & & * ( fmt + 1 ) = = ' l ' ) {
qualifier = ' q ' ;
fmt + = 2 ;
} else if ( * fmt = = ' h ' | | * fmt = = ' l ' | | * fmt = = ' L '
| | * fmt = = ' Z ' ) {
2006-01-14 07:04:06 +03:00
qualifier = * fmt ;
+ + fmt ;
}
/* default base */
base = 10 ;
switch ( * fmt ) {
case ' c ' :
if ( ! ( flags & LEFT ) )
while ( - - field_width > 0 )
* str + + = ' ' ;
* str + + = ( unsigned char ) va_arg ( args , int ) ;
while ( - - field_width > 0 )
* str + + = ' ' ;
continue ;
case ' s ' :
s = va_arg ( args , char * ) ;
if ( ! s )
s = " <NULL> " ;
len = strnlen ( s , precision ) ;
if ( ! ( flags & LEFT ) )
while ( len < field_width - - )
* str + + = ' ' ;
for ( i = 0 ; i < len ; + + i )
* str + + = * s + + ;
while ( len < field_width - - )
* str + + = ' ' ;
continue ;
case ' p ' :
if ( field_width = = - 1 ) {
field_width = 2 * sizeof ( void * ) ;
flags | = ZEROPAD ;
}
str = number ( str ,
( unsigned long ) va_arg ( args , void * ) , 16 ,
field_width , precision , flags ) ;
continue ;
case ' n ' :
if ( qualifier = = ' l ' ) {
long * ip = va_arg ( args , long * ) ;
* ip = ( str - buf ) ;
} else if ( qualifier = = ' Z ' ) {
size_t * ip = va_arg ( args , size_t * ) ;
* ip = ( str - buf ) ;
} else {
int * ip = va_arg ( args , int * ) ;
* ip = ( str - buf ) ;
}
continue ;
case ' % ' :
* str + + = ' % ' ;
continue ;
/* integer number formats - set up the flags and "break" */
case ' o ' :
base = 8 ;
break ;
case ' X ' :
flags | = LARGE ;
case ' x ' :
base = 16 ;
break ;
case ' d ' :
case ' i ' :
flags | = SIGN ;
case ' u ' :
break ;
default :
* str + + = ' % ' ;
if ( * fmt )
* str + + = * fmt ;
else
- - fmt ;
continue ;
}
if ( qualifier = = ' l ' ) {
num = va_arg ( args , unsigned long ) ;
if ( flags & SIGN )
num = ( signed long ) num ;
2007-06-16 02:06:36 +04:00
} else if ( qualifier = = ' q ' ) {
num = va_arg ( args , unsigned long long ) ;
if ( flags & SIGN )
num = ( signed long long ) num ;
2006-01-14 07:04:06 +03:00
} else if ( qualifier = = ' Z ' ) {
num = va_arg ( args , size_t ) ;
} else if ( qualifier = = ' h ' ) {
num = ( unsigned short ) va_arg ( args , int ) ;
if ( flags & SIGN )
num = ( signed short ) num ;
} else {
num = va_arg ( args , unsigned int ) ;
if ( flags & SIGN )
num = ( signed int ) num ;
}
str = number ( str , num , base , field_width , precision , flags ) ;
}
* str = ' \0 ' ;
return str - buf ;
}
int sprintf ( char * buf , const char * fmt , . . . )
{
va_list args ;
int i ;
va_start ( args , fmt ) ;
i = vsprintf ( buf , fmt , args ) ;
va_end ( args ) ;
return i ;
}
static char sprint_buf [ 1024 ] ;
int
printf ( const char * fmt , . . . )
{
va_list args ;
int n ;
va_start ( args , fmt ) ;
n = vsprintf ( sprint_buf , fmt , args ) ;
va_end ( args ) ;
2006-10-17 00:49:27 +04:00
if ( console_ops . write )
console_ops . write ( sprint_buf , n ) ;
2006-01-14 07:04:06 +03:00
return n ;
}