Logo Search packages:      
Sourcecode: virtualbox-ose version File versions

nsTextFormatter.cpp

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is mozilla.org Code.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1998
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

/* 
 * Portable safe sprintf code.
 *
 * Code based on mozilla/nsprpub/src/io/prprf.c rev 3.7 
 *
 * Contributor(s):
 *   Kipp E.B. Hickman  <kipp@netscape.com>  (original author)
 *   Frank Yung-Fong Tang  <ftang@netscape.com>
 *   Daniele Nicolodi  <daniele@grinta.net>
 */

#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include "prdtoa.h"
#include "prlong.h"
#include "prlog.h"
#include "prmem.h"
#include "prprf.h"
#include "nsCRT.h"
#include "nsTextFormatter.h"
#include "nsString.h"
#include "nsReadableUtils.h"

/*
** Note: on some platforms va_list is defined as an array,
** and requires array notation.
*/

#ifdef HAVE_VA_COPY
#define VARARGS_ASSIGN(foo, bar)        VA_COPY(foo,bar)
#elif defined(HAVE_VA_LIST_AS_ARRAY)
#define VARARGS_ASSIGN(foo, bar)    foo[0] = bar[0]
#else
#define VARARGS_ASSIGN(foo, bar)    (foo) = (bar)
#endif

typedef struct SprintfStateStr SprintfState;

struct SprintfStateStr {
    int (*stuff)(SprintfState *ss, const PRUnichar *sp, PRUint32 len);

    PRUnichar *base;
    PRUnichar *cur;
    PRUint32 maxlen;

    void *stuffclosure;
};

/*
** Numbered Arguement State
*/
struct NumArgState{
    int         type;         /* type of the current ap                    */
    va_list ap;               /* point to the corresponding position on ap */
};

#define NAS_DEFAULT_NUM 20  /* default number of NumberedArgumentState array */

#define TYPE_INT16      0
#define TYPE_UINT16     1
#define TYPE_INTN 2
#define TYPE_UINTN      3
#define TYPE_INT32      4
#define TYPE_UINT32     5
#define TYPE_INT64      6
#define TYPE_UINT64     7
#define TYPE_STRING     8
#define TYPE_DOUBLE     9
#define TYPE_INTSTR     10
#define TYPE_UNISTRING  11
#define TYPE_UNKNOWN    20

#define _LEFT           0x1
#define _SIGNED         0x2
#define _SPACED         0x4
#define _ZEROS          0x8
#define _NEG            0x10

#define ELEMENTS_OF(array_) (sizeof(array_) / sizeof(array_[0]))

// Warning: if aDest isn't big enough this function returns the converted 
// string in allocated memory which must be freed using PR_FREE().
// May return nsnull if memory couldn't be allocated.
static PRUnichar* UTF8ToUCS2(const char *aSrc, PRUint32 aSrcLen, 
                             PRUnichar* aDest, PRUint32 aDestLen)
{
    const char *in, *inend;
    inend = aSrc + aSrcLen;
    PRUnichar *out;
    PRUint32 state;
    PRUint32 ucs4;
    // decide the length of the UCS2 first.
    PRUint32 needLen = 0;

    for (in = aSrc, state = 0, ucs4 = 0; in < inend; in++) {
            if (0 == state) {
                if (0 == (0x80 & (*in))) {
                    needLen++;
                } else if (0xC0 == (0xE0 & (*in))) {
                    needLen++;
                    state = 1;
                } else if (0xE0 == (0xF0 & (*in))) {
                    needLen++;
                    state = 2;
                } else if (0xF0 == (0xF8 & (*in))) {
                    needLen+=2;
                    state = 3;
                } else if (0xF8 == (0xFC & (*in))) {
                    needLen+=2;
                    state = 4;
                } else if (0xFC == (0xFE & (*in))) {
                    needLen+=2;
                    state = 5;
                } else {
                    needLen++;
                    state = 0;
                }
            } else {
                NS_ASSERTION((0x80 == (0xC0 & (*in))), "The input string is not in utf8");
                if(0x80 == (0xC0 & (*in))) {
                    state--;
                } else {
                    state = 0;
                }
            }
    }
    needLen++; // add null termination.

    // allocates sufficient memory if aDest is not big enough.
    if (needLen > aDestLen) {
        aDest = (PRUnichar*)PR_MALLOC(sizeof(PRUnichar) * needLen);
    }
    if (nsnull == aDest) {
        return nsnull;
    }
    out = aDest;

    for (in = aSrc, state = 0, ucs4 = 0; in < inend; in++) {
        if (0 == state) {
            if (0 == (0x80 & (*in))) {
                // ASCII
                *out++ = (PRUnichar)*in;
            } else if (0xC0 == (0xE0 & (*in))) {
                // 2 bytes UTF8
                ucs4 = (PRUint32)(*in);
                ucs4 = (ucs4 << 6) & 0x000007C0L;
                state=1;
            } else if (0xE0 == (0xF0 & (*in))) {
                ucs4 = (PRUint32)(*in);
                ucs4 = (ucs4 << 12) & 0x0000F000L;
                state=2;
            } else if (0xF0 == (0xF8 & (*in))) {
                ucs4 = (PRUint32)(*in);
                ucs4 = (ucs4 << 18) & 0x001F0000L;
                state=3;
            } else if (0xF8 == (0xFC & (*in))) {
                ucs4 = (PRUint32)(*in);
                ucs4 = (ucs4 << 24) & 0x03000000L;
                state=4;
            } else if (0xFC == (0xFE & (*in))) {
                ucs4 = (PRUint32)(*in);
                ucs4 = (ucs4 << 30) & 0x40000000L;
                state=5;
            } else {
                NS_ASSERTION(0, "The input string is not in utf8");
                state=0;
                ucs4=0;
            }
        } else {
            NS_ASSERTION((0x80 == (0xC0 & (*in))), "The input string is not in utf8");
            if (0x80 == (0xC0 & (*in))) {
                PRUint32 tmp = (*in);
                int shift = --state * 6;
                tmp = (tmp << shift) & (0x0000003FL << shift);
                ucs4 |= tmp;
                if (0 == state) {
                    if (ucs4 >= 0x00010000) {
                        if (ucs4 >= 0x00110000) {
                            *out++ = 0xFFFD;
                        } else {
                            ucs4 -= 0x00010000;
                            *out++ = 0xD800 | (0x000003FF & (ucs4 >> 10));
                            *out++ = 0xDC00 | (0x000003FF & ucs4);
                        }
                    } else {
                        *out++ = ucs4;
                    }
                    ucs4 = 0;
                }
            } else {
                state = 0;
                ucs4 = 0;
            }
        }
    }
    *out = 0x0000;
    return aDest;
}

