/****************************************************************************
*
*                            Open Watcom Project
*
*    Portions Copyright (c) 1983-2002 Sybase, Inc. All Rights Reserved.
*
*  ========================================================================
*
*    This file contains Original Code and/or Modifications of Original
*    Code as defined in and that are subject to the Sybase Open Watcom
*    Public License version 1.0 (the 'License'). You may not use this file
*    except in compliance with the License. BY USING THIS FILE YOU AGREE TO
*    ALL TERMS AND CONDITIONS OF THE LICENSE. A copy of the License is
*    provided with the Original Code and Modifications, and is also
*    available at www.sybase.com/developer/opensource.
*
*    The Original Code and all software distributed under the License are
*    distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
*    EXPRESS OR IMPLIED, AND SYBASE AND ALL CONTRIBUTORS HEREBY DISCLAIM
*    ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF
*    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR
*    NON-INFRINGEMENT. Please see the License for the specific language
*    governing rights and limitations under the License.
*
*  ========================================================================
*
* Description:  __prtf() - low level string formatter.
*
****************************************************************************/


#define __LONG_LONG_SUPPORT__

#if !defined( __NETWARE__ ) && !defined( __UNIX__ )
    #define USE_MBCS_TRANSLATION
#endif

#include "variety.h"
#ifdef SAFE_PRINTF
    #include "saferlib.h"
#endif
#include "widechar.h"
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#if defined( __WIDECHAR__ ) || defined( USE_MBCS_TRANSLATION )
    #include <mbstring.h>
#endif
#include "ftos.h"
#include "farsupp.h"
#include "printf.h"
#include "prtscncf.h"
#include "fixpoint.h"
#include "myvalist.h"

#define BUF_SIZE    72  /* 64-bit ints formatted as binary can get big */
#define TRUE        1
#define FALSE       0

#define PASCAL_STRING           'S'             /* for Novell */
#define WIDE_CHAR_STRING        'S'

#if defined( __QNX_386__ )
    /* for use in QNX 32-bit shared library */
    #pragma aux __prtf "_sl_*" far;
#endif


#if defined( __QNX__ )
    #define EFG_PRINTF __EFG_Format
#else
    #define EFG_PRINTF (*__EFG_printf)
#endif

extern FAR_STRING EFG_PRINTF( char *buffer, my_va_list *args, _mbcs_SPECS __SLIB *specs );


#if defined( __WIDECHAR__ )
    #define _FAR_OTHER_STRING             FAR_ASCII_STRING
#else
    #define _FAR_OTHER_STRING             FAR_UNI_STRING
#endif


#if defined( __WINDOWS_386__ )
    #ifdef __SW_3S
        #pragma aux slib_callback_t modify [eax edx ecx fs gs];
    #else
        #pragma aux slib_callback_t modify [fs gs];
    #endif
#endif


/* forward references */
static const CHAR_TYPE *evalflags( const CHAR_TYPE *, SPECS __SLIB * );
static FAR_STRING formstring( CHAR_TYPE *, my_va_list *, SPECS __SLIB *, CHAR_TYPE * );
static const CHAR_TYPE * getprintspecs( const CHAR_TYPE *, my_va_list *, SPECS __SLIB * );
#ifdef USE_MBCS_TRANSLATION
static void write_wide_string( FAR_UNI_STRING str, SPECS *specs,
                               slib_callback_t *out_putc );
static void write_skinny_string( FAR_ASCII_STRING str, SPECS *specs,
                                 slib_callback_t *out_putc );
#endif

#ifdef SAFE_PRINTF
    int __F_NAME(__prtf_s,__wprtf_s)
#else
    int __F_NAME(__prtf,__wprtf)
