/*
*******************************************************************************
*                                                                             *
* COPYRIGHT:                                                                  *
*   (C) Copyright International Business Machines Corporation, 1998           *
*   Licensed Material - Program-Property of IBM - All Rights Reserved.        *
*   US Government Users Restricted Rights - Use, duplication, or disclosure   *
*   restricted by GSA ADP Schedule Contract with IBM Corp.                    *
*                                                                             *
*******************************************************************************
*
* File uprintf.c
*
* Modification History:
*
*   Date        Name        Description
*   11/19/98    stephen        Creation.
*   03/12/99    stephen     Modified for new C API.
*                           Added conversion from default codepage.
*******************************************************************************
*/

#include "uprintf.h"
#include "uprntf_p.h"
#include "ustdio.h"
#include "ufile.h"
#include "ustring.h"
#include "locbund.h"
#include "umutex.h"
#include "unum.h"
#include "udat.h"

#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <float.h>
#include <limits.h>
#include <wchar.h>


u_printf_handler     g_u_printf_handlers     [256];
u_printf_info       g_u_printf_infos     [256];
bool_t            g_u_printf_inited    = FALSE;

/* buffer size for formatting */
#define UFPRINTF_BUFFER_SIZE 1024

int32_t 
u_fprintf(    UFILE        *f,
        const char    *patternSpecification,
        ... )
{
  va_list ap;
  int32_t count;
  
  va_start(ap, patternSpecification);
  count = u_vfprintf(f, patternSpecification, ap);
  va_end(ap);
  
  return count;
}

int32_t 
u_fprintf_u(    UFILE        *f,
        const UChar    *patternSpecification,
        ... )
{
  va_list ap;
  int32_t count;
  
  va_start(ap, patternSpecification);
  count = u_vfprintf_u(f, patternSpecification, ap);
  va_end(ap);
  
  return count;
}

int32_t 
u_vfprintf(    UFILE        *f,
        const char    *patternSpecification,
        va_list        ap)
{
  int32_t count;
  UChar *pattern;
  
  /* convert from the default codepage to Unicode */
  pattern = ufmt_defaultCPToUnicode(patternSpecification, 
                    strlen(patternSpecification));
  if(pattern == 0) {
    return 0;
  }
  
  /* do the work */
  count = u_vfprintf_u(f, pattern, ap);

  /* clean up */
  free(pattern);
  
  return count;
}

int32_t
u_printf_register_handler(UChar            spec, 
              u_printf_info     info,
              u_printf_handler     handler)
{
  /* lock the cache */
  umtx_lock(0);

  /* add to our list of function pointers */
  g_u_printf_infos[ (unsigned char) spec ]     = info;
  g_u_printf_handlers[ (unsigned char) spec ]     = handler;

  /* unlock the cache */
  umtx_unlock(0);
  return 0;
}

/* handle a '%' */

int32_t 
u_printf_simple_percent_info(const u_printf_spec_info     *info,
                 int32_t             *argtypes,
                 int32_t             n)
{
  /* we don't need any arguments */
  return 0;
}

int32_t
u_printf_simple_percent_handler(UFILE                 *stream,
                const u_printf_spec_info     *info,
                const ufmt_args            *args)
{
  /* put a single '%' on the stream */
  u_fputc(0x0025, stream);
  /* we wrote one character */
  return 1;
}

/* handle 's' */

int32_t 
u_printf_string_info(const u_printf_spec_info     *info,
             int32_t             *argtypes,
             int32_t             n)
{
  /* handle error */
  if(n < 1)
    return 0;

  /* we need 1 argument of type string */
  argtypes[0] = ufmt_string;
  return 1;
}

int32_t
u_printf_string_handler(UFILE                 *stream,
            const u_printf_spec_info     *info,
            const ufmt_args            *args)
{
  UChar *s;
  int32_t len, written, i;
  const char *arg = (const char*)(args[0].ptrValue);

  /* convert from the default codepage to Unicode */
  s = ufmt_defaultCPToUnicode(arg, strlen(arg));
  if(s == 0) {
    return 0;
  }
  len = u_strlen(s);
  
  /* width = minimum # of characters to write */
  /* precision = maximum # of characters to write */

  /* precision takes precedence over width */
  /* determine if the string should be truncated */
  if(info->fPrecision != -1 && len > info->fPrecision) {
    written = u_file_write(s, info->fPrecision, stream);
  }
  
  /* determine if the string should be padded */
  else if(info->fWidth != -1 && len < info->fWidth) {
    /* left justify */
    if(info->fLeft) {
      written = u_file_write(s, len, stream);
      for(i = 0; i < info->fWidth - len; ++i)
    written += u_file_write(&info->fPadChar, 1, stream);
    }
    /* right justify */
    else {
      written = 0;
      for(i = 0; i < info->fWidth - len; ++i)
    written += u_file_write(&info->fPadChar, 1, stream);
      written += u_file_write(s, len, stream);
    }
  }

  /* just write the string */
  else 
    written = u_file_write(s, len, stream);

  /* clean up */
  free(s);
  
  return written;
}

