2013-01-22 17:07:28 +00:00
/* printf - format and print data
Copyright ( C ) 1990 - 2007 Free Software Foundation , Inc .
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 , or ( at your option )
any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software Foundation ,
Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA . */
/* Usage: printf format [argument...]
A front end to the printf function that lets it be used from the shell .
Backslash escapes :
\ " = double quote
\ \ = backslash
\ a = alert ( bell )
\ b = backspace
\ c = produce no further output
2013-07-16 20:25:42 +00:00
\ e = escape
2013-01-22 17:07:28 +00:00
\ f = form feed
\ n = new line
\ r = carriage return
\ t = horizontal tab
\ v = vertical tab
\ ooo = octal number ( ooo is 1 to 3 digits )
\ xhh = hexadecimal number ( hhh is 1 to 2 digits )
\ uhhhh = 16 - bit Unicode character ( hhhh is 4 digits )
\ Uhhhhhhhh = 32 - bit Unicode character ( hhhhhhhh is 8 digits )
Additional directive :
% b = print an argument string , interpreting backslash escapes ,
except that octal escapes are of the form \ 0 or \ 0 ooo .
The ` format ' argument is re - used as many times as necessary
to convert all of the given arguments .
David MacKenzie < djm @ gnu . ai . mit . edu > */
2013-03-16 18:50:02 +00:00
/* This file has been imported from source code of printf command in GNU Coreutils version 6.9 */
2013-01-22 17:07:28 +00:00
# include <stdio.h>
# include <sys/types.h>
2013-03-16 18:50:02 +00:00
# include <inttypes.h>
2013-01-22 17:07:28 +00:00
# include "common.h"
2013-03-22 00:44:51 +00:00
struct builtin_printf_state_t
{
2013-03-24 22:33:45 +00:00
/* The status of the operation */
2013-03-22 00:40:12 +00:00
int exit_code ;
2013-03-24 22:33:45 +00:00
2013-04-10 07:12:55 +00:00
/* Whether we should stop outputting. This gets set in the case of an error, and also with the \c escape. */
2013-03-24 22:24:29 +00:00
bool early_exit ;
builtin_printf_state_t ( ) : exit_code ( 0 ) , early_exit ( false )
{
}
2013-03-22 00:44:51 +00:00
2013-03-24 22:51:18 +00:00
void verify_numeric ( const wchar_t * s , const wchar_t * end , int errcode ) ;
2013-03-22 00:44:51 +00:00
2013-03-22 00:40:12 +00:00
void print_direc ( const wchar_t * start , size_t length , wchar_t conversion ,
2013-03-22 00:44:51 +00:00
bool have_field_width , int field_width ,
bool have_precision , int precision ,
wchar_t const * argument ) ;
2013-03-22 00:40:12 +00:00
int print_formatted ( const wchar_t * format , int argc , wchar_t * * argv ) ;
2013-03-24 22:24:29 +00:00
void fatal_error ( const wchar_t * format , . . . ) ;
long print_esc ( const wchar_t * escstart , bool octal_0 ) ;
void print_esc_string ( const wchar_t * str ) ;
void print_esc_char ( wchar_t c ) ;
void append_output ( wchar_t c ) ;
void append_output ( const wchar_t * c ) ;
void append_format_output ( const wchar_t * fmt , . . . ) ;
2013-03-22 00:40:12 +00:00
} ;
2013-03-24 22:24:29 +00:00
static bool is_octal_digit ( wchar_t c )
{
return c ! = L ' \0 ' & & wcschr ( L " 01234567 " , c ) ! = NULL ;
}
static bool is_hex_digit ( wchar_t c )
2013-03-16 18:34:11 +00:00
{
2013-03-24 22:24:29 +00:00
return c ! = L ' \0 ' & & wcschr ( L " 0123456789ABCDEFabcdef " , c ) ! = NULL ;
2013-03-16 18:34:11 +00:00
}
2013-03-24 22:24:29 +00:00
static int hex_to_bin ( const wchar_t & c )
2013-03-16 18:34:11 +00:00
{
switch ( c )
{
2013-05-05 09:33:17 +00:00
case L ' 0 ' :
return 0 ;
case L ' 1 ' :
return 1 ;
case L ' 2 ' :
return 2 ;
case L ' 3 ' :
return 3 ;
case L ' 4 ' :
return 4 ;
case L ' 5 ' :
return 5 ;
case L ' 6 ' :
return 6 ;
case L ' 7 ' :
return 7 ;
case L ' 8 ' :
return 8 ;
case L ' 9 ' :
return 9 ;
case L ' a ' :
case L ' A ' :
return 10 ;
case L ' b ' :
case L ' B ' :
return 11 ;
case L ' c ' :
case L ' C ' :
return 12 ;
case L ' d ' :
case L ' D ' :
return 13 ;
case L ' e ' :
case L ' E ' :
return 14 ;
case L ' f ' :
case L ' F ' :
return 15 ;
default :
return - 1 ;
2013-03-22 00:44:51 +00:00
}
2013-03-16 18:34:11 +00:00
}
2013-01-22 17:07:28 +00:00
2013-03-24 22:24:29 +00:00
static int octal_to_bin ( wchar_t c )
2013-03-16 18:34:11 +00:00
{
switch ( c )
{
2013-05-05 09:33:17 +00:00
case L ' 0 ' :
return 0 ;
case L ' 1 ' :
return 1 ;
case L ' 2 ' :
return 2 ;
case L ' 3 ' :
return 3 ;
case L ' 4 ' :
return 4 ;
case L ' 5 ' :
return 5 ;
case L ' 6 ' :
return 6 ;
case L ' 7 ' :
return 7 ;
default :
return - 1 ;
2013-03-16 18:34:11 +00:00
}
}
2013-01-22 17:07:28 +00:00
/* This message appears in N_() here rather than just in _() below because
the sole use would have been in a # define . */
static wchar_t const * const cfcc_msg =
2013-03-22 00:44:51 +00:00
N_ ( L " warning: %ls: character(s) following character constant have been ignored " ) ;
2013-01-22 17:07:28 +00:00
2013-03-16 19:13:06 +00:00
double C_STRTOD ( wchar_t const * nptr , wchar_t * * endptr )
2013-01-22 17:07:28 +00:00
{
2013-03-03 09:46:18 +00:00
double r ;
2013-01-22 17:07:28 +00:00
2013-03-16 19:13:06 +00:00
const wcstring saved_locale = wsetlocale ( LC_NUMERIC , NULL ) ;
2013-01-22 17:07:28 +00:00
2013-03-03 09:46:18 +00:00
if ( ! saved_locale . empty ( ) )
2013-01-22 17:07:28 +00:00
{
2013-03-16 19:13:06 +00:00
wsetlocale ( LC_NUMERIC , L " C " ) ;
2013-01-22 17:07:28 +00:00
}
2013-03-16 18:50:02 +00:00
r = wcstod ( nptr , endptr ) ;
2013-01-22 17:07:28 +00:00
2013-03-03 09:46:18 +00:00
if ( ! saved_locale . empty ( ) )
2013-01-22 17:07:28 +00:00
{
2013-03-16 19:13:06 +00:00
wsetlocale ( LC_NUMERIC , saved_locale . c_str ( ) ) ;
2013-01-22 17:07:28 +00:00
}
2013-03-03 09:46:18 +00:00
return r ;
2013-01-22 17:07:28 +00:00
}
2013-03-24 22:24:29 +00:00
void builtin_printf_state_t : : fatal_error ( const wchar_t * fmt , . . . )
{
// Don't error twice
if ( early_exit )
return ;
va_list va ;
va_start ( va , fmt ) ;
2013-03-24 22:33:45 +00:00
wcstring errstr = vformat_string ( fmt , va ) ;
2013-03-24 22:24:29 +00:00
va_end ( va ) ;
2013-03-24 22:33:45 +00:00
stderr_buffer . append ( errstr ) ;
if ( ! string_suffixes_string ( L " \n " , errstr ) )
stderr_buffer . push_back ( L ' \n ' ) ;
2013-03-24 22:24:29 +00:00
this - > exit_code = STATUS_BUILTIN_ERROR ;
this - > early_exit = true ;
}
void builtin_printf_state_t : : append_output ( wchar_t c )
{
// Don't output if we're done
if ( early_exit )
return ;
stdout_buffer . push_back ( c ) ;
}
void builtin_printf_state_t : : append_output ( const wchar_t * c )
{
// Don't output if we're done
if ( early_exit )
return ;
stdout_buffer . append ( c ) ;
}
void builtin_printf_state_t : : append_format_output ( const wchar_t * fmt , . . . )
{
// Don't output if we're done
if ( early_exit )
return ;
va_list va ;
va_start ( va , fmt ) ;
append_formatv ( stdout_buffer , fmt , va ) ;
va_end ( va ) ;
}
2013-03-24 22:51:18 +00:00
void builtin_printf_state_t : : verify_numeric ( const wchar_t * s , const wchar_t * end , int errcode )
2013-01-22 17:07:28 +00:00
{
2013-03-24 22:51:18 +00:00
if ( errcode ! = 0 )
2013-01-22 17:07:28 +00:00
{
2013-03-24 22:51:18 +00:00
this - > fatal_error ( L " %ls: %s " , s , strerror ( errcode ) ) ;
2013-01-22 17:07:28 +00:00
}
2013-03-03 09:46:18 +00:00
else if ( * end )
2013-01-22 17:07:28 +00:00
{
2013-03-03 09:46:18 +00:00
if ( s = = end )
2013-03-24 22:24:29 +00:00
this - > fatal_error ( _ ( L " %ls: expected a numeric value " ) , s ) ;
2013-03-03 09:46:18 +00:00
else
2013-03-24 22:24:29 +00:00
this - > fatal_error ( _ ( L " %ls: value not completely converted " ) , s ) ;
2013-01-22 17:07:28 +00:00
}
}
2013-03-22 00:40:12 +00:00
template < typename T >
2013-03-22 05:36:21 +00:00
static T raw_string_to_scalar_type ( const wchar_t * s , wchar_t * * end ) ;
2013-03-22 00:40:12 +00:00
2013-03-27 18:35:30 +00:00
// we use wcstoll instead of wcstoimax because FreeBSD 8 has busted wcstoumax and wcstoimax - see #626
2013-03-22 00:40:12 +00:00
template < >
intmax_t raw_string_to_scalar_type ( const wchar_t * s , wchar_t * * end )
{
2013-03-27 18:35:30 +00:00
return wcstoll ( s , end , 0 ) ;
2013-03-22 00:40:12 +00:00
}
template < >
uintmax_t raw_string_to_scalar_type ( const wchar_t * s , wchar_t * * end )
{
2013-03-27 18:35:30 +00:00
return wcstoull ( s , end , 0 ) ;
2013-03-22 00:40:12 +00:00
}
template < >
long double raw_string_to_scalar_type ( const wchar_t * s , wchar_t * * end )
{
return C_STRTOD ( s , end ) ;
}
template < typename T >
2013-03-22 05:36:21 +00:00
static T string_to_scalar_type ( const wchar_t * s , builtin_printf_state_t * state )
2013-03-22 00:40:12 +00:00
{
T val ;
if ( * s = = L ' \" ' | | * s = = L ' \' ' )
2013-03-22 00:44:51 +00:00
{
2013-11-27 20:10:28 +00:00
wchar_t ch = * + + s ;
2013-03-22 00:44:51 +00:00
val = ch ;
}
else
2013-03-22 00:40:12 +00:00
{
wchar_t * end = NULL ;
errno = 0 ;
val = raw_string_to_scalar_type < T > ( s , & end ) ;
2013-03-24 22:51:18 +00:00
state - > verify_numeric ( s , end , errno ) ;
2013-03-22 00:40:12 +00:00
}
return val ;
}
2013-01-22 17:07:28 +00:00
/* Output a single-character \ escape. */
2013-03-24 22:24:29 +00:00
void builtin_printf_state_t : : print_esc_char ( wchar_t c )
2013-01-22 17:07:28 +00:00
{
2013-03-03 09:46:18 +00:00
switch ( c )
2013-01-22 17:07:28 +00:00
{
2013-03-22 00:44:51 +00:00
case L ' a ' : /* Alert. */
2013-03-24 22:24:29 +00:00
this - > append_output ( L ' \a ' ) ;
2013-03-22 00:44:51 +00:00
break ;
2013-03-03 09:46:18 +00:00
case L ' b ' : /* Backspace. */
2013-03-24 22:24:29 +00:00
this - > append_output ( L ' \b ' ) ;
2013-03-22 00:44:51 +00:00
break ;
case L ' c ' : /* Cancel the rest of the output. */
2013-03-24 22:24:29 +00:00
this - > early_exit = true ;
2013-03-22 00:44:51 +00:00
break ;
2013-07-16 20:25:42 +00:00
case L ' e ' : /* Escape */
this - > append_output ( L ' \x1B ' ) ;
break ;
2013-03-22 00:44:51 +00:00
case L ' f ' : /* Form feed. */
2013-03-24 22:24:29 +00:00
this - > append_output ( L ' \f ' ) ;
2013-03-22 00:44:51 +00:00
break ;
case L ' n ' : /* New line. */
2013-03-24 22:24:29 +00:00
this - > append_output ( L ' \n ' ) ;
2013-03-22 00:44:51 +00:00
break ;
2013-03-22 05:36:21 +00:00
case L ' r ' : /* Carriage return. */
2013-03-24 22:24:29 +00:00
this - > append_output ( L ' \r ' ) ;
2013-03-22 00:44:51 +00:00
break ;
case L ' t ' : /* Horizontal tab. */
2013-03-24 22:24:29 +00:00
this - > append_output ( L ' \t ' ) ;
2013-03-22 00:44:51 +00:00
break ;
case L ' v ' : /* Vertical tab. */
2013-03-24 22:24:29 +00:00
this - > append_output ( L ' \v ' ) ;
2013-03-22 00:44:51 +00:00
break ;
default :
2013-03-24 22:24:29 +00:00
this - > append_output ( c ) ;
2013-03-22 00:44:51 +00:00
break ;
2013-01-22 17:07:28 +00:00
}
}
/* Print a \ escape sequence starting at ESCSTART.
Return the number of characters in the escape sequence
besides the backslash .
If OCTAL_0 is nonzero , octal escapes are of the form \ 0 ooo , where o
is an octal digit ; otherwise they are of the form \ ooo . */
2013-03-24 22:24:29 +00:00
long builtin_printf_state_t : : print_esc ( const wchar_t * escstart , bool octal_0 )
2013-01-22 17:07:28 +00:00
{
2013-03-03 09:46:18 +00:00
const wchar_t * p = escstart + 1 ;
int esc_value = 0 ; /* Value of \nnn escape. */
int esc_length ; /* Length of \nnn escape. */
2013-01-22 17:07:28 +00:00
2013-03-03 09:46:18 +00:00
if ( * p = = L ' x ' )
2013-01-22 17:07:28 +00:00
{
2013-03-03 09:46:18 +00:00
/* A hexadecimal \xhh escape sequence must have 1 or 2 hex. digits. */
2013-03-24 22:24:29 +00:00
for ( esc_length = 0 , + + p ; esc_length < 2 & & is_hex_digit ( * p ) ; + + esc_length , + + p )
esc_value = esc_value * 16 + hex_to_bin ( * p ) ;
2013-03-03 09:46:18 +00:00
if ( esc_length = = 0 )
2013-03-24 22:24:29 +00:00
this - > fatal_error ( _ ( L " missing hexadecimal number in escape " ) ) ;
2015-01-15 19:21:07 +00:00
this - > append_output ( ENCODE_DIRECT_BASE + esc_value % 256 ) ;
2013-01-22 17:07:28 +00:00
}
2013-03-24 22:24:29 +00:00
else if ( is_octal_digit ( * p ) )
2013-01-22 17:07:28 +00:00
{
2013-03-03 09:46:18 +00:00
/* Parse \0ooo (if octal_0 && *p == L'0') or \ooo (otherwise).
Allow \ ooo if octal_0 & & * p ! = L ' 0 ' ; this is an undocumented
extension to POSIX that is compatible with Bash 2.05 b . */
2015-01-15 19:21:07 +00:00
/* Wrap mod 256, which matches historic behavior */
2013-03-24 22:24:29 +00:00
for ( esc_length = 0 , p + = octal_0 & & * p = = L ' 0 ' ; esc_length < 3 & & is_octal_digit ( * p ) ; + + esc_length , + + p )
esc_value = esc_value * 8 + octal_to_bin ( * p ) ;
2015-01-15 19:21:07 +00:00
this - > append_output ( ENCODE_DIRECT_BASE + esc_value % 256 ) ;
2013-03-22 00:44:51 +00:00
}
2013-07-16 20:25:42 +00:00
else if ( * p & & wcschr ( L " \" \\ abcefnrtv " , * p ) )
{
2013-03-16 19:13:06 +00:00
print_esc_char ( * p + + ) ;
2013-07-16 20:25:42 +00:00
}
2013-03-03 09:46:18 +00:00
else if ( * p = = L ' u ' | | * p = = L ' U ' )
2013-01-22 17:07:28 +00:00
{
2013-03-03 09:46:18 +00:00
wchar_t esc_char = * p ;
2013-03-29 00:45:49 +00:00
p + + ;
uint32_t uni_value = 0 ;
for ( size_t esc_length = 0 ; esc_length < ( esc_char = = L ' u ' ? 4 : 8 ) ; esc_length + + )
2013-03-03 09:46:18 +00:00
{
2013-03-24 22:24:29 +00:00
if ( ! is_hex_digit ( * p ) )
2013-03-29 00:45:49 +00:00
{
/* Escape sequence must be done. Complain if we didn't get anything */
if ( esc_length = = 0 )
{
this - > fatal_error ( _ ( L " Missing hexadecimal number in Unicode escape " ) ) ;
}
break ;
}
2013-03-24 22:24:29 +00:00
uni_value = uni_value * 16 + hex_to_bin ( * p ) ;
2013-03-29 00:45:49 +00:00
p + + ;
2013-03-03 09:46:18 +00:00
}
2013-05-05 09:33:17 +00:00
2013-03-29 00:45:49 +00:00
/* PCA GNU printf respects the limitations described in ISO N717, about which universal characters "shall not" be specified. I believe this limitation is for the benefit of compilers; I see no reason to impose it in builtin_printf.
2013-05-05 09:33:17 +00:00
2013-03-29 00:45:49 +00:00
If __STDC_ISO_10646__ is defined , then it means wchar_t can and does hold Unicode code points , so just use that . If not defined , use the % lc printf conversion ; this probably won ' t do anything good if your wide character set is not Unicode , but such platforms are exceedingly rare .
*/
if ( uni_value > 0x10FFFF )
{
this - > fatal_error ( _ ( L " Unicode character out of range: \\ %c%0*x " ) , esc_char , ( esc_char = = L ' u ' ? 4 : 8 ) , uni_value ) ;
}
else
{
# if defined(__STDC_ISO_10646__)
this - > append_output ( uni_value ) ;
# else
this - > append_format_output ( L " %lc " , uni_value ) ;
# endif
2013-03-29 06:32:50 +00:00
}
2013-01-22 17:07:28 +00:00
}
2013-03-03 09:46:18 +00:00
else
2013-01-22 17:07:28 +00:00
{
2013-04-04 22:56:58 +00:00
this - > append_output ( L ' \\ ' ) ;
2013-03-03 09:46:18 +00:00
if ( * p )
{
2013-04-04 22:56:58 +00:00
this - > append_output ( * p ) ;
2013-03-03 09:46:18 +00:00
p + + ;
}
2013-01-22 17:07:28 +00:00
}
2013-03-03 09:46:18 +00:00
return p - escstart - 1 ;
2013-01-22 17:07:28 +00:00
}
/* Print string STR, evaluating \ escapes. */
2013-03-24 22:24:29 +00:00
void builtin_printf_state_t : : print_esc_string ( const wchar_t * str )
2013-01-22 17:07:28 +00:00
{
2013-03-03 09:46:18 +00:00
for ( ; * str ; str + + )
if ( * str = = L ' \\ ' )
2013-03-16 19:13:06 +00:00
str + = print_esc ( str , true ) ;
2013-03-03 09:46:18 +00:00
else
2013-04-03 20:30:45 +00:00
this - > append_output ( * str ) ;
2013-01-22 17:07:28 +00:00
}
/* Evaluate a printf conversion specification. START is the start of
the directive , LENGTH is its length , and CONVERSION specifies the
type of conversion . LENGTH does not include any length modifier or
the conversion specifier itself . FIELD_WIDTH and PRECISION are the
field width and precision for ' * ' values , if HAVE_FIELD_WIDTH and
HAVE_PRECISION are true , respectively . ARGUMENT is the argument to
be formatted . */
2013-03-22 00:40:12 +00:00
void builtin_printf_state_t : : print_direc ( const wchar_t * start , size_t length , wchar_t conversion ,
2013-03-22 00:44:51 +00:00
bool have_field_width , int field_width ,
bool have_precision , int precision ,
wchar_t const * argument )
2013-01-22 17:07:28 +00:00
{
2013-03-24 21:10:31 +00:00
// Start with everything except the conversion specifier
wcstring fmt ( start , length ) ;
2013-01-22 17:07:28 +00:00
2013-03-24 22:51:18 +00:00
/* Create a copy of the % directive, with an intmax_t-wide width modifier substituted for any existing integer length modifier. */
switch ( conversion )
2013-03-03 09:46:18 +00:00
{
2013-03-24 22:51:18 +00:00
case L ' d ' :
case L ' i ' :
2013-04-04 22:56:58 +00:00
case L ' u ' :
2013-03-24 22:51:18 +00:00
fmt . append ( L " ll " ) ;
break ;
case L ' a ' :
case L ' e ' :
case L ' f ' :
case L ' g ' :
case L ' A ' :
case L ' E ' :
case L ' F ' :
case L ' G ' :
fmt . append ( L " L " ) ;
break ;
case L ' s ' :
2014-09-22 06:19:57 +00:00
case L ' c ' :
2013-04-04 22:56:58 +00:00
fmt . append ( L " l " ) ;
2013-03-24 22:51:18 +00:00
break ;
default :
break ;
2013-03-03 09:46:18 +00:00
}
2013-01-22 17:07:28 +00:00
2013-03-24 22:51:18 +00:00
// Append the conversion itself
fmt . push_back ( conversion ) ;
2013-01-22 17:07:28 +00:00
switch ( conversion )
{
2013-03-03 09:46:18 +00:00
case L ' d ' :
case L ' i ' :
{
2013-03-22 00:40:12 +00:00
intmax_t arg = string_to_scalar_type < intmax_t > ( argument , this ) ;
2013-03-24 22:51:18 +00:00
if ( ! have_field_width )
2013-03-03 09:46:18 +00:00
{
2013-03-24 22:51:18 +00:00
if ( ! have_precision )
2013-03-24 22:24:29 +00:00
this - > append_format_output ( fmt . c_str ( ) , arg ) ;
2013-03-03 09:46:18 +00:00
else
2013-03-24 22:24:29 +00:00
this - > append_format_output ( fmt . c_str ( ) , precision , arg ) ;
2013-03-03 09:46:18 +00:00
}
else
{
2013-03-24 22:51:18 +00:00
if ( ! have_precision )
2013-03-24 22:24:29 +00:00
this - > append_format_output ( fmt . c_str ( ) , field_width , arg ) ;
2013-03-03 09:46:18 +00:00
else
2013-03-24 22:24:29 +00:00
this - > append_format_output ( fmt . c_str ( ) , field_width , precision , arg ) ;
2013-03-03 09:46:18 +00:00
}
}
break ;
case L ' o ' :
case L ' u ' :
case L ' x ' :
case L ' X ' :
{
2013-03-22 00:40:12 +00:00
uintmax_t arg = string_to_scalar_type < uintmax_t > ( argument , this ) ;
2013-03-03 09:46:18 +00:00
if ( ! have_field_width )
{
if ( ! have_precision )
2013-03-24 22:24:29 +00:00
this - > append_format_output ( fmt . c_str ( ) , arg ) ;
2013-03-03 09:46:18 +00:00
else
2013-03-24 22:24:29 +00:00
this - > append_format_output ( fmt . c_str ( ) , precision , arg ) ;
2013-03-03 09:46:18 +00:00
}
else
{
if ( ! have_precision )
2013-03-24 22:24:29 +00:00
this - > append_format_output ( fmt . c_str ( ) , field_width , arg ) ;
2013-03-03 09:46:18 +00:00
else
2013-03-24 22:24:29 +00:00
this - > append_format_output ( fmt . c_str ( ) , field_width , precision , arg ) ;
2013-03-03 09:46:18 +00:00
}
}
break ;
case L ' a ' :
case L ' A ' :
case L ' e ' :
case L ' E ' :
case L ' f ' :
case L ' F ' :
case L ' g ' :
case L ' G ' :
{
2013-03-22 00:40:12 +00:00
long double arg = string_to_scalar_type < long double > ( argument , this ) ;
2013-03-03 09:46:18 +00:00
if ( ! have_field_width )
{
if ( ! have_precision )
2013-03-24 22:24:29 +00:00
this - > append_format_output ( fmt . c_str ( ) , arg ) ;
2013-03-03 09:46:18 +00:00
else
2013-03-24 22:24:29 +00:00
this - > append_format_output ( fmt . c_str ( ) , precision , arg ) ;
2013-03-03 09:46:18 +00:00
}
else
{
if ( ! have_precision )
2013-03-24 22:24:29 +00:00
this - > append_format_output ( fmt . c_str ( ) , field_width , arg ) ;
2013-03-03 09:46:18 +00:00
else
2013-03-24 22:24:29 +00:00
this - > append_format_output ( fmt . c_str ( ) , field_width , precision , arg ) ;
2013-03-03 09:46:18 +00:00
}
}
break ;
case L ' c ' :
if ( ! have_field_width )
2013-03-24 22:24:29 +00:00
this - > append_format_output ( fmt . c_str ( ) , * argument ) ;
2013-03-03 09:46:18 +00:00
else
2013-03-24 22:24:29 +00:00
this - > append_format_output ( fmt . c_str ( ) , field_width , * argument ) ;
2013-03-03 09:46:18 +00:00
break ;
case L ' s ' :
if ( ! have_field_width )
{
2013-03-22 00:44:51 +00:00
if ( ! have_precision )
{
2013-03-24 22:24:29 +00:00
this - > append_format_output ( fmt . c_str ( ) , argument ) ;
2013-03-22 00:44:51 +00:00
}
2013-03-03 09:46:18 +00:00
else
2013-03-24 22:24:29 +00:00
this - > append_format_output ( fmt . c_str ( ) , precision , argument ) ;
2013-03-03 09:46:18 +00:00
}
else
{
if ( ! have_precision )
2013-03-24 22:24:29 +00:00
this - > append_format_output ( fmt . c_str ( ) , field_width , argument ) ;
2013-03-03 09:46:18 +00:00
else
2013-03-24 22:24:29 +00:00
this - > append_format_output ( fmt . c_str ( ) , field_width , precision , argument ) ;
2013-03-03 09:46:18 +00:00
}
break ;
2013-01-22 17:07:28 +00:00
}
}
2013-07-16 20:38:15 +00:00
/* For each character in str, set the corresponding boolean in the array to the given flag */
static inline void modify_allowed_format_specifiers ( bool ok [ UCHAR_MAX + 1 ] , const char * str , bool flag )
{
for ( const char * c = str ; * c ! = ' \0 ' ; c + + )
{
unsigned char idx = static_cast < unsigned char > ( * c ) ;
ok [ idx ] = flag ;
}
}
2013-01-22 17:07:28 +00:00
/* Print the text in FORMAT, using ARGV (with ARGC elements) for
arguments to any ` % ' directives .
Return the number of elements of ARGV used . */
2013-03-22 00:44:51 +00:00
int builtin_printf_state_t : : print_formatted ( const wchar_t * format , int argc , wchar_t * * argv )
{
2013-03-03 09:46:18 +00:00
int save_argc = argc ; /* Preserve original value. */
const wchar_t * f ; /* Pointer into `format'. */
const wchar_t * direc_start ; /* Start of % directive. */
size_t direc_length ; /* Length of % directive. */
bool have_field_width ; /* True if FIELD_WIDTH is valid. */
int field_width = 0 ; /* Arg to first '*'. */
bool have_precision ; /* True if PRECISION is valid. */
int precision = 0 ; /* Arg to second '*'. */
2013-03-03 10:02:32 +00:00
bool ok [ UCHAR_MAX + 1 ] = { } ; /* ok['x'] is true if %x is allowed. */
2013-03-03 09:46:18 +00:00
2013-03-03 10:02:32 +00:00
for ( f = format ; * f ! = L ' \0 ' ; + + f )
{
switch ( * f )
{
2013-03-03 09:46:18 +00:00
case L ' % ' :
direc_start = f + + ;
direc_length = 1 ;
have_field_width = have_precision = false ;
2013-03-03 10:02:32 +00:00
if ( * f = = L ' % ' )
{
2013-03-24 22:24:29 +00:00
this - > append_output ( L ' % ' ) ;
2013-03-03 09:46:18 +00:00
break ;
}
2013-03-03 10:02:32 +00:00
if ( * f = = L ' b ' )
{
2013-03-03 09:46:18 +00:00
/* FIXME: Field width and precision are not supported
for % b , even though POSIX requires it . */
2013-03-03 10:02:32 +00:00
if ( argc > 0 )
{
2013-03-16 19:13:06 +00:00
print_esc_string ( * argv ) ;
2013-03-03 09:46:18 +00:00
+ + argv ;
- - argc ;
}
break ;
}
2013-10-26 22:27:39 +00:00
2013-07-16 20:38:15 +00:00
modify_allowed_format_specifiers ( ok , " aAcdeEfFgGiosuxX " , true ) ;
2013-03-03 09:46:18 +00:00
2013-03-03 10:02:32 +00:00
for ( ; ; f + + , direc_length + + )
{
switch ( * f )
{
2013-03-03 09:46:18 +00:00
case L ' I ' :
case L ' \' ' :
2013-07-16 20:38:15 +00:00
modify_allowed_format_specifiers ( ok , " aAceEosxX " , false ) ;
2013-03-22 00:44:51 +00:00
break ;
case ' - ' :
case ' + ' :
case ' ' :
break ;
2013-03-03 09:46:18 +00:00
case L ' # ' :
2013-07-16 20:38:15 +00:00
modify_allowed_format_specifiers ( ok , " cdisu " , false ) ;
2013-03-22 00:44:51 +00:00
break ;
2013-03-03 09:46:18 +00:00
case ' 0 ' :
2013-07-16 20:38:15 +00:00
modify_allowed_format_specifiers ( ok , " cs " , false ) ;
2013-03-22 00:44:51 +00:00
break ;
2013-03-03 09:46:18 +00:00
default :
2013-03-22 00:44:51 +00:00
goto no_more_flag_characters ;
2013-03-03 09:46:18 +00:00
}
}
2013-03-22 00:44:51 +00:00
no_more_flag_characters :
;
2013-03-03 09:46:18 +00:00
2013-03-03 10:02:32 +00:00
if ( * f = = L ' * ' )
{
2013-03-03 09:46:18 +00:00
+ + f ;
+ + direc_length ;
2013-03-03 10:02:32 +00:00
if ( argc > 0 )
{
2013-03-22 00:40:12 +00:00
intmax_t width = string_to_scalar_type < intmax_t > ( * argv , this ) ;
2013-03-03 09:46:18 +00:00
if ( INT_MIN < = width & & width < = INT_MAX )
2013-03-22 00:40:12 +00:00
field_width = static_cast < int > ( width ) ;
2013-03-03 09:46:18 +00:00
else
2013-03-24 22:24:29 +00:00
this - > fatal_error ( _ ( L " invalid field width: %ls " ) , * argv ) ;
2013-03-03 09:46:18 +00:00
+ + argv ;
- - argc ;
}
2013-03-03 10:02:32 +00:00
else
{
2013-03-03 09:46:18 +00:00
field_width = 0 ;
}
have_field_width = true ;
}
2013-03-03 10:02:32 +00:00
else
{
while ( iswdigit ( * f ) )
{
2013-03-03 09:46:18 +00:00
+ + f ;
+ + direc_length ;
}
}
2013-03-03 10:02:32 +00:00
if ( * f = = L ' . ' )
{
2013-03-03 09:46:18 +00:00
+ + f ;
+ + direc_length ;
2013-07-16 20:38:15 +00:00
modify_allowed_format_specifiers ( ok , " c " , false ) ;
2013-03-03 10:02:32 +00:00
if ( * f = = L ' * ' )
{
2013-03-03 09:46:18 +00:00
+ + f ;
+ + direc_length ;
2013-03-03 10:02:32 +00:00
if ( argc > 0 )
{
2013-03-22 00:40:12 +00:00
intmax_t prec = string_to_scalar_type < intmax_t > ( * argv , this ) ;
2013-03-03 10:02:32 +00:00
if ( prec < 0 )
{
2013-03-22 00:44:51 +00:00
/* A negative precision is taken as if the
precision were omitted , so - 1 is safe
here even if prec < INT_MIN . */
precision = - 1 ;
2013-03-03 09:46:18 +00:00
}
else if ( INT_MAX < prec )
2013-03-24 22:24:29 +00:00
this - > fatal_error ( _ ( L " invalid precision: %ls " ) , * argv ) ;
2013-03-03 10:02:32 +00:00
else
{
2013-03-22 00:40:12 +00:00
precision = static_cast < int > ( prec ) ;
2013-03-03 09:46:18 +00:00
}
+ + argv ;
- - argc ;
}
2013-03-22 00:44:51 +00:00
else
{
precision = 0 ;
}
have_precision = true ;
}
2013-03-03 10:02:32 +00:00
else
{
2013-03-22 00:44:51 +00:00
while ( iswdigit ( * f ) )
{
+ + f ;
+ + direc_length ;
}
2013-03-03 09:46:18 +00:00
}
}
2013-03-22 00:44:51 +00:00
2013-03-24 22:24:29 +00:00
while ( * f = = L ' l ' | | * f = = L ' L ' | | * f = = L ' h ' | | * f = = L ' j ' | | * f = = L ' t ' | | * f = = L ' z ' )
2013-03-22 00:44:51 +00:00
+ + f ;
2013-03-24 22:24:29 +00:00
2013-03-03 10:02:32 +00:00
{
2013-11-27 20:10:28 +00:00
wchar_t conversion = * f ;
2013-11-27 20:16:34 +00:00
if ( conversion > 0xFF | | ! ok [ conversion ] )
2013-03-24 22:24:29 +00:00
{
2013-03-24 22:33:45 +00:00
this - > fatal_error ( _ ( L " %.*ls: invalid conversion specification " ) , ( int ) ( f + 1 - direc_start ) , direc_start ) ;
2013-03-24 22:24:29 +00:00
return 0 ;
}
2013-03-03 09:46:18 +00:00
}
2013-03-22 00:44:51 +00:00
print_direc ( direc_start , direc_length , * f ,
have_field_width , field_width ,
have_precision , precision ,
( argc < = 0 ? L " " : ( argc - - , * argv + + ) ) ) ;
break ;
2013-03-03 09:46:18 +00:00
case L ' \\ ' :
2013-03-16 19:13:06 +00:00
f + = print_esc ( f , false ) ;
2013-03-03 09:46:18 +00:00
break ;
default :
2013-03-24 22:24:29 +00:00
this - > append_output ( * f ) ;
2013-03-03 09:46:18 +00:00
}
2013-03-22 00:44:51 +00:00
}
2013-03-03 09:46:18 +00:00
return save_argc - argc ;
2013-01-22 17:07:28 +00:00
}
static int builtin_printf ( parser_t & parser , wchar_t * * argv )
{
2013-03-22 00:40:12 +00:00
builtin_printf_state_t state ;
2013-03-22 00:44:51 +00:00
2013-03-03 09:46:18 +00:00
wchar_t * format ;
int args_used ;
int argc = builtin_count_args ( argv ) ;
2013-01-22 17:07:28 +00:00
2013-03-03 09:46:18 +00:00
if ( argc < = 1 )
2013-01-22 17:07:28 +00:00
{
2013-03-24 22:24:29 +00:00
state . fatal_error ( _ ( L " printf: not enough arguments " ) ) ;
2013-03-22 00:40:12 +00:00
return STATUS_BUILTIN_ERROR ;
2013-01-22 17:07:28 +00:00
}
2013-03-03 09:46:18 +00:00
format = argv [ 1 ] ;
argc - = 2 ;
argv + = 2 ;
2013-01-22 17:07:28 +00:00
2013-03-03 09:46:18 +00:00
do
2013-01-22 17:07:28 +00:00
{
2013-03-22 00:40:12 +00:00
args_used = state . print_formatted ( format , argc , argv ) ;
2013-03-03 09:46:18 +00:00
argc - = args_used ;
argv + = args_used ;
2013-01-22 17:07:28 +00:00
}
2013-03-24 22:51:18 +00:00
while ( args_used > 0 & & argc > 0 & & ! state . early_exit ) ;
2013-03-22 00:44:51 +00:00
return state . exit_code ;
2013-03-03 10:02:32 +00:00
}