Back to index

lightning-sunbird  0.9+nobinonly
nsTextFormatter.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org Code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 /* 
00039  * Portable safe sprintf code.
00040  *
00041  * Code based on mozilla/nsprpub/src/io/prprf.c rev 3.7 
00042  *
00043  * Contributor(s):
00044  *   Kipp E.B. Hickman  <kipp@netscape.com>  (original author)
00045  *   Frank Yung-Fong Tang  <ftang@netscape.com>
00046  *   Daniele Nicolodi  <daniele@grinta.net>
00047  */
00048 
00049 #include <stdarg.h>
00050 #include <stddef.h>
00051 #include <stdio.h>
00052 #include <string.h>
00053 #include "prdtoa.h"
00054 #include "prlong.h"
00055 #include "prlog.h"
00056 #include "prmem.h"
00057 #include "prprf.h"
00058 #include "nsCRT.h"
00059 #include "nsTextFormatter.h"
00060 #include "nsString.h"
00061 #include "nsReadableUtils.h"
00062 
00063 /*
00064 ** Note: on some platforms va_list is defined as an array,
00065 ** and requires array notation.
00066 */
00067 
00068 #ifdef HAVE_VA_COPY
00069 #define VARARGS_ASSIGN(foo, bar)        VA_COPY(foo,bar)
00070 #elif defined(HAVE_VA_LIST_AS_ARRAY)
00071 #define VARARGS_ASSIGN(foo, bar)   foo[0] = bar[0]
00072 #else
00073 #define VARARGS_ASSIGN(foo, bar)   (foo) = (bar)
00074 #endif
00075 
00076 typedef struct SprintfStateStr SprintfState;
00077 
00078 struct SprintfStateStr {
00079     int (*stuff)(SprintfState *ss, const PRUnichar *sp, PRUint32 len);
00080 
00081     PRUnichar *base;
00082     PRUnichar *cur;
00083     PRUint32 maxlen;
00084 
00085     void *stuffclosure;
00086 };
00087 
00088 /*
00089 ** Numbered Arguement State
00090 */
00091 struct NumArgState{
00092     int           type;            /* type of the current ap                    */
00093     va_list ap;                    /* point to the corresponding position on ap */
00094 };
00095 
00096 #define NAS_DEFAULT_NUM 20  /* default number of NumberedArgumentState array */
00097 
00098 #define TYPE_INT16   0
00099 #define TYPE_UINT16  1
00100 #define TYPE_INTN    2
00101 #define TYPE_UINTN   3
00102 #define TYPE_INT32   4
00103 #define TYPE_UINT32  5
00104 #define TYPE_INT64   6
00105 #define TYPE_UINT64  7
00106 #define TYPE_STRING  8
00107 #define TYPE_DOUBLE  9
00108 #define TYPE_INTSTR  10
00109 #define TYPE_UNISTRING      11
00110 #define TYPE_UNKNOWN 20
00111 
00112 #define _LEFT        0x1
00113 #define _SIGNED             0x2
00114 #define _SPACED             0x4
00115 #define _ZEROS              0x8
00116 #define _NEG         0x10
00117 
00118 #define ELEMENTS_OF(array_) (sizeof(array_) / sizeof(array_[0]))
00119 
00120 /*
00121 ** Fill into the buffer using the data in src
00122 */
00123 static int fill2(SprintfState *ss, const PRUnichar *src, int srclen, 
00124                  int width, int flags)
00125 {
00126     PRUnichar space = ' ';
00127     int rv;
00128     
00129     width -= srclen;
00130     /* Right adjusting */
00131     if ((width > 0) && ((flags & _LEFT) == 0)) {
00132        if (flags & _ZEROS) {
00133            space = '0';
00134        }
00135        while (--width >= 0) {
00136            rv = (*ss->stuff)(ss, &space, 1);
00137            if (rv < 0) {
00138               return rv;
00139            }
00140        }
00141     }
00142 
00143     /* Copy out the source data */
00144     rv = (*ss->stuff)(ss, src, srclen);
00145     if (rv < 0) {
00146        return rv;
00147     }
00148     
00149     /* Left adjusting */
00150     if ((width > 0) && ((flags & _LEFT) != 0)) {
00151        while (--width >= 0) {
00152            rv = (*ss->stuff)(ss, &space, 1);
00153            if (rv < 0) {
00154               return rv;
00155            }
00156        }
00157     }
00158     return 0;
00159 }
00160 
00161 /*
00162 ** Fill a number. The order is: optional-sign zero-filling conversion-digits
00163 */
00164 static int fill_n(SprintfState *ss, const PRUnichar *src, int srclen, 
00165                   int width, int prec, int type, int flags)
00166 {
00167     int zerowidth   = 0;
00168     int precwidth   = 0;
00169     int signwidth   = 0;
00170     int leftspaces  = 0;
00171     int rightspaces = 0;
00172     int cvtwidth;
00173     int rv;
00174     PRUnichar sign;
00175     PRUnichar space = ' ';
00176     PRUnichar zero = '0';
00177 
00178     if ((type & 1) == 0) {
00179        if (flags & _NEG) {
00180            sign = '-';
00181            signwidth = 1;
00182        } else if (flags & _SIGNED) {
00183            sign = '+';
00184            signwidth = 1;
00185        } else if (flags & _SPACED) {
00186            sign = ' ';
00187            signwidth = 1;
00188        }
00189     }
00190     cvtwidth = signwidth + srclen;
00191 
00192     if (prec > 0) {
00193        if (prec > srclen) {
00194             /* Need zero filling */
00195            precwidth = prec - srclen;
00196            cvtwidth += precwidth;
00197        }
00198     }
00199 
00200     if ((flags & _ZEROS) && (prec < 0)) {
00201        if (width > cvtwidth) {
00202             /* Zero filling */
00203            zerowidth = width - cvtwidth;
00204            cvtwidth += zerowidth;
00205        }
00206     }
00207 
00208     if (flags & _LEFT) {
00209        if (width > cvtwidth) {
00210            /* Space filling on the right (i.e. left adjusting) */
00211            rightspaces = width - cvtwidth;
00212        }
00213     } else {
00214        if (width > cvtwidth) {
00215            /* Space filling on the left (i.e. right adjusting) */
00216            leftspaces = width - cvtwidth;
00217        }
00218     }
00219     while (--leftspaces >= 0) {
00220        rv = (*ss->stuff)(ss, &space, 1);
00221        if (rv < 0) {
00222            return rv;
00223        }
00224     }
00225     if (signwidth) {
00226        rv = (*ss->stuff)(ss, &sign, 1);
00227        if (rv < 0) {
00228            return rv;
00229        }
00230     }
00231     while (--precwidth >= 0) {
00232        rv = (*ss->stuff)(ss,  &space, 1);
00233        if (rv < 0) {
00234            return rv;
00235        }
00236     }
00237     while (--zerowidth >= 0) {
00238        rv = (*ss->stuff)(ss,  &zero, 1);
00239        if (rv < 0) {
00240            return rv;
00241        }
00242     }
00243     rv = (*ss->stuff)(ss, src, srclen);
00244     if (rv < 0) {
00245        return rv;
00246     }
00247     while (--rightspaces >= 0) {
00248        rv = (*ss->stuff)(ss,  &space, 1);
00249        if (rv < 0) {
00250            return rv;
00251        }
00252     }
00253     return 0;
00254 }
00255 
00256 /*
00257 ** Convert a long into its printable form
00258 */
00259 static int cvt_l(SprintfState *ss, long num, int width, int prec,
00260                  int radix, int type, int flags, const PRUnichar *hexp)
00261 {
00262     PRUnichar cvtbuf[100];
00263     PRUnichar *cvt;
00264     int digits;
00265 
00266     /* according to the man page this needs to happen */
00267     if ((prec == 0) && (num == 0)) {
00268        return 0;
00269     }
00270 
00271     /*
00272     ** Converting decimal is a little tricky. In the unsigned case we
00273     ** need to stop when we hit 10 digits. In the signed case, we can
00274     ** stop when the number is zero.
00275     */
00276     cvt = &cvtbuf[0] + ELEMENTS_OF(cvtbuf);
00277     digits = 0;
00278     while (num) {
00279        int digit = (((unsigned long)num) % radix) & 0xF;
00280        *--cvt = hexp[digit];
00281        digits++;
00282        num = (long)(((unsigned long)num) / radix);
00283     }
00284     if (digits == 0) {
00285        *--cvt = '0';
00286        digits++;
00287     }
00288 
00289     /*
00290     ** Now that we have the number converted without its sign, deal with
00291     ** the sign and zero padding.
00292     */
00293     return fill_n(ss, cvt, digits, width, prec, type, flags);
00294 }
00295 
00296 /*
00297 ** Convert a 64-bit integer into its printable form
00298 */
00299 static int cvt_ll(SprintfState *ss, PRInt64 num, int width, int prec,
00300                   int radix, int type, int flags, const PRUnichar *hexp)
00301 {
00302     PRUnichar cvtbuf[100];
00303     PRUnichar *cvt;
00304     int digits;
00305     PRInt64 rad;
00306 
00307     /* according to the man page this needs to happen */
00308     if ((prec == 0) && (LL_IS_ZERO(num))) {
00309        return 0;
00310     }
00311 
00312     /*
00313     ** Converting decimal is a little tricky. In the unsigned case we
00314     ** need to stop when we hit 10 digits. In the signed case, we can
00315     ** stop when the number is zero.
00316     */
00317     LL_I2L(rad, radix);
00318     cvt = &cvtbuf[0] + ELEMENTS_OF(cvtbuf);
00319     digits = 0;
00320     while (!LL_IS_ZERO(num)) {
00321        PRInt32 digit;
00322        PRInt64 quot, rem;
00323        LL_UDIVMOD(&quot, &rem, num, rad);
00324        LL_L2I(digit, rem);
00325        *--cvt = hexp[digit & 0xf];
00326        digits++;
00327        num = quot;
00328     }
00329     if (digits == 0) {
00330        *--cvt = '0';
00331        digits++;
00332     }
00333 
00334     /*
00335     ** Now that we have the number converted without its sign, deal with
00336     ** the sign and zero padding.
00337     */
00338     return fill_n(ss, cvt, digits, width, prec, type, flags);
00339 }
00340 
00341 /*
00342 ** Convert a double precision floating point number into its printable
00343 ** form.
00344 */
00345 static int cvt_f(SprintfState *ss, double d, int width, int prec, 
00346                  const PRUnichar type, int flags)
00347 {
00348     int    mode = 2;
00349     int    decpt;
00350     int    sign;
00351     char   buf[256];
00352     char * bufp = buf;
00353     int    bufsz = 256;
00354     char   num[256];
00355     char * nump;
00356     char * endnum;
00357     int    numdigits = 0;
00358     char   exp = 'e';
00359 
00360     if (prec == -1) {
00361         prec = 6;
00362     } else if (prec > 50) {
00363         // limit precision to avoid PR_dtoa bug 108335
00364         // and to prevent buffers overflows
00365         prec = 50;
00366     }
00367 
00368     switch (type) {
00369     case 'f':  
00370         numdigits = prec;
00371         mode = 3;
00372         break;
00373     case 'E':
00374         exp = 'E';
00375         // no break
00376     case 'e':
00377         numdigits = prec + 1;
00378         mode = 2;
00379         break;
00380     case 'G':
00381         exp = 'E';
00382         // no break
00383     case 'g':
00384         if (prec == 0) {
00385             prec = 1;
00386         }
00387         numdigits = prec;
00388         mode = 2;
00389         break;
00390     default:
00391         NS_ERROR("invalid type passed to cvt_f");
00392     }
00393     
00394     if (PR_dtoa(d, mode, numdigits, &decpt, &sign, &endnum, num, bufsz) == PR_FAILURE) {
00395         buf[0] = '\0';
00396         return -1;
00397     }
00398     numdigits = endnum - num;
00399     nump = num;
00400 
00401     if (sign) {
00402         *bufp++ = '-';
00403     } else if (flags & _SIGNED) {
00404         *bufp++ = '+';
00405     }
00406 
00407     if (decpt == 9999) {
00408         while ((*bufp++ = *nump++)) { }
00409     } else {
00410 
00411         switch (type) {
00412 
00413         case 'E':
00414         case 'e':
00415 
00416             *bufp++ = *nump++;                
00417             if (prec > 0) {
00418                 *bufp++ = '.';
00419                 while (*nump) {
00420                     *bufp++ = *nump++;
00421                     prec--;
00422                 }
00423                 while (prec-- > 0) {
00424                     *bufp++ = '0';
00425                 }
00426             }
00427             *bufp++ = exp;
00428             PR_snprintf(bufp, bufsz - (bufp - buf), "%+03d", decpt-1);
00429             break;
00430 
00431         case 'f':
00432 
00433             if (decpt < 1) {
00434                 *bufp++ = '0';
00435                 if (prec > 0) {
00436                     *bufp++ = '.';
00437                     while (decpt++ && prec-- > 0) {
00438                         *bufp++ = '0';
00439                     }
00440                     while (*nump && prec-- > 0) {
00441                         *bufp++ = *nump++;
00442                     }
00443                     while (prec-- > 0) {
00444                         *bufp++ = '0';
00445                     }
00446                 }
00447             } else {
00448                 while (*nump && decpt-- > 0) {
00449                     *bufp++ = *nump++;
00450                 }
00451                 while (decpt-- > 0) {
00452                     *bufp++ = '0';
00453                 }
00454                 if (prec > 0) {
00455                     *bufp++ = '.';
00456                     while (*nump && prec-- > 0) {
00457                         *bufp++ = *nump++;
00458                     }
00459                     while (prec-- > 0) {
00460                         *bufp++ = '0';
00461                     }
00462                 }
00463             }
00464             *bufp = '\0';
00465             break;
00466 
00467         case 'G':
00468         case 'g':
00469 
00470             if ((decpt < -3) || ((decpt - 1) >= prec)) {
00471                 *bufp++ = *nump++;
00472                 numdigits--;
00473                 if (numdigits > 0) {
00474                     *bufp++ = '.';
00475                     while (*nump) {
00476                         *bufp++ = *nump++;
00477                     }
00478                 }
00479                 *bufp++ = exp;
00480                 PR_snprintf(bufp, bufsz - (bufp - buf), "%+03d", decpt-1);
00481             } else {
00482                 if (decpt < 1) {
00483                     *bufp++ = '0';
00484                     if (prec > 0) {
00485                         *bufp++ = '.';
00486                         while (decpt++) {
00487                             *bufp++ = '0';
00488                         }
00489                         while (*nump) {
00490                             *bufp++ = *nump++;
00491                         }
00492                     }
00493                 } else {
00494                     while (*nump && decpt-- > 0) {
00495                         *bufp++ = *nump++;
00496                         numdigits--;
00497                     }
00498                     while (decpt-- > 0) {
00499                         *bufp++ = '0';
00500                     }
00501                     if (numdigits > 0) {
00502                         *bufp++ = '.';
00503                         while (*nump) {
00504                             *bufp++ = *nump++;
00505                         }
00506                     }
00507                 }
00508                 *bufp = '\0';
00509             }
00510         }
00511     }
00512 
00513     PRUnichar rbuf[256];
00514     PRUnichar *rbufp = rbuf;
00515     bufp = buf;
00516     // cast to PRUnichar
00517     while ((*rbufp++ = *bufp++)) { }
00518     *rbufp = '\0';
00519 
00520     return fill2(ss, rbuf, nsCRT::strlen(rbuf), width, flags);
00521 }
00522 
00523 /*
00524 ** Convert a string into its printable form. "width" is the output
00525 ** width. "prec" is the maximum number of characters of "s" to output,
00526 ** where -1 means until NUL.
00527 */
00528 static int cvt_S(SprintfState *ss, const PRUnichar *s, int width,
00529                  int prec, int flags)
00530 {
00531     int slen;
00532 
00533     if (prec == 0) {
00534        return 0;
00535     }
00536 
00537     /* Limit string length by precision value */
00538     slen = s ? nsCRT::strlen(s) : 6;
00539     if (prec > 0) {
00540        if (prec < slen) {
00541            slen = prec;
00542        }
00543     }
00544 
00545     /* and away we go */
00546     NS_NAMED_LITERAL_STRING(nullstr, "(null)");
00547 
00548     return fill2(ss, s ? s : nullstr.get(), slen, width, flags);
00549 }
00550 
00551 /*
00552 ** Convert a string into its printable form.  "width" is the output
00553 ** width. "prec" is the maximum number of characters of "s" to output,
00554 ** where -1 means until NUL.
00555 */
00556 static int cvt_s(SprintfState *ss, const char *s, int width,
00557                  int prec, int flags)
00558 {
00559     NS_ConvertUTF8toUTF16 utf16Val(s);
00560     return cvt_S(ss, utf16Val.get(), width, prec, flags);
00561 }
00562 
00563 /*
00564 ** BiuldArgArray stands for Numbered Argument list Sprintf
00565 ** for example,  
00566 **     fmp = "%4$i, %2$d, %3s, %1d";
00567 ** the number must start from 1, and no gap among them
00568 */
00569 
00570 static struct NumArgState* BuildArgArray(const PRUnichar *fmt, 
00571                                          va_list ap, int * rv, 
00572                                          struct NumArgState * nasArray)
00573 {
00574     int number = 0, cn = 0, i;
00575     const PRUnichar* p;
00576     PRUnichar  c;
00577     struct NumArgState* nas;
00578 
00579     /*
00580     ** first pass:
00581     ** detemine how many legal % I have got, then allocate space
00582     */
00583     p = fmt;
00584     *rv = 0;
00585     i = 0;
00586     while ((c = *p++) != 0) {
00587        if (c != '%') {
00588            continue;
00589         }
00590         /* skip %% case */
00591        if ((c = *p++) == '%') {
00592            continue;
00593         }
00594 
00595        while( c != 0 ){
00596            if (c > '9' || c < '0') {
00597                 /* numbered argument csae */
00598               if (c == '$') {
00599                   if (i > 0) {
00600                      *rv = -1;
00601                      return NULL;
00602                   }
00603                   number++;
00604                   break;
00605 
00606               } else {
00607                     /* non-numbered argument case */
00608                   if (number > 0) {
00609                      *rv = -1;
00610                      return NULL;
00611                   }
00612                   i = 1;
00613                   break;
00614               }
00615            }
00616            c = *p++;
00617        }
00618     }
00619 
00620     if (number == 0) {
00621        return NULL;
00622     }
00623     
00624     if (number > NAS_DEFAULT_NUM) {
00625        nas = (struct NumArgState*)PR_MALLOC(number * sizeof(struct NumArgState));
00626        if (!nas) {
00627            *rv = -1;
00628            return NULL;
00629        }
00630     } else {
00631        nas = nasArray;
00632     }
00633 
00634     for (i = 0; i < number; i++) {
00635        nas[i].type = TYPE_UNKNOWN;
00636     }
00637 
00638     /*
00639     ** second pass:
00640     ** set nas[].type
00641     */
00642     p = fmt;
00643     while ((c = *p++) != 0) {
00644        if (c != '%') {
00645             continue;
00646         }
00647         c = *p++;
00648        if (c == '%') {
00649             continue;
00650         }
00651        cn = 0;
00652         /* should imporve error check later */
00653        while (c && c != '$') {
00654            cn = cn*10 + c - '0';
00655            c = *p++;
00656        }
00657 
00658        if (!c || cn < 1 || cn > number) {
00659            *rv = -1;
00660            break;
00661         }
00662 
00663        /* nas[cn] starts from 0, and make sure 
00664            nas[cn].type is not assigned */
00665         cn--;
00666        if (nas[cn].type != TYPE_UNKNOWN) {
00667            continue;
00668         }
00669 
00670         c = *p++;
00671 
00672         /* width */
00673         if (c == '*') {
00674            /* not supported feature, for the argument is not numbered */
00675            *rv = -1;
00676            break;
00677        } else {
00678            while ((c >= '0') && (c <= '9')) {
00679                c = *p++;
00680            }
00681        }
00682 
00683        /* precision */
00684        if (c == '.') {
00685            c = *p++;
00686            if (c == '*') {
00687                /* not supported feature, for the argument is not numbered */
00688                *rv = -1;
00689                break;
00690            } else {
00691                while ((c >= '0') && (c <= '9')) {
00692                   c = *p++;
00693               }
00694            }
00695        }
00696 
00697        /* size */
00698        nas[cn].type = TYPE_INTN;
00699        if (c == 'h') {
00700            nas[cn].type = TYPE_INT16;
00701            c = *p++;
00702        } else if (c == 'L') {
00703            /* XXX not quite sure here */
00704            nas[cn].type = TYPE_INT64;
00705            c = *p++;
00706        } else if (c == 'l') {
00707            nas[cn].type = TYPE_INT32;
00708            c = *p++;
00709            if (c == 'l') {
00710                nas[cn].type = TYPE_INT64;
00711                c = *p++;
00712            }
00713        }
00714 
00715        /* format */
00716        switch (c) {
00717        case 'd':
00718        case 'c':
00719        case 'i':
00720        case 'o':
00721        case 'u':
00722        case 'x':
00723        case 'X':
00724            break;
00725 
00726        case 'e':
00727        case 'f':
00728        case 'g':
00729            nas[cn].type = TYPE_DOUBLE;
00730            break;
00731 
00732        case 'p':
00733            /* XXX should use cpp */
00734            if (sizeof(void *) == sizeof(PRInt32)) {
00735               nas[cn].type = TYPE_UINT32;
00736            } else if (sizeof(void *) == sizeof(PRInt64)) {
00737                nas[cn].type = TYPE_UINT64;
00738            } else if (sizeof(void *) == sizeof(PRIntn)) {
00739                nas[cn].type = TYPE_UINTN;
00740            } else {
00741                nas[cn].type = TYPE_UNKNOWN;
00742            }
00743            break;
00744 
00745        case 'C':
00746            /* XXX not supported I suppose */
00747            PR_ASSERT(0);
00748            nas[cn].type = TYPE_UNKNOWN;
00749            break;
00750 
00751        case 'S':
00752            nas[cn].type = TYPE_UNISTRING;
00753            break;
00754 
00755        case 's':
00756            nas[cn].type = TYPE_STRING;
00757            break;
00758 
00759        case 'n':
00760            nas[cn].type = TYPE_INTSTR;
00761            break;
00762 
00763        default:
00764            PR_ASSERT(0);
00765            nas[cn].type = TYPE_UNKNOWN;
00766            break;
00767        }
00768 
00769        /* get a legal para. */
00770        if (nas[cn].type == TYPE_UNKNOWN) {
00771            *rv = -1;
00772            break;
00773        }
00774     }
00775 
00776 
00777     /*
00778     ** third pass
00779     ** fill the nas[cn].ap
00780     */
00781     if (*rv < 0) {
00782        if( nas != nasArray ) {
00783            PR_DELETE(nas);
00784         }
00785        return NULL;
00786     }
00787 
00788     cn = 0;
00789     while (cn < number) {
00790        if (nas[cn].type == TYPE_UNKNOWN) {
00791            cn++;
00792            continue;
00793        }
00794 
00795        VARARGS_ASSIGN(nas[cn].ap, ap);
00796 
00797        switch (nas[cn].type) {
00798        case TYPE_INT16:
00799        case TYPE_UINT16:
00800        case TYPE_INTN:
00801        case TYPE_UINTN:     (void)va_arg(ap, PRIntn);      break;
00802 
00803        case TYPE_INT32:     (void)va_arg(ap, PRInt32);     break;
00804 
00805        case TYPE_UINT32:    (void)va_arg(ap, PRUint32);    break;
00806 
00807        case TYPE_INT64:     (void)va_arg(ap, PRInt64);     break;
00808 
00809        case TYPE_UINT64:    (void)va_arg(ap, PRUint64);    break;
00810 
00811        case TYPE_STRING:    (void)va_arg(ap, char*);       break;
00812 
00813        case TYPE_INTSTR:    (void)va_arg(ap, PRIntn*);     break;
00814 
00815        case TYPE_DOUBLE:    (void)va_arg(ap, double);      break;
00816 
00817        case TYPE_UNISTRING: (void)va_arg(ap, PRUnichar*);  break;
00818 
00819        default:
00820            if( nas != nasArray ) {
00821               PR_DELETE( nas );
00822             }
00823            *rv = -1;
00824            return NULL;
00825        }
00826        cn++;
00827     }
00828     return nas;
00829 }
00830 
00831 /*
00832 ** The workhorse sprintf code.
00833 */
00834 static int dosprintf(SprintfState *ss, const PRUnichar *fmt, va_list ap)
00835 {
00836     PRUnichar c;
00837     int flags, width, prec, radix, type;
00838     union {
00839        PRUnichar ch;
00840        int i;
00841        long l;
00842        PRInt64 ll;
00843        double d;
00844        const char *s;
00845        const PRUnichar *S;
00846        int *ip;
00847     } u;
00848     PRUnichar space = ' ';
00849     const PRUnichar *fmt0;
00850 
00851     nsAutoString hex;
00852     hex.AssignLiteral("0123456789abcdef");
00853 
00854     nsAutoString HEX;
00855     HEX.AssignLiteral("0123456789ABCDEF");
00856 
00857     const PRUnichar *hexp;
00858     int rv, i;
00859     struct NumArgState* nas = NULL;
00860     struct NumArgState  nasArray[NAS_DEFAULT_NUM];
00861     /* in "%4$.2f" dolPt will point to . */
00862     const PRUnichar* dolPt = NULL;
00863 
00864 
00865     /*
00866     ** build an argument array, IF the fmt is numbered argument
00867     ** list style, to contain the Numbered Argument list pointers
00868     */
00869     nas = BuildArgArray (fmt, ap, &rv, nasArray);
00870     if (rv < 0) {
00871        /* the fmt contains error Numbered Argument format, jliu@netscape.com */
00872        PR_ASSERT(0);
00873        return rv;
00874     }
00875 
00876     while ((c = *fmt++) != 0) {
00877        if (c != '%') {
00878            rv = (*ss->stuff)(ss, fmt - 1, 1);
00879            if (rv < 0) {
00880               return rv;
00881            }
00882            continue;
00883        }
00884        fmt0 = fmt - 1;
00885 
00886        /*
00887        ** Gobble up the % format string. Hopefully we have handled all
00888        ** of the strange cases!
00889        */
00890        flags = 0;
00891        c = *fmt++;
00892        if (c == '%') {
00893            /* quoting a % with %% */
00894            rv = (*ss->stuff)(ss, fmt - 1, 1);
00895            if (rv < 0) {
00896               return rv;
00897            }
00898            continue;
00899        }
00900 
00901        if (nas != NULL) {
00902            /* the fmt contains the Numbered Arguments feature */
00903            i = 0;
00904            /* should imporve error check later */
00905            while (c && c != '$') {
00906               i = (i * 10) + (c - '0');
00907               c = *fmt++;
00908            }
00909 
00910            if (nas[i-1].type == TYPE_UNKNOWN) {
00911               if (nas && (nas != nasArray)) {
00912                   PR_DELETE(nas);
00913                 }
00914               return -1;
00915            }
00916 
00917            ap = nas[i-1].ap;
00918            dolPt = fmt;
00919            c = *fmt++;
00920        }
00921 
00922        /*
00923         * Examine optional flags.  Note that we do not implement the
00924         * '#' flag of sprintf().  The ANSI C spec. of the '#' flag is
00925         * somewhat ambiguous and not ideal, which is perhaps why
00926         * the various sprintf() implementations are inconsistent
00927         * on this feature.
00928         */
00929        while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) {
00930            if (c == '-') flags |= _LEFT;
00931            if (c == '+') flags |= _SIGNED;
00932            if (c == ' ') flags |= _SPACED;
00933            if (c == '0') flags |= _ZEROS;
00934            c = *fmt++;
00935        }
00936        if (flags & _SIGNED) flags &= ~_SPACED;
00937        if (flags & _LEFT) flags &= ~_ZEROS;
00938 
00939        /* width */
00940        if (c == '*') {
00941            c = *fmt++;
00942            width = va_arg(ap, int);
00943        } else {
00944            width = 0;
00945            while ((c >= '0') && (c <= '9')) {
00946               width = (width * 10) + (c - '0');
00947               c = *fmt++;
00948            }
00949        }
00950 
00951        /* precision */
00952        prec = -1;
00953        if (c == '.') {
00954            c = *fmt++;
00955            if (c == '*') {
00956               c = *fmt++;
00957               prec = va_arg(ap, int);
00958            } else {
00959               prec = 0;
00960               while ((c >= '0') && (c <= '9')) {
00961                   prec = (prec * 10) + (c - '0');
00962                   c = *fmt++;
00963               }
00964            }
00965        }
00966 
00967        /* size */
00968        type = TYPE_INTN;
00969        if (c == 'h') {
00970            type = TYPE_INT16;
00971            c = *fmt++;
00972        } else if (c == 'L') {
00973            /* XXX not quite sure here */
00974            type = TYPE_INT64;
00975            c = *fmt++;
00976        } else if (c == 'l') {
00977            type = TYPE_INT32;
00978            c = *fmt++;
00979            if (c == 'l') {
00980               type = TYPE_INT64;
00981               c = *fmt++;
00982            }
00983        }
00984 
00985        /* format */
00986        hexp = hex.get();
00987        switch (c) {
00988         case 'd': 
00989         case 'i':                               /* decimal/integer */
00990            radix = 10;
00991            goto fetch_and_convert;
00992 
00993         case 'o':                               /* octal */
00994            radix = 8;
00995            type |= 1;
00996            goto fetch_and_convert;
00997 
00998         case 'u':                               /* unsigned decimal */
00999            radix = 10;
01000            type |= 1;
01001            goto fetch_and_convert;
01002 
01003         case 'x':                               /* unsigned hex */
01004            radix = 16;
01005            type |= 1;
01006            goto fetch_and_convert;
01007 
01008         case 'X':                               /* unsigned HEX */
01009            radix = 16;
01010            hexp = HEX.get();
01011            type |= 1;
01012            goto fetch_and_convert;
01013 
01014         fetch_and_convert:
01015            switch (type) {
01016             case TYPE_INT16:
01017               u.l = va_arg(ap, int);
01018               if (u.l < 0) {
01019                   u.l = -u.l;
01020                   flags |= _NEG;
01021               }
01022               goto do_long;
01023             case TYPE_UINT16:
01024               u.l = va_arg(ap, int) & 0xffff;
01025               goto do_long;
01026             case TYPE_INTN:
01027               u.l = va_arg(ap, int);
01028               if (u.l < 0) {
01029                   u.l = -u.l;
01030                   flags |= _NEG;
01031               }
01032               goto do_long;
01033             case TYPE_UINTN:
01034               u.l = (long)va_arg(ap, unsigned int);
01035               goto do_long;
01036 
01037             case TYPE_INT32:
01038               u.l = va_arg(ap, PRInt32);
01039               if (u.l < 0) {
01040                   u.l = -u.l;
01041                   flags |= _NEG;
01042               }
01043               goto do_long;
01044             case TYPE_UINT32:
01045               u.l = (long)va_arg(ap, PRUint32);
01046             do_long:
01047               rv = cvt_l(ss, u.l, width, prec, radix, type, flags, hexp);
01048               if (rv < 0) {
01049                   return rv;
01050               }
01051               break;
01052 
01053             case TYPE_INT64:
01054               u.ll = va_arg(ap, PRInt64);
01055               if (!LL_GE_ZERO(u.ll)) {
01056                   LL_NEG(u.ll, u.ll);
01057                   flags |= _NEG;
01058               }
01059               goto do_longlong;
01060             case TYPE_UINT64:
01061               u.ll = va_arg(ap, PRUint64);
01062             do_longlong:
01063               rv = cvt_ll(ss, u.ll, width, prec, radix, type, flags, hexp);
01064               if (rv < 0) {
01065                   return rv;
01066               }
01067               break;
01068            }
01069            break;
01070 
01071         case 'e':
01072         case 'E':
01073         case 'f':
01074         case 'g':
01075         case 'G':
01076            u.d = va_arg(ap, double);
01077             rv = cvt_f(ss, u.d, width, prec, c, flags);
01078            if (rv < 0) {
01079               return rv;
01080            }
01081            break;
01082 
01083         case 'c':
01084            u.ch = va_arg(ap, int);
01085             if ((flags & _LEFT) == 0) {
01086                 while (width-- > 1) {
01087                     rv = (*ss->stuff)(ss, &space, 1);
01088                     if (rv < 0) {
01089                         return rv;
01090                     }
01091                 }
01092             }
01093            rv = (*ss->stuff)(ss, &u.ch, 1);
01094            if (rv < 0) {
01095               return rv;
01096            }
01097             if (flags & _LEFT) {
01098                 while (width-- > 1) {
01099                     rv = (*ss->stuff)(ss, &space, 1);
01100                     if (rv < 0) {
01101                         return rv;
01102                     }
01103                 }
01104             }
01105            break;
01106 
01107         case 'p':
01108            if (sizeof(void *) == sizeof(PRInt32)) {
01109               type = TYPE_UINT32;
01110            } else if (sizeof(void *) == sizeof(PRInt64)) {
01111               type = TYPE_UINT64;
01112            } else if (sizeof(void *) == sizeof(int)) {
01113               type = TYPE_UINTN;
01114            } else {
01115               PR_ASSERT(0);
01116               break;
01117            }
01118            radix = 16;
01119            goto fetch_and_convert;
01120 
01121 #if 0
01122         case 'C':
01123            /* XXX not supported I suppose */
01124            PR_ASSERT(0);
01125            break;
01126 #endif
01127 
01128         case 'S':
01129            u.S = va_arg(ap, const PRUnichar*);
01130            rv = cvt_S(ss, u.S, width, prec, flags);
01131            if (rv < 0) {
01132               return rv;
01133            }
01134            break;
01135 
01136         case 's':
01137            u.s = va_arg(ap, const char*);
01138            rv = cvt_s(ss, u.s, width, prec, flags);
01139            if (rv < 0) {
01140               return rv;
01141            }
01142            break;
01143 
01144         case 'n':
01145            u.ip = va_arg(ap, int*);
01146            if (u.ip) {
01147               *u.ip = ss->cur - ss->base;
01148            }
01149            break;
01150 
01151         default:
01152            /* Not a % token after all... skip it */
01153 #if 0
01154            PR_ASSERT(0);
01155 #endif
01156             PRUnichar perct = '%'; 
01157            rv = (*ss->stuff)(ss, &perct, 1);
01158            if (rv < 0) {
01159               return rv;
01160            }
01161            rv = (*ss->stuff)(ss, fmt - 1, 1);
01162            if (rv < 0) {
01163               return rv;
01164            }
01165        }
01166     }
01167 
01168     /* Stuff trailing NUL */
01169     PRUnichar null = '\0';
01170 
01171     rv = (*ss->stuff)(ss, &null, 1);
01172 
01173     if( nas && ( nas != nasArray ) ){
01174        PR_DELETE( nas );
01175     }
01176 
01177     return rv;
01178 }
01179 
01180 /************************************************************************/
01181 
01182 static int
01183 StringStuff(SprintfState* ss, const PRUnichar* sp, PRUint32 len)
01184 {
01185     ptrdiff_t off = ss->cur - ss->base;
01186     
01187     nsAString* str = NS_STATIC_CAST(nsAString*,ss->stuffclosure);
01188     str->Append(sp, len);
01189 
01190     // we can assume contiguous storage
01191     nsAString::iterator begin;
01192     str->BeginWriting(begin);
01193     ss->base = begin.get();
01194     ss->cur = ss->base + off;
01195 
01196     return 0;
01197 }
01198 
01199 /*
01200 ** Stuff routine that automatically grows the malloc'd output buffer
01201 ** before it overflows.
01202 */
01203 static int GrowStuff(SprintfState *ss, const PRUnichar *sp, PRUint32 len)
01204 {
01205     ptrdiff_t off;
01206     PRUnichar *newbase;
01207     PRUint32 newlen;
01208 
01209     off = ss->cur - ss->base;
01210     if (off + len >= ss->maxlen) {
01211        /* Grow the buffer */
01212        newlen = ss->maxlen + ((len > 32) ? len : 32);
01213        if (ss->base) {
01214            newbase = (PRUnichar*) PR_REALLOC(ss->base, newlen*sizeof(PRUnichar));
01215        } else {
01216            newbase = (PRUnichar*) PR_MALLOC(newlen*sizeof(PRUnichar));
01217        }
01218        if (!newbase) {
01219            /* Ran out of memory */
01220            return -1;
01221        }
01222        ss->base = newbase;
01223        ss->maxlen = newlen;
01224        ss->cur = ss->base + off;
01225     }
01226 
01227     /* Copy data */
01228     while (len) {
01229        --len;
01230        *ss->cur++ = *sp++;
01231     }
01232     PR_ASSERT((PRUint32)(ss->cur - ss->base) <= ss->maxlen);
01233     return 0;
01234 }
01235 
01236 /*
01237 ** sprintf into a malloc'd buffer
01238 */
01239 PRUnichar * nsTextFormatter::smprintf(const PRUnichar *fmt, ...)
01240 {
01241     va_list ap;
01242     PRUnichar *rv;
01243 
01244     va_start(ap, fmt);
01245     rv = nsTextFormatter::vsmprintf(fmt, ap);
01246     va_end(ap);
01247     return rv;
01248 }
01249 
01250 PRUint32 nsTextFormatter::ssprintf(nsAString& out, const PRUnichar* fmt, ...)
01251 {
01252     va_list ap;
01253     PRUint32 rv;
01254 
01255     va_start(ap, fmt);
01256     rv = nsTextFormatter::vssprintf(out, fmt, ap);
01257     va_end(ap);
01258     return rv;
01259 }
01260 
01261 /*
01262 ** Free memory allocated, for the caller, by smprintf
01263 */
01264 void nsTextFormatter::smprintf_free(PRUnichar *mem)
01265 {
01266     PR_DELETE(mem);
01267 }
01268 
01269 PRUint32 nsTextFormatter::vssprintf(nsAString& out, const PRUnichar* fmt, va_list ap)
01270 {
01271     SprintfState ss;
01272     ss.stuff = StringStuff;
01273     ss.base = 0;
01274     ss.cur = 0;
01275     ss.maxlen = 0;
01276     ss.stuffclosure = &out;
01277 
01278     out.Truncate();
01279     int n = dosprintf(&ss, fmt, ap);
01280     return n ? n - 1 : n;
01281 }
01282 
01283 PRUnichar * nsTextFormatter::vsmprintf(const PRUnichar *fmt, va_list ap)
01284 {
01285     SprintfState ss;
01286     int rv;
01287 
01288     ss.stuff = GrowStuff;
01289     ss.base = 0;
01290     ss.cur = 0;
01291     ss.maxlen = 0;
01292     rv = dosprintf(&ss, fmt, ap);
01293     if (rv < 0) {
01294        if (ss.base) {
01295            PR_DELETE(ss.base);
01296        }
01297        return 0;
01298     }
01299     return ss.base;
01300 }
01301 
01302 /*
01303 ** Stuff routine that discards overflow data
01304 */
01305 static int LimitStuff(SprintfState *ss, const PRUnichar *sp, PRUint32 len)
01306 {
01307     PRUint32 limit = ss->maxlen - (ss->cur - ss->base);
01308 
01309     if (len > limit) {
01310        len = limit;
01311     }
01312     while (len) {
01313        --len;
01314        *ss->cur++ = *sp++;
01315     }
01316     return 0;
01317 }
01318 
01319 /*
01320 ** sprintf into a fixed size buffer. Make sure there is a NUL at the end
01321 ** when finished.
01322 */
01323 PRUint32 nsTextFormatter::snprintf(PRUnichar *out, PRUint32 outlen, const PRUnichar *fmt, ...)
01324 {
01325     va_list ap;
01326     PRUint32 rv;
01327 
01328     PR_ASSERT((PRInt32)outlen > 0);
01329     if ((PRInt32)outlen <= 0) {
01330        return 0;
01331     }
01332 
01333     va_start(ap, fmt);
01334     rv = nsTextFormatter::vsnprintf(out, outlen, fmt, ap);
01335     va_end(ap);
01336     return rv;
01337 }
01338 
01339 PRUint32 nsTextFormatter::vsnprintf(PRUnichar *out, PRUint32 outlen,const PRUnichar *fmt,
01340                                     va_list ap)
01341 {
01342     SprintfState ss;
01343     PRUint32 n;
01344 
01345     PR_ASSERT((PRInt32)outlen > 0);
01346     if ((PRInt32)outlen <= 0) {
01347        return 0;
01348     }
01349 
01350     ss.stuff = LimitStuff;
01351     ss.base = out;
01352     ss.cur = out;
01353     ss.maxlen = outlen;
01354     (void) dosprintf(&ss, fmt, ap);
01355 
01356     /* If we added chars, and we didn't append a null, do it now. */
01357     if( (ss.cur != ss.base) && (*(ss.cur - 1) != '\0') )
01358         *(--ss.cur) = '\0';
01359 
01360     n = ss.cur - ss.base;
01361     return n ? n - 1 : n;
01362 }
01363 
01364 PRUnichar * nsTextFormatter::sprintf_append(PRUnichar *last, const PRUnichar *fmt, ...)
01365 {
01366     va_list ap;
01367     PRUnichar *rv;
01368 
01369     va_start(ap, fmt);
01370     rv = nsTextFormatter::vsprintf_append(last, fmt, ap);
01371     va_end(ap);
01372     return rv;
01373 }
01374 
01375 PRUnichar * nsTextFormatter::vsprintf_append(PRUnichar *last, const PRUnichar *fmt, va_list ap)
01376 {
01377     SprintfState ss;
01378     int rv;
01379 
01380     ss.stuff = GrowStuff;
01381     if (last) {
01382        int lastlen = nsCRT::strlen(last);
01383        ss.base = last;
01384        ss.cur = last + lastlen;
01385        ss.maxlen = lastlen;
01386     } else {
01387        ss.base = 0;
01388        ss.cur = 0;
01389        ss.maxlen = 0;
01390     }
01391     rv = dosprintf(&ss, fmt, ap);
01392     if (rv < 0) {
01393        if (ss.base) {
01394            PR_DELETE(ss.base);
01395        }
01396        return 0;
01397     }
01398     return ss.base;
01399 }
01400 #ifdef DEBUG
01401 PRBool nsTextFormatter::SelfTest()
01402 { 
01403     PRBool passed = PR_TRUE ;
01404     nsAutoString fmt(NS_LITERAL_STRING("%3$s %4$S %1$d %2$d"));
01405 
01406     char utf8[] = "Hello";
01407     PRUnichar ucs2[]={'W', 'o', 'r', 'l', 'd', 0x4e00, 0xAc00, 0xFF45, 0x0103};
01408     int d=3;
01409 
01410 
01411     PRUnichar buf[256];
01412     int ret;
01413     ret = nsTextFormatter::snprintf(buf, 256, fmt.get(), d, 333, utf8, ucs2);
01414     printf("ret = %d\n", ret);
01415     nsAutoString out(buf);
01416     printf("%s \n", NS_LossyConvertUCS2toASCII(out).get());
01417     const PRUnichar *uout = out.get();
01418     for(PRUint32 i=0;i<out.Length();i++)
01419         printf("%2X ", uout[i]);
01420 
01421     return passed;
01422 }
01423 #endif