int32_t 
u_printf_integer_info(const u_printf_spec_info     *info,
              int32_t             *argtypes,
              int32_t             n)
{
  /* handle error */
  if(n < 1)
    return 0;

  /* we need 1 argument of type int */
  argtypes[0] = ufmt_int;
  return 1;
}
/* HSYS */
int32_t
u_printf_integer_handler(UFILE                 *stream,
             const u_printf_spec_info     *info,
             const ufmt_args            *args)
{
  int32_t         written     = 0;
  int32_t         len;
  long            num         = (long) (args[0].intValue);
  UNumberFormat        *format;
  UChar            result        [UFPRINTF_BUFFER_SIZE];
  int32_t        i, minDigits     = -1;
  UErrorCode        status        = U_ZERO_ERROR;


  /* mask off any necessary bits */
  if(info->fIsShort)
    num &= SHRT_MAX;
  else if(! info->fIsLong || ! info->fIsLongLong)
    num &= INT_MAX;

  /* get the formatter */
  format = u_locbund_getNumberFormat(stream->fBundle);

  /* handle error */
  if(format == 0)
    return 0;

  /* set the appropriate flags on the formatter */

  /* set the minimum integer digits */
  if(info->fPrecision != -1) {
    /* clone the stream's bundle if it isn't owned */
    if(! stream->fOwnBundle) {
      stream->fBundle     = u_locbund_clone(stream->fBundle);
      stream->fOwnBundle = TRUE;
      format           = u_locbund_getNumberFormat(stream->fBundle);
    }

    /* set the minimum # of digits */
    minDigits = unum_getAttribute(format, UNUM_MIN_INTEGER_DIGITS);
    unum_setAttribute(format, UNUM_MIN_INTEGER_DIGITS, info->fPrecision);
  }

  /* set whether to show the sign */
  if(info->fShowSign) {
    /* clone the stream's bundle if it isn't owned */
    if(! stream->fOwnBundle) {
      stream->fBundle     = u_locbund_clone(stream->fBundle);
      stream->fOwnBundle = TRUE;
      format           = u_locbund_getNumberFormat(stream->fBundle);
    }

    /* set whether to show the sign*/
    /* {sfb} TBD */
  }

  /* format the number */
  unum_format(format, num, result, UFPRINTF_BUFFER_SIZE, 0, &status);
  len = u_strlen(result);
  
  /* pad and justify, if needed */
  if(info->fWidth != -1 && len < info->fWidth) {
    /* left justify */
    if(info->fLeft) {
      written = u_file_write(result, len, stream);
      for(i = 0; i < info->fWidth - len; ++i)
    written += u_file_write(&info->fPadChar, 1, stream);
    }
    /* right justify */
    else {
      written = 0;
      for(i = 0; i < info->fWidth - len; ++i)
    written += u_file_write(&info->fPadChar, 1, stream);
      written += u_file_write(result, len, stream);
    }
  }
  /* just write the formatted output */
  else
    written = u_file_write(result, len, stream);
  
  /* restore the number format */
  if(minDigits != -1)
    unum_setAttribute(format, UNUM_MIN_INTEGER_DIGITS, minDigits);

  return written;
}

int32_t 
u_printf_hex_info(const u_printf_spec_info     *info,
          int32_t             *argtypes,
          int32_t             n)
{
  /* handle error */
  if(n < 1)
    return 0;

  /* we need 1 argument of type int */
  argtypes[0] = ufmt_int;
  return 1;
}

int32_t
u_printf_hex_handler(UFILE             *stream,
             const u_printf_spec_info     *info,
             const ufmt_args            *args)
{
  int32_t         written     = 0;
  long            num         = (long) (args[0].intValue);
  int32_t        i;
  UChar            result         [UFPRINTF_BUFFER_SIZE];
  int32_t         len        = UFPRINTF_BUFFER_SIZE;


  /* mask off any necessary bits */
  if(info->fIsShort)
    num &= SHRT_MAX;
  else if(! info->fIsLong || ! info->fIsLongLong)
    num &= INT_MAX;

  /* format the number, preserving the minimum # of digits */
  ufmt_ltou(result, &len, num, 16,
        (bool_t)(info->fSpec == 0x0078),
        (info->fPrecision == -1 && info->fZero) ? info->fWidth : info->fPrecision);
  
  /* convert to alt form, if desired */
  if(num != 0 && info->fAlt && len < UFPRINTF_BUFFER_SIZE - 2) {
    /* shift the formatted string right by 2 chars */
    memmove(result + 2, result, len * sizeof(UChar));
    result[0] = 0x0030;
    result[1] = info->fSpec;
    len += 2;
  }

  /* pad and justify, if needed */
  if(info->fWidth != -1 && len < info->fWidth) {
    /* left justify */
    if(info->fLeft) {
      written = u_file_write(result, len, stream);
      for(i = 0; i < info->fWidth - len; ++i)
    written += u_file_write(&info->fPadChar, 1, stream);
    }
    /* right justify */
    else {
      written = 0;
      for(i = 0; i < info->fWidth - len; ++i)
    written += u_file_write(&info->fPadChar, 1, stream);
      written += u_file_write(result, len, stream);
    }
  }
  /* just write the formatted output */
  else
    written = u_file_write(result, len, stream);
  
  return written;
}

int32_t 
u_printf_octal_info(const u_printf_spec_info     *info,
            int32_t             *argtypes,
            int32_t             n)
{
  /* handle error */
  if(n < 1)
    return 0;

  /* we need 1 argument of type int */
  argtypes[0] = ufmt_int;
  return 1;
}

int32_t
u_printf_octal_handler(UFILE                 *stream,
               const u_printf_spec_info     *info,
               const ufmt_args            *args)
{
  int32_t         written     = 0;
  long            num         = (long) (args[0].intValue);
  int32_t        i;
  UChar            result         [UFPRINTF_BUFFER_SIZE];
  int32_t         len        = UFPRINTF_BUFFER_SIZE;


  /* mask off any necessary bits */
  if(info->fIsShort)
    num &= SHRT_MAX;
  else if(! info->fIsLong || ! info->fIsLongLong)
    num &= INT_MAX;

  /* format the number, preserving the minimum # of digits */
  ufmt_ltou(result, &len, num, 8,
        FALSE, /* doesn't matter for octal */
        info->fPrecision == -1 && info->fZero ? info->fWidth : info->fPrecision);

  /* convert to alt form, if desired */
  if(info->fAlt && result[0] != 0x0030 && len < UFPRINTF_BUFFER_SIZE - 1) {
    /* shift the formatted string right by 1 char */
    memmove(result + 1, result, len * sizeof(UChar));
    result[0] = 0x0030;
    len += 1;
  }

  /* pad and justify, if needed */
  if(info->fWidth != -1 && len < info->fWidth) {
    /* left justify */
    if(info->fLeft) {
      written = u_file_write(result, len, stream);
      for(i = 0; i < info->fWidth - len; ++i)
    written += u_file_write(&info->fPadChar, 1, stream);
    }
    /* right justify */
    else {
      written = 0;
      for(i = 0; i < info->fWidth - len; ++i)
    written += u_file_write(&info->fPadChar, 1, stream);
      written += u_file_write(result, len, stream);
    }
  }
  /* just write the formatted output */
  else
    written = u_file_write(result, len, stream);
  
  return written;
}


int32_t 
u_printf_double_info(const u_printf_spec_info     *info,
             int32_t             *argtypes,
             int32_t             n)
{
  /* handle error */
  if(n < 1)
    return 0;

  /* we need 1 argument of type double */
  argtypes[0] = ufmt_double;
  return 1;
}