#endif
                            ( void __SLIB *dest,    /* parm for use by out_putc */
                        const CHAR_TYPE *format,    /* pointer to format string */
                        va_list args,               /* pointer to pointer to args*/
#ifdef SAFE_PRINTF
                        const char **msg,           /* rt-constraint message */
#endif
                        slib_callback_t *out_putc ) /* char output routine */
{
    CHAR_TYPE           buffer[ BUF_SIZE ];
    CHAR_TYPE           null_char = '\0';
    CHAR_TYPE           *a;
    FAR_STRING          arg;
    const CHAR_TYPE     *ctl;
    SPECS               specs;

    specs._dest = dest;
    specs._flags = 0;
    specs._version = SPECS_VERSION;
    specs._output_count = 0;
    ctl = format;
    while( *ctl != NULLCHAR ) {
        if( *ctl != '%' ) {
            (out_putc)( &specs, *ctl++ );
        } else {
            ++ctl;
            {
                my_va_list  pargs;
                pargs = MY_VA_LIST( args );
                ctl = getprintspecs( ctl, &pargs, &specs );
                MY_VA_LIST( args ) = pargs;
            }

            specs._character = *ctl++;
            if( specs._character == NULLCHAR )
                break;        /* 05-jan-89 */

            if( specs._character == 'n' ) {
#ifdef SAFE_PRINTF
                /* The %n specifier is not allowed - too dangerous. */
                *msg = "%n";
                break;
#else
                FAR_INT         iptr;

#if defined( __FAR_SUPPORT__ )
                if( specs._flags & SPF_FAR ) {
                    iptr = va_arg( args, int _WCFAR * );
                } else if( specs._flags & SPF_NEAR ) {
                    iptr = va_arg( args, int _WCNEAR * );
                } else {
                    iptr = va_arg( args, int * );
                }
#else
                iptr = va_arg( args, int * );
#endif
                if( specs._flags & SPF_CHAR ) {
                    *((FAR_CHAR)iptr) = specs._output_count;
                } else if( specs._flags & SPF_SHORT ) {
                    *((FAR_SHORT)iptr) = specs._output_count;
                } else if( specs._flags & SPF_LONG ) {
                    *((FAR_LONG)iptr) = specs._output_count;
#if defined( __LONG_LONG_SUPPORT__ )
                } else if( specs._flags & SPF_LONG_LONG ) {
                    *((FAR_INT64)iptr) = specs._output_count;
#endif
                } else {
                    *iptr = specs._output_count;
                }
#endif  /* SAFE_PRINTF */
            } else {
#ifdef SAFE_PRINTF
                if( specs._character == 's' || specs._character == 'S' ) {
                    FAR_STRING  str;
                    va_list     args_copy;

                    /* Make sure %s argument is not NULL. Note that near pointers
                     * in segmented models need special handling because only
                     * offset will be NULL, not segment.
                     */
                    va_copy( args_copy, args );
#if defined( __FAR_SUPPORT__ )
                    if( specs._flags & SPF_FAR ) {
                        str = va_arg( args_copy, CHAR_TYPE _WCFAR * );
                    } else if( specs._flags & SPF_NEAR ) {
                        CHAR_TYPE _WCNEAR   *ptr;

                        ptr = va_arg( args_copy, CHAR_TYPE _WCNEAR * );
                        if( ptr == NULL ) {
                            str = NULL;
                        } else {
                            str = ptr;
                        }
                    } else {
                        CHAR_TYPE   *ptr;

                        ptr = va_arg( args_copy, CHAR_TYPE * );
                        if( ptr == NULL ) {
                            str = NULL;
                        } else {
                            str = ptr;
                        }
                    }
#else
                    str = va_arg( args_copy, CHAR_TYPE * );
#endif
                    va_end( args_copy );
                    if( str == NULL ) {
                        *msg = "%s -> NULL";
                        break;  /* bail out */
                    }
                }
#endif  /* SAFE_PRINTF */

                {
                    my_va_list  pargs;
                    pargs = MY_VA_LIST( args );
                    arg = formstring( buffer, &pargs, &specs, &null_char );
                    MY_VA_LIST( args ) = pargs;
                }
                specs._fld_width -= specs._n0  +
                                    specs._nz0 +
                                    specs._n1  +
                                    specs._nz1 +
                                    specs._n2  +
                                    specs._nz2;
                if( !(specs._flags & SPF_LEFT_ADJUST) ) {
                    if( specs._pad_char == ' ' ) {
                        while( specs._fld_width > 0 ) {
                            (out_putc)( &specs, ' ' );
                            --specs._fld_width;
                        }
                    }
                }
                a = buffer;
                while( specs._n0 > 0 ) {
                    (out_putc)( &specs, *a );
                    ++a;
                    --specs._n0;
                }
                while( specs._nz0 > 0 ) {
                    (out_putc)( &specs, '0' );
                    --specs._nz0;
                }
                if( specs._character == 's' ) {
#if defined( __WIDECHAR__ ) && defined( USE_MBCS_TRANSLATION )
                    if( specs._flags & SPF_SHORT ) {
                        write_skinny_string( (FAR_ASCII_STRING)arg, &specs, out_putc );
                    } else
#elif !defined( __WIDECHAR__ ) && defined( USE_MBCS_TRANSLATION )
                    if( specs._flags & SPF_LONG ) {
                        write_wide_string( (FAR_UNI_STRING)arg, &specs, out_putc );
                    } else
#endif
                    {
                        while( specs._n1 > 0 ) {
                            (out_putc)( &specs, *arg++ );
                            --specs._n1;
                        }
                    }
                }
#if !defined( __WIDECHAR__ ) && defined( USE_MBCS_TRANSLATION )
                else if( specs._character == WIDE_CHAR_STRING ) {
                    write_wide_string( (FAR_UNI_STRING)arg, &specs, out_putc );
                } else
#elif !defined( __WIDECHAR__ ) && defined( __NETWARE__ )
                else if( specs._character == WIDE_CHAR_STRING ) {
                } else
#endif
                {
                    while( specs._n1 > 0 ) {
                        (out_putc)( &specs, *arg++ );
                        --specs._n1;
                    }
                }
                while( specs._nz1 > 0 ) {
                    (out_putc)( &specs, '0' );
                    --specs._nz1;
                }
                while( specs._n2 > 0 ) {
                    (out_putc)( &specs, *arg );
                    ++arg;
                    --specs._n2;
                }
                while( specs._nz2 > 0 ) {
                    (out_putc)( &specs, '0' );
                    --specs._nz2;
                }
                if( specs._flags & SPF_LEFT_ADJUST ) {
                    while( specs._fld_width > 0 ) {
                        (out_putc)( &specs, ' ' );
                        --specs._fld_width;
                    }
                }
            }
        }
    }
    return( specs._output_count );
}

