Import upstream 2.3.14
[packages/xinetd.git] / libs / src / sio / sprint.c
1 /*
2  * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis
3  * All rights reserved.  The file named COPYRIGHT specifies the terms
4  * and conditions for redistribution.
5  */
6
7
8 #include "config.h"
9 #include <stdlib.h>
10 #include <string.h>
11 #include <ctype.h>
12
13 #include "sio.h"
14 #include "impl.h"
15 #include "libportable.h"
16
17 typedef long long                       wide_int ;
18 typedef unsigned long long              u_wide_int ;
19 typedef int                             bool_int ;
20
21 static char *conv_10( wide_int num, bool_int is_unsigned, bool_int *is_negative, char *buf_end, int *len );
22
23 #define S_NULL_LEN                      6
24 static char S_NULL[S_NULL_LEN+1] = "(null)";
25
26 #define FLOAT_DIGITS                    6
27 #define MAX_FLOAT_DIGITS                17
28 #define EXPONENT_LENGTH                 10
29
30 /*
31  * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions
32  *
33  * XXX: this is a magic number; do not decrease it
34  */
35 #define NUM_BUF_SIZE                    512
36
37 /*
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.
44  *
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.
48  *
49  * NOTE: Evaluation of the c argument should not have any side-effects
50  */
51 #define INS_CHAR( c, sp, bep, odp, cc, fd )                     \
52 {                                                               \
53         if ( sp < bep )                                         \
54         {                                                       \
55                 *sp++ = c ;                                     \
56                 cc++ ;                                          \
57         }                                                       \
58         else                                                    \
59         {                                                       \
60                 if ( fd >= 0 )                                  \
61                 {                                               \
62                         odp->nextb = sp ;                       \
63                         if ( __sio_writef( odp, fd ) != bep - odp->start ) \
64                                 return( ( cc != 0 ) ? cc : SIO_ERR ) ;  \
65                         sp = odp->nextb ;                       \
66                         *sp++ = c ;                             \
67                         cc++ ;                                  \
68                 }                                               \
69         }                                                       \
70         if ( __SIO_MUST_FLUSH( *odp, c ) && fd >= 0 )           \
71         {                                                       \
72                 int b_in_buffer = sp - odp->start ;             \
73                                                                 \
74                 odp->nextb = sp ;                               \
75                 if ( __sio_writef( odp, fd ) != b_in_buffer )   \
76                         return( cc ) ;                          \
77                 sp = odp->nextb ;                               \
78         }                                                       \
79 }
80
81
82
83 #define NUM( c )                        ( c - '0' )
84
85 #define STR_TO_DEC( str, num )                                          \
86         num = NUM( *str++ ) ;                                           \
87         while ( isdigit( *str ) )                                       \
88         {                                                               \
89                 num *= 10 ;                                             \
90                 num += NUM( *str++ ) ;                                  \
91         }
92
93 /*
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
97  * to be printed.
98  */
99 #define FIX_PRECISION( adjust, precision, s, s_len )                    \
100         if ( adjust )                                                   \
101                 while ( s_len < precision && s_len < NUM_BUF_SIZE - 2 ) \
102                 {                                                       \
103                         *--s = '0' ;                                    \
104                         s_len++ ;                                       \
105                 }
106
107 /*
108  * Macro that does padding. The padding is done by printing
109  * the character ch.
110  */
111 #define PAD( width, len, ch )                                           \
112         do                                                              \
113         {                                                               \
114                 INS_CHAR( ch, sp, bep, odp, cc, fd ) ;                  \
115                 width-- ;                                               \
116         } while ( width > len )
117
118 /*
119  * Sprint is the equivalent of printf for SIO.
120  * It returns the # of chars written
121  * Assumptions:
122  *     - all floating point arguments are passed as doubles
123  */
124 /* VARARGS2 */
125 int Sprint( int fd, const char *fmt, ...) 
126 {
127         __sio_descriptor_t *dp ;
128         __sio_od_t *odp ;
129         int cc ;
130         va_list ap ;
131
132         if( sio_setup( fd, &dp, __SIO_OUTPUT_STREAM ) == SIO_ERR )
133                 return( SIO_ERR );
134         odp = ODP( dp ) ;
135
136         va_start( ap, fmt ) ;
137         cc = __sio_converter( odp, fd, fmt, ap ) ;
138         va_end( ap ) ;
139         return( cc ) ;
140 }
141
142
143 /*
144  * This is the equivalent of vfprintf for SIO
145  */
146 int Sprintv( int fd, const char *fmt, va_list ap)
147 {
148         __sio_descriptor_t *dp ;
149         __sio_od_t *odp ;
150
151         if( sio_setup( fd, &dp, __SIO_OUTPUT_STREAM ) == SIO_ERR )
152                 return( SIO_ERR );
153         odp = ODP( dp ) ;
154         return( __sio_converter( odp, fd, fmt, ap ) ) ;
155 }
156
157
158 /*
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
162  * in buf).
163  */
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 */
167 {
168         char *s = buf ;
169         char *p = NULL ;
170         int decimal_point ;
171                 
172         if ( precision > MAX_FLOAT_DIGITS )
173                 precision = MAX_FLOAT_DIGITS ;
174
175         if ( format == 'f' )
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 ) ;
179
180         /*
181          * Check for Infinity and NaN
182          */
183         if ( isalpha( *p ) )
184         {
185                 *len = strlen( strcpy( buf, p ) ) ;
186                 *is_negative = FALSE ;
187                 return( buf ) ;
188         }
189         
190         if ( format == 'f' )
191                 if ( decimal_point <= 0 )
192                 {
193                         *s++ = '0' ;
194                         if ( precision > 0 )
195                         {
196                                 *s++ = '.' ;
197                                 while ( decimal_point++ < 0 )
198                                         *s++ = '0' ;
199                         }
200                         else if ( add_dp )
201                                 *s++ = '.' ;
202                 }
203                 else
204                 {
205                         while ( decimal_point-- > 0 )
206                                 *s++ = *p++ ;
207                         if ( precision > 0 || add_dp ) *s++ = '.' ;
208                 }
209         else
210         {
211                 *s++ = *p++ ;
212                 if ( precision > 0 || add_dp ) *s++ = '.' ;
213         }
214
215         /*
216          * copy the rest of p, the NUL is NOT copied
217          */
218         while ( *p ) *s++ = *p++ ;                      
219         
220         if ( format != 'f' )
221         {
222                 char temp[ EXPONENT_LENGTH ] ;          /* for exponent conversion */
223                 int t_len ;
224                 bool_int exponent_is_negative ;
225
226                 *s++ = format ;         /* either e or E */
227                 decimal_point-- ;
228                 if ( decimal_point != 0 )
229                 {
230                         p = conv_10( (wide_int)decimal_point, FALSE, &exponent_is_negative,
231                                         &temp[ EXPONENT_LENGTH ], &t_len ) ;
232                         *s++ = exponent_is_negative ? '-' : '+' ;
233                         
234                         /*
235                          * Make sure the exponent has at least 2 digits
236                          */
237                         if ( t_len == 1 )
238                                 *s++ = '0' ;
239                         while ( t_len-- ) *s++ = *p++ ;
240                 }
241                 else
242                 {
243                         *s++ = '+' ;
244                         *s++ = '0' ;
245                         *s++ = '0' ;
246                 }
247         }
248
249         *len = s - buf ;
250         return( buf ) ;
251 }
252
253
254 /*
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
257  * Return value:
258  *                      a pointer to a string containing the number
259  *
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 ])
263  */
264 static char *conv_p2( u_wide_int num, int nbits, char format, char *buf_end, int *len )
265 {
266         int mask = ( 1 << nbits ) - 1 ;
267         char *p = buf_end ;
268         static const char low_digits[] = "0123456789abcdef" ;
269         static const char upper_digits[] = "0123456789ABCDEF" ;
270         const char *digits = ( format == 'X' ) ? upper_digits : low_digits ;
271
272         do
273         {
274                 *--p = digits[ num & mask ] ;
275                 num >>= nbits ;
276         }
277         while( num ) ;
278
279         *len = buf_end - p ;
280         return( p ) ;
281 }
282
283 /*
284  * Convert num to its decimal format.
285  * Return value:
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)
290  *
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 ])
294  */
295 static char *conv_10( wide_int num, bool_int is_unsigned, bool_int *is_negative, char *buf_end, int *len )
296 {
297         char *p = buf_end ;
298         u_wide_int magnitude ;
299
300         if ( is_unsigned )
301         {
302                 magnitude = (u_wide_int) num ;
303                 *is_negative = FALSE ;
304         }
305         else
306         {
307                 *is_negative = ( num < 0 ) ;
308
309                 /*
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
316                  *              d. add 1
317                  */
318                 if ( *is_negative )
319                 {
320                         wide_int t = num + 1 ;
321
322                         magnitude = ( (u_wide_int) -t ) + 1 ;
323                 }
324                 else
325                         magnitude = (u_wide_int) num ;
326         }
327
328         /*
329          * We use a do-while loop so that we write at least 1 digit 
330          */
331         do
332         {
333                 u_wide_int new_magnitude = magnitude / 10 ;
334
335                 *--p = magnitude - new_magnitude*10 + '0' ;
336                 magnitude = new_magnitude ;
337         }
338         while ( magnitude ) ;
339         
340         *len = buf_end - p ;
341         return( p ) ;
342 }
343
344
345 /*
346  * Do format conversion placing the output in odp.
347  * Note: we do not support %n for security reasons.
348  */
349 int __sio_converter( __sio_od_t *odp, int fd, const char *fmt, va_list ap )
350 {
351         char *sp = NULL;
352         char *bep = NULL;
353         int cc = 0 ;
354         int i ;
355
356         char *s = NULL;
357         char *q = NULL;
358         int s_len ;
359
360         int min_width = 0 ;
361         int precision = 0 ;
362         enum { LEFT, RIGHT } adjust ;
363         char pad_char ;
364         char prefix_char ;
365
366         double fp_num ;
367         wide_int i_num = 0 ;
368         u_wide_int ui_num ;
369
370         char num_buf[ NUM_BUF_SIZE ] ;
371         char char_buf[ 2 ] ;            /* for printing %% and %<unknown> */
372
373         /*
374          * Flag variables
375          */
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 ;
384
385         sp = odp->nextb ;
386         bep = odp->buf_end ;
387
388         while ( *fmt )
389         {
390                 if ( *fmt != '%' )
391                 {
392                         INS_CHAR( *fmt, sp, bep, odp, cc, fd ) ;
393                 }
394                 else
395                 {
396                         /*
397                          * Default variable settings
398                          */
399                         adjust = RIGHT ;
400                         alternate_form = print_sign = print_blank = NO ;
401                         pad_char = ' ' ;
402                         prefix_char = NUL ;
403
404                         fmt++ ;
405
406                         /*
407                          * Try to avoid checking for flags, width or precision
408                          */
409                         if ( isascii( *fmt ) && ! islower( *fmt ) )
410                         {
411                                 /*
412                                  * Recognize flags: -, #, BLANK, +
413                                  */
414                                 for ( ;; fmt++ )
415                                 {
416                                         if ( *fmt == '-' )
417                                                 adjust = LEFT ;
418                                         else if ( *fmt == '+' )
419                                                 print_sign = YES ;
420                                         else if ( *fmt == '#' )
421                                                 alternate_form = YES ;
422                                         else if ( *fmt == ' ' )
423                                                 print_blank = YES ;
424                                         else if ( *fmt == '0' )
425                                                 pad_char = '0' ;
426                                         else
427                                                 break ;
428                                 }
429
430                                 /*
431                                  * Check if a width was specified
432                                  */
433                                 if ( isdigit( *fmt ) )
434                                 {
435                                         STR_TO_DEC( fmt, min_width ) ;
436                                         adjust_width = YES ;
437                                 }
438                                 else if ( *fmt == '*' )
439                                 {
440                                         min_width = va_arg( ap, int ) ;
441                                         fmt++ ;
442                                         adjust_width = YES ;
443                                         if ( min_width < 0 )
444                                         {
445                                                 adjust = LEFT ;
446                                                 min_width = -min_width ;
447                                         }
448                                 }
449                                 else
450                                         adjust_width = NO ;
451                                 
452                                 /*
453                                  * Check if a precision was specified.
454                                  */
455                                 if ( *fmt == '.' )
456                                 {
457                                         adjust_precision = YES ;
458                                         fmt++ ;
459                                         if ( isdigit( *fmt ) )
460                                         {
461                                                 STR_TO_DEC( fmt, precision ) ;
462                                         }
463                                         else if ( *fmt == '*' )
464                                         {
465                                                 precision = va_arg( ap, int ) ;
466                                                 fmt++ ;
467                                                 if ( precision < 0 )
468                                                         precision = 0 ;
469                                         }
470                                         else
471                                                 precision = 0 ;
472                                 }
473                                 else
474                                         adjust_precision = NO ;
475                         }
476                         else
477                                 adjust_precision = adjust_width = NO ;
478
479                         /*
480                          * Modifier check
481                          */
482                         if ( *fmt == 'l' )
483                         {
484                                 if( *(fmt+1) == 'l' ) {
485                                         is_quad = YES;
486                                         fmt++;
487                                 } else {
488                                         is_quad = NO;
489                                         is_long = YES;
490                                 }
491                                 fmt++ ;
492                         } else {
493                                 is_long = NO;
494                                 is_quad = NO;
495                         }
496
497                         if ( *fmt == 'q' )
498                         {
499                                 is_quad = YES;
500                                 fmt++;
501                         }
502
503                         /*
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.
510                          *
511                          * NOTE: pad_char may be set to '0' because of the 0 flag.
512                          *                      It is reset to ' ' by non-numeric formats
513                          */
514                         switch( *fmt )
515                         {
516                                 case 'd':
517                                 case 'i':
518                                 case 'u':
519                                         if ( is_long )
520                                                 i_num = va_arg( ap, long ) ;
521                                         else
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 ) ;
526
527                                         if ( *fmt != 'u' )
528                                         {
529                                                 if ( is_negative )
530                                                         prefix_char = '-' ;
531                                                 else if ( print_sign )
532                                                         prefix_char = '+' ;
533                                                 else if ( print_blank )
534                                                         prefix_char = ' ' ;
535                                         }
536                                         break ;
537                                 
538
539                                 case 'o':
540                                         if ( is_long )
541                                                 ui_num = va_arg( ap, u_wide_int ) ; 
542                                         else
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' )
548                                         {
549                                                 *--s = '0' ;
550                                                 s_len++ ;
551                                         }
552                                         break ;
553                                 
554
555                                 case 'x':
556                                 case 'X':
557                                         if ( is_long )
558                                                 ui_num = (u_wide_int) va_arg( ap, unsigned long ) ;
559                                         else if ( is_quad )
560                                                 ui_num = (u_wide_int) va_arg( ap, u_wide_int ) ;
561                                         else
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 )
567                                         {
568                                                 *--s = *fmt ;           /* 'x' or 'X' */
569                                                 *--s = '0' ;
570                                                 s_len += 2 ;
571                                         }
572                                         break ;
573                                 
574
575                                 case 's':
576                                         s = va_arg( ap, char * ) ;
577                                         if ( s != NULL )
578                                         {
579                                                 s_len = strlen( s ) ;
580                                                 if ( adjust_precision && precision < s_len )
581                                                         s_len = precision ;
582                                         }
583                                         else
584                                         {
585                                                 s = S_NULL ;
586                                                 s_len = S_NULL_LEN ;
587                                         }
588                                         pad_char = ' ' ;
589                                         break ;
590
591
592                                 case 'f':
593                                 case 'e':
594                                 case 'E':
595                                         fp_num = va_arg( ap, double ) ;
596
597                                         s = conv_fp( *fmt, fp_num, alternate_form,
598                                                 ( adjust_precision == NO ) ? FLOAT_DIGITS : precision,
599                                                 &is_negative, &num_buf[ 1 ], &s_len ) ;
600                                         if ( is_negative )
601                                                 prefix_char = '-' ;
602                                         else if ( print_sign )
603                                                 prefix_char = '+' ;
604                                         else if ( print_blank )
605                                                 prefix_char = ' ' ;
606                                         break ;
607
608
609                                 case 'g':
610                                 case 'G':
611                                         if ( adjust_precision == NO )
612                                                 precision = FLOAT_DIGITS ;
613                                         else if ( precision == 0 )
614                                                 precision = 1 ;
615                                         if ( precision > MAX_FLOAT_DIGITS )
616                                                 precision = MAX_FLOAT_DIGITS ;
617                                         /*
618                                          * We use &num_buf[ 1 ], so that we have room for the sign
619                                          */
620                                         s = gcvt( va_arg( ap, double ), precision, &num_buf[ 1 ] ) ;
621                                         if ( *s == '-' )
622                                                 prefix_char = *s++ ;
623                                         else if ( print_sign )
624                                                 prefix_char = '+' ;
625                                         else if ( print_blank )
626                                                 prefix_char = ' ' ;
627
628                                         s_len = strlen( s ) ;
629
630                                         if ( alternate_form && ( q = strchr( s, '.' ) ) == NULL )
631                                                 s[ s_len++ ] = '.' ;
632                                         if ( *fmt == 'G' && ( q = strchr( s, 'e' ) ) != NULL )
633                                                 *q = 'E' ;
634                                         break ;
635
636
637                                 case 'c':
638                                         char_buf[ 0 ] = (char) (va_arg( ap, int )) ;
639                                         s = &char_buf[ 0 ] ;
640                                         s_len = 1 ;
641                                         pad_char = ' ' ;
642                                         break ;
643
644
645                                 case '%':
646                                         char_buf[ 0 ] = '%' ;
647                                         s = &char_buf[ 0 ] ;
648                                         s_len = 1 ;
649                                         pad_char = ' ' ;
650                                         break ;
651
652                                 /*
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".
659                                  */
660                                 case 'p':
661                                         /* Assume that pointers are of same size as "unsigned long" */
662                                         ui_num = (u_wide_int)(unsigned long)va_arg( ap, char * ) ;
663
664                                         if ( sizeof( char * ) <= sizeof( u_wide_int ) )
665                                                 s = conv_p2( ui_num, 4, 'x', 
666                                                 &num_buf[ NUM_BUF_SIZE ], &s_len ) ;
667                                         else
668                                         {
669                                                 s = "%p" ;
670                                                 s_len = 2 ;
671                                         }
672                                         pad_char = ' ' ;
673                                         break ;
674                                         
675
676                                 case NUL:
677                                         /*
678                                          * The last character of the format string was %.
679                                          * We ignore it.
680                                          */
681                                         continue ;
682
683
684                                         /*
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.
693                                          */
694                                 default:
695                                         char_buf[ 0 ] = '%' ;
696                                         char_buf[ 1 ] = *fmt ;
697                                         s = char_buf ;
698                                         s_len = 2 ;
699                                         pad_char = ' ' ;
700                                         break ;
701                         }
702
703                         if ( prefix_char != NUL )
704                         {
705                                 *--s = prefix_char ;
706                                 s_len++ ;
707                         }
708
709                         if ( adjust_width && adjust == RIGHT && min_width > s_len )
710                         {
711                                 if ( pad_char == '0' && prefix_char != NUL )
712                                 {
713                                         INS_CHAR( *s, sp, bep, odp, cc, fd )
714                                         s++ ;
715                                         s_len-- ;
716                                         min_width-- ;
717                                 }
718                                 PAD( min_width, s_len, pad_char ) ;
719                         }
720
721                         /*
722                          * Print the string s. 
723                          */
724                         for ( i = s_len ; i != 0 ; i-- )
725                         {
726                                 INS_CHAR( *s, sp, bep, odp, cc, fd ) ;
727                                 s++ ;
728                         }
729
730                         /*
731                          * Ignore pad_char for right padding, as padding
732                          * with zeroes would produce a different number.
733                          */
734                         if ( adjust_width && adjust == LEFT && min_width > s_len )
735                                 PAD( min_width, s_len, ' ' ) ;
736                 }
737                 fmt++ ;
738         }
739         odp->nextb = sp ;
740         return( cc ) ;
741 }
742