2 * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis
3 * All rights reserved. The file named COPYRIGHT specifies the terms
4 * and conditions for redistribution.
15 #include "libportable.h"
17 typedef long long wide_int ;
18 typedef unsigned long long u_wide_int ;
19 typedef int bool_int ;
21 static char *conv_10( wide_int num, bool_int is_unsigned, bool_int *is_negative, char *buf_end, int *len );
24 static char S_NULL[S_NULL_LEN+1] = "(null)";
26 #define FLOAT_DIGITS 6
27 #define MAX_FLOAT_DIGITS 17
28 #define EXPONENT_LENGTH 10
31 * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions
33 * XXX: this is a magic number; do not decrease it
35 #define NUM_BUF_SIZE 512
38 * The INS_CHAR macro inserts a character in the buffer and writes
39 * the buffer back to disk if necessary
40 * It uses the char pointers sp and bep:
41 * sp points to the next available character in the buffer
42 * bep points to the end-of-buffer+1
43 * While using this macro, note that the nextb pointer is NOT updated.
45 * No I/O is performed if fd is not positive. Negative fd values imply
46 * conversion with the output directed to a string. Excess characters
47 * are discarded if the string overflows.
49 * NOTE: Evaluation of the c argument should not have any side-effects
51 #define INS_CHAR( c, sp, bep, odp, cc, fd ) \
63 if ( __sio_writef( odp, fd ) != bep - odp->start ) \
64 return( ( cc != 0 ) ? cc : SIO_ERR ) ; \
70 if ( __SIO_MUST_FLUSH( *odp, c ) && fd >= 0 ) \
72 int b_in_buffer = sp - odp->start ; \
75 if ( __sio_writef( odp, fd ) != b_in_buffer ) \
83 #define NUM( c ) ( c - '0' )
85 #define STR_TO_DEC( str, num ) \
86 num = NUM( *str++ ) ; \
87 while ( isdigit( *str ) ) \
90 num += NUM( *str++ ) ; \
94 * This macro does zero padding so that the precision
95 * requirement is satisfied. The padding is done by
96 * adding '0's to the left of the string that is going
99 #define FIX_PRECISION( adjust, precision, s, s_len ) \
101 while ( s_len < precision && s_len < NUM_BUF_SIZE - 2 ) \
108 * Macro that does padding. The padding is done by printing
111 #define PAD( width, len, ch ) \
114 INS_CHAR( ch, sp, bep, odp, cc, fd ) ; \
116 } while ( width > len )
119 * Sprint is the equivalent of printf for SIO.
120 * It returns the # of chars written
122 * - all floating point arguments are passed as doubles
125 int Sprint( int fd, const char *fmt, ...)
127 __sio_descriptor_t *dp ;
132 if( sio_setup( fd, &dp, __SIO_OUTPUT_STREAM ) == SIO_ERR )
136 va_start( ap, fmt ) ;
137 cc = __sio_converter( odp, fd, fmt, ap ) ;
144 * This is the equivalent of vfprintf for SIO
146 int Sprintv( int fd, const char *fmt, va_list ap)
148 __sio_descriptor_t *dp ;
151 if( sio_setup( fd, &dp, __SIO_OUTPUT_STREAM ) == SIO_ERR )
154 return( __sio_converter( odp, fd, fmt, ap ) ) ;
159 * Convert a floating point number to a string formats 'f', 'e' or 'E'.
160 * The result is placed in buf, and len denotes the length of the string
161 * The sign is returned in the is_negative argument (and is not placed
164 static char *conv_fp( char format, double num, boolean_e add_dp,
165 int precision, bool_int *is_negative, char buf[], int *len )
166 /* always add decimal point if YES */
172 if ( precision > MAX_FLOAT_DIGITS )
173 precision = MAX_FLOAT_DIGITS ;
176 p = (char *)fcvt( num, precision, &decimal_point, is_negative ) ;
177 else /* either e or E format */
178 p = (char *)ecvt( num, precision+1, &decimal_point, is_negative ) ;
181 * Check for Infinity and NaN
185 *len = strlen( strcpy( buf, p ) ) ;
186 *is_negative = FALSE ;
191 if ( decimal_point <= 0 )
197 while ( decimal_point++ < 0 )
205 while ( decimal_point-- > 0 )
207 if ( precision > 0 || add_dp ) *s++ = '.' ;
212 if ( precision > 0 || add_dp ) *s++ = '.' ;
216 * copy the rest of p, the NUL is NOT copied
218 while ( *p ) *s++ = *p++ ;
222 char temp[ EXPONENT_LENGTH ] ; /* for exponent conversion */
224 bool_int exponent_is_negative ;
226 *s++ = format ; /* either e or E */
228 if ( decimal_point != 0 )
230 p = conv_10( (wide_int)decimal_point, FALSE, &exponent_is_negative,
231 &temp[ EXPONENT_LENGTH ], &t_len ) ;
232 *s++ = exponent_is_negative ? '-' : '+' ;
235 * Make sure the exponent has at least 2 digits
239 while ( t_len-- ) *s++ = *p++ ;
255 * Convert num to a base X number where X is a power of 2. nbits determines X.
256 * For example, if nbits is 3, we do base 8 conversion
258 * a pointer to a string containing the number
260 * The caller provides a buffer for the string: that is the buf_end argument
261 * which is a pointer to the END of the buffer + 1 (i.e. if the buffer
262 * is declared as buf[ 100 ], buf_end should be &buf[ 100 ])
264 static char *conv_p2( u_wide_int num, int nbits, char format, char *buf_end, int *len )
266 int mask = ( 1 << nbits ) - 1 ;
268 static const char low_digits[] = "0123456789abcdef" ;
269 static const char upper_digits[] = "0123456789ABCDEF" ;
270 const char *digits = ( format == 'X' ) ? upper_digits : low_digits ;
274 *--p = digits[ num & mask ] ;
284 * Convert num to its decimal format.
286 * - a pointer to a string containing the number (no sign)
287 * - len contains the length of the string
288 * - is_negative is set to TRUE or FALSE depending on the sign
289 * of the number (always set to FALSE if is_unsigned is TRUE)
291 * The caller provides a buffer for the string: that is the buf_end argument
292 * which is a pointer to the END of the buffer + 1 (i.e. if the buffer
293 * is declared as buf[ 100 ], buf_end should be &buf[ 100 ])
295 static char *conv_10( wide_int num, bool_int is_unsigned, bool_int *is_negative, char *buf_end, int *len )
298 u_wide_int magnitude ;
302 magnitude = (u_wide_int) num ;
303 *is_negative = FALSE ;
307 *is_negative = ( num < 0 ) ;
310 * On a 2's complement machine, negating the most negative integer
311 * results in a number that cannot be represented as a signed integer.
312 * Here is what we do to obtain the number's magnitude:
313 * a. add 1 to the number
314 * b. negate it (becomes positive)
315 * c. convert it to unsigned
320 wide_int t = num + 1 ;
322 magnitude = ( (u_wide_int) -t ) + 1 ;
325 magnitude = (u_wide_int) num ;
329 * We use a do-while loop so that we write at least 1 digit
333 u_wide_int new_magnitude = magnitude / 10 ;
335 *--p = magnitude - new_magnitude*10 + '0' ;
336 magnitude = new_magnitude ;
338 while ( magnitude ) ;
346 * Do format conversion placing the output in odp.
347 * Note: we do not support %n for security reasons.
349 int __sio_converter( __sio_od_t *odp, int fd, const char *fmt, va_list ap )
362 enum { LEFT, RIGHT } adjust ;
370 char num_buf[ NUM_BUF_SIZE ] ;
371 char char_buf[ 2 ] ; /* for printing %% and %<unknown> */
376 boolean_e is_long = NO;
377 boolean_e is_quad = NO;
378 boolean_e alternate_form ;
379 boolean_e print_sign ;
380 boolean_e print_blank ;
381 boolean_e adjust_precision ;
382 boolean_e adjust_width ;
383 bool_int is_negative ;
392 INS_CHAR( *fmt, sp, bep, odp, cc, fd ) ;
397 * Default variable settings
400 alternate_form = print_sign = print_blank = NO ;
407 * Try to avoid checking for flags, width or precision
409 if ( isascii( *fmt ) && ! islower( *fmt ) )
412 * Recognize flags: -, #, BLANK, +
418 else if ( *fmt == '+' )
420 else if ( *fmt == '#' )
421 alternate_form = YES ;
422 else if ( *fmt == ' ' )
424 else if ( *fmt == '0' )
431 * Check if a width was specified
433 if ( isdigit( *fmt ) )
435 STR_TO_DEC( fmt, min_width ) ;
438 else if ( *fmt == '*' )
440 min_width = va_arg( ap, int ) ;
446 min_width = -min_width ;
453 * Check if a precision was specified.
457 adjust_precision = YES ;
459 if ( isdigit( *fmt ) )
461 STR_TO_DEC( fmt, precision ) ;
463 else if ( *fmt == '*' )
465 precision = va_arg( ap, int ) ;
474 adjust_precision = NO ;
477 adjust_precision = adjust_width = NO ;
484 if( *(fmt+1) == 'l' ) {
504 * Argument extraction and printing.
505 * First we determine the argument type.
506 * Then, we convert the argument to a string.
507 * On exit from the switch, s points to the string that
508 * must be printed, s_len has the length of the string
509 * The precision requirements, if any, are reflected in s_len.
511 * NOTE: pad_char may be set to '0' because of the 0 flag.
512 * It is reset to ' ' by non-numeric formats
520 i_num = va_arg( ap, long ) ;
522 i_num = (wide_int) va_arg( ap, int ) ;
523 s = conv_10( i_num, (*fmt) == 'u', &is_negative,
524 &num_buf[ NUM_BUF_SIZE ], &s_len ) ;
525 FIX_PRECISION( adjust_precision, precision, s, s_len ) ;
531 else if ( print_sign )
533 else if ( print_blank )
541 ui_num = va_arg( ap, u_wide_int ) ;
543 ui_num = (u_wide_int) va_arg( ap, unsigned int ) ;
544 s = conv_p2( ui_num, 3, *fmt,
545 &num_buf[ NUM_BUF_SIZE ], &s_len ) ;
546 FIX_PRECISION( adjust_precision, precision, s, s_len ) ;
547 if ( alternate_form && *s != '0' )
558 ui_num = (u_wide_int) va_arg( ap, unsigned long ) ;
560 ui_num = (u_wide_int) va_arg( ap, u_wide_int ) ;
562 ui_num = (u_wide_int) va_arg( ap, unsigned int ) ;
563 s = conv_p2( ui_num, 4, *fmt,
564 &num_buf[ NUM_BUF_SIZE ], &s_len ) ;
565 FIX_PRECISION( adjust_precision, precision, s, s_len ) ;
566 if ( alternate_form && i_num != 0 )
568 *--s = *fmt ; /* 'x' or 'X' */
576 s = va_arg( ap, char * ) ;
579 s_len = strlen( s ) ;
580 if ( adjust_precision && precision < s_len )
595 fp_num = va_arg( ap, double ) ;
597 s = conv_fp( *fmt, fp_num, alternate_form,
598 ( adjust_precision == NO ) ? FLOAT_DIGITS : precision,
599 &is_negative, &num_buf[ 1 ], &s_len ) ;
602 else if ( print_sign )
604 else if ( print_blank )
611 if ( adjust_precision == NO )
612 precision = FLOAT_DIGITS ;
613 else if ( precision == 0 )
615 if ( precision > MAX_FLOAT_DIGITS )
616 precision = MAX_FLOAT_DIGITS ;
618 * We use &num_buf[ 1 ], so that we have room for the sign
620 s = gcvt( va_arg( ap, double ), precision, &num_buf[ 1 ] ) ;
623 else if ( print_sign )
625 else if ( print_blank )
628 s_len = strlen( s ) ;
630 if ( alternate_form && ( q = strchr( s, '.' ) ) == NULL )
632 if ( *fmt == 'G' && ( q = strchr( s, 'e' ) ) != NULL )
638 char_buf[ 0 ] = (char) (va_arg( ap, int )) ;
646 char_buf[ 0 ] = '%' ;
653 * Always extract the argument as a "char *" pointer. We
654 * should be using "void *" but there are still machines
655 * that don't understand it.
656 * If the pointer size is equal to the size of an unsigned
657 * integer we convert the pointer to a hex number, otherwise
658 * we print "%p" to indicate that we don't handle "%p".
661 /* Assume that pointers are of same size as "unsigned long" */
662 ui_num = (u_wide_int)(unsigned long)va_arg( ap, char * ) ;
664 if ( sizeof( char * ) <= sizeof( u_wide_int ) )
665 s = conv_p2( ui_num, 4, 'x',
666 &num_buf[ NUM_BUF_SIZE ], &s_len ) ;
678 * The last character of the format string was %.
685 * The default case is for unrecognized %'s.
686 * We print %<char> to help the user identify what
687 * option is not understood.
688 * This is also useful in case the user wants to pass
689 * the output of __sio_converter to another function
690 * that understands some other %<char> (like syslog).
691 * Note that we can't point s inside fmt because the
692 * unknown <char> could be preceded by width etc.
695 char_buf[ 0 ] = '%' ;
696 char_buf[ 1 ] = *fmt ;
703 if ( prefix_char != NUL )
709 if ( adjust_width && adjust == RIGHT && min_width > s_len )
711 if ( pad_char == '0' && prefix_char != NUL )
713 INS_CHAR( *s, sp, bep, odp, cc, fd )
718 PAD( min_width, s_len, pad_char ) ;
722 * Print the string s.
724 for ( i = s_len ; i != 0 ; i-- )
726 INS_CHAR( *s, sp, bep, odp, cc, fd ) ;
731 * Ignore pad_char for right padding, as padding
732 * with zeroes would produce a different number.
734 if ( adjust_width && adjust == LEFT && min_width > s_len )
735 PAD( min_width, s_len, ' ' ) ;