/*
** Fill into the buffer using the data in src
*/
static int fill2(SprintfState *ss, const PRUnichar *src, int srclen, 
                 int width, int flags)
{
    PRUnichar space = ' ';
    int rv;
    
    width -= srclen;
    /* Right adjusting */
    if ((width > 0) && ((flags & _LEFT) == 0)) {
      if (flags & _ZEROS) {
          space = '0';
      }
      while (--width >= 0) {
          rv = (*ss->stuff)(ss, &space, 1);
          if (rv < 0) {
            return rv;
          }
      }
    }

    /* Copy out the source data */
    rv = (*ss->stuff)(ss, src, srclen);
    if (rv < 0) {
      return rv;
    }
    
    /* Left adjusting */
    if ((width > 0) && ((flags & _LEFT) != 0)) {
      while (--width >= 0) {
          rv = (*ss->stuff)(ss, &space, 1);
          if (rv < 0) {
            return rv;
          }
      }
    }
    return 0;
}

/*
** Fill a number. The order is: optional-sign zero-filling conversion-digits
*/
static int fill_n(SprintfState *ss, const PRUnichar *src, int srclen, 
                  int width, int prec, int type, int flags)
{
    int zerowidth   = 0;
    int precwidth   = 0;
    int signwidth   = 0;
    int leftspaces  = 0;
    int rightspaces = 0;
    int cvtwidth;
    int rv;
    PRUnichar sign;
    PRUnichar space = ' ';
    PRUnichar zero = '0';

    if ((type & 1) == 0) {
      if (flags & _NEG) {
          sign = '-';
          signwidth = 1;
      } else if (flags & _SIGNED) {
          sign = '+';
          signwidth = 1;
      } else if (flags & _SPACED) {
          sign = ' ';
          signwidth = 1;
      }
    }
    cvtwidth = signwidth + srclen;

    if (prec > 0) {
      if (prec > srclen) {
            /* Need zero filling */
          precwidth = prec - srclen;
          cvtwidth += precwidth;
      }
    }

    if ((flags & _ZEROS) && (prec < 0)) {
      if (width > cvtwidth) {
            /* Zero filling */
          zerowidth = width - cvtwidth;
          cvtwidth += zerowidth;
      }
    }

    if (flags & _LEFT) {
      if (width > cvtwidth) {
          /* Space filling on the right (i.e. left adjusting) */
          rightspaces = width - cvtwidth;
      }
    } else {
      if (width > cvtwidth) {
          /* Space filling on the left (i.e. right adjusting) */
          leftspaces = width - cvtwidth;
      }
    }
    while (--leftspaces >= 0) {
      rv = (*ss->stuff)(ss, &space, 1);
      if (rv < 0) {
          return rv;
      }
    }
    if (signwidth) {
      rv = (*ss->stuff)(ss, &sign, 1);
      if (rv < 0) {
          return rv;
      }
    }
    while (--precwidth >= 0) {
      rv = (*ss->stuff)(ss,  &space, 1);
      if (rv < 0) {
          return rv;
      }
    }
    while (--zerowidth >= 0) {
      rv = (*ss->stuff)(ss,  &zero, 1);
      if (rv < 0) {
          return rv;
      }
    }
    rv = (*ss->stuff)(ss, src, srclen);
    if (rv < 0) {
      return rv;
    }
    while (--rightspaces >= 0) {
      rv = (*ss->stuff)(ss,  &space, 1);
      if (rv < 0) {
          return rv;
      }
    }
    return 0;
}