int32_t
u_printf_double_handler(UFILE                 *stream,
            const u_printf_spec_info     *info,
            const ufmt_args            *args)
{
  int32_t         written     = 0;
  int32_t         len;
  double        num         = (double) (args[0].doubleValue);
  UNumberFormat        *format;
  UChar            result        [UFPRINTF_BUFFER_SIZE];
  int32_t        i, minDecimalDigits;
  int32_t        maxDecimalDigits;
  UErrorCode        status        = U_ZERO_ERROR;

  /* mask off any necessary bits */
  /*  if(! info->fIsLongDouble)
      num &= DBL_MAX;*/

  /* get the formatter */
  format = u_locbund_getNumberFormat(stream->fBundle);

  /* handle error */
  if(format == 0)
    return 0;

  /* set the appropriate flags on the formatter */

  /* clone the stream's bundle if it isn't owned */
  if(! stream->fOwnBundle) {
    stream->fBundle     = u_locbund_clone(stream->fBundle);
    stream->fOwnBundle     = TRUE;
    format           = u_locbund_getNumberFormat(stream->fBundle);
  }

  /* set the number of decimal digits */

  /* save the formatter's state */
  minDecimalDigits = unum_getAttribute(format, UNUM_MIN_FRACTION_DIGITS);
  maxDecimalDigits = unum_getAttribute(format, UNUM_MAX_FRACTION_DIGITS);

  if(info->fPrecision != -1) {
    /* set the # of decimal digits */
    unum_setAttribute(format, UNUM_FRACTION_DIGITS, info->fPrecision);
  }
  else if(info->fPrecision == 0 && ! info->fAlt) {
    /* no decimal point in this case */
    unum_setAttribute(format, UNUM_FRACTION_DIGITS, 0);
  }
  else if(info->fAlt) {
    /* '#' means always show decimal point */
    /* copy of printf behavior on Solaris - '#' shows 6 digits */
    unum_setAttribute(format, UNUM_FRACTION_DIGITS, 6);
  }
  else {
    /* # of decimal digits is 6 if precision not specified */
    unum_setAttribute(format, UNUM_FRACTION_DIGITS, 6);
  }

  /* set whether to show the sign */
  if(info->fShowSign) {
    /* set whether to show the sign*/
    /* {sfb} TBD */
  }

  /* format the number */
  unum_formatDouble(format, num, result, UFPRINTF_BUFFER_SIZE, 0, &status);
  len = u_strlen(result);

  /* pad and justify, if needed */
  if(info->fWidth != -1 && len < info->fWidth) {
    /* left justify */
    if(info->fLeft) {
      written = u_file_write(result, len, stream);
      for(i = 0; i < info->fWidth - len; ++i)
    written += u_file_write(&info->fPadChar, 1, stream);
    }
    /* right justify */
    else {
      written = 0;
      for(i = 0; i < info->fWidth - len; ++i)
    written += u_file_write(&info->fPadChar, 1, stream);
      written += u_file_write(result, len, stream);
    }
  }
  /* just write the formatted output */
  else
    written = u_file_write(result, len, stream);
  
  /* restore the number format */
  unum_setAttribute(format, UNUM_MIN_FRACTION_DIGITS, minDecimalDigits);
  unum_setAttribute(format, UNUM_MAX_FRACTION_DIGITS, maxDecimalDigits);

  return written;
}


int32_t 
u_printf_char_info(const u_printf_spec_info     *info,
           int32_t             *argtypes,
           int32_t             n)
{
  /* handle error */
  if(n < 1)
    return 0;

  /* we need 1 argument of type char */
  argtypes[0] = ufmt_char;
  return 1;
}

int32_t
u_printf_char_handler(UFILE                 *stream,
              const u_printf_spec_info         *info,
              const ufmt_args              *args)
{
  UChar *s;
  int32_t len, written = 0, i;
  unsigned char arg = (unsigned char)(args[0].intValue);

  /* convert from default codepage to Unicode */
  s = ufmt_defaultCPToUnicode(&arg, 1);
  if(s == 0) {
    return 0;
  }
  len = u_strlen(s);
  
  /* width = minimum # of characters to write */
  /* precision = maximum # of characters to write */

  /* precision takes precedence over width */
  /* determine if the string should be truncated */
  if(info->fPrecision != -1 && len > info->fPrecision) {
    written = u_file_write(s, info->fPrecision, stream);
  }
  
  /* determine if the string should be padded */
  else if(info->fWidth != -1 && len < info->fWidth) {
    /* left justify */
    if(info->fLeft) {
      written = u_file_write(s, len, stream);
      for(i = 0; i < info->fWidth - len; ++i)
    written += u_file_write(&info->fPadChar, 1, stream);
    }
    /* right justify */
    else {
      written = 0;
      for(i = 0; i < info->fWidth - len; ++i)
    written += u_file_write(&info->fPadChar, 1, stream);
      written += u_file_write(s, len, stream);
    }
  }

  /* just write the string */
  else 
    written = u_file_write(s, len, stream);

  /* clean up */
  free(s);

  return written;
}


int32_t 
u_printf_pointer_info(const u_printf_spec_info     *info,
              int32_t             *argtypes,
              int32_t             n)
{
  /* handle error */
  if(n < 1)
    return 0;

  /* we need 1 argument of type void* */
  argtypes[0] = ufmt_pointer;
  return 1;
}

int32_t
u_printf_pointer_handler(UFILE                 *stream,
             const u_printf_spec_info     *info,
             const ufmt_args            *args)
{
  int32_t         written     = 0;
  long            num         = (long) (args[0].intValue);
  int32_t        i;
  UChar            result         [UFPRINTF_BUFFER_SIZE];
  int32_t         len        = UFPRINTF_BUFFER_SIZE;


  /* format the pointer in hex */
  ufmt_ltou(result, &len, num, 16, TRUE, info->fPrecision);

  /* pad and justify, if needed */
  if(info->fWidth != -1 && len < info->fWidth) {
    /* left justify */
    if(info->fLeft) {
      written = u_file_write(result, len, stream);
      for(i = 0; i < info->fWidth - len; ++i)
    written += u_file_write(&info->fPadChar, 1, stream);
    }
    /* right justify */
    else {
      written = 0;
      for(i = 0; i < info->fWidth - len; ++i)
    written += u_file_write(&info->fPadChar, 1, stream);
      written += u_file_write(result, len, stream);
    }
  }
  /* just write the formatted output */
  else
    written = u_file_write(result, len, stream);
  
  return written;
}


int32_t 
u_printf_scientific_info(const u_printf_spec_info     *info,
             int32_t             *argtypes,
             int32_t             n)
{
  /* handle error */
  if(n < 1)
    return 0;
  
  /* we need 1 argument of type double */
  argtypes[0] = ufmt_double;
  return 1;
}

