xnprintf.c

Go to the documentation of this file.
00001 
00008 /* $Id: xnprintf.c,v 1.11 2005/09/15 20:45:06 seymour Exp $ */
00009 
00010 /*###########################################################################
00011 #                                                                           #
00012 #                                xnprintf()                                 #
00013 #                                                                           #
00014 #               Copyright (c) 2002-2003 David TAILLANDIER                   #
00015 #                                                                           #
00016 ###########################################################################*/
00017 
00018 /*
00019 
00020 This software is distributed under the "modified BSD licence".
00021 
00022 This software is also released with GNU license (GPL) in another file (same
00023 source-code, only license differ).
00024 
00025 
00026 
00027 Redistribution and use in source and binary forms, with or without
00028 modification, are permitted provided that the following conditions are met:
00029 
00030 Redistributions of source code must retain the above copyright notice, this
00031 list of conditions and the following disclaimer. Redistributions in binary
00032 form must reproduce the above copyright notice, this list of conditions and
00033 the following disclaimer in the documentation and/or other materials
00034 provided with the distribution. The name of the author may not be used to
00035 endorse or promote products derived from this software without specific
00036 prior written permission. 
00037 
00038 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
00039 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00040 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
00041 EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00042 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00043 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
00044 OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
00045 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
00046 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
00047 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00048 
00049 
00050 
00051 
00052 ====================
00053 Revision history:
00054 
00055 december 29th 2002 David TAILLANDIER  a little simplification into the
00056    handling of (v)as(n)printf into core(). puts() return-value is now checked.
00057 december 15th 2002 David TAILLANDIER  asnprintf and vasnprintf added.
00058    return value of asprintf and vasprintf adapted to standards.
00059    Some other minor code modifications.
00060    First bugs found (and corrected):
00061       - memory was not freed when (v)asprintf returned an error.
00062       - problem when submiting an empty string.
00063       - (v)snprintf did not checked the case of a NULL string pointer.
00064       - incorrect handling of empty string with (v)asprintf.
00065       - erroneous test-condition in type_s() witch made an infinite loop in a
00066         lot of cases.
00067 
00068 may 23rd 2002 David TAILLANDIER  some minor code enhacement and
00069    simplification. No bug reported.
00070 
00071 february 28th 2002 David TAILLANDIER  Initial release.
00072    Added a test suite. Every major bugs seems corrected.
00073 
00074 february 16th 2002 David TAILLANDIER  early-buggy initial release
00075 
00076 ====================
00077 
00078 
00079 'printf' function family use the following format string:
00080 
00081    %[flag][width][.prec][modifier]type
00082 
00083    %% is the escape sequence to print a '%'
00084    %  followed by an unknown format will print the characters without
00085       trying to do any interpretation
00086 
00087    flag:   none   +     -     #     (blank)
00088    width:  n    0n    *
00089    prec:   none   .0    .n     .*
00090    modifier:    F N L h l    ('F' and 'N' are ms-dos/16-bit specific)
00091    type:  d i o u x X f e g E G c s p n
00092 
00093 
00094 The function needs to allocate memory to store the full text before to
00095 actually writting it.  i.e if you want to fnprintf() 1000 characters, the
00096 functions will allocate 1000 bytes.
00097 This behaviour can be modified: you have to customise the code to fluch the
00098 internal buffer (writing to screen or file) when it reach a given size. Then
00099 the buffer can have a shorter length. But what ?  If you really need to write
00100 HUGE string, don't use printf !
00101 During the process, some other memory is allocated (1024 bytes minimum)
00102 to handle the output of partial sprintf() calls. If you have only 10000 bytes
00103 free in memory, you *may* not be able to nprintf() a 8000 bytes-long text.
00104 
00105 note: if a buffer overflow occurs, exit() is called. This situation should
00106 never appear ... but if you want to be *really* sure, you have to modify the
00107 code to handle those situations (only one place to modify).
00108 A buffer overflow can only occur if your sprintf() do strange things or when
00109 you use strange formats.
00110 
00111 
00112 
00113 internals
00114 =========
00115 
00116 A temporary buffer is allocated at the beggining ; It is then enlarged when
00117 needed (never srinked) by every sub-functions ; It is freed when the print is
00118 done. The goal is to save a lot of alloc/free. Its initial size is given by
00119 the macro 'ALLOC_CHUNK'. It had to be enlarged very few times because its size
00120 is big enought to handle common sprintf() queries.
00121 The macro 'ALLOC_SECURITY_MARGIN' gives the number of bytes used as security
00122 margin because sprintf() *may* write more characters as expected (but no
00123 enought to overflow beyond 'ALLOC_SECURITY_MARGIN').
00124 In the unlikely case a buffer overflow occurs, a marquer should detect it at
00125 the end of the buffer ; "exit(-1)" is then done.
00126 
00127 
00128 
00129 notes
00130 =====
00131 
00132 Nearly every printf() implementation have some minor differences and/or
00133 incompatibility when pushing them into some limits.
00134 
00135 Exemples:
00136    printf( NULL );
00137          with djgpp -->  "(null)"
00138          with old Borland -->  "*k7_":?;!:+=)jdH#"
00139          because the old Borland library does not check against NULL
00140          other library may prints  "NULL"   or   "!!NULL POINTER!!"   or
00141          anything else
00142 
00143    printf( "%-" );
00144          some implementations prints  "-"
00145          but some prints nothing because the '-' is read as a flag
00146    printf( "%bbb%f", 123.456 );
00147          old Borland compilers print "%bbb%f" instead of "bbb123.456"
00148 
00149 All of these examples only appear when you use printf in a strange way.
00150 
00151 */
00152 
00153 /*#########################################################################*/
00154 
00155 #include <stdio.h>
00156 #include <string.h>
00157 #include <stdlib.h>
00158 #include <stdarg.h>
00159 #include <ctype.h>
00160 #include <limits.h>
00161 
00162 /* va_copy is not portable.  GNU suggests the following work-around
00163  * -JL 10/13/2004
00164  */
00165 #if ! defined va_copy
00166 #if defined __va_copy
00167 #define va_copy __va_copy
00168 #else
00169 #define va_copy(dst, src) memcpy (&dst, &src, sizeof(va_list))
00170 #endif
00171 #endif
00172 
00173 #if defined _16BIT_ || defined __TINY__ || defined __SMALL__ || defined __MEDIUM__ || defined __COMPACT__ || defined __LARGE__ || defined __HUGE__
00174 #    define __DOS_16BIT__
00175 #endif
00176 
00177 
00178 #ifdef __DOS_16BIT__
00179 #    define ALLOC_CHUNK 512
00180 #    define ALLOC_SECURITY_MARGIN 100  /* don't need big margin for ms-dos/16-bit ; saves memory */
00181 #else
00182 #    define ALLOC_CHUNK 2048
00183 #    define ALLOC_SECURITY_MARGIN 1024   /* big value because some platforms have very big 'G' exponant */
00184 #endif                                   /* (for example, the VAX have 308 digit exponant)              */
00185 #if ALLOC_CHUNK < ALLOC_SECURITY_MARGIN
00186 #    error  !!! ALLOC_CHUNK < ALLOC_SECURITY_MARGIN !!!
00187 #endif
00188 /* note: to have some interest, ALLOC_CHUNK should be much greater than ALLOC_SECURITY_MARGIN */
00189 
00190 /*
00191  *  To save a lot of push/pop, every variable are stored into this
00192  *  structure, which is passed among nearly every sub-functions.
00193  */
00194 typedef struct {
00195      const char * src_string;        /* current position into intput string */
00196      char *       buffer_base;       /* output buffer */
00197      char *       dest_string;       /* current position into output string */
00198      size_t       buffer_len;        /* length of output buffer */
00199      size_t       real_len;          /* real current length of output text */
00200      size_t       pseudo_len;        /* total length of output text if it where not limited in size */
00201      size_t       maxlen;
00202      va_list      vargs;             /* pointer to current position into vargs */
00203      char *       sprintf_string;
00204      FILE *       fprintf_file;
00205      char         flavour;           /* kind of function:  asprintf() nprintf() snprintf() or fnprintf() */
00206 } xprintf_struct;
00207 
00208 /*########################### realloc_buff ################################*/
00209 /*
00210  *  Realloc buffer if needed
00211  *  Return value:  0 = ok
00212  *               EOF = not enought memory
00213  */
00214 static
00215 int
00216 realloc_buff( xprintf_struct * s,
00217               size_t len
00218             )
00219 {
00220      char * ptr;
00221 
00222      if ( len + ALLOC_SECURITY_MARGIN + s->real_len   >   s->buffer_len )
00223        {
00224           len += s->real_len + ALLOC_CHUNK;
00225           ptr = (char *)realloc( (void *)(s->buffer_base), len );
00226           if ( ptr == NULL )
00227             {
00228                s->buffer_base = NULL;
00229                return EOF;
00230             }
00231 
00232           s->dest_string = ptr + (size_t)( s->dest_string - s->buffer_base );
00233           s->buffer_base = ptr;
00234           s->buffer_len = len;
00235 
00236           (s->buffer_base)[(s->buffer_len)-1] = 1;        /* overflow marquer */
00237        }
00238 
00239      return 0;
00240 }
00241 
00242 /*############################ usual_char #################################*/
00243 /*
00244  *  Prints 'usual' characters    up to next '%'
00245  *                            or up to end of text
00246  */
00247 static
00248 int
00249 usual_char( xprintf_struct * s )
00250 {
00251      size_t len;
00252 
00253      len = strcspn( s->src_string, "%" );     /* reachs the next '%' or end of input string */
00254      /* note: 'len' is never 0 because the presence of '%' */
00255      /* or end-of-line is checked in the calling function  */
00256 
00257      if ( realloc_buff(s,len) == EOF )
00258           return EOF;
00259 
00260      memcpy( s->dest_string, s->src_string, len );
00261      s->src_string  += len;
00262      s->dest_string += len;
00263      s->real_len    += len;
00264      s->pseudo_len  += len;
00265 
00266      return 0;
00267 }
00268 
00269 /*############################ print_it ###################################*/
00270 /*
00271  *  Return value: 0 = ok
00272  *                EOF = error
00273  */
00274 static
00275 int
00276 print_it( xprintf_struct * s,
00277           size_t           approx_len,
00278           const char *     format_string,
00279           ...
00280         )
00281 {
00282      va_list varg;
00283      int     vsprintf_len;
00284      size_t  len;
00285 
00286      if ( realloc_buff(s,approx_len) == EOF )
00287           return EOF;
00288 
00289      va_start( varg, format_string );
00290      vsprintf_len = vsprintf( s->dest_string, format_string, varg );
00291      va_end( varg );
00292 
00293      if ( (s->buffer_base)[(s->buffer_len)-1] != 1 )     /* check for overflow */
00294        {
00295           fprintf( stderr, "ERROR in xnprintf library: overflow" );
00296           exit( -1 );            /* ... sorry ... what else to do ?    */
00297        }
00298 
00299      if ( vsprintf_len == EOF )    /* must be done *after* overflow-check */
00300           return EOF;
00301 
00302      s->pseudo_len  += vsprintf_len;
00303      len = strlen( s->dest_string );
00304      s->real_len    += len;
00305      s->dest_string += len;
00306 
00307      return 0;
00308 }
00309 
00310 /*############################## type_s ###################################*/
00311 /*
00312  *  Prints a string (%s)
00313  *  We need special handling because:
00314  *     a: the length of the string is unknown
00315  *     b: when .prec is used, we must not access any extra byte of the
00316  *        string (of course, if the original sprintf() does... what the
00317  *        hell, not my problem)
00318  *
00319  *  Return value: 0 = ok
00320  *                EOF = error
00321  */
00322 static
00323 int
00324 type_s( xprintf_struct * s,
00325         int              width,
00326         int              prec,
00327         const char *     format_string,
00328 #ifdef __DOS_16BIT__
00329         const char far * arg_string,
00330         int             modifier
00331 #else
00332         const char *     arg_string
00333 #endif
00334       )
00335 {
00336      size_t       string_len;
00337 
00338      if ( arg_string == NULL )
00339           return print_it( s, 6, "(null)", 0 );
00340 
00341      /* hand-made strlen() whitch stops when 'prec' is reached. */
00342      /* if 'prec' is -1 then it is never reached.               */
00343      string_len = 0;
00344      while ( arg_string[string_len]!=0 && (unsigned int)prec!=string_len )
00345           string_len++;
00346 
00347      if ( width != -1 )
00348           if ( string_len < (unsigned int)width )
00349                string_len = (unsigned int)width;
00350 
00351 #ifdef __DOS_16BIT__
00352      switch( modifier )
00353        {
00354           case 'N':   return print_it( s, string_len, format_string, (const char near*)arg_string );
00355           case 'F':   return print_it( s, string_len, format_string, (const char far*)arg_string );
00356        }
00357      /* modifier == -1 */
00358      return print_it( s, string_len, format_string, (const char*)arg_string );
00359 #else
00360      return print_it( s, string_len, format_string, arg_string );
00361 #endif
00362 }
00363 
00364 /*############################### getint ##################################*/
00365 /*
00366  *  Read a serie of digits. Stop when non-digit is found.
00367  *  Return value: the value read (between 0 and 32767).
00368  *  Note: no checks are made against overflow. If the string contain a big
00369  *  number, then the return value won't be what we want (but, in this case,
00370  *  the programmer don't know whatr he wants, then no problem).
00371  */
00372 static
00373 int
00374 getint( const char * * string )
00375 {
00376      int i;
00377 
00378      i = 0;
00379 
00380      while ( isdigit(**string) != 0 )
00381        {
00382           i = (i*10) + (**string-'0');
00383           (*string)++;
00384        }
00385 
00386      if ( i<0 || i>32767 )
00387           i = 32767;      /* if we have i==-10 this is not because the number is */
00388                           /* negative ; This is because the number is biiiig     */
00389      return i;
00390 }
00391 
00392 /*############################## dispatch #################################*/
00393 /*
00394  *  Read a part of the format string. A part is 'usual characters' (ie "blabla")
00395  *  or '%%' escape sequence (to print a single '%') or any combination of
00396  *  format specifier (ie "%i" or "%10.2d").
00397  *  After the current part is managed, the function returns to caller with
00398  *  everything ready to manage the following part.
00399  *  The caller must ensure than the string is not empty, i.e. the first byte
00400  *  is not zero.
00401  *
00402  *  Return value:  0 = ok
00403  *                 EOF = error
00404  */
00405 static
00406 int
00407 dispatch( xprintf_struct * s )
00408 {
00409      const char * initial_ptr;
00410      char   format_string[24];        /* max length may be something like  "% +-#032768.32768Ld" */
00411      char * format_ptr;
00412      int    flag_plus, flag_minus, flag_space, flag_sharp, flag_zero;
00413      int    width;
00414      int    prec;
00415      int   modifier;
00416      char   type;
00417      int    approx_width;
00418      /* most of those variables are here to rewrite the format string */
00419 
00420 #define SRCTXT  (s->src_string)
00421 #define DESTTXT (s->dest_string)
00422 
00423 /* incoherent format string. Characters after the '%' will be printed with the next call */
00424 #define INCOHERENT()         do {SRCTXT=initial_ptr; if (1) return 0;} while (0)     /* do/while to avoid */
00425 #define INCOHERENT_TEST()    do {if(*SRCTXT==0)   INCOHERENT();} while (0)    /* a null statement  */
00426 
00427 /* 'normal' text */
00428      if ( *SRCTXT != '%' )
00429           return usual_char( s );
00430 
00431 /* we then have a '%' */
00432      SRCTXT++;
00433      /* don't check for end-of-string ; this is done later */
00434 
00435 /* '%%' escape sequence */
00436      if ( *SRCTXT == '%' )
00437        {
00438           if ( realloc_buff(s,1) == EOF )   /* because we can have "%%%%%%%%..." */
00439                return EOF;
00440           *DESTTXT = '%';
00441           DESTTXT++;
00442           SRCTXT++;
00443           (s->real_len)++;
00444           (s->pseudo_len)++;
00445           return 0;
00446        }
00447 
00448 /* '%' managing */
00449      initial_ptr = SRCTXT;   /* save current pointer in case of incorrect        */
00450                              /* 'decoding'. Points just after the '%' so the '%' */
00451                              /* won't be printed in any case, as required.       */
00452      /* flag */
00453      flag_plus  = 0;     flag_minus = 0;     flag_space = 0;
00454      flag_sharp = 0;     flag_zero  = 0;
00455 
00456      for ( /*none*/; /*none*/; SRCTXT++ )
00457        {
00458           if ( *SRCTXT == ' ' ) { flag_space = 1;  continue; }
00459           if ( *SRCTXT == '+' ) { flag_plus  = 1;  continue; }
00460           if ( *SRCTXT == '-' ) { flag_minus = 1;  continue; }
00461           if ( *SRCTXT == '#' ) { flag_sharp = 1;  continue; }
00462           if ( *SRCTXT == '0' ) { flag_zero  = 1;  continue; }
00463           break;
00464        }
00465 
00466      INCOHERENT_TEST();     /* here is the first test for end of string */
00467 
00468      /* width */
00469      if ( *SRCTXT == '*' )      /* width given by next argument */
00470        {
00471           SRCTXT++;
00472           width = va_arg( s->vargs, int );
00473           if ( (unsigned int)width > 0x3fffU )    /* 'unsigned' to check against negative values too */
00474                width = 0x3fff;
00475        }
00476      else if ( isdigit(*SRCTXT) != 0 )      /* width given as ASCII number */
00477        {
00478           width = getint( &SRCTXT );
00479        }
00480      else
00481           width = -1;       /* no width specified */
00482 
00483      INCOHERENT_TEST();
00484 
00485      /* .prec */
00486      if ( *SRCTXT == '.' )
00487        {
00488           SRCTXT++;
00489           if ( *SRCTXT == '*' )     /* .prec given by next argument */
00490             {
00491                prec = va_arg( s->vargs, int );
00492                if ( (unsigned int)prec >= 0x3fffU )    /* 'unsigned' to check against negative values too */
00493                     prec = 0x3fff;
00494             }
00495           else
00496             {       /* .prec given as ASCII number */
00497                if ( isdigit(*SRCTXT) == 0 )
00498                     INCOHERENT();
00499                prec = getint( &SRCTXT );
00500             }
00501           INCOHERENT_TEST();
00502        }
00503      else
00504           prec = -1;        /* no .prec specified */
00505 
00506      /* modifier */
00507      if ( *SRCTXT == 'L' || *SRCTXT == 'h' || *SRCTXT == 'l'
00508 #ifdef __DOS_16BIT__
00509           || *SRCTXT == 'F' || *SRCTXT == 'N'
00510 #endif
00511         )
00512        {
00513           modifier = *SRCTXT;
00514           SRCTXT++;
00515           if ( modifier=='l' && *SRCTXT=='l' )
00516             {
00517                SRCTXT++;
00518                modifier = 'L';      /* 'll' == 'L'      long long == long double */
00519             }                       /* only for compatibility ; not portable     */
00520           INCOHERENT_TEST();
00521        }
00522      else
00523        {
00524           modifier = -1;    /* no modifier specified */
00525        }
00526 
00527      /* type */
00528      type = *SRCTXT;
00529      if ( strchr("diouxXfegEGcspn",type) == NULL )
00530           INCOHERENT();    /* unknown type */
00531      SRCTXT++;
00532 
00533      /* rewrite format-string */
00534      format_string[0] = '%';
00535      format_ptr = &(format_string[1]);
00536 
00537      if ( flag_plus  != 0 )    {   *format_ptr = '+';    format_ptr++;   }
00538      if ( flag_minus != 0 )    {   *format_ptr = '-';    format_ptr++;   }
00539      if ( flag_space != 0 )    {   *format_ptr = ' ';    format_ptr++;   }
00540      if ( flag_sharp != 0 )    {   *format_ptr = '#';    format_ptr++;   }
00541      if ( flag_zero  != 0 )    {   *format_ptr = '0';    format_ptr++;   }    /* '0' *must* be the last one */
00542 
00543      if ( width != -1 )
00544        {
00545           sprintf( format_ptr, "%i", width );   /* itoa() may be better but not ANSI nor POSIX */
00546           format_ptr += strlen( format_ptr );
00547        }
00548 
00549      if ( prec != -1 )
00550        {
00551           *format_ptr = '.';
00552           format_ptr++;
00553           sprintf( format_ptr, "%i", prec );
00554           format_ptr += strlen( format_ptr );
00555        }
00556 
00557      if ( modifier != -1 )
00558        {
00559           *format_ptr = modifier;
00560           format_ptr++;
00561        }
00562 
00563      *format_ptr = type;
00564      format_ptr++;
00565      *format_ptr = 0;
00566 
00567      /* vague approximation of minimal length if width or prec are specified */
00568      approx_width = width + prec;
00569      if ( approx_width < 0 )    /* because  width == -1   and/or   prec == -1 */
00570           approx_width = 0;
00571 
00572      switch ( type )
00573        {
00574           /* int */
00575           case 'd':
00576           case 'i':
00577           case 'o':
00578           case 'u':
00579           case 'x':
00580           case 'X':   switch ( modifier )
00581                         {
00582                            case -1 :   return print_it( s, approx_width, format_string, va_arg(s->vargs,int) );
00583                            case 'l':   return print_it( s, approx_width, format_string, va_arg(s->vargs,long int) );
00584                            case 'h':   return print_it( s, approx_width, format_string, va_arg(s->vargs, /*short*/ int) );
00585                            default :   INCOHERENT();                             /* 'int' instead of 'short int' because  */
00586                         }                                                        /* of default promotion is 'int'         */
00587 
00588           /* char */
00589           case 'c':   if ( modifier != -1 )
00590                            if ( print_it(s,approx_width,format_string,va_arg(s->vargs,int)) != EOF )
00591                       INCOHERENT();                         /* note: because of default promotion */
00592                                                             /* 'int' is used instead of 'char'    */
00593           /* math */
00594           case 'e':
00595           case 'f':
00596           case 'g':
00597           case 'E':
00598           case 'G':   switch ( modifier )
00599                         {
00600                            case -1 :   /* because of default promotion, no modifier means 'l' */
00601                            case 'l':   return print_it( s, approx_width, format_string, va_arg(s->vargs,double) );
00602                            case 'L':   return print_it( s, approx_width, format_string, va_arg(s->vargs,long double) );
00603                            default:    INCOHERENT();
00604                         }
00605 
00606 #ifdef __DOS_16BIT__
00607           /* string */
00608           case 's':   switch ( modifier )
00609                         {
00610                            case -1 :   return type_s( s, width, prec, format_string, (const char far*)va_arg(s->vargs,const char*), modifier );
00611                            case 'N':   return type_s( s, width, prec, format_string, (const char far*)va_arg(s->vargs,const char near*), modifier );
00612                            case 'F':   return type_s( s, width, prec, format_string, (const char far*)va_arg(s->vargs,const char far*), modifier );
00613                            default :   INCOHERENT();
00614                         }
00615 
00616           /* pointer */
00617           case 'p':   switch ( modifier )
00618                         {
00619                            case -1 :   return print_it( s, approx_width, format_string, va_arg(s->vargs,void*) );
00620                            case 'N':   return print_it( s, approx_width, format_string, va_arg(s->vargs,void near*) );
00621                            case 'F':   return print_it( s, approx_width, format_string, va_arg(s->vargs,void far*) );
00622                            default:    INCOHERENT();
00623                         }
00624 
00625           /* store */
00626           case 'n':   {   
00627                          int far * p;
00628 
00629                          switch ( modifier )
00630                            {
00631                               case -1 :   p = (int far*)va_arg( s->vargs, int* );
00632                                           break;
00633                               case 'N':   p = (int far*)va_arg( s->vargs, int near* );
00634                                           break;
00635                               case 'F':   p = (int far*)va_arg( s->vargs, int far* );
00636                                           break;
00637                               default :   INCOHERENT();
00638                            }
00639 
00640                          if ( p != NULL )
00641                            {
00642                               *p = s->pseudo_len;
00643                               return 0;
00644                            }
00645                          return EOF;
00646                       }
00647 
00648 #else   /* end of ms-dos/16-bit section */
00649           /* string */
00650           case 's':   return type_s( s, width, prec, format_string, va_arg(s->vargs,const char*) );
00651 
00652           /* pointer */
00653           case 'p':   if ( modifier == -1 )
00654                            return print_it( s, approx_width, format_string, va_arg(s->vargs,void *) );
00655                       INCOHERENT();
00656 
00657           /* store */
00658           case 'n':   if ( modifier == -1 )
00659                         {
00660                            int * p;
00661                            p = va_arg( s->vargs, int * );
00662                            if ( p != NULL )
00663                              {
00664                                 *p = s->pseudo_len;
00665                                 return 0;
00666                              }
00667                            return EOF;
00668                         }
00669                       INCOHERENT();
00670 
00671 #endif
00672        }  /* switch */
00673 
00674      INCOHERENT();  /* unknown type */
00675 
00676 #undef INCOHERENT
00677 #undef INCOHERENT_TEST
00678 #undef SRCTXT
00679 #undef DESTTXT
00680 }
00681 
00682 /*################################ core ###################################*/
00683 /*
00684  *  flavour: 'p' = (v)printf
00685  *           's' = (v)sprintf
00686  *           'f' = (v)fprintf
00687  *           'a' = (v)as(n)printf
00688  *
00689  *  Return value: number of *virtualy* written characters
00690  *                EOF = error
00691  */
00692 static
00693 int
00694 core( xprintf_struct * s )
00695 {
00696      size_t len, save_len;
00697      char * dummy_base;
00698 
00699      /* basic checks */
00700      if ( (int)(s->maxlen) <= 0 )       /* 'int' to check against some convertion */
00701           return EOF;                   /* error for example if value is (int)-10 */
00702      s->maxlen--;      /* because initial maxlen counts final 0 */
00703      /* note: now 'maxlen' _can_ be zero */
00704 
00705      if ( s->src_string == NULL )
00706           s->src_string = "(null)";
00707 
00708      /* struct init and memory allocation */
00709      s->buffer_base = NULL;
00710      s->buffer_len = 0;
00711      s->real_len = 0;
00712      s->pseudo_len = 0;
00713      if ( realloc_buff(s,0) == EOF )
00714           return EOF;
00715      s->dest_string = s->buffer_base;
00716 
00717      /* process source string */
00718      for (;;)
00719        {
00720           /* up to end of source string */
00721           if ( *(s->src_string) == 0 )
00722             {
00723                *(s->dest_string) = 0;              /* final 0 */
00724                len = s->real_len + 1;
00725                break;
00726             }
00727 
00728           if ( dispatch(s) == EOF )
00729                goto free_EOF;
00730 
00731           /* up to end of dest string */
00732           if ( s->real_len >= s->maxlen )
00733             {
00734                (s->buffer_base)[s->maxlen] = 0;    /* final 0 */
00735                len = s->maxlen + 1;
00736                break;
00737             }
00738        }
00739 
00740      /* for (v)asnprintf */
00741      dummy_base = s->buffer_base;
00742      save_len = 0;                     /* just to avoid a compiler warning */
00743 
00744      /*---*/
00745      switch ( s->flavour )
00746        {
00747           /* (v)snprintf() */
00748           case 's':   memcpy( s->sprintf_string, s->buffer_base, len );
00749                       break;
00750 
00751           /* (v)nprintf() */
00752           case 'p':   if ( puts(s->buffer_base) == EOF )
00753                            goto free_EOF;
00754                       break;
00755 
00756           /* (v)fnprintf() */
00757           case 'f' :  if ( fwrite(s->buffer_base,1,len-1,s->fprintf_file) != len-1 )      /* -1 because we don't write the final zero */
00758                            goto free_EOF;
00759                       break;
00760 
00761           /* (v)as(n)printf() */
00762           default  :  dummy_base = (s->buffer_base) + (s->real_len);
00763                       save_len = s->real_len;
00764        }
00765 
00766      /*
00767       * process the remaining of source string to compute 'pseudo_len'. We
00768       * overwrite again and again, starting at 'dummy_base' because we don't
00769       * need the text, only char count.
00770       */
00771      while( *(s->src_string) != 0 )     /* up to end of source string */
00772        {
00773           s->real_len = 0;
00774           s->dest_string = dummy_base;
00775           if ( dispatch(s) == EOF )
00776                goto free_EOF;
00777        }
00778 
00779      if ( s->flavour == 'a' )      /* (v)as(n)printf    reduce buffer size */
00780        {
00781           s->buffer_base = (char *)realloc( (void *)(s->buffer_base), save_len+1 );
00782           if ( s->buffer_base == NULL )
00783                return EOF;     /* should rarely happen because we shrink the buffer */
00784           return s->pseudo_len;
00785        }
00786 
00787      free( s->buffer_base );
00788      return s->pseudo_len;
00789 
00790 free_EOF:
00791      if ( s->buffer_base != NULL )
00792           free( s->buffer_base );
00793      return EOF;
00794 }
00795 
00796 /*############################## _nprintf #################################*/
00797 int
00798 _xn_nprintf( int          maxlen,
00799           const char * format_string,
00800           ...
00801         )
00802 {
00803      xprintf_struct s;
00804      va_list        vargs;
00805      int            retval;
00806 
00807      va_start( vargs, format_string );
00808 
00809      s.src_string = format_string;
00810      s.maxlen = (size_t)maxlen;
00811      /* s.vargs = vargs; */
00812      va_copy(s.vargs, vargs);
00813      s.flavour = 'p';
00814 
00815      retval = core( &s );
00816      va_end( vargs );
00817      return retval;
00818 }
00819 
00820 /*############################# _vnprintf #################################*/
00821 int
00822 _xn_vnprintf( int          maxlen,
00823            const char * format_string,
00824            va_list      vargs
00825          )
00826 {
00827      xprintf_struct s;
00828 
00829      s.src_string = format_string;
00830      s.maxlen = (size_t)maxlen;
00831      /* s.vargs = vargs; */
00832      va_copy(s.vargs, vargs);
00833      s.flavour = 'p';
00834 
00835      return core( &s );
00836 }
00837 
00838 /*############################## _snprintf ################################*/
00839 int
00840 _xn_snprintf( char *       dest_string,
00841            int          maxlen,
00842            const char * format_string,
00843            ...
00844          )
00845 {
00846      xprintf_struct s;
00847      va_list        vargs;
00848      int            retval;
00849 
00850      if ( dest_string == NULL )
00851           return EOF;
00852 
00853      va_start( vargs, format_string );
00854 
00855      /* memset to avoid purify UMR errors */
00856      memset(&s, 0, sizeof(s));
00857 
00858      s.src_string = format_string;
00859      s.maxlen = (size_t)maxlen;
00860      /* s.vargs = vargs; */
00861      va_copy(s.vargs, vargs);
00862      s.sprintf_string = dest_string;
00863      s.flavour = 's';
00864 
00865      retval = core( &s );
00866      va_end( vargs );
00867      if ( retval == EOF )
00868           if ( maxlen > 0 )
00869                dest_string[0] = 0;
00870 
00871      return retval;
00872 }
00873 
00874 /*############################# _vsnprintf ################################*/
00875 int
00876 _xn_vsnprintf( char *       dest_string,
00877             int          maxlen,
00878             const char * format_string,
00879             va_list      vargs
00880           )
00881 {
00882      xprintf_struct s;
00883      int            retval;
00884 
00885      if ( dest_string == NULL )
00886           return EOF;
00887 
00888      s.src_string = format_string;
00889      s.maxlen = (size_t)maxlen;
00890      /* s.vargs = vargs; */
00891      va_copy(s.vargs, vargs);
00892      s.sprintf_string = dest_string;
00893      s.flavour = 's';
00894 
00895      retval = core( &s );
00896      if ( retval == EOF )
00897           if ( maxlen > 0 )
00898                dest_string[0] = 0;
00899 
00900      return retval;
00901 }
00902 
00903 /*############################## _fnprintf ################################*/
00904 int
00905 _xn_fnprintf( FILE *       file,
00906            int          maxlen,
00907            const char * format_string,
00908            ...
00909          )
00910 {
00911      xprintf_struct s;
00912      va_list        vargs;
00913      int            retval;
00914 
00915      va_start( vargs, format_string );
00916 
00917      s.src_string = format_string;
00918      s.maxlen = (size_t)maxlen;
00919      /* s.vargs = vargs; */
00920      va_copy(s.vargs, vargs);
00921      s.fprintf_file = file;
00922      s.flavour = 'f';
00923 
00924      retval = core( &s );
00925      va_end( vargs );
00926      return retval;
00927 }
00928 
00929 /*############################# _vfnprintf ################################*/
00930 int
00931 _xn_vfnprintf( FILE *       file,
00932             int          maxlen,
00933             const char * format_string,
00934             va_list      vargs
00935           )
00936 {
00937      xprintf_struct s;
00938 
00939      s.src_string = format_string;
00940      s.maxlen = (size_t)maxlen;
00941      /* s.vargs = vargs; */
00942      va_copy(s.vargs, vargs);
00943      s.fprintf_file = file;
00944      s.flavour = 'f';
00945 
00946      return core( &s );
00947 }
00948 
00949 /*############################## _asprintf ################################*/
00950 int
00951 _xn_asprintf( char **      ptr,
00952            const char * format_string,
00953            ...
00954          )
00955 {
00956      xprintf_struct s;
00957      va_list        vargs;
00958      int            retval;
00959 
00960      va_start( vargs, format_string );
00961 
00962      s.src_string = format_string;
00963      /* s.vargs = vargs; */
00964      va_copy(s.vargs, vargs);
00965      s.flavour = 'a';
00966      s.maxlen = (size_t)INT_MAX;
00967 
00968      retval = core( &s );
00969      va_end( vargs );
00970      if ( retval != EOF )
00971        {
00972           *ptr = NULL;
00973           return EOF;
00974        }
00975 
00976      *ptr = s.buffer_base;
00977      return retval;
00978 }
00979 
00980 /*############################# _vasprintf ################################*/
00981 int
00982 _xn_vasprintf( char **      ptr,
00983             const char * format_string,
00984             va_list      vargs
00985           )
00986 {
00987      xprintf_struct s;
00988      int            retval;
00989 
00990      /* to shut up purify */
00991      memset(&s, 0x0, sizeof(s));
00992 
00993      s.src_string = format_string;
00994      /* s.vargs = vargs; */
00995      va_copy(s.vargs, vargs);
00996      s.flavour = 'a';
00997      s.maxlen = (size_t)INT_MAX;
00998 
00999      retval = core( &s );
01000      if ( retval == EOF )
01001        {
01002           *ptr = NULL;
01003           return EOF;
01004        }
01005 
01006      *ptr = s.buffer_base;
01007      return retval;
01008 }
01009 
01010 /*############################# _asnprintf ################################*/
01011 int
01012 _xn_asnprintf( char **      ptr,
01013             int          maxlen,
01014             const char * format_string,
01015             ...
01016           )
01017 {
01018      xprintf_struct s;
01019      va_list        vargs;
01020      int            retval;
01021 
01022      va_start( vargs, format_string );
01023 
01024      s.src_string = format_string;
01025      /* s.vargs = vargs; */
01026      va_copy(s.vargs, vargs);
01027      s.flavour = 'a';
01028      s.maxlen = (size_t)maxlen;
01029 
01030      retval = core( &s );
01031      va_end( vargs );
01032      if ( retval != EOF )
01033        {
01034           *ptr = NULL;
01035           return EOF;
01036        }
01037 
01038      *ptr = s.buffer_base;
01039      return retval;
01040 }
01041 
01042 /*############################# _vasnprintf ###############################*/
01043 int
01044 _xn_vasnprintf( char **      ptr,
01045              int          maxlen,
01046              const char * format_string,
01047              va_list      vargs
01048            )
01049 {
01050      xprintf_struct s;
01051      int            retval;
01052 
01053      s.src_string = format_string;
01054      /* s.vargs = vargs; */
01055      va_copy(s.vargs, vargs);
01056      s.flavour = 'a';
01057      s.maxlen = (size_t)maxlen;
01058 
01059      retval = core( &s );
01060      if ( retval == EOF )
01061        {
01062           *ptr = NULL;
01063           return EOF;
01064        }
01065 
01066      *ptr = s.buffer_base;
01067      return retval;
01068 }
01069 
01070 /*############################# END OF FILE ###############################*/
01071