/*
** Convert a long into its printable form
*/
static int cvt_l(SprintfState *ss, long num, int width, int prec,
                 int radix, int type, int flags, const PRUnichar *hexp)
{
    PRUnichar cvtbuf[100];
    PRUnichar *cvt;
    int digits;

    /* according to the man page this needs to happen */
    if ((prec == 0) && (num == 0)) {
      return 0;
    }

    /*
    ** Converting decimal is a little tricky. In the unsigned case we
    ** need to stop when we hit 10 digits. In the signed case, we can
    ** stop when the number is zero.
    */
    cvt = &cvtbuf[0] + ELEMENTS_OF(cvtbuf);
    digits = 0;
    while (num) {
      int digit = (((unsigned long)num) % radix) & 0xF;
      *--cvt = hexp[digit];
      digits++;
      num = (long)(((unsigned long)num) / radix);
    }
    if (digits == 0) {
      *--cvt = '0';
      digits++;
    }

    /*
    ** Now that we have the number converted without its sign, deal with
    ** the sign and zero padding.
    */
    return fill_n(ss, cvt, digits, width, prec, type, flags);
}

/*
** Convert a 64-bit integer into its printable form
*/
static int cvt_ll(SprintfState *ss, PRInt64 num, int width, int prec,
                  int radix, int type, int flags, const PRUnichar *hexp)
{
    PRUnichar cvtbuf[100];
    PRUnichar *cvt;
    int digits;
    PRInt64 rad;

    /* according to the man page this needs to happen */
    if ((prec == 0) && (LL_IS_ZERO(num))) {
      return 0;
    }

    /*
    ** Converting decimal is a little tricky. In the unsigned case we
    ** need to stop when we hit 10 digits. In the signed case, we can
    ** stop when the number is zero.
    */
    LL_I2L(rad, radix);
    cvt = &cvtbuf[0] + ELEMENTS_OF(cvtbuf);
    digits = 0;
    while (!LL_IS_ZERO(num)) {
      PRInt32 digit;
      PRInt64 quot, rem;
      LL_UDIVMOD(&quot, &rem, num, rad);
      LL_L2I(digit, rem);
      *--cvt = hexp[digit & 0xf];
      digits++;
      num = quot;
    }
    if (digits == 0) {
      *--cvt = '0';
      digits++;
    }

    /*
    ** Now that we have the number converted without its sign, deal with
    ** the sign and zero padding.
    */
    return fill_n(ss, cvt, digits, width, prec, type, flags);
}

/*
** Convert a double precision floating point number into its printable
** form.
*/
static int cvt_f(SprintfState *ss, double d, int width, int prec, 
                 const PRUnichar type, int flags)
{
    int    mode = 2;
    int    decpt;
    int    sign;
    char   buf[256];
    char * bufp = buf;
    int    bufsz = 256;
    char   num[256];
    char * nump;
    char * endnum;
    int    numdigits = 0;
    char   exp = 'e';

    if (prec == -1) {
        prec = 6;
    } else if (prec > 50) {
        // limit precision to avoid PR_dtoa bug 108335
        // and to prevent buffers overflows
        prec = 50;
    }

    switch (type) {
    case 'f':  
        numdigits = prec;
        mode = 3;
        break;
    case 'E':
        exp = 'E';
        // no break
    case 'e':
        numdigits = prec + 1;
        mode = 2;
        break;
    case 'G':
        exp = 'E';
        // no break
    case 'g':
        if (prec == 0) {
            prec = 1;
        }
        numdigits = prec;
        mode = 2;
        break;
    default:
        NS_ERROR("invalid type passed to cvt_f");
    }
    
    if (PR_dtoa(d, mode, numdigits, &decpt, &sign, &endnum, num, bufsz) == PR_FAILURE) {
        buf[0] = '\0';
        return -1;
    }
    numdigits = endnum - num;
    nump = num;

    if (sign) {
        *bufp++ = '-';
    } else if (flags & _SIGNED) {
        *bufp++ = '+';
    }

    if (decpt == 9999) {
        while ((*bufp++ = *nump++)) { }
    } else {

        switch (type) {

        case 'E':
        case 'e':

            *bufp++ = *nump++;                
            if (prec > 0) {
                *bufp++ = '.';
                while (*nump) {
                    *bufp++ = *nump++;
                    prec--;
                }
                while (prec-- > 0) {
                    *bufp++ = '0';
                }
            }
            *bufp++ = exp;
            PR_snprintf(bufp, bufsz - (bufp - buf), "%+03d", decpt-1);
            break;

        case 'f':

            if (decpt < 1) {
                *bufp++ = '0';
                if (prec > 0) {
                    *bufp++ = '.';
                    while (decpt++ && prec-- > 0) {
                        *bufp++ = '0';
                    }
                    while (*nump && prec-- > 0) {
                        *bufp++ = *nump++;
                    }
                    while (prec-- > 0) {
                        *bufp++ = '0';
                    }
                }
            } else {
                while (*nump && decpt-- > 0) {
                    *bufp++ = *nump++;
                }
                while (decpt-- > 0) {
                    *bufp++ = '0';
                }
                if (prec > 0) {
                    *bufp++ = '.';
                    while (*nump && prec-- > 0) {
                        *bufp++ = *nump++;
                    }
                    while (prec-- > 0) {
                        *bufp++ = '0';
                    }
                }
            }
            *bufp = '\0';
            break;

        case 'G':
        case 'g':

            if ((decpt < -3) || ((decpt - 1) >= prec)) {
                *bufp++ = *nump++;
                numdigits--;
                if (numdigits > 0) {
                    *bufp++ = '.';
                    while (*nump) {
                        *bufp++ = *nump++;
                    }
                }
                *bufp++ = exp;
                PR_snprintf(bufp, bufsz - (bufp - buf), "%+03d", decpt-1);
            } else {
                if (decpt < 1) {
                    *bufp++ = '0';
                    if (prec > 0) {
                        *bufp++ = '.';
                        while (decpt++) {
                            *bufp++ = '0';
                        }
                        while (*nump) {
                            *bufp++ = *nump++;
                        }
                    }
                } else {
                    while (*nump && decpt-- > 0) {
                        *bufp++ = *nump++;
                        numdigits--;
                    }
                    while (decpt-- > 0) {
                        *bufp++ = '0';
                    }
                    if (numdigits > 0) {
                        *bufp++ = '.';
                        while (*nump) {
                            *bufp++ = *nump++;
                        }
                    }
                }
                *bufp = '\0';
            }
        }
    }

    PRUnichar rbuf[256];
    PRUnichar *rbufp = rbuf;
    bufp = buf;
    // cast to PRUnichar
    while ((*rbufp++ = *bufp++)) { }
    *rbufp = '\0';

    return fill2(ss, rbuf, nsCRT::strlen(rbuf), width, flags);
}