int32_t
u_printf_scientific_handler(UFILE             *stream,
                const u_printf_spec_info     *info,
                const ufmt_args            *args)
{
  int32_t         written     = 0;
  int32_t         len;
  double        num         = (double) (args[0].doubleValue);
  UNumberFormat        *format;
  UChar            result        [UFPRINTF_BUFFER_SIZE];
  int32_t        i, minDecimalDigits;
  int32_t        maxDecimalDigits;
  UErrorCode        status        = U_ZERO_ERROR;
  

  /* mask off any necessary bits */
  /*  if(! info->fIsLongDouble)
      num &= DBL_MAX;*/

  /* get the formatter */
  format = u_locbund_getScientificFormat(stream->fBundle);

  /* handle error */
  if(format == 0)
    return 0;

  /* set the appropriate flags on the formatter */

  /* clone the stream's bundle if it isn't owned */
  if(! stream->fOwnBundle) {
    stream->fBundle     = u_locbund_clone(stream->fBundle);
    stream->fOwnBundle     = TRUE;
    format           = u_locbund_getScientificFormat(stream->fBundle);
  }

  /* set the number of decimal digits */

  /* save the formatter's state */
  minDecimalDigits = unum_getAttribute(format, UNUM_MIN_FRACTION_DIGITS);
  maxDecimalDigits = unum_getAttribute(format, UNUM_MAX_FRACTION_DIGITS);

  if(info->fPrecision != -1) {
    /* set the # of decimal digits */
    unum_setAttribute(format, UNUM_FRACTION_DIGITS, info->fPrecision);
  }
  else if(info->fPrecision == 0 && ! info->fAlt) {
    /* no decimal point in this case */
    unum_setAttribute(format, UNUM_FRACTION_DIGITS, 0);
  }
  else if(info->fAlt) {
    /* '#' means always show decimal point */
    /* copy of printf behavior on Solaris - '#' shows 6 digits */
    unum_setAttribute(format, UNUM_FRACTION_DIGITS, 6);
  }
  else {
    /* # of decimal digits is 6 if precision not specified */
    unum_setAttribute(format, UNUM_FRACTION_DIGITS, 6);
  }

  /* set whether to show the sign */
  if(info->fShowSign) {
    /* set whether to show the sign*/
    /* {sfb} TBD */
  }

  /* format the number */
  unum_formatDouble(format, num, result, UFPRINTF_BUFFER_SIZE, 0, &status);
  len = u_strlen(result);

  /* pad and justify, if needed */
  if(info->fWidth != -1 && len < info->fWidth) {
    /* left justify */
    if(info->fLeft) {
      written = u_file_write(result, len, stream);
      for(i = 0; i < info->fWidth - len; ++i)
    written += u_file_write(&info->fPadChar, 1, stream);
    }
    /* right justify */
    else {
      written = 0;
      for(i = 0; i < info->fWidth - len; ++i)
    written += u_file_write(&info->fPadChar, 1, stream);
      written += u_file_write(result, len, stream);
    }
  }
  /* just write the formatted output */
  else
    written = u_file_write(result, len, stream);
  
  /* restore the number format */
  unum_setAttribute(format, UNUM_MIN_FRACTION_DIGITS, minDecimalDigits);
  unum_setAttribute(format, UNUM_MAX_FRACTION_DIGITS, maxDecimalDigits);

  return written;
}

int32_t 
u_printf_date_info(const u_printf_spec_info     *info,
           int32_t             *argtypes,
           int32_t             n)
{
  /* handle error */
  if(n < 1)
    return 0;
  
  /* we need 1 argument of type Date */
  argtypes[0] = ufmt_date;
  return 1;
}

int32_t
u_printf_date_handler(UFILE             *stream,
              const u_printf_spec_info     *info,
              const ufmt_args         *args)
{
  int32_t         written     = 0;
  int32_t         len;
  UDate            num         = (UDate) (args[0].dateValue);
  UDateFormat        *format;
  UChar            result        [UFPRINTF_BUFFER_SIZE];
  int32_t        i;
  UErrorCode        status        = U_ZERO_ERROR;


  /* get the formatter */
  format = u_locbund_getDateFormat(stream->fBundle);
  
  /* handle error */
  if(format == 0)
    return 0;

  /* format the date */
  udat_format(format, num, result, UFPRINTF_BUFFER_SIZE, 0, &status);
  len = u_strlen(result);

  /* pad and justify, if needed */
  if(info->fWidth != -1 && len < info->fWidth) {
    /* left justify */
    if(info->fLeft) {
      written = u_file_write(result, len, stream);
      for(i = 0; i < info->fWidth - len; ++i)
    written += u_file_write(&info->fPadChar, 1, stream);
    }
    /* right justify */
    else {
      written = 0;
      for(i = 0; i < info->fWidth - len; ++i)
    written += u_file_write(&info->fPadChar, 1, stream);
      written += u_file_write(result, len, stream);
    }
  }
  /* just write the formatted output */
  else
    written = u_file_write(result, len, stream);
  
  return written;
}

int32_t 
u_printf_time_info(const u_printf_spec_info     *info,
           int32_t             *argtypes,
           int32_t             n)
{
  /* handle error */
  if(n < 1)
    return 0;
  
  /* we need 1 argument of type date */
  argtypes[0] = ufmt_date;
  return 1;
}

int32_t
u_printf_time_handler(UFILE             *stream,
              const u_printf_spec_info     *info,
              const ufmt_args         *args)
{
  int32_t         written     = 0;
  int32_t         len;
  UDate            num         = (UDate) (args[0].dateValue);
  UDateFormat        *format;
  UChar            result        [UFPRINTF_BUFFER_SIZE];
  int32_t        i;
  UErrorCode        status        = U_ZERO_ERROR;


  /* get the formatter */
  format = u_locbund_getTimeFormat(stream->fBundle);
  
  /* handle error */
  if(format == 0)
    return 0;

  /* format the time */
  udat_format(format, num, result, UFPRINTF_BUFFER_SIZE, 0, &status);
  len = u_strlen(result);

  /* pad and justify, if needed */
  if(info->fWidth != -1 && len < info->fWidth) {
    /* left justify */
    if(info->fLeft) {
      written = u_file_write(result, len, stream);
      for(i = 0; i < info->fWidth - len; ++i)
    written += u_file_write(&info->fPadChar, 1, stream);
    }
    /* right justify */
    else {
      written = 0;
      for(i = 0; i < info->fWidth - len; ++i)
    written += u_file_write(&info->fPadChar, 1, stream);
      written += u_file_write(result, len, stream);
    }
  }
  /* just write the formatted output */
  else
    written = u_file_write(result, len, stream);
  
  return written;
}