static const CHAR_TYPE * getprintspecs( const CHAR_TYPE *ctl,
                                        my_va_list *pargs,
                                        SPECS __SLIB *specs )
{
    specs->_pad_char = ' ';
    ctl = evalflags( ctl, specs );
    specs->_fld_width = 0;
    if( *ctl == '*' ) {
        specs->_fld_width = va_arg( pargs->v, int );
        if( specs->_fld_width < 0 ) {
            specs->_fld_width = - specs->_fld_width;
            specs->_flags |= SPF_LEFT_ADJUST;
        }
        ctl++;
    } else {
        while(( *ctl >= '0' ) && ( *ctl <= '9' )) {
            specs->_fld_width = specs->_fld_width * 10 + ( *ctl++ - '0' );
        }
    }
    specs->_prec = -1;
    if( *ctl == '.' ) {
        specs->_prec = 0;
        ctl++;
        if( *ctl == '*' ) {
            specs->_prec = va_arg( pargs->v, int );
            if( specs->_prec < 0 )
                specs->_prec = -1;    /* 19-jul-90 */
            ctl++;
        } else {
            while(( *ctl >= '0' ) && ( *ctl <= '9' )) {
                specs->_prec = specs->_prec * 10 + ( *ctl++ - '0' );
            }
        }
        /*
        "For b, d, i, o, u, x, X, e, E, f, g and G conversions, leading
        zeros (following any indication of sign or base) are used to
        pad the field width; no space padding is performed. If the 0
        or - flags both appear, the 0 flag is ignored.  For b, d, i, o,
        u, x or X conversions, if a precision is specified, the 0 flag
        is ignored. For other conversions, the behaviour is undefined."
        */
//      if( specs->_prec != -1 )  specs->_pad_char = ' '; /* 30-jul-95 *//*removed by JBS*/
    }
    switch( *ctl ) {
    case 'l':
#if defined( __LONG_LONG_SUPPORT__ )
        if( ctl[1] == 'l' ) {
            specs->_flags |= SPF_LONG_LONG;
            ctl += 2;
            break;
        }
#endif
        /* fall through */
    ZSPEC_CASE_LONG
    TSPEC_CASE_LONG
    case 'w':
        specs->_flags |= SPF_LONG;
        ctl++;
        break;
    case 'h':
        if( ctl[1] == 'h' ) {
            specs->_flags |= SPF_CHAR;
            ctl += 2;
            break;
        }
        specs->_flags |= SPF_SHORT;
        ctl++;
        break;
#if defined( __LONG_LONG_SUPPORT__ )
    case 'I':
        if(( ctl[1] == '6' ) && ( ctl[2] == '4' )) {
            specs->_flags |= SPF_LONG_LONG;
            ctl += 3;
        }
        break;
    JSPEC_CASE_LLONG
        /* fall through */
#endif
    case 'L':
        specs->_flags |= SPF_LONG_DOUBLE | SPF_LONG_LONG;
        ctl++;
        break;
#if defined( __FAR_SUPPORT__ )
    case 'F':                   /* conflicts with ISO-defined 'F' conversion */
        /* fall through */
#endif
    case 'W':                   /* 8086 specific flag for FAR pointer */
        specs->_flags |= SPF_FAR;
        ctl++;
        break;
    case 'N':                   /* 8086 specific flag for NEAR pointer */
        specs->_flags |= SPF_NEAR;
        ctl++;
        break;
#if defined( TSPEC_IS_INT ) || defined( ZSPEC_IS_INT )
    TSPEC_CASE_INT      /* If either 't' or 'z' spec corresponds to 'int',  */
    ZSPEC_CASE_INT      /* we need to parse and ignore the spec.            */
        ctl++;
        break;
#endif
    }
    return( ctl );
}