/*
** Convert a string into its printable form. "width" is the output
** width. "prec" is the maximum number of characters of "s" to output,
** where -1 means until NUL.
*/
static int cvt_S(SprintfState *ss, const PRUnichar *s, int width,
                 int prec, int flags)
{
    int slen;

    if (prec == 0) {
      return 0;
    }

    /* Limit string length by precision value */
    slen = s ? nsCRT::strlen(s) : 6;
    if (prec > 0) {
      if (prec < slen) {
          slen = prec;
      }
    }

    /* and away we go */
    NS_NAMED_LITERAL_STRING(nullstr, "(null)");

    return fill2(ss, s ? s : nullstr.get(), slen, width, flags);
}

/*
** Convert a string into its printable form.  "width" is the output
** width. "prec" is the maximum number of characters of "s" to output,
** where -1 means until NUL.
*/
static int cvt_s(SprintfState *ss, const char *s, int width,
                 int prec, int flags)
{
    // convert s from UTF8 to PRUnichar*
    // Fix me !!!
    PRUnichar buf[256];
    PRUnichar *retbuf = nsnull;

    if (s) {
        retbuf = UTF8ToUCS2(s, strlen(s), buf, 256);        
        if(nsnull == retbuf) {
            return -1;
        }
    }
    int ret = cvt_S(ss, retbuf, width, prec, flags);

    if (retbuf != buf) {
        PR_DELETE(retbuf);
    }
    return ret;
}

/*
** BiuldArgArray stands for Numbered Argument list Sprintf
** for example,  
**    fmp = "%4$i, %2$d, %3s, %1d";
** the number must start from 1, and no gap among them
*/

static struct NumArgState* BuildArgArray(const PRUnichar *fmt, 
                                         va_list ap, int * rv, 
                                         struct NumArgState * nasArray)
{
    int number = 0, cn = 0, i;
    const PRUnichar* p;
    PRUnichar  c;
    struct NumArgState* nas;

    /*
    **      first pass:
    **      detemine how many legal % I have got, then allocate space
    */
    p = fmt;
    *rv = 0;
    i = 0;
    while ((c = *p++) != 0) {
      if (c != '%') {
          continue;
        }
        /* skip %% case */
      if ((c = *p++) == '%') {
          continue;
        }

      while( c != 0 ){
          if (c > '9' || c < '0') {
                /* numbered argument csae */
            if (c == '$') {
                if (i > 0) {
                  *rv = -1;
                  return NULL;
                }
                number++;
                break;

            } else {
                    /* non-numbered argument case */
                if (number > 0) {
                  *rv = -1;
                  return NULL;
                }
                i = 1;
                break;
            }
          }
          c = *p++;
      }
    }

    if (number == 0) {
      return NULL;
    }
    
    if (number > NAS_DEFAULT_NUM) {
      nas = (struct NumArgState*)PR_MALLOC(number * sizeof(struct NumArgState));
      if (!nas) {
          *rv = -1;
          return NULL;
      }
    } else {
      nas = nasArray;
    }

    for (i = 0; i < number; i++) {
      nas[i].type = TYPE_UNKNOWN;
    }

