home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / nsprpub / pr / src / io / prprf.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  26.0 KB  |  1,227 lines

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /*
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  * 
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  * 
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. /*
  20. ** Portable safe sprintf code.
  21. **
  22. ** Author: Kipp E.B. Hickman
  23. */
  24. #include <stdarg.h>
  25. #include <stddef.h>
  26. #include <stdio.h>
  27. #include <string.h>
  28. #include "primpl.h"
  29. #include "prprf.h"
  30. #include "prlong.h"
  31. #include "prlog.h"
  32. #include "prmem.h"
  33.  
  34. /*
  35. ** Note: on some platforms va_list is defined as an array,
  36. ** and requires array notation.
  37. */
  38. #if defined(MACLINUX) || defined(WIN16)
  39. #define VARARGS_ASSIGN(foo, bar) foo[0] = bar[0]
  40. #else
  41. #define VARARGS_ASSIGN(foo, bar) (foo) = (bar)
  42. #endif /*MACLINUX*/
  43.  
  44. /*
  45. ** WARNING: This code may *NOT* call PR_LOG (because PR_LOG calls it)
  46. */
  47.  
  48. /*
  49. ** XXX This needs to be internationalized!
  50. */
  51.  
  52. typedef struct SprintfStateStr SprintfState;
  53.  
  54. struct SprintfStateStr {
  55.     int (*stuff)(SprintfState *ss, const char *sp, PRUint32 len);
  56.  
  57.     char *base;
  58.     char *cur;
  59.     PRUint32 maxlen;
  60.  
  61.     int (*func)(void *arg, const char *sp, PRUint32 len);
  62.     void *arg;
  63. };
  64.  
  65. /*
  66. ** Numbered Arguement State
  67. */
  68. struct NumArgState{
  69.     int        type;        /* type of the current ap                    */
  70.     va_list ap;            /* point to the corresponding position on ap */
  71. };
  72.  
  73. static PRBool  l10n_debug_init = PR_FALSE;
  74. static PRBool  l10n_debug = PR_FALSE;
  75.  
  76. #define NAS_DEFAULT_NUM 20  /* default number of NumberedArgumentState array */
  77.  
  78.  
  79. #define TYPE_INT16    0
  80. #define TYPE_UINT16    1
  81. #define TYPE_INTN    2
  82. #define TYPE_UINTN    3
  83. #define TYPE_INT32    4
  84. #define TYPE_UINT32    5
  85. #define TYPE_INT64    6
  86. #define TYPE_UINT64    7
  87. #define TYPE_STRING    8
  88. #define TYPE_DOUBLE    9
  89. #define TYPE_INTSTR    10
  90. #define TYPE_UNKNOWN    20
  91.  
  92. #define _LEFT        0x1
  93. #define _SIGNED        0x2
  94. #define _SPACED        0x4
  95. #define _ZEROS        0x8
  96. #define _NEG        0x10
  97.  
  98. /*
  99. ** Fill into the buffer using the data in src
  100. */
  101. static int fill2(SprintfState *ss, const char *src, int srclen, int width,
  102.         int flags)
  103. {
  104.     char space = ' ';
  105.     int rv;
  106.  
  107.     width -= srclen;
  108.     if ((width > 0) && ((flags & _LEFT) == 0)) {    /* Right adjusting */
  109.     if (flags & _ZEROS) {
  110.         space = '0';
  111.     }
  112.     while (--width >= 0) {
  113.         rv = (*ss->stuff)(ss, &space, 1);
  114.         if (rv < 0) {
  115.         return rv;
  116.         }
  117.     }
  118.     }
  119.  
  120.     /* Copy out the source data */
  121.     rv = (*ss->stuff)(ss, src, srclen);
  122.     if (rv < 0) {
  123.     return rv;
  124.     }
  125.  
  126.     if ((width > 0) && ((flags & _LEFT) != 0)) {    /* Left adjusting */
  127.     while (--width >= 0) {
  128.         rv = (*ss->stuff)(ss, &space, 1);
  129.         if (rv < 0) {
  130.         return rv;
  131.         }
  132.     }
  133.     }
  134.     return 0;
  135. }
  136.  
  137. /*
  138. ** Fill a number. The order is: optional-sign zero-filling conversion-digits
  139. */
  140. static int fill_n(SprintfState *ss, const char *src, int srclen, int width,
  141.           int prec, int type, int flags)
  142. {
  143.     int zerowidth = 0;
  144.     int precwidth = 0;
  145.     int signwidth = 0;
  146.     int leftspaces = 0;
  147.     int rightspaces = 0;
  148.     int cvtwidth;
  149.     int rv;
  150.     char sign;
  151.  
  152.     if ((type & 1) == 0) {
  153.     if (flags & _NEG) {
  154.         sign = '-';
  155.         signwidth = 1;
  156.     } else if (flags & _SIGNED) {
  157.         sign = '+';
  158.         signwidth = 1;
  159.     } else if (flags & _SPACED) {
  160.         sign = ' ';
  161.         signwidth = 1;
  162.     }
  163.     }
  164.     cvtwidth = signwidth + srclen;
  165.  
  166.     if (prec > 0) {
  167.     if (prec > srclen) {
  168.         precwidth = prec - srclen;        /* Need zero filling */
  169.         cvtwidth += precwidth;
  170.     }
  171.     }
  172.  
  173.     if ((flags & _ZEROS) && (prec < 0)) {
  174.     if (width > cvtwidth) {
  175.         zerowidth = width - cvtwidth;    /* Zero filling */
  176.         cvtwidth += zerowidth;
  177.     }
  178.     }
  179.  
  180.     if (flags & _LEFT) {
  181.     if (width > cvtwidth) {
  182.         /* Space filling on the right (i.e. left adjusting) */
  183.         rightspaces = width - cvtwidth;
  184.     }
  185.     } else {
  186.     if (width > cvtwidth) {
  187.         /* Space filling on the left (i.e. right adjusting) */
  188.         leftspaces = width - cvtwidth;
  189.     }
  190.     }
  191.     while (--leftspaces >= 0) {
  192.     rv = (*ss->stuff)(ss, " ", 1);
  193.     if (rv < 0) {
  194.         return rv;
  195.     }
  196.     }
  197.     if (signwidth) {
  198.     rv = (*ss->stuff)(ss, &sign, 1);
  199.     if (rv < 0) {
  200.         return rv;
  201.     }
  202.     }
  203.     while (--precwidth >= 0) {
  204.     rv = (*ss->stuff)(ss, "0", 1);
  205.     if (rv < 0) {
  206.         return rv;
  207.     }
  208.     }
  209.     while (--zerowidth >= 0) {
  210.     rv = (*ss->stuff)(ss, "0", 1);
  211.     if (rv < 0) {
  212.         return rv;
  213.     }
  214.     }
  215.     rv = (*ss->stuff)(ss, src, srclen);
  216.     if (rv < 0) {
  217.     return rv;
  218.     }
  219.     while (--rightspaces >= 0) {
  220.     rv = (*ss->stuff)(ss, " ", 1);
  221.     if (rv < 0) {
  222.         return rv;
  223.     }
  224.     }
  225.     return 0;
  226. }
  227.  
  228. /*
  229. ** Convert a long into its printable form
  230. */
  231. static int cvt_l(SprintfState *ss, long num, int width, int prec, int radix,
  232.          int type, int flags, const char *hexp)
  233. {
  234.     char cvtbuf[100];
  235.     char *cvt;
  236.     int digits;
  237.  
  238.     /* according to the man page this needs to happen */
  239.     if ((prec == 0) && (num == 0)) {
  240.     return 0;
  241.     }
  242.  
  243.     /*
  244.     ** Converting decimal is a little tricky. In the unsigned case we
  245.     ** need to stop when we hit 10 digits. In the signed case, we can
  246.     ** stop when the number is zero.
  247.     */
  248.     cvt = cvtbuf + sizeof(cvtbuf);
  249.     digits = 0;
  250.     while (num) {
  251.     int digit = (((unsigned long)num) % radix) & 0xF;
  252.     *--cvt = hexp[digit];
  253.     digits++;
  254.     num = (long)((unsigned long)num) / radix;
  255.     }
  256.     if (digits == 0) {
  257.     *--cvt = '0';
  258.     digits++;
  259.     }
  260.  
  261.     /*
  262.     ** Now that we have the number converted without its sign, deal with
  263.     ** the sign and zero padding.
  264.     */
  265.     return fill_n(ss, cvt, digits, width, prec, type, flags);
  266. }
  267.  
  268. /*
  269. ** Convert a 64-bit integer into its printable form
  270. */
  271. static int cvt_ll(SprintfState *ss, PRInt64 num, int width, int prec, int radix,
  272.           int type, int flags, const char *hexp)
  273. {
  274.     char cvtbuf[100];
  275.     char *cvt;
  276.     int digits;
  277.     PRInt64 rad;
  278.  
  279.     /* according to the man page this needs to happen */
  280.     if ((prec == 0) && (LL_IS_ZERO(num))) {
  281.     return 0;
  282.     }
  283.  
  284.     /*
  285.     ** Converting decimal is a little tricky. In the unsigned case we
  286.     ** need to stop when we hit 10 digits. In the signed case, we can
  287.     ** stop when the number is zero.
  288.     */
  289.     LL_I2L(rad, radix);
  290.     cvt = cvtbuf + sizeof(cvtbuf);
  291.     digits = 0;
  292.     while (!LL_IS_ZERO(num)) {
  293.     PRInt32 digit;
  294.     PRInt64 quot, rem;
  295.     LL_UDIVMOD(", &rem, num, rad);
  296.     LL_L2I(digit, rem);
  297.     *--cvt = hexp[digit & 0xf];
  298.     digits++;
  299.     num = quot;
  300.     }
  301.     if (digits == 0) {
  302.     *--cvt = '0';
  303.     digits++;
  304.     }
  305.  
  306.     /*
  307.     ** Now that we have the number converted without its sign, deal with
  308.     ** the sign and zero padding.
  309.     */
  310.     return fill_n(ss, cvt, digits, width, prec, type, flags);
  311. }
  312.  
  313. /*
  314. ** Convert a double precision floating point number into its printable
  315. ** form.
  316. **
  317. ** XXX stop using sprintf to convert floating point
  318. */
  319. static int cvt_f(SprintfState *ss, double d, const char *fmt0, const char *fmt1)
  320. {
  321.     char fin[20];
  322.     char fout[300];
  323.     int amount = fmt1 - fmt0;
  324.  
  325.     PR_ASSERT((amount > 0) && (amount < sizeof(fin)));
  326.     if (amount >= sizeof(fin)) {
  327.     /* Totally bogus % command to sprintf. Just ignore it */
  328.     return 0;
  329.     }
  330.     memcpy(fin, fmt0, amount);
  331.     fin[amount] = 0;
  332.  
  333.     /* Convert floating point using the native sprintf code */
  334. #ifdef DEBUG
  335.     {
  336.         const char *p = fin;
  337.         while (*p) {
  338.             PR_ASSERT(*p != 'L');
  339.             p++;
  340.         }
  341.     }
  342. #endif
  343.     sprintf(fout, fin, d);
  344.  
  345.     /*
  346.     ** This assert will catch overflow's of fout, when building with
  347.     ** debugging on. At least this way we can track down the evil piece
  348.     ** of calling code and fix it!
  349.     */
  350.     PR_ASSERT(strlen(fout) < sizeof(fout));
  351.  
  352.     return (*ss->stuff)(ss, fout, strlen(fout));
  353. }
  354.  
  355. /*
  356. ** Convert a string into its printable form.  "width" is the output
  357. ** width. "prec" is the maximum number of characters of "s" to output,
  358. ** where -1 means until NUL.
  359. */
  360. static int cvt_s(SprintfState *ss, const char *s, int width, int prec,
  361.          int flags)
  362. {
  363.     int slen;
  364.  
  365.     if (prec == 0)
  366.     return 0;
  367.  
  368.     /* Limit string length by precision value */
  369.     slen = s ? strlen(s) : 6;
  370.     if (prec > 0) {
  371.     if (prec < slen) {
  372.         slen = prec;
  373.     }
  374.     }
  375.  
  376.     /* and away we go */
  377.     return fill2(ss, s ? s : "(null)", slen, width, flags);
  378. }
  379.  
  380. /*
  381. ** BiuldArgArray stands for Numbered Argument list Sprintf
  382. ** for example,  
  383. **    fmp = "%4$i, %2$d, %3s, %1d";
  384. ** the number must start from 1, and no gap among them
  385. */
  386.  
  387. static struct NumArgState* BuildArgArray( const char *fmt, va_list ap, int* rv, struct NumArgState* nasArray )
  388. {
  389.     int number = 0, cn = 0, i;
  390.     const char* p;
  391.     char  c;
  392.     struct NumArgState* nas;
  393.     
  394.  
  395.     /*
  396.     ** set the l10n_debug flag
  397.     ** this routine should be executed only once
  398.     ** 'cause getenv does take time
  399.     */
  400.     if( !l10n_debug_init ){
  401.     l10n_debug_init = PR_TRUE;
  402.     p = getenv( "NETSCAPE_LOCALIZATION_DEBUG" );
  403.     if( ( p != NULL ) && ( *p == '1' ) ){
  404.         l10n_debug = PR_TRUE;
  405.     }
  406.     }
  407.  
  408.  
  409.     /*
  410.     **    first pass:
  411.     **    detemine how many legal % I have got, then allocate space
  412.     */
  413.  
  414.     p = fmt;
  415.     *rv = 0;
  416.     i = 0;
  417.     while( ( c = *p++ ) != 0 ){
  418.     if( c != '%' )
  419.         continue;
  420.     if( ( c = *p++ ) == '%' )    /* skip %% case */
  421.         continue;
  422.  
  423.     while( c != 0 ){
  424.         if( c > '9' || c < '0' ){
  425.         if( c == '$' ){        /* numbered argument csae */
  426.             if( i > 0 ){
  427.             *rv = -1;
  428.             if( l10n_debug )
  429.                 printf( "either no *OR* all arguments are numbered \"%s\"\n", fmt );
  430.             return NULL;
  431.             }
  432.             number++;
  433.             break;
  434.  
  435.         } else{            /* non-numbered argument case */
  436.             if( number > 0 ){
  437.             if( l10n_debug )
  438.                 printf( "either no *OR* all arguments are numbered \"%s\"\n", fmt );
  439.             *rv = -1;
  440.             return NULL;
  441.             }
  442.             i = 1;
  443.             break;
  444.         }
  445.         }
  446.  
  447.         c = *p++;
  448.     }
  449.     }
  450.  
  451.     if( number == 0 ){
  452.     return NULL;
  453.     }
  454.  
  455.     
  456.     if( number > NAS_DEFAULT_NUM ){
  457.     nas = (struct NumArgState*)PR_MALLOC( number * sizeof( struct NumArgState ) );
  458.     if( !nas ){
  459.         *rv = -1;
  460.         if( l10n_debug )
  461.         printf( "PR_MALLOC() error for \"%s\"\n", fmt );
  462.         return NULL;
  463.     }
  464.     } else {
  465.     nas = nasArray;
  466.     }
  467.  
  468.     for( i = 0; i < number; i++ ){
  469.     nas[i].type = TYPE_UNKNOWN;
  470.     }
  471.  
  472.  
  473.     /*
  474.     ** second pass:
  475.     ** set nas[].type
  476.     */
  477.  
  478.     p = fmt;
  479.     while( ( c = *p++ ) != 0 ){
  480.         if( c != '%' )    continue;
  481.         c = *p++;
  482.     if( c == '%' )    continue;
  483.  
  484.     cn = 0;
  485.     while( c && c != '$' ){        /* should imporve error check later */
  486.         cn = cn*10 + c - '0';
  487.         c = *p++;
  488.     }
  489.  
  490.     if( !c || cn < 1 || cn > number ){
  491.         *rv = -1;
  492.         if( l10n_debug )
  493.         printf( "invalid argument number (valid range [1, %d]), \"%s\"\n", number, fmt );
  494.         break;
  495.         }
  496.  
  497.     /* nas[cn] starts from 0, and make sure nas[cn].type is not assigned */
  498.         cn--;
  499.     if( nas[cn].type != TYPE_UNKNOWN )
  500.         continue;
  501.  
  502.         c = *p++;
  503.  
  504.         /* width */
  505.         if (c == '*') {
  506.         /* not supported feature, for the argument is not numbered */
  507.         *rv = -1;
  508.         if( l10n_debug )
  509.         printf( "* width specifier not support for numbered arguments \"%s\"\n", fmt );
  510.         break;
  511.     } else {
  512.         while ((c >= '0') && (c <= '9')) {
  513.             c = *p++;
  514.         }
  515.     }
  516.  
  517.     /* precision */
  518.     if (c == '.') {
  519.         c = *p++;
  520.         if (c == '*') {
  521.             /* not supported feature, for the argument is not numbered */
  522.         if( l10n_debug )
  523.             printf( "* precision specifier not support for numbered arguments \"%s\"\n", fmt );
  524.             *rv = -1;
  525.             break;
  526.         } else {
  527.             while ((c >= '0') && (c <= '9')) {
  528.             c = *p++;
  529.         }
  530.         }
  531.     }
  532.  
  533.     /* size */
  534.     nas[cn].type = TYPE_INTN;
  535.     if (c == 'h') {
  536.         nas[cn].type = TYPE_INT16;
  537.         c = *p++;
  538.     } else if (c == 'L') {
  539.         /* XXX not quite sure here */
  540.         nas[cn].type = TYPE_INT64;
  541.         c = *p++;
  542.     } else if (c == 'l') {
  543.         nas[cn].type = TYPE_INT32;
  544.         c = *p++;
  545.         if (c == 'l') {
  546.             nas[cn].type = TYPE_INT64;
  547.             c = *p++;
  548.         }
  549.     }
  550.  
  551.     /* format */
  552.     switch (c) {
  553.     case 'd':
  554.     case 'c':
  555.     case 'i':
  556.     case 'o':
  557.     case 'u':
  558.     case 'x':
  559.     case 'X':
  560.         break;
  561.  
  562.     case 'e':
  563.     case 'f':
  564.     case 'g':
  565.         nas[ cn ].type = TYPE_DOUBLE;
  566.         break;
  567.  
  568.     case 'p':
  569.         /* XXX should use cpp */
  570.         if (sizeof(void *) == sizeof(PRInt32)) {
  571.         nas[ cn ].type = TYPE_UINT32;
  572.         } else if (sizeof(void *) == sizeof(PRInt64)) {
  573.             nas[ cn ].type = TYPE_UINT64;
  574.         } else if (sizeof(void *) == sizeof(PRIntn)) {
  575.             nas[ cn ].type = TYPE_UINTN;
  576.         } else {
  577.             nas[ cn ].type = TYPE_UNKNOWN;
  578.         }
  579.         break;
  580.  
  581.     case 'C':
  582.     case 'S':
  583.     case 'E':
  584.     case 'G':
  585.         /* XXX not supported I suppose */
  586.         PR_ASSERT(0);
  587.         nas[ cn ].type = TYPE_UNKNOWN;
  588.         break;
  589.  
  590.     case 's':
  591.         nas[ cn ].type = TYPE_STRING;
  592.         break;
  593.  
  594.     case 'n':
  595.         nas[ cn ].type = TYPE_INTSTR;
  596.         break;
  597.  
  598.     default:
  599.         PR_ASSERT(0);
  600.         nas[ cn ].type = TYPE_UNKNOWN;
  601.         break;
  602.     }
  603.  
  604.     /* get a legal para. */
  605.     if( nas[ cn ].type == TYPE_UNKNOWN ){
  606.         if( l10n_debug )
  607.         printf( "unknown type \"%s\"\n", fmt );
  608.         *rv = -1;
  609.         break;
  610.     }
  611.     }
  612.  
  613.  
  614.     /*
  615.     ** third pass
  616.     ** fill the nas[cn].ap
  617.     */
  618.  
  619.     if( *rv < 0 ){
  620.     if( nas != nasArray )
  621.         PR_DELETE( nas );
  622.     return NULL;
  623.     }
  624.  
  625.     cn = 0;
  626.     while( cn < number ){
  627.     if( nas[cn].type == TYPE_UNKNOWN ){
  628.         cn++;
  629.         continue;
  630.     }
  631.  
  632.     VARARGS_ASSIGN(nas[cn].ap, ap);
  633.  
  634.     switch( nas[cn].type ){
  635.     case TYPE_INT16:
  636.     case TYPE_UINT16:
  637.     case TYPE_INTN:
  638.     case TYPE_UINTN:        (void)va_arg( ap, PRIntn );        break;
  639.  
  640.     case TYPE_INT32:        (void)va_arg( ap, PRInt32 );        break;
  641.  
  642.     case TYPE_UINT32:    (void)va_arg( ap, PRUint32 );    break;
  643.  
  644.     case TYPE_INT64:    (void)va_arg( ap, PRInt64 );        break;
  645.  
  646.     case TYPE_UINT64:    (void)va_arg( ap, PRUint64 );        break;
  647.  
  648.     case TYPE_STRING:    (void)va_arg( ap, char* );        break;
  649.  
  650.     case TYPE_INTSTR:    (void)va_arg( ap, PRIntn* );        break;
  651.  
  652.     case TYPE_DOUBLE:    (void)va_arg( ap, double );        break;
  653.  
  654.     default:
  655.         if( nas != nasArray )
  656.         PR_DELETE( nas );
  657.         *rv = -1;
  658.         return NULL;
  659.     }
  660.  
  661.     cn++;
  662.     }
  663.  
  664.  
  665.     return nas;
  666. }
  667.  
  668. /*
  669. ** The workhorse sprintf code.
  670. */
  671. static int dosprintf(SprintfState *ss, const char *fmt, va_list ap)
  672. {
  673.     char c;
  674.     int flags, width, prec, radix, type;
  675.     union {
  676.     char ch;
  677.     int i;
  678.     long l;
  679.     PRInt64 ll;
  680.     double d;
  681.     const char *s;
  682.     int *ip;
  683.     } u;
  684.     const char *fmt0;
  685.     static char *hex = "0123456789abcdef";
  686.     static char *HEX = "0123456789ABCDEF";
  687.     char *hexp;
  688.     int rv, i;
  689.     struct NumArgState* nas = NULL;
  690.     struct NumArgState  nasArray[ NAS_DEFAULT_NUM ];
  691.     char  pattern[20];
  692.     const char* dolPt = NULL;  /* in "%4$.2f", dolPt will poiont to . */
  693.  
  694.  
  695.     /*
  696.     ** build an argument array, IF the fmt is numbered argument
  697.     ** list style, to contain the Numbered Argument list pointers
  698.     */
  699.  
  700.     nas = BuildArgArray( fmt, ap, &rv, nasArray );
  701.     if( rv < 0 ){
  702.     /* the fmt contains error Numbered Argument format, jliu@netscape.com */
  703.     PR_ASSERT(0);
  704.     return rv;
  705.     }
  706.  
  707.     while ((c = *fmt++) != 0) {
  708.     if (c != '%') {
  709.         rv = (*ss->stuff)(ss, fmt - 1, 1);
  710.         if (rv < 0) {
  711.         return rv;
  712.         }
  713.         continue;
  714.     }
  715.     fmt0 = fmt - 1;
  716.  
  717.     /*
  718.     ** Gobble up the % format string. Hopefully we have handled all
  719.     ** of the strange cases!
  720.     */
  721.     flags = 0;
  722.     c = *fmt++;
  723.     if (c == '%') {
  724.         /* quoting a % with %% */
  725.         rv = (*ss->stuff)(ss, fmt - 1, 1);
  726.         if (rv < 0) {
  727.         return rv;
  728.         }
  729.         continue;
  730.     }
  731.  
  732.     if( nas != NULL ){
  733.         /* the fmt contains the Numbered Arguments feature */
  734.         i = 0;
  735.         while( c && c != '$' ){        /* should imporve error check later */
  736.         i = ( i * 10 ) + ( c - '0' );
  737.         c = *fmt++;
  738.         }
  739.  
  740.         if( nas[i-1].type == TYPE_UNKNOWN ){
  741.         if( l10n_debug )
  742.             printf( "numbered argument type unknown\n" );
  743.         if( nas && ( nas != nasArray ) )
  744.             PR_DELETE( nas );
  745.         return -1;
  746.         }
  747.  
  748.         ap = nas[i-1].ap;
  749.         dolPt = fmt;
  750.         c = *fmt++;
  751.     }
  752.  
  753.     /*
  754.      * Examine optional flags.  Note that we do not implement the
  755.      * '#' flag of sprintf().  The ANSI C spec. of the '#' flag is
  756.      * somewhat ambiguous and not ideal, which is perhaps why
  757.      * the various sprintf() implementations are inconsistent
  758.      * on this feature.
  759.      */
  760.     while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) {
  761.         if (c == '-') flags |= _LEFT;
  762.         if (c == '+') flags |= _SIGNED;
  763.         if (c == ' ') flags |= _SPACED;
  764.         if (c == '0') flags |= _ZEROS;
  765.         c = *fmt++;
  766.     }
  767.     if (flags & _SIGNED) flags &= ~_SPACED;
  768.     if (flags & _LEFT) flags &= ~_ZEROS;
  769.  
  770.     /* width */
  771.     if (c == '*') {
  772.         c = *fmt++;
  773.         width = va_arg(ap, int);
  774.     } else {
  775.         width = 0;
  776.         while ((c >= '0') && (c <= '9')) {
  777.         width = (width * 10) + (c - '0');
  778.         c = *fmt++;
  779.         }
  780.     }
  781.  
  782.     /* precision */
  783.     prec = -1;
  784.     if (c == '.') {
  785.         c = *fmt++;
  786.         if (c == '*') {
  787.         c = *fmt++;
  788.         prec = va_arg(ap, int);
  789.         } else {
  790.         prec = 0;
  791.         while ((c >= '0') && (c <= '9')) {
  792.             prec = (prec * 10) + (c - '0');
  793.             c = *fmt++;
  794.         }
  795.         }
  796.     }
  797.  
  798.     /* size */
  799.     type = TYPE_INTN;
  800.     if (c == 'h') {
  801.         type = TYPE_INT16;
  802.         c = *fmt++;
  803.     } else if (c == 'L') {
  804.         /* XXX not quite sure here */
  805.         type = TYPE_INT64;
  806.         c = *fmt++;
  807.     } else if (c == 'l') {
  808.         type = TYPE_INT32;
  809.         c = *fmt++;
  810.         if (c == 'l') {
  811.         type = TYPE_INT64;
  812.         c = *fmt++;
  813.         }
  814.     }
  815.  
  816.     /* format */
  817.     hexp = hex;
  818.     switch (c) {
  819.       case 'd': case 'i':            /* decimal/integer */
  820.         radix = 10;
  821.         goto fetch_and_convert;
  822.  
  823.       case 'o':                /* octal */
  824.         radix = 8;
  825.         type |= 1;
  826.         goto fetch_and_convert;
  827.  
  828.       case 'u':                /* unsigned decimal */
  829.         radix = 10;
  830.         type |= 1;
  831.         goto fetch_and_convert;
  832.  
  833.       case 'x':                /* unsigned hex */
  834.         radix = 16;
  835.         type |= 1;
  836.         goto fetch_and_convert;
  837.  
  838.       case 'X':                /* unsigned HEX */
  839.         radix = 16;
  840.         hexp = HEX;
  841.         type |= 1;
  842.         goto fetch_and_convert;
  843.  
  844.       fetch_and_convert:
  845.         switch (type) {
  846.           case TYPE_INT16:
  847.         u.l = va_arg(ap, int);
  848.         if (u.l < 0) {
  849.             u.l = -u.l;
  850.             flags |= _NEG;
  851.         }
  852.         goto do_long;
  853.           case TYPE_UINT16:
  854.         u.l = va_arg(ap, int) & 0xffff;
  855.         goto do_long;
  856.           case TYPE_INTN:
  857.         u.l = va_arg(ap, int);
  858.         if (u.l < 0) {
  859.             u.l = -u.l;
  860.             flags |= _NEG;
  861.         }
  862.         goto do_long;
  863.           case TYPE_UINTN:
  864.         u.l = (long)va_arg(ap, unsigned int);
  865.         goto do_long;
  866.  
  867.           case TYPE_INT32:
  868.         u.l = va_arg(ap, PRInt32);
  869.         if (u.l < 0) {
  870.             u.l = -u.l;
  871.             flags |= _NEG;
  872.         }
  873.         goto do_long;
  874.           case TYPE_UINT32:
  875.         u.l = (long)va_arg(ap, PRUint32);
  876.           do_long:
  877.         rv = cvt_l(ss, u.l, width, prec, radix, type, flags, hexp);
  878.         if (rv < 0) {
  879.             return rv;
  880.         }
  881.         break;
  882.  
  883.           case TYPE_INT64:
  884.         u.ll = va_arg(ap, PRInt64);
  885.         if (!LL_GE_ZERO(u.ll)) {
  886.             LL_NEG(u.ll, u.ll);
  887.             flags |= _NEG;
  888.         }
  889.         goto do_longlong;
  890.           case TYPE_UINT64:
  891.         u.ll = va_arg(ap, PRUint64);
  892.           do_longlong:
  893.         rv = cvt_ll(ss, u.ll, width, prec, radix, type, flags, hexp);
  894.         if (rv < 0) {
  895.             return rv;
  896.         }
  897.         break;
  898.         }
  899.         break;
  900.  
  901.       case 'e':
  902.       case 'E':
  903.       case 'f':
  904.       case 'g':
  905.         u.d = va_arg(ap, double);
  906.         if( nas != NULL ){
  907.         i = fmt - dolPt;
  908.         if( i < sizeof( pattern ) ){
  909.             pattern[0] = '%';
  910.             memcpy( &pattern[1], dolPt, i );
  911.             rv = cvt_f(ss, u.d, pattern, &pattern[i+1] );
  912.         }
  913.         } else
  914.         rv = cvt_f(ss, u.d, fmt0, fmt);
  915.  
  916.         if (rv < 0) {
  917.         return rv;
  918.         }
  919.         break;
  920.  
  921.       case 'c':
  922.         u.ch = va_arg(ap, int);
  923.             if ((flags & _LEFT) == 0) {
  924.                 while (width-- > 1) {
  925.                     rv = (*ss->stuff)(ss, " ", 1);
  926.                     if (rv < 0) {
  927.                         return rv;
  928.                     }
  929.                 }
  930.             }
  931.         rv = (*ss->stuff)(ss, &u.ch, 1);
  932.         if (rv < 0) {
  933.         return rv;
  934.         }
  935.             if (flags & _LEFT) {
  936.                 while (width-- > 1) {
  937.                     rv = (*ss->stuff)(ss, " ", 1);
  938.                     if (rv < 0) {
  939.                         return rv;
  940.                     }
  941.                 }
  942.             }
  943.         break;
  944.  
  945.       case 'p':
  946.         if (sizeof(void *) == sizeof(PRInt32)) {
  947.             type = TYPE_UINT32;
  948.         } else if (sizeof(void *) == sizeof(PRInt64)) {
  949.             type = TYPE_UINT64;
  950.         } else if (sizeof(void *) == sizeof(int)) {
  951.         type = TYPE_UINTN;
  952.         } else {
  953.         PR_ASSERT(0);
  954.         break;
  955.         }
  956.         radix = 16;
  957.         goto fetch_and_convert;
  958.  
  959. #if 0
  960.       case 'C':
  961.       case 'S':
  962.       case 'E':
  963.       case 'G':
  964.         /* XXX not supported I suppose */
  965.         PR_ASSERT(0);
  966.         break;
  967. #endif
  968.  
  969.       case 's':
  970.         u.s = va_arg(ap, const char*);
  971.         rv = cvt_s(ss, u.s, width, prec, flags);
  972.         if (rv < 0) {
  973.         return rv;
  974.         }
  975.         break;
  976.  
  977.       case 'n':
  978.         u.ip = va_arg(ap, int*);
  979.         if (u.ip) {
  980.         *u.ip = ss->cur - ss->base;
  981.         }
  982.         break;
  983.  
  984.       default:
  985.         /* Not a % token after all... skip it */
  986. #if 0
  987.         PR_ASSERT(0);
  988. #endif
  989.         rv = (*ss->stuff)(ss, "%", 1);
  990.         if (rv < 0) {
  991.         return rv;
  992.         }
  993.         rv = (*ss->stuff)(ss, fmt - 1, 1);
  994.         if (rv < 0) {
  995.         return rv;
  996.         }
  997.     }
  998.     }
  999.  
  1000.     /* Stuff trailing NUL */
  1001.     rv = (*ss->stuff)(ss, "\0", 1);
  1002.  
  1003.     if( nas && ( nas != nasArray ) ){
  1004.     PR_DELETE( nas );
  1005.     }
  1006.  
  1007.     return rv;
  1008. }
  1009.  
  1010. /************************************************************************/
  1011.  
  1012. static int FuncStuff(SprintfState *ss, const char *sp, PRUint32 len)
  1013. {
  1014.     int rv;
  1015.  
  1016.     rv = (*ss->func)(ss->arg, sp, len);
  1017.     if (rv < 0) {
  1018.     return rv;
  1019.     }
  1020.     ss->maxlen += len;
  1021.     return 0;
  1022. }
  1023.  
  1024. PR_IMPLEMENT(PRUint32) PR_sxprintf(PRStuffFunc func, void *arg, 
  1025.                                  const char *fmt, ...)
  1026. {
  1027.     va_list ap;
  1028.     int rv;
  1029.  
  1030.     va_start(ap, fmt);
  1031.     rv = PR_vsxprintf(func, arg, fmt, ap);
  1032.     va_end(ap);
  1033.     return rv;
  1034. }
  1035.  
  1036. PR_IMPLEMENT(PRUint32) PR_vsxprintf(PRStuffFunc func, void *arg, 
  1037.                                   const char *fmt, va_list ap)
  1038. {
  1039.     SprintfState ss;
  1040.     int rv;
  1041.  
  1042.     ss.stuff = FuncStuff;
  1043.     ss.func = func;
  1044.     ss.arg = arg;
  1045.     ss.maxlen = 0;
  1046.     rv = dosprintf(&ss, fmt, ap);
  1047.     return (rv < 0) ? (PRUint32)-1 : ss.maxlen;
  1048. }
  1049.  
  1050. /*
  1051. ** Stuff routine that automatically grows the malloc'd output buffer
  1052. ** before it overflows.
  1053. */
  1054. static int GrowStuff(SprintfState *ss, const char *sp, PRUint32 len)
  1055. {
  1056.     ptrdiff_t off;
  1057.     char *newbase;
  1058.     PRUint32 newlen;
  1059.  
  1060.     off = ss->cur - ss->base;
  1061.     if (off + len >= ss->maxlen) {
  1062.     /* Grow the buffer */
  1063.     newlen = ss->maxlen + ((len > 32) ? len : 32);
  1064.     if (ss->base) {
  1065.         newbase = (char*) PR_REALLOC(ss->base, newlen);
  1066.     } else {
  1067.         newbase = (char*) PR_MALLOC(newlen);
  1068.     }
  1069.     if (!newbase) {
  1070.         /* Ran out of memory */
  1071.         return -1;
  1072.     }
  1073.     ss->base = newbase;
  1074.     ss->maxlen = newlen;
  1075.     ss->cur = ss->base + off;
  1076.     }
  1077.  
  1078.     /* Copy data */
  1079.     while (len) {
  1080.     --len;
  1081.     *ss->cur++ = *sp++;
  1082.     }
  1083.     PR_ASSERT((PRUint32)(ss->cur - ss->base) <= ss->maxlen);
  1084.     return 0;
  1085. }
  1086.  
  1087. /*
  1088. ** sprintf into a malloc'd buffer
  1089. */
  1090. PR_IMPLEMENT(char *) PR_smprintf(const char *fmt, ...)
  1091. {
  1092.     va_list ap;
  1093.     char *rv;
  1094.  
  1095.     va_start(ap, fmt);
  1096.     rv = PR_vsmprintf(fmt, ap);
  1097.     va_end(ap);
  1098.     return rv;
  1099. }
  1100.  
  1101. /*
  1102. ** Free memory allocated, for the caller, by PR_smprintf
  1103. */
  1104. PR_IMPLEMENT(void) PR_smprintf_free(char *mem)
  1105. {
  1106.     PR_DELETE(mem);
  1107. }
  1108.  
  1109. PR_IMPLEMENT(char *) PR_vsmprintf(const char *fmt, va_list ap)
  1110. {
  1111.     SprintfState ss;
  1112.     int rv;
  1113.  
  1114.     ss.stuff = GrowStuff;
  1115.     ss.base = 0;
  1116.     ss.cur = 0;
  1117.     ss.maxlen = 0;
  1118.     rv = dosprintf(&ss, fmt, ap);
  1119.     if (rv < 0) {
  1120.     if (ss.base) {
  1121.         PR_DELETE(ss.base);
  1122.     }
  1123.     return 0;
  1124.     }
  1125.     return ss.base;
  1126. }
  1127.  
  1128. /*
  1129. ** Stuff routine that discards overflow data
  1130. */
  1131. static int LimitStuff(SprintfState *ss, const char *sp, PRUint32 len)
  1132. {
  1133.     PRUint32 limit = ss->maxlen - (ss->cur - ss->base);
  1134.  
  1135.     if (len > limit) {
  1136.     len = limit;
  1137.     }
  1138.     while (len) {
  1139.     --len;
  1140.     *ss->cur++ = *sp++;
  1141.     }
  1142.     return 0;
  1143. }
  1144.  
  1145. /*
  1146. ** sprintf into a fixed size buffer. Make sure there is a NUL at the end
  1147. ** when finished.
  1148. */
  1149. PR_IMPLEMENT(PRUint32) PR_snprintf(char *out, PRUint32 outlen, const char *fmt, ...)
  1150. {
  1151.     va_list ap;
  1152.     int rv;
  1153.  
  1154.     PR_ASSERT((PRInt32)outlen > 0);
  1155.     if ((PRInt32)outlen <= 0) {
  1156.     return 0;
  1157.     }
  1158.  
  1159.     va_start(ap, fmt);
  1160.     rv = PR_vsnprintf(out, outlen, fmt, ap);
  1161.     va_end(ap);
  1162.     return rv;
  1163. }
  1164.  
  1165. PR_IMPLEMENT(PRUint32) PR_vsnprintf(char *out, PRUint32 outlen,const char *fmt,
  1166.                                   va_list ap)
  1167. {
  1168.     SprintfState ss;
  1169.     PRUint32 n;
  1170.  
  1171.     PR_ASSERT((PRInt32)outlen > 0);
  1172.     if ((PRInt32)outlen <= 0) {
  1173.     return 0;
  1174.     }
  1175.  
  1176.     ss.stuff = LimitStuff;
  1177.     ss.base = out;
  1178.     ss.cur = out;
  1179.     ss.maxlen = outlen;
  1180.     (void) dosprintf(&ss, fmt, ap);
  1181.  
  1182.     /* If we added chars, and we didn't append a null, do it now. */
  1183.     if( (ss.cur != ss.base) && (*(ss.cur - 1) != '\0') )
  1184.         *(--ss.cur) = '\0';
  1185.  
  1186.     n = ss.cur - ss.base;
  1187.     return n ? n - 1 : n;
  1188. }
  1189.  
  1190. PR_IMPLEMENT(char *) PR_sprintf_append(char *last, const char *fmt, ...)
  1191. {
  1192.     va_list ap;
  1193.     char *rv;
  1194.  
  1195.     va_start(ap, fmt);
  1196.     rv = PR_vsprintf_append(last, fmt, ap);
  1197.     va_end(ap);
  1198.     return rv;
  1199. }
  1200.  
  1201. PR_IMPLEMENT(char *) PR_vsprintf_append(char *last, const char *fmt, va_list ap)
  1202. {
  1203.     SprintfState ss;
  1204.     int rv;
  1205.  
  1206.     ss.stuff = GrowStuff;
  1207.     if (last) {
  1208.     int lastlen = strlen(last);
  1209.     ss.base = last;
  1210.     ss.cur = last + lastlen;
  1211.     ss.maxlen = lastlen;
  1212.     } else {
  1213.     ss.base = 0;
  1214.     ss.cur = 0;
  1215.     ss.maxlen = 0;
  1216.     }
  1217.     rv = dosprintf(&ss, fmt, ap);
  1218.     if (rv < 0) {
  1219.     if (ss.base) {
  1220.         PR_DELETE(ss.base);
  1221.     }
  1222.     return 0;
  1223.     }
  1224.     return ss.base;
  1225. }
  1226.  
  1227.