int32_t 
u_printf_percent_info(const u_printf_spec_info     *info,
              int32_t             *argtypes,
              int32_t             n)
{
  /* handle error */
  if(n < 1)
    return 0;

  /* we need 1 argument of type double */
  argtypes[0] = ufmt_double;
  return 1;
}

int32_t
u_printf_percent_handler(UFILE                 *stream,
             const u_printf_spec_info     *info,
             const ufmt_args            *args)
{
  int32_t         written     = 0;
  int32_t         len;
  double        num         = (double) (args[0].doubleValue);
  UNumberFormat        *format;
  UChar            result        [UFPRINTF_BUFFER_SIZE];
  int32_t        i, minDecimalDigits;
  int32_t        maxDecimalDigits;
  UErrorCode        status        = U_ZERO_ERROR;


  /* mask off any necessary bits */
  /*  if(! info->fIsLongDouble)
      num &= DBL_MAX;*/

  /* get the formatter */
  format = u_locbund_getPercentFormat(stream->fBundle);

  /* handle error */
  if(format == 0)
    return 0;

  /* set the appropriate flags on the formatter */

  /* clone the stream's bundle if it isn't owned */
  if(! stream->fOwnBundle) {
    stream->fBundle     = u_locbund_clone(stream->fBundle);
    stream->fOwnBundle     = TRUE;
    format           = u_locbund_getPercentFormat(stream->fBundle);
  }

  /* set the number of decimal digits */

  /* save the formatter's state */
  minDecimalDigits = unum_getAttribute(format, UNUM_MIN_FRACTION_DIGITS);
  maxDecimalDigits = unum_getAttribute(format, UNUM_MAX_FRACTION_DIGITS);

  if(info->fPrecision != -1) {
    /* set the # of decimal digits */
    unum_setAttribute(format, UNUM_FRACTION_DIGITS, info->fPrecision);
  }
  else if(info->fPrecision == 0 && ! info->fAlt) {
    /* no decimal point in this case */
    unum_setAttribute(format, UNUM_FRACTION_DIGITS, 0);
  }
  else if(info->fAlt) {
    /* '#' means always show decimal point */
    /* copy of printf behavior on Solaris - '#' shows 6 digits */
    unum_setAttribute(format, UNUM_FRACTION_DIGITS, 6);
  }
  else {
    /* # of decimal digits is 6 if precision not specified */
    unum_setAttribute(format, UNUM_FRACTION_DIGITS, 6);
  }

  /* set whether to show the sign */
  if(info->fShowSign) {
    /* set whether to show the sign*/
    /* {sfb} TBD */
  }

  /* format the number */
  unum_formatDouble(format, num, result, UFPRINTF_BUFFER_SIZE, 0, &status);
  len = u_strlen(result);

  /* pad and justify, if needed */
  if(info->fWidth != -1 && len < info->fWidth) {
    /* left justify */
    if(info->fLeft) {
      written = u_file_write(result, len, stream);
      for(i = 0; i < info->fWidth - len; ++i)
    written += u_file_write(&info->fPadChar, 1, stream);
    }
    /* right justify */
    else {
      written = 0;
      for(i = 0; i < info->fWidth - len; ++i)
    written += u_file_write(&info->fPadChar, 1, stream);
      written += u_file_write(result, len, stream);
    }
  }
  /* just write the formatted output */
  else
    written = u_file_write(result, len, stream);
  
  /* restore the number format */
  unum_setAttribute(format, UNUM_MIN_FRACTION_DIGITS, minDecimalDigits);
  unum_setAttribute(format, UNUM_MAX_FRACTION_DIGITS, maxDecimalDigits);

  return written;
}


int32_t 
u_printf_currency_info(const u_printf_spec_info     *info,
               int32_t                 *argtypes,
               int32_t                 n)
{
  /* handle error */
  if(n < 1)
    return 0;

  /* we need 1 argument of type double */
  argtypes[0] = ufmt_double;
  return 1;
}

int32_t
u_printf_currency_handler(UFILE             *stream,
              const u_printf_spec_info     *info,
              const ufmt_args            *args)
{
  int32_t         written     = 0;
  int32_t         len;
  double        num         = (double) (args[0].doubleValue);
  UNumberFormat        *format;
  UChar            result        [UFPRINTF_BUFFER_SIZE];
  int32_t        i, minDecimalDigits;
  int32_t        maxDecimalDigits;
  UErrorCode        status        = U_ZERO_ERROR;


  /* mask off any necessary bits */
  /*  if(! info->fIsLongDouble)
      num &= DBL_MAX;*/

  /* get the formatter */
  format = u_locbund_getCurrencyFormat(stream->fBundle);

  /* handle error */
  if(format == 0)
    return 0;

  /* set the appropriate flags on the formatter */

  /* clone the stream's bundle if it isn't owned */
  if(! stream->fOwnBundle) {
    stream->fBundle     = u_locbund_clone(stream->fBundle);
    stream->fOwnBundle     = TRUE;
    format           = u_locbund_getCurrencyFormat(stream->fBundle);
  }

  /* set the number of decimal digits */

  /* save the formatter's state */
  minDecimalDigits = unum_getAttribute(format, UNUM_MIN_FRACTION_DIGITS);
  maxDecimalDigits = unum_getAttribute(format, UNUM_MAX_FRACTION_DIGITS);

  if(info->fPrecision != -1) {
    /* set the # of decimal digits */
    unum_setAttribute(format, UNUM_FRACTION_DIGITS, info->fPrecision);
  }
  else if(info->fPrecision == 0 && ! info->fAlt) {
    /* no decimal point in this case */
    unum_setAttribute(format, UNUM_FRACTION_DIGITS, 0);
  }
  else if(info->fAlt) {
    /* '#' means always show decimal point */
    /* copy of printf behavior on Solaris - '#' shows 6 digits */
    unum_setAttribute(format, UNUM_FRACTION_DIGITS, 6);
  }
  else {
    /* # of decimal digits is 6 if precision not specified */
    unum_setAttribute(format, UNUM_FRACTION_DIGITS, 6);
  }

  /* set whether to show the sign */
  if(info->fShowSign) {
    /* set whether to show the sign*/
    /* {sfb} TBD */
  }

  /* format the number */
  unum_formatDouble(format, num, result, UFPRINTF_BUFFER_SIZE, 0, &status);
  len = u_strlen(result);

  /* pad and justify, if needed */
  if(info->fWidth != -1 && len < info->fWidth) {
    /* left justify */
    if(info->fLeft) {
      written = u_file_write(result, len, stream);
      for(i = 0; i < info->fWidth - len; ++i)
    written += u_file_write(&info->fPadChar, 1, stream);
    }
    /* right justify */
    else {
      written = 0;
      for(i = 0; i < info->fWidth - len; ++i)
    written += u_file_write(&info->fPadChar, 1, stream);
      written += u_file_write(result, len, stream);
    }
  }
  /* just write the formatted output */
  else
    written = u_file_write(result, len, stream);
  
  /* restore the number format */
  unum_setAttribute(format, UNUM_MIN_FRACTION_DIGITS, minDecimalDigits);
  unum_setAttribute(format, UNUM_MAX_FRACTION_DIGITS, maxDecimalDigits);

  return written;
}