    /*
    ** second pass:
    ** set nas[].type
    */
    p = fmt;
    while ((c = *p++) != 0) {
      if (c != '%') {
            continue;
        }
        c = *p++;
      if (c == '%') {
            continue;
        }
      cn = 0;
        /* should imporve error check later */
      while (c && c != '$') {
          cn = cn*10 + c - '0';
          c = *p++;
      }

      if (!c || cn < 1 || cn > number) {
          *rv = -1;
          break;
        }

      /* nas[cn] starts from 0, and make sure 
           nas[cn].type is not assigned */
        cn--;
      if (nas[cn].type != TYPE_UNKNOWN) {
          continue;
        }

        c = *p++;

        /* width */
        if (c == '*') {
          /* not supported feature, for the argument is not numbered */
          *rv = -1;
          break;
      } else {
          while ((c >= '0') && (c <= '9')) {
              c = *p++;
          }
      }

      /* precision */
      if (c == '.') {
          c = *p++;
          if (c == '*') {
              /* not supported feature, for the argument is not numbered */
              *rv = -1;
              break;
          } else {
              while ((c >= '0') && (c <= '9')) {
                c = *p++;
            }
          }
      }

      /* size */
      nas[cn].type = TYPE_INTN;
      if (c == 'h') {
          nas[cn].type = TYPE_INT16;
          c = *p++;
      } else if (c == 'L') {
          /* XXX not quite sure here */
          nas[cn].type = TYPE_INT64;
          c = *p++;
      } else if (c == 'l') {
          nas[cn].type = TYPE_INT32;
          c = *p++;
          if (c == 'l') {
              nas[cn].type = TYPE_INT64;
              c = *p++;
          }
      }

      /* format */
      switch (c) {
      case 'd':
      case 'c':
      case 'i':
      case 'o':
      case 'u':
      case 'x':
      case 'X':
          break;

      case 'e':
      case 'f':
      case 'g':
          nas[cn].type = TYPE_DOUBLE;
          break;

      case 'p':
          /* XXX should use cpp */
          if (sizeof(void *) == sizeof(PRInt32)) {
            nas[cn].type = TYPE_UINT32;
          } else if (sizeof(void *) == sizeof(PRInt64)) {
              nas[cn].type = TYPE_UINT64;
          } else if (sizeof(void *) == sizeof(PRIntn)) {
              nas[cn].type = TYPE_UINTN;
          } else {
              nas[cn].type = TYPE_UNKNOWN;
          }
          break;

      case 'C':
          /* XXX not supported I suppose */
          PR_ASSERT(0);
          nas[cn].type = TYPE_UNKNOWN;
          break;

      case 'S':
          nas[cn].type = TYPE_UNISTRING;
          break;

      case 's':
          nas[cn].type = TYPE_STRING;
          break;

      case 'n':
          nas[cn].type = TYPE_INTSTR;
          break;

      default:
          PR_ASSERT(0);
          nas[cn].type = TYPE_UNKNOWN;
          break;
      }

      /* get a legal para. */
      if (nas[cn].type == TYPE_UNKNOWN) {
          *rv = -1;
          break;
      }
    }


    /*
    ** third pass
    ** fill the nas[cn].ap
    */
    if (*rv < 0) {
      if( nas != nasArray ) {
          PR_DELETE(nas);
        }
      return NULL;
    }

    cn = 0;
    while (cn < number) {
      if (nas[cn].type == TYPE_UNKNOWN) {
          cn++;
          continue;
      }

      VARARGS_ASSIGN(nas[cn].ap, ap);

      switch (nas[cn].type) {
      case TYPE_INT16:
      case TYPE_UINT16:
      case TYPE_INTN:
      case TYPE_UINTN:     (void)va_arg(ap, PRIntn);      break;

      case TYPE_INT32:     (void)va_arg(ap, PRInt32);     break;

      case TYPE_UINT32:    (void)va_arg(ap, PRUint32);    break;

      case TYPE_INT64:     (void)va_arg(ap, PRInt64);     break;

      case TYPE_UINT64:    (void)va_arg(ap, PRUint64);    break;

      case TYPE_STRING:    (void)va_arg(ap, char*);       break;

      case TYPE_INTSTR:    (void)va_arg(ap, PRIntn*);     break;

      case TYPE_DOUBLE:    (void)va_arg(ap, double);      break;

      case TYPE_UNISTRING: (void)va_arg(ap, PRUnichar*);  break;

      default:
          if( nas != nasArray ) {
            PR_DELETE( nas );
            }
          *rv = -1;
          return NULL;
      }
      cn++;
    }
    return nas;
}