static const CHAR_TYPE *evalflags( const CHAR_TYPE *ctl, SPECS __SLIB *specs )
{
    specs->_flags = 0;
    for( ; ; ctl++ ) {
        if( *ctl == '-' ) {
            specs->_flags |= SPF_LEFT_ADJUST;
        } else if( *ctl == '#' ) {
            specs->_flags |= SPF_ALT;
        } else if( *ctl == '+' ) {
            specs->_flags |= SPF_FORCE_SIGN;
            specs->_flags &= ~SPF_BLANK;
        } else if( *ctl == ' ' ) {
            if( ( specs->_flags & SPF_FORCE_SIGN ) == 0 ) {
                specs->_flags |= SPF_BLANK;
            }
        } else if( *ctl == '0' ) {
            specs->_pad_char = '0';
#ifdef __QNX__
            specs->_flags |= SPF_ZERO_PAD;
#endif
        } else {
            break;
        }
    }
    return( ctl );
}


static int far_strlen( FAR_STRING s, int precision )
{
    int     len;

    len = 0;
    while(( len != precision ) && ( *s++ != NULLCHAR ))
        ++len;

    return( len );
}

/*
 * far_other_strlen - calculates the length of an ascii string
 *                    for the unicode version
 *                  - calculates the length of a unicode string for
 *                    the standard version
 */

static int far_other_strlen( FAR_STRING s, int precision )
{
    int                 len = 0;
    _FAR_OTHER_STRING   ptr = (_FAR_OTHER_STRING)s;

#if !defined( __WIDECHAR__ ) && defined( USE_MBCS_TRANSLATION )
    char                mbBuf[MB_CUR_MAX];
    int                 chBytes;

    if( precision == -1 ) {
        while( *ptr ) {
            chBytes = wctomb( mbBuf, *ptr++ );
            if( chBytes != -1 ) {
                len += chBytes;
            }
        }
        return( len );
    }
    while( *ptr && ( len <= precision )) {
        chBytes = wctomb( mbBuf, *ptr++ );
        if( chBytes != -1 ) {
            len += chBytes;
        }
    }
    return(( len <= precision ) ? len : precision );

#else

    while( *ptr++ && ( len != precision ))
        ++len;

    return( len );
#endif
}

static void fmt4hex( unsigned value, CHAR_TYPE *buf, int maxlen )
{
    int     i, len;

    __F_NAME(itoa,_itow)( value, buf, 16 );
    len = __F_NAME(strlen,wcslen)( buf );
    for( i = maxlen - 1; len; --i ) {
        --len;
        buf[i] = buf[len];
    }
    while( i >= 0 ) {
        buf[i] = '0';
        --i;
    }
    buf[maxlen] = NULLCHAR;
}


static void FixedPoint_Format( CHAR_TYPE *buf, long value, SPECS __SLIB *specs )
{
    T32         at;
    int         i;
    CHAR_TYPE   *bufp;

    at.sWhole = value;
    if( at.sWhole < 0 ) {
        at.sWhole = - at.sWhole;
        *buf++ = '-';
    }
    if( specs->_prec == -1 )
        specs->_prec = 4;

    __F_NAME(itoa,_itow)( at.wd.hi, buf, 10 );
    bufp = buf;         /* remember start address of buffer */
    while( *buf )
        ++buf;

    if( specs->_prec != 0 ) {
        *buf++ = '.';
        for( i = 0; i < specs->_prec; i++ ) {
            at.wd.hi = 0;
            at.uWhole *= 10;
            *buf++ = at.bite.b3 + '0';
        }
        *buf = NULLCHAR;
    }
    if( at.wd.lo & 0x8000 ) {   /* fraction >= .5, need to round */
        for(;;) {                               /* 22-dec-91 */
            if( buf == bufp ) {
                *buf++ = '1';
                while( *buf == '0' )
                    ++buf;

                if( *buf == '.' ) {
                    *buf++ = '0';
                    *buf++ = '.';
                    while( *buf == '0' ) {
                        ++buf;
                    }
                }
                *buf++ = '0';
                *buf = NULLCHAR;
                break;
            }
            --buf;
            if( *buf == '.' )
                --buf;

            if( *buf != '9' ) {
                ++ *buf;
                break;
            }
            *buf = '0';
        }
    }
}