int32_t 
u_printf_ustring_info(const u_printf_spec_info     *info,
              int32_t             *argtypes,
              int32_t             n)
{
  /* handle error */
  if(n < 1)
    return 0;

  /* we need 1 argument of type ustring */
  argtypes[0] = ufmt_ustring;
  return 1;
}

int32_t
u_printf_ustring_handler(UFILE                 *stream,
             const u_printf_spec_info     *info,
             const ufmt_args         *args)
{
  int32_t len, written, i;
  const UChar *arg = (const UChar*)(args[0].ptrValue);

  /* allocate enough space for the buffer */
  len = u_strlen(arg);

  /* width = minimum # of characters to write */
  /* precision = maximum # of characters to write */

  /* precision takes precedence over width */
  /* determine if the string should be truncated */
  if(info->fPrecision != -1 && len > info->fPrecision) {
    written = u_file_write(arg, info->fPrecision, stream);
  }
  
  /* determine if the string should be padded */
  else if(info->fWidth != -1 && len < info->fWidth) {
    /* left justify */
    if(info->fLeft) {
      written = u_file_write(arg, len, stream);
      for(i = 0; i < info->fWidth - len; ++i)
    written += u_file_write(&info->fPadChar, 1, stream);
    }
    /* right justify */
    else {
      written = 0;
      for(i = 0; i < info->fWidth - len; ++i)
    written += u_file_write(&info->fPadChar, 1, stream);
      written += u_file_write(arg, len, stream);
    }
  }

  /* just write the string */
  else 
    written = u_file_write(arg, len, stream);

  return written;
}



int32_t 
u_printf_uchar_info(const u_printf_spec_info     *info,
            int32_t             *argtypes,
            int32_t             n)
{
  /* handle error */
  if(n < 1)
    return 0;

  /* we need 1 argument of type uchar */
  argtypes[0] = ufmt_uchar;
  return 1;
}

int32_t
u_printf_uchar_handler(UFILE                 *stream,
               const u_printf_spec_info     *info,
               const ufmt_args            *args)
{
  int32_t written = 0, i;
  UChar arg = (UChar)(args[0].intValue);
  

  /* width = minimum # of characters to write */
  /* precision = maximum # of characters to write */

  /* precision takes precedence over width */
  /* determine if the char should be printed */
  if(info->fPrecision != -1 && info->fPrecision < 1) {
    /* write nothing */
    written = 0;
  }
  
  /* determine if the character should be padded */
  else if(info->fWidth != -1 && info->fWidth > 1) {
    /* left justify */
    if(info->fLeft) {
      written = u_file_write(&arg, 1, stream);
      for(i = 0; i < info->fWidth - 1; ++i)
    written += u_file_write(&info->fPadChar, 1, stream);
    }
    /* right justify */
    else {
      written = 0;
      for(i = 0; i < info->fWidth - 1; ++i)
    written += u_file_write(&info->fPadChar, 1, stream);
      written += u_file_write(&arg, 1, stream);
    }
  }

  /* just write the character */
  else 
    written = u_file_write(&arg, 1, stream);

  return written;
}

int32_t 
u_printf_scidbl_info(const u_printf_spec_info     *info,
             int32_t             *argtypes,
             int32_t             n)
{
  /* handle error */
  if(n < 1)
    return 0;

  /* we need 1 argument of type double */
  argtypes[0] = ufmt_double;
  return 1;
}

int32_t
u_printf_scidbl_handler(UFILE                 *stream,
            const u_printf_spec_info     *info,
            const ufmt_args            *args)
{
  double     num = (double)(args[0].doubleValue);
  bool_t     useE;

  /* a precision of 0 is taken as 1 */
  if(info->fPrecision == 0)
    ((u_printf_spec_info*)info)->fPrecision = 1;

  /* determine whether to use 'e' or 'f' */
  useE = (num < 0.0001 || (info->fPrecision != -1 && num > pow(10.0, info->fPrecision)));
  
  /* use 'e' */
  if(useE) {
    /* adjust the specifier */
    ((u_printf_spec_info*)info)->fSpec = 0x0065;
    /* call the scientific handler */
    return u_printf_scientific_handler(stream, info, args);
  }
  /* use 'f' */
  else {
    /* adjust the specifier */
    ((u_printf_spec_info*)info)->fSpec = 0x0066;
    /* call the double handler */
    return u_printf_double_handler(stream, info, args);
  }
}


int32_t 
u_printf_count_info(const u_printf_spec_info     *info,
            int32_t             *argtypes,
            int32_t             n)
{
  /* handle error */
  if(n < 1)
    return 0;

  /* we need 1 argument of type count */
  argtypes[0] = ufmt_count;
  return 1;
}

int32_t
u_printf_count_handler(UFILE                 *stream,
               const u_printf_spec_info     *info,
               const ufmt_args            *args)
{
  int *count = (int*)(args[0].ptrValue);

  /* in the special case of count, the u_printf_spec_info's width */
  /* will contain the # of chars written thus far */
  *count = info->fWidth;

  return 0;
}


int32_t 
u_printf_spellout_info(const u_printf_spec_info *info,
               int32_t             *argtypes,
               int32_t             n)
{
  /* handle error */
  if(n < 1)
    return 0;

  /* we need 1 argument of type double */
  argtypes[0] = ufmt_double;
  return 1;
}