/*
** The workhorse sprintf code.
*/
static int dosprintf(SprintfState *ss, const PRUnichar *fmt, va_list ap)
{
    PRUnichar c;
    int flags, width, prec, radix, type;
    union {
      PRUnichar ch;
      int i;
      long l;
      PRInt64 ll;
      double d;
      const char *s;
      const PRUnichar *S;
      int *ip;
    } u;
    PRUnichar space = ' ';
    const PRUnichar *fmt0;

    nsAutoString hex;
    hex.AssignLiteral("0123456789abcdef");

    nsAutoString HEX;
    HEX.AssignLiteral("0123456789ABCDEF");

    const PRUnichar *hexp;
    int rv, i;
    struct NumArgState* nas = NULL;
    struct NumArgState  nasArray[NAS_DEFAULT_NUM];
    /* in "%4$.2f" dolPt will point to . */
    const PRUnichar* dolPt = NULL;


    /*
    ** build an argument array, IF the fmt is numbered argument
    ** list style, to contain the Numbered Argument list pointers
    */
    nas = BuildArgArray (fmt, ap, &rv, nasArray);
    if (rv < 0) {
      /* the fmt contains error Numbered Argument format, jliu@netscape.com */
      PR_ASSERT(0);
      return rv;
    }

    while ((c = *fmt++) != 0) {
      if (c != '%') {
          rv = (*ss->stuff)(ss, fmt - 1, 1);
          if (rv < 0) {
            return rv;
          }
          continue;
      }
      fmt0 = fmt - 1;

      /*
      ** Gobble up the % format string. Hopefully we have handled all
      ** of the strange cases!
      */
      flags = 0;
      c = *fmt++;
      if (c == '%') {
          /* quoting a % with %% */
          rv = (*ss->stuff)(ss, fmt - 1, 1);
          if (rv < 0) {
            return rv;
          }
          continue;
      }

      if (nas != NULL) {
          /* the fmt contains the Numbered Arguments feature */
          i = 0;
          /* should imporve error check later */
          while (c && c != '$') {
            i = (i * 10) + (c - '0');
            c = *fmt++;
          }

          if (nas[i-1].type == TYPE_UNKNOWN) {
            if (nas && (nas != nasArray)) {
                PR_DELETE(nas);
                }
            return -1;
          }

          ap = nas[i-1].ap;
          dolPt = fmt;
          c = *fmt++;
      }

      /*
       * Examine optional flags.  Note that we do not implement the
       * '#' flag of sprintf().  The ANSI C spec. of the '#' flag is
       * somewhat ambiguous and not ideal, which is perhaps why
       * the various sprintf() implementations are inconsistent
       * on this feature.
       */
      while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) {
          if (c == '-') flags |= _LEFT;
          if (c == '+') flags |= _SIGNED;
          if (c == ' ') flags |= _SPACED;
          if (c == '0') flags |= _ZEROS;
          c = *fmt++;
      }
      if (flags & _SIGNED) flags &= ~_SPACED;
      if (flags & _LEFT) flags &= ~_ZEROS;

      /* width */
      if (c == '*') {
          c = *fmt++;
          width = va_arg(ap, int);
      } else {
          width = 0;
          while ((c >= '0') && (c <= '9')) {
            width = (width * 10) + (c - '0');
            c = *fmt++;
          }
      }

      /* precision */
      prec = -1;
      if (c == '.') {
          c = *fmt++;
          if (c == '*') {
            c = *fmt++;
            prec = va_arg(ap, int);
          } else {
            prec = 0;
            while ((c >= '0') && (c <= '9')) {
                prec = (prec * 10) + (c - '0');
                c = *fmt++;
            }
          }
      }

      /* size */
      type = TYPE_INTN;
      if (c == 'h') {
          type = TYPE_INT16;
          c = *fmt++;
      } else if (c == 'L') {
          /* XXX not quite sure here */
          type = TYPE_INT64;
          c = *fmt++;
      } else if (c == 'l') {
          type = TYPE_INT32;
          c = *fmt++;
          if (c == 'l') {
            type = TYPE_INT64;
            c = *fmt++;
          }
      }

      /* format */
      hexp = hex.get();
      switch (c) {
        case 'd': 
        case 'i':                               /* decimal/integer */
          radix = 10;
          goto fetch_and_convert;

        case 'o':                               /* octal */
          radix = 8;
          type |= 1;
          goto fetch_and_convert;

        case 'u':                               /* unsigned decimal */
          radix = 10;
          type |= 1;
          goto fetch_and_convert;

        case 'x':                               /* unsigned hex */
          radix = 16;
          type |= 1;
          goto fetch_and_convert;

        case 'X':                               /* unsigned HEX */
          radix = 16;
          hexp = HEX.get();
          type |= 1;
          goto fetch_and_convert;

        fetch_and_convert:
          switch (type) {
            case TYPE_INT16:
            u.l = va_arg(ap, int);
            if (u.l < 0) {
                u.l = -u.l;
                flags |= _NEG;
            }
            goto do_long;
            case TYPE_UINT16:
            u.l = va_arg(ap, int) & 0xffff;
            goto do_long;
            case TYPE_INTN:
            u.l = va_arg(ap, int);
            if (u.l < 0) {
                u.l = -u.l;
                flags |= _NEG;
            }
            goto do_long;
            case TYPE_UINTN:
            u.l = (long)va_arg(ap, unsigned int);
            goto do_long;

            case TYPE_INT32:
            u.l = va_arg(ap, PRInt32);
            if (u.l < 0) {
                u.l = -u.l;
                flags |= _NEG;
            }
            goto do_long;
            case TYPE_UINT32:
            u.l = (long)va_arg(ap, PRUint32);
            do_long:
            rv = cvt_l(ss, u.l, width, prec, radix, type, flags, hexp);
            if (rv < 0) {
                return rv;
            }
            break;

            case TYPE_INT64:
            u.ll = va_arg(ap, PRInt64);
            if (!LL_GE_ZERO(u.ll)) {
                LL_NEG(u.ll, u.ll);
                flags |= _NEG;
            }
            goto do_longlong;
            case TYPE_UINT64:
            u.ll = va_arg(ap, PRUint64);
            do_longlong:
            rv = cvt_ll(ss, u.ll, width, prec, radix, type, flags, hexp);
            if (rv < 0) {
                return rv;
            }
            break;
          }
          break;

        case 'e':
        case 'E':
        case 'f':
        case 'g':
        case 'G':
          u.d = va_arg(ap, double);
            rv = cvt_f(ss, u.d, width, prec, c, flags);
          if (rv < 0) {
            return rv;
          }
          break;

        case 'c':
          u.ch = va_arg(ap, int);
            if ((flags & _LEFT) == 0) {
                while (width-- > 1) {
                    rv = (*ss->stuff)(ss, &space, 1);
                    if (rv < 0) {
                        return rv;
                    }
                }
            }
          rv = (*ss->stuff)(ss, &u.ch, 1);
          if (rv < 0) {
            return rv;
          }
            if (flags & _LEFT) {
                while (width-- > 1) {
                    rv = (*ss->stuff)(ss, &space, 1);
                    if (rv < 0) {
                        return rv;
                    }
                }
            }
          break;

        case 'p':
          if (sizeof(void *) == sizeof(PRInt32)) {
            type = TYPE_UINT32;
          } else if (sizeof(void *) == sizeof(PRInt64)) {
            type = TYPE_UINT64;
          } else if (sizeof(void *) == sizeof(int)) {
            type = TYPE_UINTN;
          } else {
            PR_ASSERT(0);
            break;
          }
          radix = 16;
          goto fetch_and_convert;

#if 0
        case 'C':
          /* XXX not supported I suppose */
          PR_ASSERT(0);
          break;
#endif

        case 'S':
          u.S = va_arg(ap, const PRUnichar*);
          rv = cvt_S(ss, u.S, width, prec, flags);
          if (rv < 0) {
            return rv;
          }
          break;

        case 's':
          u.s = va_arg(ap, const char*);
          rv = cvt_s(ss, u.s, width, prec, flags);
          if (rv < 0) {
            return rv;
          }
          break;

        case 'n':
          u.ip = va_arg(ap, int*);
          if (u.ip) {
            *u.ip = ss->cur - ss->base;
          }
          break;

        default:
          /* Not a % token after all... skip it */
#if 0
          PR_ASSERT(0);
#endif
            PRUnichar perct = '%'; 
          rv = (*ss->stuff)(ss, &perct, 1);
          if (rv < 0) {
            return rv;
          }
          rv = (*ss->stuff)(ss, fmt - 1, 1);
          if (rv < 0) {
            return rv;
          }
      }
    }

    /* Stuff trailing NUL */
    PRUnichar null = '\0';

    rv = (*ss->stuff)(ss, &null, 1);

    if( nas && ( nas != nasArray ) ){
      PR_DELETE( nas );
    }

    return rv;
}