static void float_format( CHAR_TYPE *buffer, my_va_list *pargs, SPECS __SLIB *specs )
{
#ifdef __WIDECHAR__
    char                mbBuffer[BUF_SIZE*MB_CUR_MAX];
    _mbcs_SPECS         mbSpecs;
    int                 count;
    size_t              rc;
    char                *p;
#endif // __WIDECHAR__

#ifdef __WIDECHAR__
    /*
     * EFG_PRINTF can only handle MBCS buffers and the MBCS version of the
     * SPECS structure.  So, make a _mbcs_SPECS structure equivalent to the
     * _wide_SPECS one, and use that instead.  Note that we can't use
     * memcpy() because some field sizes are different.
     */
    mbSpecs._dest = NULL;               /* this field isn't actually used */
    mbSpecs._flags = specs->_flags;
    mbSpecs._version = specs->_version;
    mbSpecs._fld_width = specs->_fld_width;
    mbSpecs._prec = specs->_prec;
    mbSpecs._output_count = specs->_output_count;
    mbSpecs._n0 = specs->_n0;
    mbSpecs._nz0 = specs->_nz0;
    mbSpecs._n1 = specs->_n1;
    mbSpecs._nz1 = specs->_nz1;
    mbSpecs._n2 = specs->_n2;
    mbSpecs._nz2 = specs->_nz2;
    mbSpecs._character = (char)specs->_character;
    mbSpecs._pad_char = (char)specs->_pad_char;
#endif

#ifdef __WIDECHAR__
    EFG_PRINTF( mbBuffer, pargs, &mbSpecs );
#else
    EFG_PRINTF( buffer, pargs, specs );
#endif

#ifdef __WIDECHAR__
    /*
     * Now convert the returned information back into our _wide_SPECS
     * structure.  We can't just use mbstowcs because it's an array of
     * characters, not a string.
     */
    p = mbBuffer;
    for( count = 0; count < BUF_SIZE; count++ ) {
        rc = mbtowc( &(buffer[count]), p, MB_CUR_MAX );
        if( rc == -1 ) {
            buffer[count] = L'?';
        }
        p = _mbsinc( p );
    }
    specs->_flags = mbSpecs._flags;
    specs->_version = mbSpecs._version;
    specs->_fld_width = mbSpecs._fld_width;
    specs->_prec = mbSpecs._prec;
    specs->_output_count = mbSpecs._output_count;
    specs->_n0 = mbSpecs._n0;
    specs->_nz0 = mbSpecs._nz0;
    specs->_n1 = mbSpecs._n1;
    specs->_nz1 = mbSpecs._nz1;
    specs->_n2 = mbSpecs._n2;
    specs->_nz2 = mbSpecs._nz2;
    specs->_character = (wchar_t) mbSpecs._character;
    specs->_pad_char = (wchar_t) mbSpecs._pad_char;
#endif
}

static void SetZeroPad( SPECS __SLIB *specs )
{
    int         n;

    if( !(specs->_flags & SPF_LEFT_ADJUST) ) {
        if( specs->_pad_char == '0' ) {
            n = specs->_fld_width - specs->_n0 - specs->_nz0 -
                    specs->_n1 - specs->_nz1 - specs->_n2 - specs->_nz2;
            if( n > 0 ) {
                specs->_nz0 += n;
            }
        }
    }
}


#if !defined( __WIDECHAR__ ) && defined( USE_MBCS_TRANSLATION )
static void write_wide_string( FAR_UNI_STRING str, SPECS *specs,
                               slib_callback_t *out_putc )
{
    int     bytes;
    char    mbBuf[MB_CUR_MAX];
    char    *mbBufPtr;

    while( specs->_n1 > 0 ) {
        bytes = wctomb( mbBuf, *str++ );
        if( bytes != -1 ) {
            if( bytes <= specs->_n1 ) {
                mbBufPtr = mbBuf;
                while( bytes-- ) {
                    (out_putc)( specs, *mbBufPtr++ );
                    --specs->_n1;
                }
            } else {
                specs->_n1 = 0;
            }
        }
    }
}
#endif


#if defined( __WIDECHAR__ ) && defined( USE_MBCS_TRANSLATION )
static void write_skinny_string( FAR_ASCII_STRING str, SPECS *specs,
                                 slib_callback_t *out_putc )
{
    int                 bytes;
    wchar_t             wc;
    FAR_ASCII_STRING    mbPtr = str;
    char                mbBuf[MB_CUR_MAX];

    while( specs->_n1 > 0 ) {
        mbBuf[0] = *mbPtr++;
        if( _ismbblead( mbBuf[0] ) )
            mbBuf[1] = *mbPtr++;

        bytes = mbtowc( &wc, mbBuf, MB_CUR_MAX );
        if( bytes <= specs->_n1 ) {
            if( bytes != -1 ) {
                (out_putc)( specs, wc );
                specs->_n1 -= bytes;
            }
        } else {
            specs->_n1 = 0;
        }
    }
}
#endif