int32_t
u_printf_spellout_handler(UFILE             *stream,
              const u_printf_spec_info     *info,
              const ufmt_args            *args)
{
  int32_t         written     = 0;
  int32_t         len;
  double        num         = (double) (args[0].doubleValue);
  UNumberFormat        *format;
  UChar            result        [UFPRINTF_BUFFER_SIZE];
  int32_t        i, minDecimalDigits;
  int32_t        maxDecimalDigits;
  UErrorCode        status        = U_ZERO_ERROR;


  /* mask off any necessary bits */
  /*  if(! info->fIsLongDouble)
      num &= DBL_MAX;*/

  /* get the formatter */
  format = u_locbund_getSpelloutFormat(stream->fBundle);

  /* handle error */
  if(format == 0)
    return 0;

  /* set the appropriate flags on the formatter */

  /* clone the stream's bundle if it isn't owned */
  if(! stream->fOwnBundle) {
    stream->fBundle     = u_locbund_clone(stream->fBundle);
    stream->fOwnBundle     = TRUE;
    format           = u_locbund_getSpelloutFormat(stream->fBundle);
  }

  /* set the number of decimal digits */

  /* save the formatter's state */
  minDecimalDigits = unum_getAttribute(format, UNUM_MIN_FRACTION_DIGITS);
  maxDecimalDigits = unum_getAttribute(format, UNUM_MAX_FRACTION_DIGITS);

  if(info->fPrecision != -1) {
    /* set the # of decimal digits */
    unum_setAttribute(format, UNUM_FRACTION_DIGITS, info->fPrecision);
  }
  else if(info->fPrecision == 0 && ! info->fAlt) {
    /* no decimal point in this case */
    unum_setAttribute(format, UNUM_FRACTION_DIGITS, 0);
  }
  else if(info->fAlt) {
    /* '#' means always show decimal point */
    /* copy of printf behavior on Solaris - '#' shows 6 digits */
    unum_setAttribute(format, UNUM_FRACTION_DIGITS, 6);
  }
  else {
    /* # of decimal digits is 6 if precision not specified */
    unum_setAttribute(format, UNUM_FRACTION_DIGITS, 6);
  }

  /* set whether to show the sign */
  if(info->fShowSign) {
    /* set whether to show the sign*/
    /* {sfb} TBD */
  }

  /* format the number */
  unum_formatDouble(format, num, result, UFPRINTF_BUFFER_SIZE, 0, &status);
  len = u_strlen(result);

  /* pad and justify, if needed */
  if(info->fWidth != -1 && len < info->fWidth) {
    /* left justify */
    if(info->fLeft) {
      written = u_file_write(result, len, stream);
      for(i = 0; i < info->fWidth - len; ++i)
    written += u_file_write(&info->fPadChar, 1, stream);
    }
    /* right justify */
    else {
      written = 0;
      for(i = 0; i < info->fWidth - len; ++i)
    written += u_file_write(&info->fPadChar, 1, stream);
      written += u_file_write(result, len, stream);
    }
  }
  /* just write the formatted output */
  else
    written = u_file_write(result, len, stream);
  
  /* restore the number format */
  unum_setAttribute(format, UNUM_MIN_FRACTION_DIGITS, minDecimalDigits);
  unum_setAttribute(format, UNUM_MAX_FRACTION_DIGITS, maxDecimalDigits);

  return written;
}

void
u_printf_init()
{
  int32_t i;
  /*Mutex *lock;*/

  /* if we're already inited, do nothing */
  if(g_u_printf_inited)
    return;

  /* lock the cache */
  umtx_lock(0);

  /* if we're already inited, do nothing */
  if(g_u_printf_inited) {
    umtx_unlock(0);
    return;
  }

  /* initialize all handlers and infos to 0 */
  for(i = 0; i < 256; ++i) {
    g_u_printf_infos[i]         = 0;
    g_u_printf_handlers[i]     = 0;
  }

  /* register the handlers for standard specifiers */
  /* don't use u_printf_register_handler to avoid mutex creation */

  /* handle '%' */
  g_u_printf_infos[ 0x0025 ]     = u_printf_simple_percent_info;
  g_u_printf_handlers[ 0x0025 ] = u_printf_simple_percent_handler;

  /* handle 's' */
  g_u_printf_infos[ 0x0073 ]     = u_printf_string_info;
  g_u_printf_handlers[ 0x0073 ] = u_printf_string_handler;

  /* handle 'd' */
  g_u_printf_infos[ 0x0064 ]     = u_printf_integer_info;
  g_u_printf_handlers[ 0x0064 ] = u_printf_integer_handler;

  /* handle 'i' */
  g_u_printf_infos[ 0x0069 ]     = u_printf_integer_info;
  g_u_printf_handlers[ 0x0069 ] = u_printf_integer_handler;

  /* handle 'o' */
  g_u_printf_infos[ 0x006F ]     = u_printf_octal_info;
  g_u_printf_handlers[ 0x006F ] = u_printf_octal_handler;

  /* handle 'u' */
  g_u_printf_infos[ 0x0075 ]     = u_printf_integer_info;
  g_u_printf_handlers[ 0x0075 ] = u_printf_integer_handler;

  /* handle 'x' */
  g_u_printf_infos[ 0x0078 ]     = u_printf_hex_info;
  g_u_printf_handlers[ 0x0078 ] = u_printf_hex_handler;

  /* handle 'X' */
  g_u_printf_infos[ 0x0058 ]     = u_printf_hex_info;
  g_u_printf_handlers[ 0x0058 ] = u_printf_hex_handler;

  /* handle 'f' */
  g_u_printf_infos[ 0x0066 ]     = u_printf_double_info;
  g_u_printf_handlers[ 0x0066 ] = u_printf_double_handler;

  /* handle 'c' */
  g_u_printf_infos[ 0x0063 ]     = u_printf_char_info;
  g_u_printf_handlers[ 0x0063 ] = u_printf_char_handler;

  /* handle 'p' */
  g_u_printf_infos[ 0x0070 ]     = u_printf_pointer_info;
  g_u_printf_handlers[ 0x0070 ] = u_printf_pointer_handler;

  /* handle 'e' */
  g_u_printf_infos[ 0x0065 ]     = u_printf_scientific_info;
  g_u_printf_handlers[ 0x0065 ] = u_printf_scientific_handler;

  /* handle 'E' */
  g_u_printf_infos[ 0x0045 ]     = u_printf_scientific_info;
  g_u_printf_handlers[ 0x0045 ] = u_printf_scientific_handler;

  /* handle 'D' */
  g_u_printf_infos[ 0x0044 ]     = u_printf_date_info;
  g_u_printf_handlers[ 0x0044 ] = u_printf_date_handler;

  /* handle 'P' */
  g_u_printf_infos[ 0x0050 ]     = u_printf_percent_info;
  g_u_printf_handlers[ 0x0050 ] = u_printf_percent_handler;

  /* handle 'M' */
  g_u_printf_infos[ 0x004D ]     = u_printf_currency_info;
  g_u_printf_handlers[ 0x004D ] = u_printf_currency_handler;

  /* handle 'T' */
  g_u_printf_infos[ 0x0054 ]     = u_printf_time_info;
  g_u_printf_handlers[ 0x0054 ] = u_printf_time_handler;

  /* handle 'K' */
  g_u_printf_infos[ 0x004B ]     = u_printf_uchar_info;
  g_u_printf_handlers[ 0x004B ] = u_printf_uchar_handler;

  /* handle 'U' */
  g_u_printf_infos[ 0x0055 ]     = u_printf_ustring_info;
  g_u_printf_handlers[ 0x0055 ] = u_printf_ustring_handler;

  /* handle 'g' */
  g_u_printf_infos[ 0x0067 ]     = u_printf_scidbl_info;
  g_u_printf_handlers[ 0x0067 ] = u_printf_scidbl_handler;

  /* handle 'G' */
  g_u_printf_infos[ 0x0047 ]     = u_printf_scidbl_info;
  g_u_printf_handlers[ 0x0047 ] = u_printf_scidbl_handler;

  /* handle 'n' */
  g_u_printf_infos[ 0x006E ]     = u_printf_count_info;
  g_u_printf_handlers[ 0x006E ] = u_printf_count_handler;

  /* handle 'V' */
  g_u_printf_infos[ 0x0056 ]     = u_printf_spellout_info;
  g_u_printf_handlers[ 0x0056 ] = u_printf_spellout_handler;


  /* we're finished */
  g_u_printf_inited = TRUE;

  /* unlock the cache */
  umtx_unlock(0);
}


