VirtualBox

source: vbox/trunk/src/libs/xpcom18a4/xpcom/ds/nsTextFormatter.cpp@ 4837

Last change on this file since 4837 was 1, checked in by vboxsync, 54 years ago

import

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.3 KB
Line 
1/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 *
15 * The Original Code is mozilla.org Code.
16 *
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
21 *
22 * Contributor(s):
23 *
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
35 *
36 * ***** END LICENSE BLOCK ***** */
37
38/*
39 * Portable safe sprintf code.
40 *
41 * Code based on mozilla/nsprpub/src/io/prprf.c rev 3.7
42 *
43 * Contributor(s):
44 * Kipp E.B. Hickman <kipp@netscape.com> (original author)
45 * Frank Yung-Fong Tang <ftang@netscape.com>
46 * Daniele Nicolodi <daniele@grinta.net>
47 */
48
49#include <stdarg.h>
50#include <stddef.h>
51#include <stdio.h>
52#include <string.h>
53#include "prdtoa.h"
54#include "prlong.h"
55#include "prlog.h"
56#include "prmem.h"
57#include "prprf.h"
58#include "nsCRT.h"
59#include "nsTextFormatter.h"
60#include "nsString.h"
61#include "nsReadableUtils.h"
62
63/*
64** Note: on some platforms va_list is defined as an array,
65** and requires array notation.
66*/
67
68#ifdef HAVE_VA_COPY
69#define VARARGS_ASSIGN(foo, bar) VA_COPY(foo,bar)
70#elif defined(HAVE_VA_LIST_AS_ARRAY)
71#define VARARGS_ASSIGN(foo, bar) foo[0] = bar[0]
72#else
73#define VARARGS_ASSIGN(foo, bar) (foo) = (bar)
74#endif
75
76typedef struct SprintfStateStr SprintfState;
77
78struct SprintfStateStr {
79 int (*stuff)(SprintfState *ss, const PRUnichar *sp, PRUint32 len);
80
81 PRUnichar *base;
82 PRUnichar *cur;
83 PRUint32 maxlen;
84
85 void *stuffclosure;
86};
87
88/*
89** Numbered Arguement State
90*/
91struct NumArgState{
92 int type; /* type of the current ap */
93 va_list ap; /* point to the corresponding position on ap */
94};
95
96#define NAS_DEFAULT_NUM 20 /* default number of NumberedArgumentState array */
97
98#define TYPE_INT16 0
99#define TYPE_UINT16 1
100#define TYPE_INTN 2
101#define TYPE_UINTN 3
102#define TYPE_INT32 4
103#define TYPE_UINT32 5
104#define TYPE_INT64 6
105#define TYPE_UINT64 7
106#define TYPE_STRING 8
107#define TYPE_DOUBLE 9
108#define TYPE_INTSTR 10
109#define TYPE_UNISTRING 11
110#define TYPE_UNKNOWN 20
111
112#define _LEFT 0x1
113#define _SIGNED 0x2
114#define _SPACED 0x4
115#define _ZEROS 0x8
116#define _NEG 0x10
117
118#define ELEMENTS_OF(array_) (sizeof(array_) / sizeof(array_[0]))
119
120// Warning: if aDest isn't big enough this function returns the converted
121// string in allocated memory which must be freed using PR_FREE().
122// May return nsnull if memory couldn't be allocated.
123static PRUnichar* UTF8ToUCS2(const char *aSrc, PRUint32 aSrcLen,
124 PRUnichar* aDest, PRUint32 aDestLen)
125{
126 const char *in, *inend;
127 inend = aSrc + aSrcLen;
128 PRUnichar *out;
129 PRUint32 state;
130 PRUint32 ucs4;
131 // decide the length of the UCS2 first.
132 PRUint32 needLen = 0;
133
134 for (in = aSrc, state = 0, ucs4 = 0; in < inend; in++) {
135 if (0 == state) {
136 if (0 == (0x80 & (*in))) {
137 needLen++;
138 } else if (0xC0 == (0xE0 & (*in))) {
139 needLen++;
140 state = 1;
141 } else if (0xE0 == (0xF0 & (*in))) {
142 needLen++;
143 state = 2;
144 } else if (0xF0 == (0xF8 & (*in))) {
145 needLen+=2;
146 state = 3;
147 } else if (0xF8 == (0xFC & (*in))) {
148 needLen+=2;
149 state = 4;
150 } else if (0xFC == (0xFE & (*in))) {
151 needLen+=2;
152 state = 5;
153 } else {
154 needLen++;
155 state = 0;
156 }
157 } else {
158 NS_ASSERTION((0x80 == (0xC0 & (*in))), "The input string is not in utf8");
159 if(0x80 == (0xC0 & (*in))) {
160 state--;
161 } else {
162 state = 0;
163 }
164 }
165 }
166 needLen++; // add null termination.
167
168 // allocates sufficient memory if aDest is not big enough.
169 if (needLen > aDestLen) {
170 aDest = (PRUnichar*)PR_MALLOC(sizeof(PRUnichar) * needLen);
171 }
172 if (nsnull == aDest) {
173 return nsnull;
174 }
175 out = aDest;
176
177 for (in = aSrc, state = 0, ucs4 = 0; in < inend; in++) {
178 if (0 == state) {
179 if (0 == (0x80 & (*in))) {
180 // ASCII
181 *out++ = (PRUnichar)*in;
182 } else if (0xC0 == (0xE0 & (*in))) {
183 // 2 bytes UTF8
184 ucs4 = (PRUint32)(*in);
185 ucs4 = (ucs4 << 6) & 0x000007C0L;
186 state=1;
187 } else if (0xE0 == (0xF0 & (*in))) {
188 ucs4 = (PRUint32)(*in);
189 ucs4 = (ucs4 << 12) & 0x0000F000L;
190 state=2;
191 } else if (0xF0 == (0xF8 & (*in))) {
192 ucs4 = (PRUint32)(*in);
193 ucs4 = (ucs4 << 18) & 0x001F0000L;
194 state=3;
195 } else if (0xF8 == (0xFC & (*in))) {
196 ucs4 = (PRUint32)(*in);
197 ucs4 = (ucs4 << 24) & 0x03000000L;
198 state=4;
199 } else if (0xFC == (0xFE & (*in))) {
200 ucs4 = (PRUint32)(*in);
201 ucs4 = (ucs4 << 30) & 0x40000000L;
202 state=5;
203 } else {
204 NS_ASSERTION(0, "The input string is not in utf8");
205 state=0;
206 ucs4=0;
207 }
208 } else {
209 NS_ASSERTION((0x80 == (0xC0 & (*in))), "The input string is not in utf8");
210 if (0x80 == (0xC0 & (*in))) {
211 PRUint32 tmp = (*in);
212 int shift = --state * 6;
213 tmp = (tmp << shift) & (0x0000003FL << shift);
214 ucs4 |= tmp;
215 if (0 == state) {
216 if (ucs4 >= 0x00010000) {
217 if (ucs4 >= 0x00110000) {
218 *out++ = 0xFFFD;
219 } else {
220 ucs4 -= 0x00010000;
221 *out++ = 0xD800 | (0x000003FF & (ucs4 >> 10));
222 *out++ = 0xDC00 | (0x000003FF & ucs4);
223 }
224 } else {
225 *out++ = ucs4;
226 }
227 ucs4 = 0;
228 }
229 } else {
230 state = 0;
231 ucs4 = 0;
232 }
233 }
234 }
235 *out = 0x0000;
236 return aDest;
237}
238
239/*
240** Fill into the buffer using the data in src
241*/
242static int fill2(SprintfState *ss, const PRUnichar *src, int srclen,
243 int width, int flags)
244{
245 PRUnichar space = ' ';
246 int rv;
247
248 width -= srclen;
249 /* Right adjusting */
250 if ((width > 0) && ((flags & _LEFT) == 0)) {
251 if (flags & _ZEROS) {
252 space = '0';
253 }
254 while (--width >= 0) {
255 rv = (*ss->stuff)(ss, &space, 1);
256 if (rv < 0) {
257 return rv;
258 }
259 }
260 }
261
262 /* Copy out the source data */
263 rv = (*ss->stuff)(ss, src, srclen);
264 if (rv < 0) {
265 return rv;
266 }
267
268 /* Left adjusting */
269 if ((width > 0) && ((flags & _LEFT) != 0)) {
270 while (--width >= 0) {
271 rv = (*ss->stuff)(ss, &space, 1);
272 if (rv < 0) {
273 return rv;
274 }
275 }
276 }
277 return 0;
278}
279
280/*
281** Fill a number. The order is: optional-sign zero-filling conversion-digits
282*/
283static int fill_n(SprintfState *ss, const PRUnichar *src, int srclen,
284 int width, int prec, int type, int flags)
285{
286 int zerowidth = 0;
287 int precwidth = 0;
288 int signwidth = 0;
289 int leftspaces = 0;
290 int rightspaces = 0;
291 int cvtwidth;
292 int rv;
293 PRUnichar sign;
294 PRUnichar space = ' ';
295 PRUnichar zero = '0';
296
297 if ((type & 1) == 0) {
298 if (flags & _NEG) {
299 sign = '-';
300 signwidth = 1;
301 } else if (flags & _SIGNED) {
302 sign = '+';
303 signwidth = 1;
304 } else if (flags & _SPACED) {
305 sign = ' ';
306 signwidth = 1;
307 }
308 }
309 cvtwidth = signwidth + srclen;
310
311 if (prec > 0) {
312 if (prec > srclen) {
313 /* Need zero filling */
314 precwidth = prec - srclen;
315 cvtwidth += precwidth;
316 }
317 }
318
319 if ((flags & _ZEROS) && (prec < 0)) {
320 if (width > cvtwidth) {
321 /* Zero filling */
322 zerowidth = width - cvtwidth;
323 cvtwidth += zerowidth;
324 }
325 }
326
327 if (flags & _LEFT) {
328 if (width > cvtwidth) {
329 /* Space filling on the right (i.e. left adjusting) */
330 rightspaces = width - cvtwidth;
331 }
332 } else {
333 if (width > cvtwidth) {
334 /* Space filling on the left (i.e. right adjusting) */
335 leftspaces = width - cvtwidth;
336 }
337 }
338 while (--leftspaces >= 0) {
339 rv = (*ss->stuff)(ss, &space, 1);
340 if (rv < 0) {
341 return rv;
342 }
343 }
344 if (signwidth) {
345 rv = (*ss->stuff)(ss, &sign, 1);
346 if (rv < 0) {
347 return rv;
348 }
349 }
350 while (--precwidth >= 0) {
351 rv = (*ss->stuff)(ss, &space, 1);
352 if (rv < 0) {
353 return rv;
354 }
355 }
356 while (--zerowidth >= 0) {
357 rv = (*ss->stuff)(ss, &zero, 1);
358 if (rv < 0) {
359 return rv;
360 }
361 }
362 rv = (*ss->stuff)(ss, src, srclen);
363 if (rv < 0) {
364 return rv;
365 }
366 while (--rightspaces >= 0) {
367 rv = (*ss->stuff)(ss, &space, 1);
368 if (rv < 0) {
369 return rv;
370 }
371 }
372 return 0;
373}
374
375/*
376** Convert a long into its printable form
377*/
378static int cvt_l(SprintfState *ss, long num, int width, int prec,
379 int radix, int type, int flags, const PRUnichar *hexp)
380{
381 PRUnichar cvtbuf[100];
382 PRUnichar *cvt;
383 int digits;
384
385 /* according to the man page this needs to happen */
386 if ((prec == 0) && (num == 0)) {
387 return 0;
388 }
389
390 /*
391 ** Converting decimal is a little tricky. In the unsigned case we
392 ** need to stop when we hit 10 digits. In the signed case, we can
393 ** stop when the number is zero.
394 */
395 cvt = &cvtbuf[0] + ELEMENTS_OF(cvtbuf);
396 digits = 0;
397 while (num) {
398 int digit = (((unsigned long)num) % radix) & 0xF;
399 *--cvt = hexp[digit];
400 digits++;
401 num = (long)(((unsigned long)num) / radix);
402 }
403 if (digits == 0) {
404 *--cvt = '0';
405 digits++;
406 }
407
408 /*
409 ** Now that we have the number converted without its sign, deal with
410 ** the sign and zero padding.
411 */
412 return fill_n(ss, cvt, digits, width, prec, type, flags);
413}
414
415/*
416** Convert a 64-bit integer into its printable form
417*/
418static int cvt_ll(SprintfState *ss, PRInt64 num, int width, int prec,
419 int radix, int type, int flags, const PRUnichar *hexp)
420{
421 PRUnichar cvtbuf[100];
422 PRUnichar *cvt;
423 int digits;
424 PRInt64 rad;
425
426 /* according to the man page this needs to happen */
427 if ((prec == 0) && (LL_IS_ZERO(num))) {
428 return 0;
429 }
430
431 /*
432 ** Converting decimal is a little tricky. In the unsigned case we
433 ** need to stop when we hit 10 digits. In the signed case, we can
434 ** stop when the number is zero.
435 */
436 LL_I2L(rad, radix);
437 cvt = &cvtbuf[0] + ELEMENTS_OF(cvtbuf);
438 digits = 0;
439 while (!LL_IS_ZERO(num)) {
440 PRInt32 digit;
441 PRInt64 quot, rem;
442 LL_UDIVMOD(&quot, &rem, num, rad);
443 LL_L2I(digit, rem);
444 *--cvt = hexp[digit & 0xf];
445 digits++;
446 num = quot;
447 }
448 if (digits == 0) {
449 *--cvt = '0';
450 digits++;
451 }
452
453 /*
454 ** Now that we have the number converted without its sign, deal with
455 ** the sign and zero padding.
456 */
457 return fill_n(ss, cvt, digits, width, prec, type, flags);
458}
459
460/*
461** Convert a double precision floating point number into its printable
462** form.
463*/
464static int cvt_f(SprintfState *ss, double d, int width, int prec,
465 const PRUnichar type, int flags)
466{
467 int mode = 2;
468 int decpt;
469 int sign;
470 char buf[256];
471 char * bufp = buf;
472 int bufsz = 256;
473 char num[256];
474 char * nump;
475 char * endnum;
476 int numdigits = 0;
477 char exp = 'e';
478
479 if (prec == -1) {
480 prec = 6;
481 } else if (prec > 50) {
482 // limit precision to avoid PR_dtoa bug 108335
483 // and to prevent buffers overflows
484 prec = 50;
485 }
486
487 switch (type) {
488 case 'f':
489 numdigits = prec;
490 mode = 3;
491 break;
492 case 'E':
493 exp = 'E';
494 // no break
495 case 'e':
496 numdigits = prec + 1;
497 mode = 2;
498 break;
499 case 'G':
500 exp = 'E';
501 // no break
502 case 'g':
503 if (prec == 0) {
504 prec = 1;
505 }
506 numdigits = prec;
507 mode = 2;
508 break;
509 default:
510 NS_ERROR("invalid type passed to cvt_f");
511 }
512
513 if (PR_dtoa(d, mode, numdigits, &decpt, &sign, &endnum, num, bufsz) == PR_FAILURE) {
514 buf[0] = '\0';
515 return -1;
516 }
517 numdigits = endnum - num;
518 nump = num;
519
520 if (sign) {
521 *bufp++ = '-';
522 } else if (flags & _SIGNED) {
523 *bufp++ = '+';
524 }
525
526 if (decpt == 9999) {
527 while ((*bufp++ = *nump++)) { }
528 } else {
529
530 switch (type) {
531
532 case 'E':
533 case 'e':
534
535 *bufp++ = *nump++;
536 if (prec > 0) {
537 *bufp++ = '.';
538 while (*nump) {
539 *bufp++ = *nump++;
540 prec--;
541 }
542 while (prec-- > 0) {
543 *bufp++ = '0';
544 }
545 }
546 *bufp++ = exp;
547 PR_snprintf(bufp, bufsz - (bufp - buf), "%+03d", decpt-1);
548 break;
549
550 case 'f':
551
552 if (decpt < 1) {
553 *bufp++ = '0';
554 if (prec > 0) {
555 *bufp++ = '.';
556 while (decpt++ && prec-- > 0) {
557 *bufp++ = '0';
558 }
559 while (*nump && prec-- > 0) {
560 *bufp++ = *nump++;
561 }
562 while (prec-- > 0) {
563 *bufp++ = '0';
564 }
565 }
566 } else {
567 while (*nump && decpt-- > 0) {
568 *bufp++ = *nump++;
569 }
570 while (decpt-- > 0) {
571 *bufp++ = '0';
572 }
573 if (prec > 0) {
574 *bufp++ = '.';
575 while (*nump && prec-- > 0) {
576 *bufp++ = *nump++;
577 }
578 while (prec-- > 0) {
579 *bufp++ = '0';
580 }
581 }
582 }
583 *bufp = '\0';
584 break;
585
586 case 'G':
587 case 'g':
588
589 if ((decpt < -3) || ((decpt - 1) >= prec)) {
590 *bufp++ = *nump++;
591 numdigits--;
592 if (numdigits > 0) {
593 *bufp++ = '.';
594 while (*nump) {
595 *bufp++ = *nump++;
596 }
597 }
598 *bufp++ = exp;
599 PR_snprintf(bufp, bufsz - (bufp - buf), "%+03d", decpt-1);
600 } else {
601 if (decpt < 1) {
602 *bufp++ = '0';
603 if (prec > 0) {
604 *bufp++ = '.';
605 while (decpt++) {
606 *bufp++ = '0';
607 }
608 while (*nump) {
609 *bufp++ = *nump++;
610 }
611 }
612 } else {
613 while (*nump && decpt-- > 0) {
614 *bufp++ = *nump++;
615 numdigits--;
616 }
617 while (decpt-- > 0) {
618 *bufp++ = '0';
619 }
620 if (numdigits > 0) {
621 *bufp++ = '.';
622 while (*nump) {
623 *bufp++ = *nump++;
624 }
625 }
626 }
627 *bufp = '\0';
628 }
629 }
630 }
631
632 PRUnichar rbuf[256];
633 PRUnichar *rbufp = rbuf;
634 bufp = buf;
635 // cast to PRUnichar
636 while ((*rbufp++ = *bufp++)) { }
637 *rbufp = '\0';
638
639 return fill2(ss, rbuf, nsCRT::strlen(rbuf), width, flags);
640}
641
642/*
643** Convert a string into its printable form. "width" is the output
644** width. "prec" is the maximum number of characters of "s" to output,
645** where -1 means until NUL.
646*/
647static int cvt_S(SprintfState *ss, const PRUnichar *s, int width,
648 int prec, int flags)
649{
650 int slen;
651
652 if (prec == 0) {
653 return 0;
654 }
655
656 /* Limit string length by precision value */
657 slen = s ? nsCRT::strlen(s) : 6;
658 if (prec > 0) {
659 if (prec < slen) {
660 slen = prec;
661 }
662 }
663
664 /* and away we go */
665 NS_NAMED_LITERAL_STRING(nullstr, "(null)");
666
667 return fill2(ss, s ? s : nullstr.get(), slen, width, flags);
668}
669
670/*
671** Convert a string into its printable form. "width" is the output
672** width. "prec" is the maximum number of characters of "s" to output,
673** where -1 means until NUL.
674*/
675static int cvt_s(SprintfState *ss, const char *s, int width,
676 int prec, int flags)
677{
678 // convert s from UTF8 to PRUnichar*
679 // Fix me !!!
680 PRUnichar buf[256];
681 PRUnichar *retbuf = nsnull;
682
683 if (s) {
684 retbuf = UTF8ToUCS2(s, strlen(s), buf, 256);
685 if(nsnull == retbuf) {
686 return -1;
687 }
688 }
689 int ret = cvt_S(ss, retbuf, width, prec, flags);
690
691 if (retbuf != buf) {
692 PR_DELETE(retbuf);
693 }
694 return ret;
695}
696
697/*
698** BiuldArgArray stands for Numbered Argument list Sprintf
699** for example,
700** fmp = "%4$i, %2$d, %3s, %1d";
701** the number must start from 1, and no gap among them
702*/
703
704static struct NumArgState* BuildArgArray(const PRUnichar *fmt,
705 va_list ap, int * rv,
706 struct NumArgState * nasArray)
707{
708 int number = 0, cn = 0, i;
709 const PRUnichar* p;
710 PRUnichar c;
711 struct NumArgState* nas;
712
713 /*
714 ** first pass:
715 ** detemine how many legal % I have got, then allocate space
716 */
717 p = fmt;
718 *rv = 0;
719 i = 0;
720 while ((c = *p++) != 0) {
721 if (c != '%') {
722 continue;
723 }
724 /* skip %% case */
725 if ((c = *p++) == '%') {
726 continue;
727 }
728
729 while( c != 0 ){
730 if (c > '9' || c < '0') {
731 /* numbered argument csae */
732 if (c == '$') {
733 if (i > 0) {
734 *rv = -1;
735 return NULL;
736 }
737 number++;
738 break;
739
740 } else {
741 /* non-numbered argument case */
742 if (number > 0) {
743 *rv = -1;
744 return NULL;
745 }
746 i = 1;
747 break;
748 }
749 }
750 c = *p++;
751 }
752 }
753
754 if (number == 0) {
755 return NULL;
756 }
757
758 if (number > NAS_DEFAULT_NUM) {
759 nas = (struct NumArgState*)PR_MALLOC(number * sizeof(struct NumArgState));
760 if (!nas) {
761 *rv = -1;
762 return NULL;
763 }
764 } else {
765 nas = nasArray;
766 }
767
768 for (i = 0; i < number; i++) {
769 nas[i].type = TYPE_UNKNOWN;
770 }
771
772 /*
773 ** second pass:
774 ** set nas[].type
775 */
776 p = fmt;
777 while ((c = *p++) != 0) {
778 if (c != '%') {
779 continue;
780 }
781 c = *p++;
782 if (c == '%') {
783 continue;
784 }
785 cn = 0;
786 /* should imporve error check later */
787 while (c && c != '$') {
788 cn = cn*10 + c - '0';
789 c = *p++;
790 }
791
792 if (!c || cn < 1 || cn > number) {
793 *rv = -1;
794 break;
795 }
796
797 /* nas[cn] starts from 0, and make sure
798 nas[cn].type is not assigned */
799 cn--;
800 if (nas[cn].type != TYPE_UNKNOWN) {
801 continue;
802 }
803
804 c = *p++;
805
806 /* width */
807 if (c == '*') {
808 /* not supported feature, for the argument is not numbered */
809 *rv = -1;
810 break;
811 } else {
812 while ((c >= '0') && (c <= '9')) {
813 c = *p++;
814 }
815 }
816
817 /* precision */
818 if (c == '.') {
819 c = *p++;
820 if (c == '*') {
821 /* not supported feature, for the argument is not numbered */
822 *rv = -1;
823 break;
824 } else {
825 while ((c >= '0') && (c <= '9')) {
826 c = *p++;
827 }
828 }
829 }
830
831 /* size */
832 nas[cn].type = TYPE_INTN;
833 if (c == 'h') {
834 nas[cn].type = TYPE_INT16;
835 c = *p++;
836 } else if (c == 'L') {
837 /* XXX not quite sure here */
838 nas[cn].type = TYPE_INT64;
839 c = *p++;
840 } else if (c == 'l') {
841 nas[cn].type = TYPE_INT32;
842 c = *p++;
843 if (c == 'l') {
844 nas[cn].type = TYPE_INT64;
845 c = *p++;
846 }
847 }
848
849 /* format */
850 switch (c) {
851 case 'd':
852 case 'c':
853 case 'i':
854 case 'o':
855 case 'u':
856 case 'x':
857 case 'X':
858 break;
859
860 case 'e':
861 case 'f':
862 case 'g':
863 nas[cn].type = TYPE_DOUBLE;
864 break;
865
866 case 'p':
867 /* XXX should use cpp */
868 if (sizeof(void *) == sizeof(PRInt32)) {
869 nas[cn].type = TYPE_UINT32;
870 } else if (sizeof(void *) == sizeof(PRInt64)) {
871 nas[cn].type = TYPE_UINT64;
872 } else if (sizeof(void *) == sizeof(PRIntn)) {
873 nas[cn].type = TYPE_UINTN;
874 } else {
875 nas[cn].type = TYPE_UNKNOWN;
876 }
877 break;
878
879 case 'C':
880 /* XXX not supported I suppose */
881 PR_ASSERT(0);
882 nas[cn].type = TYPE_UNKNOWN;
883 break;
884
885 case 'S':
886 nas[cn].type = TYPE_UNISTRING;
887 break;
888
889 case 's':
890 nas[cn].type = TYPE_STRING;
891 break;
892
893 case 'n':
894 nas[cn].type = TYPE_INTSTR;
895 break;
896
897 default:
898 PR_ASSERT(0);
899 nas[cn].type = TYPE_UNKNOWN;
900 break;
901 }
902
903 /* get a legal para. */
904 if (nas[cn].type == TYPE_UNKNOWN) {
905 *rv = -1;
906 break;
907 }
908 }
909
910
911 /*
912 ** third pass
913 ** fill the nas[cn].ap
914 */
915 if (*rv < 0) {
916 if( nas != nasArray ) {
917 PR_DELETE(nas);
918 }
919 return NULL;
920 }
921
922 cn = 0;
923 while (cn < number) {
924 if (nas[cn].type == TYPE_UNKNOWN) {
925 cn++;
926 continue;
927 }
928
929 VARARGS_ASSIGN(nas[cn].ap, ap);
930
931 switch (nas[cn].type) {
932 case TYPE_INT16:
933 case TYPE_UINT16:
934 case TYPE_INTN:
935 case TYPE_UINTN: (void)va_arg(ap, PRIntn); break;
936
937 case TYPE_INT32: (void)va_arg(ap, PRInt32); break;
938
939 case TYPE_UINT32: (void)va_arg(ap, PRUint32); break;
940
941 case TYPE_INT64: (void)va_arg(ap, PRInt64); break;
942
943 case TYPE_UINT64: (void)va_arg(ap, PRUint64); break;
944
945 case TYPE_STRING: (void)va_arg(ap, char*); break;
946
947 case TYPE_INTSTR: (void)va_arg(ap, PRIntn*); break;
948
949 case TYPE_DOUBLE: (void)va_arg(ap, double); break;
950
951 case TYPE_UNISTRING: (void)va_arg(ap, PRUnichar*); break;
952
953 default:
954 if( nas != nasArray ) {
955 PR_DELETE( nas );
956 }
957 *rv = -1;
958 return NULL;
959 }
960 cn++;
961 }
962 return nas;
963}
964
965/*
966** The workhorse sprintf code.
967*/
968static int dosprintf(SprintfState *ss, const PRUnichar *fmt, va_list ap)
969{
970 PRUnichar c;
971 int flags, width, prec, radix, type;
972 union {
973 PRUnichar ch;
974 int i;
975 long l;
976 PRInt64 ll;
977 double d;
978 const char *s;
979 const PRUnichar *S;
980 int *ip;
981 } u;
982 PRUnichar space = ' ';
983 const PRUnichar *fmt0;
984
985 nsAutoString hex;
986 hex.AssignLiteral("0123456789abcdef");
987
988 nsAutoString HEX;
989 HEX.AssignLiteral("0123456789ABCDEF");
990
991 const PRUnichar *hexp;
992 int rv, i;
993 struct NumArgState* nas = NULL;
994 struct NumArgState nasArray[NAS_DEFAULT_NUM];
995 /* in "%4$.2f" dolPt will point to . */
996 const PRUnichar* dolPt = NULL;
997
998
999 /*
1000 ** build an argument array, IF the fmt is numbered argument
1001 ** list style, to contain the Numbered Argument list pointers
1002 */
1003 nas = BuildArgArray (fmt, ap, &rv, nasArray);
1004 if (rv < 0) {
1005 /* the fmt contains error Numbered Argument format, jliu@netscape.com */
1006 PR_ASSERT(0);
1007 return rv;
1008 }
1009
1010 while ((c = *fmt++) != 0) {
1011 if (c != '%') {
1012 rv = (*ss->stuff)(ss, fmt - 1, 1);
1013 if (rv < 0) {
1014 return rv;
1015 }
1016 continue;
1017 }
1018 fmt0 = fmt - 1;
1019
1020 /*
1021 ** Gobble up the % format string. Hopefully we have handled all
1022 ** of the strange cases!
1023 */
1024 flags = 0;
1025 c = *fmt++;
1026 if (c == '%') {
1027 /* quoting a % with %% */
1028 rv = (*ss->stuff)(ss, fmt - 1, 1);
1029 if (rv < 0) {
1030 return rv;
1031 }
1032 continue;
1033 }
1034
1035 if (nas != NULL) {
1036 /* the fmt contains the Numbered Arguments feature */
1037 i = 0;
1038 /* should imporve error check later */
1039 while (c && c != '$') {
1040 i = (i * 10) + (c - '0');
1041 c = *fmt++;
1042 }
1043
1044 if (nas[i-1].type == TYPE_UNKNOWN) {
1045 if (nas && (nas != nasArray)) {
1046 PR_DELETE(nas);
1047 }
1048 return -1;
1049 }
1050
1051 ap = nas[i-1].ap;
1052 dolPt = fmt;
1053 c = *fmt++;
1054 }
1055
1056 /*
1057 * Examine optional flags. Note that we do not implement the
1058 * '#' flag of sprintf(). The ANSI C spec. of the '#' flag is
1059 * somewhat ambiguous and not ideal, which is perhaps why
1060 * the various sprintf() implementations are inconsistent
1061 * on this feature.
1062 */
1063 while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) {
1064 if (c == '-') flags |= _LEFT;
1065 if (c == '+') flags |= _SIGNED;
1066 if (c == ' ') flags |= _SPACED;
1067 if (c == '0') flags |= _ZEROS;
1068 c = *fmt++;
1069 }
1070 if (flags & _SIGNED) flags &= ~_SPACED;
1071 if (flags & _LEFT) flags &= ~_ZEROS;
1072
1073 /* width */
1074 if (c == '*') {
1075 c = *fmt++;
1076 width = va_arg(ap, int);
1077 } else {
1078 width = 0;
1079 while ((c >= '0') && (c <= '9')) {
1080 width = (width * 10) + (c - '0');
1081 c = *fmt++;
1082 }
1083 }
1084
1085 /* precision */
1086 prec = -1;
1087 if (c == '.') {
1088 c = *fmt++;
1089 if (c == '*') {
1090 c = *fmt++;
1091 prec = va_arg(ap, int);
1092 } else {
1093 prec = 0;
1094 while ((c >= '0') && (c <= '9')) {
1095 prec = (prec * 10) + (c - '0');
1096 c = *fmt++;
1097 }
1098 }
1099 }
1100
1101 /* size */
1102 type = TYPE_INTN;
1103 if (c == 'h') {
1104 type = TYPE_INT16;
1105 c = *fmt++;
1106 } else if (c == 'L') {
1107 /* XXX not quite sure here */
1108 type = TYPE_INT64;
1109 c = *fmt++;
1110 } else if (c == 'l') {
1111 type = TYPE_INT32;
1112 c = *fmt++;
1113 if (c == 'l') {
1114 type = TYPE_INT64;
1115 c = *fmt++;
1116 }
1117 }
1118
1119 /* format */
1120 hexp = hex.get();
1121 switch (c) {
1122 case 'd':
1123 case 'i': /* decimal/integer */
1124 radix = 10;
1125 goto fetch_and_convert;
1126
1127 case 'o': /* octal */
1128 radix = 8;
1129 type |= 1;
1130 goto fetch_and_convert;
1131
1132 case 'u': /* unsigned decimal */
1133 radix = 10;
1134 type |= 1;
1135 goto fetch_and_convert;
1136
1137 case 'x': /* unsigned hex */
1138 radix = 16;
1139 type |= 1;
1140 goto fetch_and_convert;
1141
1142 case 'X': /* unsigned HEX */
1143 radix = 16;
1144 hexp = HEX.get();
1145 type |= 1;
1146 goto fetch_and_convert;
1147
1148 fetch_and_convert:
1149 switch (type) {
1150 case TYPE_INT16:
1151 u.l = va_arg(ap, int);
1152 if (u.l < 0) {
1153 u.l = -u.l;
1154 flags |= _NEG;
1155 }
1156 goto do_long;
1157 case TYPE_UINT16:
1158 u.l = va_arg(ap, int) & 0xffff;
1159 goto do_long;
1160 case TYPE_INTN:
1161 u.l = va_arg(ap, int);
1162 if (u.l < 0) {
1163 u.l = -u.l;
1164 flags |= _NEG;
1165 }
1166 goto do_long;
1167 case TYPE_UINTN:
1168 u.l = (long)va_arg(ap, unsigned int);
1169 goto do_long;
1170
1171 case TYPE_INT32:
1172 u.l = va_arg(ap, PRInt32);
1173 if (u.l < 0) {
1174 u.l = -u.l;
1175 flags |= _NEG;
1176 }
1177 goto do_long;
1178 case TYPE_UINT32:
1179 u.l = (long)va_arg(ap, PRUint32);
1180 do_long:
1181 rv = cvt_l(ss, u.l, width, prec, radix, type, flags, hexp);
1182 if (rv < 0) {
1183 return rv;
1184 }
1185 break;
1186
1187 case TYPE_INT64:
1188 u.ll = va_arg(ap, PRInt64);
1189 if (!LL_GE_ZERO(u.ll)) {
1190 LL_NEG(u.ll, u.ll);
1191 flags |= _NEG;
1192 }
1193 goto do_longlong;
1194 case TYPE_UINT64:
1195 u.ll = va_arg(ap, PRUint64);
1196 do_longlong:
1197 rv = cvt_ll(ss, u.ll, width, prec, radix, type, flags, hexp);
1198 if (rv < 0) {
1199 return rv;
1200 }
1201 break;
1202 }
1203 break;
1204
1205 case 'e':
1206 case 'E':
1207 case 'f':
1208 case 'g':
1209 case 'G':
1210 u.d = va_arg(ap, double);
1211 rv = cvt_f(ss, u.d, width, prec, c, flags);
1212 if (rv < 0) {
1213 return rv;
1214 }
1215 break;
1216
1217 case 'c':
1218 u.ch = va_arg(ap, int);
1219 if ((flags & _LEFT) == 0) {
1220 while (width-- > 1) {
1221 rv = (*ss->stuff)(ss, &space, 1);
1222 if (rv < 0) {
1223 return rv;
1224 }
1225 }
1226 }
1227 rv = (*ss->stuff)(ss, &u.ch, 1);
1228 if (rv < 0) {
1229 return rv;
1230 }
1231 if (flags & _LEFT) {
1232 while (width-- > 1) {
1233 rv = (*ss->stuff)(ss, &space, 1);
1234 if (rv < 0) {
1235 return rv;
1236 }
1237 }
1238 }
1239 break;
1240
1241 case 'p':
1242 if (sizeof(void *) == sizeof(PRInt32)) {
1243 type = TYPE_UINT32;
1244 } else if (sizeof(void *) == sizeof(PRInt64)) {
1245 type = TYPE_UINT64;
1246 } else if (sizeof(void *) == sizeof(int)) {
1247 type = TYPE_UINTN;
1248 } else {
1249 PR_ASSERT(0);
1250 break;
1251 }
1252 radix = 16;
1253 goto fetch_and_convert;
1254
1255#if 0
1256 case 'C':
1257 /* XXX not supported I suppose */
1258 PR_ASSERT(0);
1259 break;
1260#endif
1261
1262 case 'S':
1263 u.S = va_arg(ap, const PRUnichar*);
1264 rv = cvt_S(ss, u.S, width, prec, flags);
1265 if (rv < 0) {
1266 return rv;
1267 }
1268 break;
1269
1270 case 's':
1271 u.s = va_arg(ap, const char*);
1272 rv = cvt_s(ss, u.s, width, prec, flags);
1273 if (rv < 0) {
1274 return rv;
1275 }
1276 break;
1277
1278 case 'n':
1279 u.ip = va_arg(ap, int*);
1280 if (u.ip) {
1281 *u.ip = ss->cur - ss->base;
1282 }
1283 break;
1284
1285 default:
1286 /* Not a % token after all... skip it */
1287#if 0
1288 PR_ASSERT(0);
1289#endif
1290 PRUnichar perct = '%';
1291 rv = (*ss->stuff)(ss, &perct, 1);
1292 if (rv < 0) {
1293 return rv;
1294 }
1295 rv = (*ss->stuff)(ss, fmt - 1, 1);
1296 if (rv < 0) {
1297 return rv;
1298 }
1299 }
1300 }
1301
1302 /* Stuff trailing NUL */
1303 PRUnichar null = '\0';
1304
1305 rv = (*ss->stuff)(ss, &null, 1);
1306
1307 if( nas && ( nas != nasArray ) ){
1308 PR_DELETE( nas );
1309 }
1310
1311 return rv;
1312}
1313
1314/************************************************************************/
1315
1316static int
1317StringStuff(SprintfState* ss, const PRUnichar* sp, PRUint32 len)
1318{
1319 ptrdiff_t off = ss->cur - ss->base;
1320
1321 nsAString* str = NS_STATIC_CAST(nsAString*,ss->stuffclosure);
1322 str->Append(sp, len);
1323
1324 // we can assume contiguous storage
1325 nsAString::iterator begin;
1326 str->BeginWriting(begin);
1327 ss->base = begin.get();
1328 ss->cur = ss->base + off;
1329
1330 return 0;
1331}
1332
1333/*
1334** Stuff routine that automatically grows the malloc'd output buffer
1335** before it overflows.
1336*/
1337static int GrowStuff(SprintfState *ss, const PRUnichar *sp, PRUint32 len)
1338{
1339 ptrdiff_t off;
1340 PRUnichar *newbase;
1341 PRUint32 newlen;
1342
1343 off = ss->cur - ss->base;
1344 if (off + len >= ss->maxlen) {
1345 /* Grow the buffer */
1346 newlen = ss->maxlen + ((len > 32) ? len : 32);
1347 if (ss->base) {
1348 newbase = (PRUnichar*) PR_REALLOC(ss->base, newlen*sizeof(PRUnichar));
1349 } else {
1350 newbase = (PRUnichar*) PR_MALLOC(newlen*sizeof(PRUnichar));
1351 }
1352 if (!newbase) {
1353 /* Ran out of memory */
1354 return -1;
1355 }
1356 ss->base = newbase;
1357 ss->maxlen = newlen;
1358 ss->cur = ss->base + off;
1359 }
1360
1361 /* Copy data */
1362 while (len) {
1363 --len;
1364 *ss->cur++ = *sp++;
1365 }
1366 PR_ASSERT((PRUint32)(ss->cur - ss->base) <= ss->maxlen);
1367 return 0;
1368}
1369
1370/*
1371** sprintf into a malloc'd buffer
1372*/
1373PRUnichar * nsTextFormatter::smprintf(const PRUnichar *fmt, ...)
1374{
1375 va_list ap;
1376 PRUnichar *rv;
1377
1378 va_start(ap, fmt);
1379 rv = nsTextFormatter::vsmprintf(fmt, ap);
1380 va_end(ap);
1381 return rv;
1382}
1383
1384PRUint32 nsTextFormatter::ssprintf(nsAString& out, const PRUnichar* fmt, ...)
1385{
1386 va_list ap;
1387 PRUint32 rv;
1388
1389 va_start(ap, fmt);
1390 rv = nsTextFormatter::vssprintf(out, fmt, ap);
1391 va_end(ap);
1392 return rv;
1393}
1394
1395/*
1396** Free memory allocated, for the caller, by smprintf
1397*/
1398void nsTextFormatter::smprintf_free(PRUnichar *mem)
1399{
1400 PR_DELETE(mem);
1401}
1402
1403PRUint32 nsTextFormatter::vssprintf(nsAString& out, const PRUnichar* fmt, va_list ap)
1404{
1405 SprintfState ss;
1406 ss.stuff = StringStuff;
1407 ss.base = 0;
1408 ss.cur = 0;
1409 ss.maxlen = 0;
1410 ss.stuffclosure = &out;
1411
1412 out.Truncate();
1413 int n = dosprintf(&ss, fmt, ap);
1414 return n ? n - 1 : n;
1415}
1416
1417PRUnichar * nsTextFormatter::vsmprintf(const PRUnichar *fmt, va_list ap)
1418{
1419 SprintfState ss;
1420 int rv;
1421
1422 ss.stuff = GrowStuff;
1423 ss.base = 0;
1424 ss.cur = 0;
1425 ss.maxlen = 0;
1426 rv = dosprintf(&ss, fmt, ap);
1427 if (rv < 0) {
1428 if (ss.base) {
1429 PR_DELETE(ss.base);
1430 }
1431 return 0;
1432 }
1433 return ss.base;
1434}
1435
1436/*
1437** Stuff routine that discards overflow data
1438*/
1439static int LimitStuff(SprintfState *ss, const PRUnichar *sp, PRUint32 len)
1440{
1441 PRUint32 limit = ss->maxlen - (ss->cur - ss->base);
1442
1443 if (len > limit) {
1444 len = limit;
1445 }
1446 while (len) {
1447 --len;
1448 *ss->cur++ = *sp++;
1449 }
1450 return 0;
1451}
1452
1453/*
1454** sprintf into a fixed size buffer. Make sure there is a NUL at the end
1455** when finished.
1456*/
1457PRUint32 nsTextFormatter::snprintf(PRUnichar *out, PRUint32 outlen, const PRUnichar *fmt, ...)
1458{
1459 va_list ap;
1460 PRUint32 rv;
1461
1462 PR_ASSERT((PRInt32)outlen > 0);
1463 if ((PRInt32)outlen <= 0) {
1464 return 0;
1465 }
1466
1467 va_start(ap, fmt);
1468 rv = nsTextFormatter::vsnprintf(out, outlen, fmt, ap);
1469 va_end(ap);
1470 return rv;
1471}
1472
1473PRUint32 nsTextFormatter::vsnprintf(PRUnichar *out, PRUint32 outlen,const PRUnichar *fmt,
1474 va_list ap)
1475{
1476 SprintfState ss;
1477 PRUint32 n;
1478
1479 PR_ASSERT((PRInt32)outlen > 0);
1480 if ((PRInt32)outlen <= 0) {
1481 return 0;
1482 }
1483
1484 ss.stuff = LimitStuff;
1485 ss.base = out;
1486 ss.cur = out;
1487 ss.maxlen = outlen;
1488 (void) dosprintf(&ss, fmt, ap);
1489
1490 /* If we added chars, and we didn't append a null, do it now. */
1491 if( (ss.cur != ss.base) && (*(ss.cur - 1) != '\0') )
1492 *(--ss.cur) = '\0';
1493
1494 n = ss.cur - ss.base;
1495 return n ? n - 1 : n;
1496}
1497
1498PRUnichar * nsTextFormatter::sprintf_append(PRUnichar *last, const PRUnichar *fmt, ...)
1499{
1500 va_list ap;
1501 PRUnichar *rv;
1502
1503 va_start(ap, fmt);
1504 rv = nsTextFormatter::vsprintf_append(last, fmt, ap);
1505 va_end(ap);
1506 return rv;
1507}
1508
1509PRUnichar * nsTextFormatter::vsprintf_append(PRUnichar *last, const PRUnichar *fmt, va_list ap)
1510{
1511 SprintfState ss;
1512 int rv;
1513
1514 ss.stuff = GrowStuff;
1515 if (last) {
1516 int lastlen = nsCRT::strlen(last);
1517 ss.base = last;
1518 ss.cur = last + lastlen;
1519 ss.maxlen = lastlen;
1520 } else {
1521 ss.base = 0;
1522 ss.cur = 0;
1523 ss.maxlen = 0;
1524 }
1525 rv = dosprintf(&ss, fmt, ap);
1526 if (rv < 0) {
1527 if (ss.base) {
1528 PR_DELETE(ss.base);
1529 }
1530 return 0;
1531 }
1532 return ss.base;
1533}
1534#ifdef DEBUG
1535PRBool nsTextFormatter::SelfTest()
1536{
1537 PRBool passed = PR_TRUE ;
1538 nsAutoString fmt(NS_LITERAL_STRING("%3$s %4$S %1$d %2$d"));
1539
1540 char utf8[] = "Hello";
1541 PRUnichar ucs2[]={'W', 'o', 'r', 'l', 'd', 0x4e00, 0xAc00, 0xFF45, 0x0103};
1542 int d=3;
1543
1544
1545 PRUnichar buf[256];
1546 int ret;
1547 ret = nsTextFormatter::snprintf(buf, 256, fmt.get(), d, 333, utf8, ucs2);
1548 printf("ret = %d\n", ret);
1549 nsAutoString out(buf);
1550 printf("%s \n", NS_LossyConvertUCS2toASCII(out).get());
1551 const PRUnichar *uout = out.get();
1552 for(PRUint32 i=0;i<out.Length();i++)
1553 printf("%2X ", uout[i]);
1554
1555 return passed;
1556}
1557#endif
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use