/************************************************************************/

static int
StringStuff(SprintfState* ss, const PRUnichar* sp, PRUint32 len)
{
    ptrdiff_t off = ss->cur - ss->base;
    
    nsAString* str = NS_STATIC_CAST(nsAString*,ss->stuffclosure);
    str->Append(sp, len);

    // we can assume contiguous storage
    nsAString::iterator begin;
    str->BeginWriting(begin);
    ss->base = begin.get();
    ss->cur = ss->base + off;

    return 0;
}

/*
** Stuff routine that automatically grows the malloc'd output buffer
** before it overflows.
*/
static int GrowStuff(SprintfState *ss, const PRUnichar *sp, PRUint32 len)
{
    ptrdiff_t off;
    PRUnichar *newbase;
    PRUint32 newlen;

    off = ss->cur - ss->base;
    if (off + len >= ss->maxlen) {
      /* Grow the buffer */
      newlen = ss->maxlen + ((len > 32) ? len : 32);
      if (ss->base) {
          newbase = (PRUnichar*) PR_REALLOC(ss->base, newlen*sizeof(PRUnichar));
      } else {
          newbase = (PRUnichar*) PR_MALLOC(newlen*sizeof(PRUnichar));
      }
      if (!newbase) {
          /* Ran out of memory */
          return -1;
      }
      ss->base = newbase;
      ss->maxlen = newlen;
      ss->cur = ss->base + off;
    }

    /* Copy data */
    while (len) {
      --len;
      *ss->cur++ = *sp++;
    }
    PR_ASSERT((PRUint32)(ss->cur - ss->base) <= ss->maxlen);
    return 0;
}

/*
** sprintf into a malloc'd buffer
*/
PRUnichar * nsTextFormatter::smprintf(const PRUnichar *fmt, ...)
{
    va_list ap;
    PRUnichar *rv;

    va_start(ap, fmt);
    rv = nsTextFormatter::vsmprintf(fmt, ap);
    va_end(ap);
    return rv;
}