static FAR_STRING formstring( CHAR_TYPE *buffer, my_va_list *pargs,
                              SPECS __SLIB *specs, CHAR_TYPE *null_string )
{
    FAR_STRING              arg;
    int                     length;
    int                     radix;
#if defined( __LONG_LONG_SUPPORT__ )
    unsigned long long      long_long_value;
#endif
    unsigned long           long_value;
    unsigned int            int_value;
#if defined( __FAR_SUPPORT__ )
    unsigned int            seg_value;
#endif
#if !defined( __WIDECHAR__ ) && defined( USE_MBCS_TRANSLATION )
    int                     bytes;
#endif

    arg = buffer;

    specs->_n0 = specs->_nz0 =
    specs->_n1 = specs->_nz1 =
    specs->_n2 = specs->_nz2 = 0;

    if( ( specs->_character == 'b' ) ||
        ( specs->_character == 'o' ) ||
        ( specs->_character == 'u' ) ||
        ( specs->_character == 'x' ) ||
        ( specs->_character == 'X' ) ) {
#if defined( __LONG_LONG_SUPPORT__ )
        if( specs->_flags & SPF_LONG_LONG ) {
            long_long_value = va_arg( pargs->v, unsigned long long );
        } else
#endif
        if( specs->_flags & SPF_LONG ) {
            long_value = va_arg( pargs->v, unsigned long );
        } else {
            long_value = va_arg( pargs->v, unsigned );
            if( specs->_flags & SPF_SHORT ) {    /* JBS 92/02/12 */
                long_value = (unsigned short) long_value;
            } else if( specs->_flags & SPF_CHAR ) {
                long_value = (unsigned char)long_value;
            }
        }
    } else

    if( ( specs->_character == 'd' ) ||
        ( specs->_character == 'i' ) ) {

#if defined( __LONG_LONG_SUPPORT__ )
        if( specs->_flags & SPF_LONG_LONG ) {
            long_long_value = va_arg( pargs->v, long long );
        } else
#endif
        if( specs->_flags & SPF_LONG ) {
            long_value = va_arg( pargs->v, long );
        } else {
            long_value = va_arg( pargs->v, int );
            if( specs->_flags & SPF_SHORT ) {    /* JBS 92/02/12 */
                long_value = (short) long_value;
            } else if( specs->_flags & SPF_CHAR ) {
                long_value = (signed char)long_value;
            }
        }
        {
            int negative = FALSE;

#if defined( __LONG_LONG_SUPPORT__ )
            if( specs->_flags & SPF_LONG_LONG ) {
                if( (long long)long_long_value < 0 ) {
                    negative = TRUE;
                }
            } else
#endif
            if( (long)long_value < 0 ) {
                negative = TRUE;
            }
            if( negative ) {
                buffer[specs->_n0++] = '-';

#if defined( __LONG_LONG_SUPPORT__ )
                if( specs->_flags & SPF_LONG_LONG ) {
                    long_long_value = -long_long_value;
                } else
#endif
                long_value = - long_value;
            } else if( specs->_flags & SPF_FORCE_SIGN ) {
                buffer[specs->_n0++] = '+';
            } else if( specs->_flags & SPF_BLANK ) {
                buffer[specs->_n0++] = ' ';
            }
        }
    }

    radix  = 10;                        /* base 10 for 'd', 'i' and 'u' */
    switch( specs->_character ) {
    case 'f':
    case 'F':
        if( specs->_flags & SPF_SHORT ) {       /* "%hf"  13-jun-91 */
            long_value = va_arg( pargs->v, long );
            FixedPoint_Format( buffer, long_value, specs );
            specs->_n1 = far_strlen( buffer, -1 );
            break;
        }
        /* types f & F fall through */

    case 'g':
    case 'G':
    case 'e':
    case 'E':
        float_format( buffer, pargs, specs );
        SetZeroPad( specs );
        arg++; // = &buffer[1];
        break;

    case 's':
#ifndef __NETWARE__
    case WIDE_CHAR_STRING:
#else
    case PASCAL_STRING:
#endif
        // arg has been initialized to point to buffer
        // set buffer[0] to a null character assuming pointer will be NULL
        // If pointer is not null, then arg will be assigned the pointer
        buffer[0] = '\0';                       // assume null pointer
#if defined( __FAR_SUPPORT__ )
        if( specs->_flags & SPF_FAR ) {
            CHAR_TYPE _WCFAR *temp = va_arg( pargs->v, CHAR_TYPE _WCFAR * );

            if( temp ) {
                arg = temp;
            }
        } else if( specs->_flags & SPF_NEAR ) {
            CHAR_TYPE _WCNEAR *temp = va_arg( pargs->v, CHAR_TYPE _WCNEAR * );

            if( temp ) {
                arg = (void *)temp;
            }
        } else {
            CHAR_TYPE *temp = va_arg( pargs->v, CHAR_TYPE * );

            if( temp ) {
                arg = temp;
            }
        }
#else
        {
            CHAR_TYPE *temp = va_arg( pargs->v, CHAR_TYPE * );

            if( temp ) {
                arg = temp;
            }
        }
#endif

#ifdef __NETWARE__
        if( specs->_character == PASCAL_STRING ) {
#ifdef __WIDECHAR__
            if( specs->_flags & SPF_SHORT )
#else
            if( specs->_flags & SPF_LONG )
#endif
            {
                length = *( (_FAR_OTHER_STRING)arg );
                arg = (FAR_STRING)( (_FAR_OTHER_STRING)arg + 1 );
            } else {
                length = *arg++;
            }
        } else
#elif !defined( __NETWARE__ ) && !defined( __WIDECHAR__ )
        if( specs->_character == WIDE_CHAR_STRING ) {
            if( specs->_flags & SPF_SHORT ) {
                length = far_strlen( arg, specs->_prec );
            } else {
                length = far_other_strlen( arg, specs->_prec );
            }
        } else
#endif

#ifdef __WIDECHAR__
        if( specs->_flags & SPF_SHORT ) {
#else
        if( specs->_flags & SPF_LONG ) {
#endif
            length = far_other_strlen( arg, specs->_prec );
        } else {
            length = far_strlen( arg, specs->_prec );
        }

        specs->_n1 = length;
        if(( specs->_prec >= 0 ) && ( specs->_prec < length )) {
            specs->_n1 = specs->_prec;
        }
        break;

    case 'x':
    case 'X':
        if( specs->_flags & SPF_ALT ) {
#if defined( __LONG_LONG_SUPPORT__ )
            if( specs->_flags & SPF_LONG_LONG ) {
                if( long_long_value != 0 ) {
                    buffer[specs->_n0++] = '0';
                    buffer[specs->_n0++] = specs->_character;
                }
            } else
#endif
            if( long_value != 0 ) {
                buffer[specs->_n0++] = '0';
                buffer[specs->_n0++] = specs->_character;
            }
        }
        radix = 16;                 /* base 16 */
        goto processNumericTypes;

    case 'b':           /* CDH 2003 Apr 23 *//* Add binary mode */
        radix = 2;                  /* base 2 */
        goto processNumericTypes;

    case 'o':
        radix = 8;                  /* base 8 */
        /* 'o' conversion falls through */

    case 'd':
    case 'i':
    case 'u':
//       'x' and 'X' jumps here

processNumericTypes:
        if( specs->_prec != -1 )
            specs->_pad_char = ' ';     /* 30-jul-95, 11-may-99 */

        /* radix contains the base; 8 for 'o', 10 for 'd' and 'i' and 'u',
           16 for 'x' and 'X', and 2 for 'b' */

        arg = &buffer[ specs->_n0 ];

#if defined( __LONG_LONG_SUPPORT__ )
        if( specs->_flags & SPF_LONG_LONG ) {
            if(( specs->_prec == 0 ) && ( long_long_value == 0 )) {
                *arg = '\0';
                length = 0;
            } else {
                __F_NAME(ulltoa,_ulltow)( long_long_value, &buffer[specs->_n0], radix );
                if( specs->_character == 'X' ) {
                    __F_NAME(strupr,_wcsupr)( buffer );
                }
                length = far_strlen( arg, -1 );
            }
        } else
#endif
        if(( specs->_prec == 0 ) && ( long_value == 0 )) {
            *arg = '\0';
            length = 0;
        } else {
            __F_NAME(ultoa,_ultow)( long_value, &buffer[specs->_n0], radix );
            if( specs->_character == 'X' ) {
                __F_NAME(strupr,_wcsupr)( buffer );
            }
            length = far_strlen( arg, -1 );
        }
        specs->_n1 = length;
        if( specs->_n1 < specs->_prec ) {
            specs->_nz0 = specs->_prec - specs->_n1;
        } else if( specs->_flags & SPF_ALT && radix < 10
         && (!length || (arg[0] != '0')) ) {
            /* For 'b' and 'o' conversions, alternate format forces the number to
             * start with a zero (effectively increases precision by one), but
             * only if it doesn't start with a zero already.
             */
            ++specs->_nz0;
        }
        if( specs->_prec == -1 ) {
            SetZeroPad( specs );
        }
        break;

    case 'p':
    case 'P':
#if defined( __FAR_SUPPORT__ )
    #if defined( __BIG_DATA__ )
        if( !( specs->_flags & (SPF_NEAR|SPF_FAR) ) ) {
            specs->_flags |= SPF_FAR;
        }
        if( specs->_fld_width == 0 ) {
            if( specs->_flags & SPF_NEAR ) {
                specs->_fld_width = sizeof( unsigned ) * 2;
            } else {
                specs->_fld_width = sizeof( CHAR_TYPE _WCFAR * ) * 2 + 1;
            }
        }
    #else
        if( specs->_fld_width == 0 ) {
            if( specs->_flags & SPF_FAR ) {
                specs->_fld_width = sizeof( CHAR_TYPE _WCFAR * ) * 2 + 1;
            } else {
                specs->_fld_width = sizeof( unsigned ) * 2;
            }
        }
    #endif
#else
        if( specs->_fld_width == 0 ) {
            specs->_fld_width = sizeof( unsigned ) * 2;
        }
#endif
        specs->_flags &= ~( SPF_BLANK | SPF_FORCE_SIGN );
        int_value = va_arg( pargs->v, unsigned );               /* offset */
#if defined( __FAR_SUPPORT__ )
        if( specs->_flags & SPF_FAR ) {
            seg_value = va_arg( pargs->v, unsigned ) & 0xFFFF; /* segment */
            /* use "unsigned short" for 386 instead of "unsigned" 21-jul-89 */
            fmt4hex( seg_value, buffer, sizeof( unsigned short ) * 2 );
            buffer[sizeof(unsigned short)*2] = ':';
            fmt4hex( int_value, buffer + sizeof( unsigned short ) * 2 + 1,
                       sizeof( unsigned ) * 2 );
        } else {
            fmt4hex( int_value, buffer, sizeof( unsigned ) * 2 );
        }
#else
        fmt4hex( int_value, buffer, sizeof( unsigned ) * 2 );
#endif
        if( specs->_character == 'P' ) {
            __F_NAME(strupr,_wcsupr)( buffer );
        }
        specs->_n0 = far_strlen( arg, -1 );
        break;

    case 'c':
#if defined( __WIDECHAR__ ) && defined( USE_MBCS_TRANSLATION )
        if( specs->_flags & SPF_SHORT ) {
            char *      mbPtr;
            char        mbBuf[MB_CUR_MAX];
            wchar_t     wc;

            mbPtr = va_arg( pargs->v, char* );
            mbBuf[0] = mbPtr[0];
            if( _ismbblead( mbBuf[0] ) )
                mbBuf[1] = mbPtr[1];

            if( mbtowc( &wc, mbBuf, MB_CUR_MAX ) != -1 ) {
                buffer[0] = wc;
            }
        } else {
            buffer[0] = va_arg( pargs->v, int );
        }
        specs->_n0 = 1;
#elif !defined( __WIDECHAR__ ) && defined( USE_MBCS_TRANSLATION )
        specs->_n0 = 1;
        if( specs->_flags & SPF_LONG ) {
            char        mbBuf[MB_CUR_MAX];
            wchar_t     wc;

            wc = va_arg( pargs->v, int );
            if( wctomb( mbBuf, wc ) != -1 ) {
                buffer[0] = mbBuf[0];
                if( _ismbblead( mbBuf[0] ) ) {
                    buffer[1] = mbBuf[1];
                    specs->_n0++;
                }
            }
        } else {
            buffer[0] = va_arg( pargs->v, int );
        }
#else
        specs->_n0 = 1;
        buffer[0] = va_arg( pargs->v, int );
#endif
        break;

#if !defined( __WIDECHAR__ ) && defined( USE_MBCS_TRANSLATION )
    case 'C':
        bytes = wctomb( buffer, va_arg( pargs->v, int ) );
//      if( bytes != -1  &&  bytes <= specs->_prec ) {
        if( bytes != -1 ) { /* Normative Addendum 4.5.3.3.1: no precision */
            specs->_n0 = bytes;
        } else {
            specs->_n0 = 0;
        }
        break;
#endif

    default:
        specs->_fld_width = 0;
        buffer[ 0 ] = specs->_character;
        specs->_n0 = 1;
        break;
    }
    return( arg );
}