#define U_PRINTF_MAX_ARGS 32
#define UP_PERCENT 0x0025

int32_t 
u_vfprintf_u(    UFILE        *f,
        const UChar    *patternSpecification,
        va_list        ap)
{
  u_printf_spec         spec;
  const UChar         *alias;
  int32_t         count, written;

  int32_t         num_args_wanted;
  int32_t         ufmt_types     [U_PRINTF_MAX_ARGS];
  ufmt_args     args[U_PRINTF_MAX_ARGS];  
        
  u_printf_info        info;
  u_printf_handler    handler;

  int32_t         cur_arg;


  /* init our function tables */
  if(! g_u_printf_inited)
    u_printf_init();

  /* alias the pattern */
  alias = patternSpecification;
  
  /* haven't written anything yet */
  written = 0;

  /* iterate through the pattern */
  for(;;) {

    /* find the next '%' */
    count = 0;
    while(*alias != UP_PERCENT && *alias != 0x0000) {
      *alias++;
      ++count;
    }

    /* write any characters before the '%' */
    if(count > 0)
      written += u_file_write(alias - count, count, f);

    /* break if at end of string */
    if(*alias == 0x0000)
      break;
    
    /* parse the specifier */
    count = u_printf_parse_spec(alias, &spec);

    /* fill in the precision and width, if specified out of line */

    /* width specified out of line */
    if(spec.fInfo.fWidth == -2) {
      if(spec.fWidthPos != -1) {
    /* handle positional parameter */
      }
      else {
    /* read the width from the argument list */
    spec.fInfo.fWidth = va_arg(ap, int);
      }
    
      /* if it's negative, take the absolute value and set left alignment */
      if(spec.fInfo.fWidth < 0) {
    spec.fInfo.fWidth     *= -1;
    spec.fInfo.fLeft     = TRUE;
      }
    }

    /* precision specified out of line */
    if(spec.fInfo.fPrecision == -2) {
      if(spec.fPrecisionPos != -1) {
    /* handle positional parameter */
      }
      else {
    /* read the precision from the argument list */
    spec.fInfo.fPrecision = va_arg(ap, int);
      }
      
      /* if it's negative, set it to zero */
      if(spec.fInfo.fPrecision < 0)
    spec.fInfo.fPrecision = 0;
    }
    
    /* query the info function for argument information */
    info = g_u_printf_infos[ (unsigned char) spec.fInfo.fSpec ];
    if(info != 0) { 
      num_args_wanted = (*info)(&spec.fInfo, 
                ufmt_types,
                U_PRINTF_MAX_ARGS);
    }
    else
      num_args_wanted = 0;

    /* fill in the requested arguments */
    for(cur_arg = 0; 
    cur_arg < num_args_wanted && cur_arg < U_PRINTF_MAX_ARGS; 
    ++cur_arg) {
      
      switch(ufmt_types[cur_arg]) {

      case ufmt_count:
    args[cur_arg].intValue = va_arg(ap, int);
    /* set the spec's width to the # of chars written */
    spec.fInfo.fWidth = written;
    break;

      case ufmt_int:
    args[cur_arg].intValue = va_arg(ap, int);
    break;
    
      case ufmt_char:
    args[cur_arg].intValue = va_arg(ap, int);
    break;
    
      case ufmt_wchar:
    args[cur_arg].wcharValue = va_arg(ap, wchar_t);
    break;
    
      case ufmt_string:
    args[cur_arg].ptrValue = va_arg(ap, char*);
    break;
    
      case ufmt_wstring:
    args[cur_arg].ptrValue = va_arg(ap, wchar_t*);
    break;
    
      case ufmt_pointer:
    args[cur_arg].ptrValue = va_arg(ap, void*);
    break;
    
      case ufmt_float:
    args[cur_arg].floatValue = va_arg(ap, float);
    break;
    
      case ufmt_double:
    args[cur_arg].doubleValue = va_arg(ap, double);
    break;

      case ufmt_date:
    args[cur_arg].dateValue = va_arg(ap, UDate);
    break;

      case ufmt_ustring:
    args[cur_arg].ptrValue = va_arg(ap, UChar*);
    break;

      case ufmt_uchar:
    args[cur_arg].intValue = va_arg(ap, int);
    break;
      }
    }
    
    /* call the handler function */
    handler = g_u_printf_handlers[ (unsigned char) spec.fInfo.fSpec ];
    if(handler != 0) {
      written += (*handler)(f, &spec.fInfo, args);
    }
    /* just echo unknown tags */
    else
      written += u_file_write(alias, count, f);
    
    /* update the pointer in pattern and continue */
    alias += count;
  }
  
  /* return # of UChars written */
  return written;
}