PRUint32 nsTextFormatter::ssprintf(nsAString& out, const PRUnichar* fmt, ...)
{
    va_list ap;
    PRUint32 rv;

    va_start(ap, fmt);
    rv = nsTextFormatter::vssprintf(out, fmt, ap);
    va_end(ap);
    return rv;
}

/*
** Free memory allocated, for the caller, by smprintf
*/
void nsTextFormatter::smprintf_free(PRUnichar *mem)
{
    PR_DELETE(mem);
}

PRUint32 nsTextFormatter::vssprintf(nsAString& out, const PRUnichar* fmt, va_list ap)
{
    SprintfState ss;
    ss.stuff = StringStuff;
    ss.base = 0;
    ss.cur = 0;
    ss.maxlen = 0;
    ss.stuffclosure = &out;

    out.Truncate();
    int n = dosprintf(&ss, fmt, ap);
    return n ? n - 1 : n;
}

PRUnichar * nsTextFormatter::vsmprintf(const PRUnichar *fmt, va_list ap)
{
    SprintfState ss;
    int rv;

    ss.stuff = GrowStuff;
    ss.base = 0;
    ss.cur = 0;
    ss.maxlen = 0;
    rv = dosprintf(&ss, fmt, ap);
    if (rv < 0) {
      if (ss.base) {
          PR_DELETE(ss.base);
      }
      return 0;
    }
    return ss.base;
}

/*
** Stuff routine that discards overflow data
*/
static int LimitStuff(SprintfState *ss, const PRUnichar *sp, PRUint32 len)
{
    PRUint32 limit = ss->maxlen - (ss->cur - ss->base);

    if (len > limit) {
      len = limit;
    }
    while (len) {
      --len;
      *ss->cur++ = *sp++;
    }
    return 0;
}

/*
** sprintf into a fixed size buffer. Make sure there is a NUL at the end
** when finished.
*/
PRUint32 nsTextFormatter::snprintf(PRUnichar *out, PRUint32 outlen, const PRUnichar *fmt, ...)
{
    va_list ap;
    PRUint32 rv;

    PR_ASSERT((PRInt32)outlen > 0);
    if ((PRInt32)outlen <= 0) {
      return 0;
    }

    va_start(ap, fmt);
    rv = nsTextFormatter::vsnprintf(out, outlen, fmt, ap);
    va_end(ap);
    return rv;
}

PRUint32 nsTextFormatter::vsnprintf(PRUnichar *out, PRUint32 outlen,const PRUnichar *fmt,
                                    va_list ap)
{
    SprintfState ss;
    PRUint32 n;

    PR_ASSERT((PRInt32)outlen > 0);
    if ((PRInt32)outlen <= 0) {
      return 0;
    }

    ss.stuff = LimitStuff;
    ss.base = out;
    ss.cur = out;
    ss.maxlen = outlen;
    (void) dosprintf(&ss, fmt, ap);

    /* If we added chars, and we didn't append a null, do it now. */
    if( (ss.cur != ss.base) && (*(ss.cur - 1) != '\0') )
        *(--ss.cur) = '\0';

    n = ss.cur - ss.base;
    return n ? n - 1 : n;
}

PRUnichar * nsTextFormatter::sprintf_append(PRUnichar *last, const PRUnichar *fmt, ...)
{
    va_list ap;
    PRUnichar *rv;

    va_start(ap, fmt);
    rv = nsTextFormatter::vsprintf_append(last, fmt, ap);
    va_end(ap);
    return rv;
}

PRUnichar * nsTextFormatter::vsprintf_append(PRUnichar *last, const PRUnichar *fmt, va_list ap)
{
    SprintfState ss;
    int rv;

    ss.stuff = GrowStuff;
    if (last) {
      int lastlen = nsCRT::strlen(last);
      ss.base = last;
      ss.cur = last + lastlen;
      ss.maxlen = lastlen;
    } else {
      ss.base = 0;
      ss.cur = 0;
      ss.maxlen = 0;
    }
    rv = dosprintf(&ss, fmt, ap);
    if (rv < 0) {
      if (ss.base) {
          PR_DELETE(ss.base);
      }
      return 0;
    }
    return ss.base;
}
#ifdef DEBUG
PRBool nsTextFormatter::SelfTest()
{ 
    PRBool passed = PR_TRUE ;
    nsAutoString fmt(NS_LITERAL_STRING("%3$s %4$S %1$d %2$d"));

    char utf8[] = "Hello";
    PRUnichar ucs2[]={'W', 'o', 'r', 'l', 'd', 0x4e00, 0xAc00, 0xFF45, 0x0103};
    int d=3;


    PRUnichar buf[256];
    int ret;
    ret = nsTextFormatter::snprintf(buf, 256, fmt.get(), d, 333, utf8, ucs2);
    printf("ret = %d\n", ret);
    nsAutoString out(buf);
    printf("%s \n", NS_LossyConvertUCS2toASCII(out).get());
    const PRUnichar *uout = out.get();
    for(PRUint32 i=0;i<out.Length();i++)
        printf("%2X ", uout[i]);

    return passed;
}
#endif

Generated by  Doxygen 1.6.0   Back to index