Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/string/nsTextFormatter.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
/*
8
 * Portable safe sprintf code.
9
 *
10
 * Code based on mozilla/nsprpub/src/io/prprf.c rev 3.7
11
 *
12
 * Contributor(s):
13
 *   Kipp E.B. Hickman  <kipp@netscape.com>  (original author)
14
 *   Frank Yung-Fong Tang  <ftang@netscape.com>
15
 *   Daniele Nicolodi  <daniele@grinta.net>
16
 */
17
18
/*
19
 * Copied from xpcom/ds/nsTextFormatter.cpp r1.22
20
 *     Changed to use nsMemory and Frozen linkage
21
 *                  -- Prasad <prasad@medhas.org>
22
 */
23
24
#include <stddef.h>
25
#include <stdio.h>
26
#include <string.h>
27
#include "prdtoa.h"
28
#include "mozilla/Logging.h"
29
#include "mozilla/Sprintf.h"
30
#include "nsCRTGlue.h"
31
#include "nsTextFormatter.h"
32
#include "nsMemory.h"
33
34
struct nsTextFormatter::SprintfStateStr
35
{
36
  int (*stuff)(SprintfStateStr* aState, const char16_t* aStr, uint32_t aLen);
37
38
  char16_t* base;
39
  char16_t* cur;
40
  uint32_t maxlen;
41
42
  void* stuffclosure;
43
};
44
45
48
#define _LEFT   0x1
46
48
#define _SIGNED   0x2
47
24
#define _SPACED   0x4
48
24
#define _ZEROS    0x8
49
24
#define _NEG    0x10
50
48
#define _UNSIGNED       0x20
51
52
24
#define ELEMENTS_OF(array_) (sizeof(array_) / sizeof(array_[0]))
53
54
/*
55
** Fill into the buffer using the data in src
56
*/
57
int
58
nsTextFormatter::fill2(SprintfStateStr* aState, const char16_t* aSrc, int aSrcLen, int aWidth,
59
                       int aFlags)
60
0
{
61
0
  char16_t space = ' ';
62
0
  int rv;
63
0
64
0
  aWidth -= aSrcLen;
65
0
  /* Right adjusting */
66
0
  if ((aWidth > 0) && ((aFlags & _LEFT) == 0)) {
67
0
    if (aFlags & _ZEROS) {
68
0
      space = '0';
69
0
    }
70
0
    while (--aWidth >= 0) {
71
0
      rv = (*aState->stuff)(aState, &space, 1);
72
0
      if (rv < 0) {
73
0
        return rv;
74
0
      }
75
0
    }
76
0
  }
77
0
78
0
  /* Copy out the source data */
79
0
  rv = (*aState->stuff)(aState, aSrc, aSrcLen);
80
0
  if (rv < 0) {
81
0
    return rv;
82
0
  }
83
0
84
0
  /* Left adjusting */
85
0
  if ((aWidth > 0) && ((aFlags & _LEFT) != 0)) {
86
0
    while (--aWidth >= 0) {
87
0
      rv = (*aState->stuff)(aState, &space, 1);
88
0
      if (rv < 0) {
89
0
        return rv;
90
0
      }
91
0
    }
92
0
  }
93
0
  return 0;
94
0
}
95
96
/*
97
** Fill a number. The order is: optional-sign zero-filling conversion-digits
98
*/
99
int
100
nsTextFormatter::fill_n(nsTextFormatter::SprintfStateStr* aState, const char16_t* aSrc,
101
                        int aSrcLen, int aWidth, int aPrec, int aFlags)
102
24
{
103
24
  int zerowidth   = 0;
104
24
  int precwidth   = 0;
105
24
  int signwidth   = 0;
106
24
  int leftspaces  = 0;
107
24
  int rightspaces = 0;
108
24
  int cvtwidth;
109
24
  int rv;
110
24
  char16_t sign;
111
24
  char16_t space = ' ';
112
24
  char16_t zero = '0';
113
24
114
24
  if ((aFlags & _UNSIGNED) == 0) {
115
24
    if (aFlags & _NEG) {
116
0
      sign = '-';
117
0
      signwidth = 1;
118
24
    } else if (aFlags & _SIGNED) {
119
0
      sign = '+';
120
0
      signwidth = 1;
121
24
    } else if (aFlags & _SPACED) {
122
0
      sign = ' ';
123
0
      signwidth = 1;
124
0
    }
125
24
  }
126
24
  cvtwidth = signwidth + aSrcLen;
127
24
128
24
  if (aPrec > 0) {
129
0
    if (aPrec > aSrcLen) {
130
0
      /* Need zero filling */
131
0
      precwidth = aPrec - aSrcLen;
132
0
      cvtwidth += precwidth;
133
0
    }
134
0
  }
135
24
136
24
  if ((aFlags & _ZEROS) && (aPrec < 0)) {
137
0
    if (aWidth > cvtwidth) {
138
0
      /* Zero filling */
139
0
      zerowidth = aWidth - cvtwidth;
140
0
      cvtwidth += zerowidth;
141
0
    }
142
0
  }
143
24
144
24
  if (aFlags & _LEFT) {
145
0
    if (aWidth > cvtwidth) {
146
0
      /* Space filling on the right (i.e. left adjusting) */
147
0
      rightspaces = aWidth - cvtwidth;
148
0
    }
149
24
  } else {
150
24
    if (aWidth > cvtwidth) {
151
0
      /* Space filling on the left (i.e. right adjusting) */
152
0
      leftspaces = aWidth - cvtwidth;
153
0
    }
154
24
  }
155
24
  while (--leftspaces >= 0) {
156
0
    rv = (*aState->stuff)(aState, &space, 1);
157
0
    if (rv < 0) {
158
0
      return rv;
159
0
    }
160
0
  }
161
24
  if (signwidth) {
162
0
    rv = (*aState->stuff)(aState, &sign, 1);
163
0
    if (rv < 0) {
164
0
      return rv;
165
0
    }
166
24
  }
167
24
  while (--precwidth >= 0) {
168
0
    rv = (*aState->stuff)(aState,  &space, 1);
169
0
    if (rv < 0) {
170
0
      return rv;
171
0
    }
172
0
  }
173
24
  while (--zerowidth >= 0) {
174
0
    rv = (*aState->stuff)(aState,  &zero, 1);
175
0
    if (rv < 0) {
176
0
      return rv;
177
0
    }
178
0
  }
179
24
  rv = (*aState->stuff)(aState, aSrc, aSrcLen);
180
24
  if (rv < 0) {
181
0
    return rv;
182
0
  }
183
24
  while (--rightspaces >= 0) {
184
0
    rv = (*aState->stuff)(aState,  &space, 1);
185
0
    if (rv < 0) {
186
0
      return rv;
187
0
    }
188
0
  }
189
24
  return 0;
190
24
}
191
192
/*
193
** Convert a 64-bit integer into its printable form
194
*/
195
int
196
nsTextFormatter::cvt_ll(SprintfStateStr* aState, uint64_t aNum, int aWidth, int aPrec, int aRadix,
197
                        int aFlags, const char16_t* aHexStr)
198
24
{
199
24
  char16_t cvtbuf[100];
200
24
  char16_t* cvt;
201
24
  int digits;
202
24
203
24
  /* according to the man page this needs to happen */
204
24
  if (aPrec == 0 && aNum == 0) {
205
0
    return 0;
206
0
  }
207
24
208
24
  /*
209
24
  ** Converting decimal is a little tricky. In the unsigned case we
210
24
  ** need to stop when we hit 10 digits. In the signed case, we can
211
24
  ** stop when the number is zero.
212
24
  */
213
24
  cvt = &cvtbuf[0] + ELEMENTS_OF(cvtbuf);
214
24
  digits = 0;
215
60
  while (aNum != 0) {
216
36
    uint64_t quot = aNum / aRadix;
217
36
    uint64_t rem = aNum % aRadix;
218
36
    *--cvt = aHexStr[rem & 0xf];
219
36
    digits++;
220
36
    aNum = quot;
221
36
  }
222
24
  if (digits == 0) {
223
0
    *--cvt = '0';
224
0
    digits++;
225
0
  }
226
24
227
24
  /*
228
24
  ** Now that we have the number converted without its sign, deal with
229
24
  ** the sign and zero padding.
230
24
  */
231
24
  return fill_n(aState, cvt, digits, aWidth, aPrec, aFlags);
232
24
}
233
234
/*
235
** Convert a double precision floating point number into its printable
236
** form.
237
*/
238
int
239
nsTextFormatter::cvt_f(SprintfStateStr* aState, double aDouble, int aWidth, int aPrec,
240
                       const char16_t aType, int aFlags)
241
0
{
242
0
  int    mode = 2;
243
0
  int    decpt;
244
0
  int    sign;
245
0
  char   buf[256];
246
0
  char*  bufp = buf;
247
0
  int    bufsz = 256;
248
0
  char   num[256];
249
0
  char*  nump;
250
0
  char*  endnum;
251
0
  int    numdigits = 0;
252
0
  char   exp = 'e';
253
0
254
0
  if (aPrec == -1) {
255
0
    aPrec = 6;
256
0
  } else if (aPrec > 50) {
257
0
    // limit precision to avoid PR_dtoa bug 108335
258
0
    // and to prevent buffers overflows
259
0
    aPrec = 50;
260
0
  }
261
0
262
0
  switch (aType) {
263
0
    case 'f':
264
0
      numdigits = aPrec;
265
0
      mode = 3;
266
0
      break;
267
0
    case 'E':
268
0
      exp = 'E';
269
0
      MOZ_FALLTHROUGH;
270
0
    case 'e':
271
0
      numdigits = aPrec + 1;
272
0
      mode = 2;
273
0
      break;
274
0
    case 'G':
275
0
      exp = 'E';
276
0
      MOZ_FALLTHROUGH;
277
0
    case 'g':
278
0
      if (aPrec == 0) {
279
0
        aPrec = 1;
280
0
      }
281
0
      numdigits = aPrec;
282
0
      mode = 2;
283
0
      break;
284
0
    default:
285
0
      NS_ERROR("invalid aType passed to cvt_f");
286
0
  }
287
0
288
0
  if (PR_dtoa(aDouble, mode, numdigits, &decpt, &sign,
289
0
              &endnum, num, bufsz) == PR_FAILURE) {
290
0
    buf[0] = '\0';
291
0
    return -1;
292
0
  }
293
0
  numdigits = endnum - num;
294
0
  nump = num;
295
0
296
0
  if (sign) {
297
0
    *bufp++ = '-';
298
0
  } else if (aFlags & _SIGNED) {
299
0
    *bufp++ = '+';
300
0
  }
301
0
302
0
  if (decpt == 9999) {
303
0
    while ((*bufp++ = *nump++)) {
304
0
    }
305
0
  } else {
306
0
307
0
    switch (aType) {
308
0
309
0
      case 'E':
310
0
      case 'e':
311
0
312
0
        *bufp++ = *nump++;
313
0
        if (aPrec > 0) {
314
0
          *bufp++ = '.';
315
0
          while (*nump) {
316
0
            *bufp++ = *nump++;
317
0
            aPrec--;
318
0
          }
319
0
          while (aPrec-- > 0) {
320
0
            *bufp++ = '0';
321
0
          }
322
0
        }
323
0
        *bufp++ = exp;
324
0
325
0
        ::snprintf(bufp, bufsz - (bufp - buf), "%+03d", decpt - 1);
326
0
        break;
327
0
328
0
      case 'f':
329
0
330
0
        if (decpt < 1) {
331
0
          *bufp++ = '0';
332
0
          if (aPrec > 0) {
333
0
            *bufp++ = '.';
334
0
            while (decpt++ && aPrec-- > 0) {
335
0
              *bufp++ = '0';
336
0
            }
337
0
            while (*nump && aPrec-- > 0) {
338
0
              *bufp++ = *nump++;
339
0
            }
340
0
            while (aPrec-- > 0) {
341
0
              *bufp++ = '0';
342
0
            }
343
0
          }
344
0
        } else {
345
0
          while (*nump && decpt-- > 0) {
346
0
            *bufp++ = *nump++;
347
0
          }
348
0
          while (decpt-- > 0) {
349
0
            *bufp++ = '0';
350
0
          }
351
0
          if (aPrec > 0) {
352
0
            *bufp++ = '.';
353
0
            while (*nump && aPrec-- > 0) {
354
0
              *bufp++ = *nump++;
355
0
            }
356
0
            while (aPrec-- > 0) {
357
0
              *bufp++ = '0';
358
0
            }
359
0
          }
360
0
        }
361
0
        *bufp = '\0';
362
0
        break;
363
0
364
0
      case 'G':
365
0
      case 'g':
366
0
367
0
        if ((decpt < -3) || ((decpt - 1) >= aPrec)) {
368
0
          *bufp++ = *nump++;
369
0
          numdigits--;
370
0
          if (numdigits > 0) {
371
0
            *bufp++ = '.';
372
0
            while (*nump) {
373
0
              *bufp++ = *nump++;
374
0
            }
375
0
          }
376
0
          *bufp++ = exp;
377
0
          ::snprintf(bufp, bufsz - (bufp - buf), "%+03d", decpt - 1);
378
0
        } else {
379
0
          if (decpt < 1) {
380
0
            *bufp++ = '0';
381
0
            if (aPrec > 0) {
382
0
              *bufp++ = '.';
383
0
              while (decpt++) {
384
0
                *bufp++ = '0';
385
0
              }
386
0
              while (*nump) {
387
0
                *bufp++ = *nump++;
388
0
              }
389
0
            }
390
0
          } else {
391
0
            while (*nump && decpt-- > 0) {
392
0
              *bufp++ = *nump++;
393
0
              numdigits--;
394
0
            }
395
0
            while (decpt-- > 0) {
396
0
              *bufp++ = '0';
397
0
            }
398
0
            if (numdigits > 0) {
399
0
              *bufp++ = '.';
400
0
              while (*nump) {
401
0
                *bufp++ = *nump++;
402
0
              }
403
0
            }
404
0
          }
405
0
          *bufp = '\0';
406
0
        }
407
0
    }
408
0
  }
409
0
410
0
  char16_t rbuf[256];
411
0
  char16_t* rbufp = rbuf;
412
0
  bufp = buf;
413
0
  // cast to char16_t
414
0
  while ((*rbufp++ = *bufp++)) {
415
0
  }
416
0
  *rbufp = '\0';
417
0
418
0
  return fill2(aState, rbuf, NS_strlen(rbuf), aWidth, aFlags);
419
0
}
420
421
/*
422
** Convert a string into its printable form. |aWidth| is the output
423
** width. |aPrec| is the maximum number of characters of |aStr| to output,
424
** where -1 means until NUL.
425
*/
426
int
427
nsTextFormatter::cvt_S(SprintfStateStr* aState, const char16_t* aStr, int aWidth, int aPrec,
428
                       int aFlags)
429
0
{
430
0
  int slen;
431
0
432
0
  if (aPrec == 0) {
433
0
    return 0;
434
0
  }
435
0
436
0
  /* Limit string length by precision value */
437
0
  slen = aStr ? NS_strlen(aStr) : 6;
438
0
  if (aPrec > 0) {
439
0
    if (aPrec < slen) {
440
0
      slen = aPrec;
441
0
    }
442
0
  }
443
0
444
0
  /* and away we go */
445
0
  return fill2(aState, aStr ? aStr : u"(null)", slen, aWidth, aFlags);
446
0
}
447
448
/*
449
** Convert a string into its printable form.  |aWidth| is the output
450
** width. |aPrec| is the maximum number of characters of |aStr| to output,
451
** where -1 means until NUL.
452
*/
453
int
454
nsTextFormatter::cvt_s(nsTextFormatter::SprintfStateStr* aState, const char* aStr, int aWidth,
455
                       int aPrec, int aFlags)
456
0
{
457
0
  // Be sure to handle null the same way as %S.
458
0
  if (aStr == nullptr) {
459
0
    return cvt_S(aState, nullptr, aWidth, aPrec, aFlags);
460
0
  }
461
0
  NS_ConvertUTF8toUTF16 utf16Val(aStr);
462
0
  return cvt_S(aState, utf16Val.get(), aWidth, aPrec, aFlags);
463
0
}
464
465
/*
466
** The workhorse sprintf code.
467
*/
468
int
469
nsTextFormatter::dosprintf(SprintfStateStr* aState, const char16_t* aFmt,
470
                           mozilla::Span<BoxedValue> aValues)
471
12
{
472
12
  static const char16_t space = ' ';
473
12
  static const char16_t hex[] = u"0123456789abcdef";
474
12
  static const char16_t HEX[] = u"0123456789ABCDEF";
475
12
  static const BoxedValue emptyString(u"");
476
12
477
12
  char16_t c;
478
12
  int flags, width, prec, radix;
479
12
480
12
  const char16_t* hexp;
481
12
482
12
  // Next argument for non-numbered arguments.
483
12
  size_t nextNaturalArg = 0;
484
12
  // True if we ever saw a numbered argument.
485
12
  bool sawNumberedArg = false;
486
12
487
48
  while ((c = *aFmt++) != 0) {
488
36
    int rv;
489
36
490
36
    if (c != '%') {
491
12
      rv = (*aState->stuff)(aState, aFmt - 1, 1);
492
12
      if (rv < 0) {
493
0
        return rv;
494
0
      }
495
12
      continue;
496
12
    }
497
24
498
24
    // Save the location of the "%" in case we decide it isn't a
499
24
    // format and want to just emit the text from the format string.
500
24
    const char16_t* percentPointer = aFmt - 1;
501
24
502
24
    /*
503
24
    ** Gobble up the % format string. Hopefully we have handled all
504
24
    ** of the strange cases!
505
24
    */
506
24
    flags = 0;
507
24
    c = *aFmt++;
508
24
    if (c == '%') {
509
0
      /* quoting a % with %% */
510
0
      rv = (*aState->stuff)(aState, aFmt - 1, 1);
511
0
      if (rv < 0) {
512
0
        return rv;
513
0
      }
514
0
      continue;
515
0
    }
516
24
517
24
    // Check for a numbered argument.
518
24
    bool sawWidth = false;
519
24
    const BoxedValue* thisArg = nullptr;
520
24
    if (c >= '0' && c <= '9') {
521
0
      size_t argNumber = 0;
522
0
      while (c && c >= '0' && c <= '9') {
523
0
        argNumber = (argNumber * 10) + (c - '0');
524
0
        c = *aFmt++;
525
0
      }
526
0
527
0
      if (c == '$') {
528
0
        // Mixing numbered arguments and implicit arguments is
529
0
        // disallowed.
530
0
        if (nextNaturalArg > 0) {
531
0
          return -1;
532
0
        }
533
0
534
0
        c = *aFmt++;
535
0
536
0
        // Numbered arguments start at 1.
537
0
        --argNumber;
538
0
        if (argNumber >= aValues.Length()) {
539
0
          // A correctness issue but not a safety issue.
540
0
          MOZ_ASSERT(false);
541
0
          thisArg = &emptyString;
542
0
        } else {
543
0
          thisArg = &aValues[argNumber];
544
0
        }
545
0
        sawNumberedArg = true;
546
0
      } else {
547
0
        width = argNumber;
548
0
        sawWidth = true;
549
0
      }
550
0
    }
551
24
552
24
    if (!sawWidth) {
553
24
      /*
554
24
       * Examine optional flags.  Note that we do not implement the
555
24
       * '#' flag of sprintf().  The ANSI C spec. of the '#' flag is
556
24
       * somewhat ambiguous and not ideal, which is perhaps why
557
24
       * the various sprintf() implementations are inconsistent
558
24
       * on this feature.
559
24
       */
560
24
      while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) {
561
0
        if (c == '-') {
562
0
          flags |= _LEFT;
563
0
        }
564
0
        if (c == '+') {
565
0
          flags |= _SIGNED;
566
0
        }
567
0
        if (c == ' ') {
568
0
          flags |= _SPACED;
569
0
        }
570
0
        if (c == '0') {
571
0
          flags |= _ZEROS;
572
0
        }
573
0
        c = *aFmt++;
574
0
      }
575
24
      if (flags & _SIGNED) {
576
0
        flags &= ~_SPACED;
577
0
      }
578
24
      if (flags & _LEFT) {
579
0
        flags &= ~_ZEROS;
580
0
      }
581
24
582
24
      /* width */
583
24
      if (c == '*') {
584
0
        // Not supported with numbered arguments.
585
0
        if (sawNumberedArg) {
586
0
          return -1;
587
0
        }
588
0
589
0
        if (nextNaturalArg >= aValues.Length() || !aValues[nextNaturalArg].IntCompatible()) {
590
0
          // A correctness issue but not a safety issue.
591
0
          MOZ_ASSERT(false);
592
0
          width = 0;
593
0
        } else {
594
0
          width = aValues[nextNaturalArg++].mValue.mInt;
595
0
        }
596
0
        c = *aFmt++;
597
24
      } else {
598
24
        width = 0;
599
24
        while ((c >= '0') && (c <= '9')) {
600
0
          width = (width * 10) + (c - '0');
601
0
          c = *aFmt++;
602
0
        }
603
24
      }
604
24
    }
605
24
606
24
    /* precision */
607
24
    prec = -1;
608
24
    if (c == '.') {
609
0
      c = *aFmt++;
610
0
      if (c == '*') {
611
0
        // Not supported with numbered arguments.
612
0
        if (sawNumberedArg) {
613
0
          return -1;
614
0
        }
615
0
616
0
        if (nextNaturalArg >= aValues.Length() || !aValues[nextNaturalArg].IntCompatible()) {
617
0
          // A correctness issue but not a safety issue.
618
0
          MOZ_ASSERT(false);
619
0
        } else {
620
0
          prec = aValues[nextNaturalArg++].mValue.mInt;
621
0
        }
622
0
        c = *aFmt++;
623
0
      } else {
624
0
        prec = 0;
625
0
        while ((c >= '0') && (c <= '9')) {
626
0
          prec = (prec * 10) + (c - '0');
627
0
          c = *aFmt++;
628
0
        }
629
0
      }
630
0
    }
631
24
632
24
    // If the argument isn't known yet, find it now.  This is done
633
24
    // after the width and precision code, in case '*' was used.
634
24
    if (thisArg == nullptr) {
635
24
      // Mixing numbered arguments and implicit arguments is
636
24
      // disallowed.
637
24
      if (sawNumberedArg) {
638
0
        return -1;
639
0
      }
640
24
641
24
      if (nextNaturalArg >= aValues.Length()) {
642
0
        // A correctness issue but not a safety issue.
643
0
        MOZ_ASSERT(false);
644
0
        thisArg = &emptyString;
645
24
      } else {
646
24
        thisArg = &aValues[nextNaturalArg++];
647
24
      }
648
24
    }
649
24
650
24
    /* Size.  Defaults to 32 bits.  */
651
24
    uint64_t mask = UINT32_MAX;
652
24
    if (c == 'h') {
653
0
      c = *aFmt++;
654
0
      mask = UINT16_MAX;
655
24
    } else if (c == 'L') {
656
0
      c = *aFmt++;
657
0
      mask = UINT64_MAX;
658
24
    } else if (c == 'l') {
659
24
      c = *aFmt++;
660
24
      if (c == 'l') {
661
0
        c = *aFmt++;
662
0
        mask = UINT64_MAX;
663
24
      } else {
664
24
        mask = UINT32_MAX;
665
24
      }
666
24
    }
667
24
668
24
    /* format */
669
24
    hexp = hex;
670
24
    radix = 10;
671
24
    // Several `MOZ_ASSERT`s below check for argument compatibility
672
24
    // with the format specifier.  These are only debug assertions,
673
24
    // not release assertions, and exist to catch problems in C++
674
24
    // callers of `nsTextFormatter`, as we do not have compile-time
675
24
    // checking of format strings.  In release mode, these assertions
676
24
    // will be no-ops, and we will fall through to printing the
677
24
    // argument based on the known type of the argument.
678
24
    switch (c) {
679
24
      case 'd':
680
24
      case 'i':                               /* decimal/integer */
681
24
        MOZ_ASSERT(thisArg->IntCompatible());
682
24
        break;
683
24
684
24
      case 'o':                               /* octal */
685
0
        MOZ_ASSERT(thisArg->IntCompatible());
686
0
        radix = 8;
687
0
        flags |= _UNSIGNED;
688
0
        break;
689
24
690
24
      case 'u':                               /* unsigned decimal */
691
0
        MOZ_ASSERT(thisArg->IntCompatible());
692
0
        radix = 10;
693
0
        flags |= _UNSIGNED;
694
0
        break;
695
24
696
24
      case 'x':                               /* unsigned hex */
697
0
        MOZ_ASSERT(thisArg->IntCompatible());
698
0
        radix = 16;
699
0
        flags |= _UNSIGNED;
700
0
        break;
701
24
702
24
      case 'X':                               /* unsigned HEX */
703
0
        MOZ_ASSERT(thisArg->IntCompatible());
704
0
        radix = 16;
705
0
        hexp = HEX;
706
0
        flags |= _UNSIGNED;
707
0
        break;
708
24
709
24
      case 'e':
710
0
      case 'E':
711
0
      case 'f':
712
0
      case 'g':
713
0
      case 'G':
714
0
        MOZ_ASSERT(thisArg->mKind == DOUBLE);
715
0
        // Type-based printing below.
716
0
        break;
717
0
718
0
      case 'S':
719
0
        MOZ_ASSERT(thisArg->mKind == STRING16);
720
0
        // Type-based printing below.
721
0
        break;
722
0
723
0
      case 's':
724
0
        MOZ_ASSERT(thisArg->mKind == STRING);
725
0
        // Type-based printing below.
726
0
        break;
727
0
728
0
      case 'c': {
729
0
          if (!thisArg->IntCompatible()) {
730
0
            MOZ_ASSERT(false);
731
0
            // Type-based printing below.
732
0
            break;
733
0
          }
734
0
735
0
          if ((flags & _LEFT) == 0) {
736
0
            while (width-- > 1) {
737
0
              rv = (*aState->stuff)(aState, &space, 1);
738
0
              if (rv < 0) {
739
0
                return rv;
740
0
              }
741
0
            }
742
0
          }
743
0
          char16_t ch = thisArg->mValue.mInt;
744
0
          rv = (*aState->stuff)(aState, &ch, 1);
745
0
          if (rv < 0) {
746
0
            return rv;
747
0
          }
748
0
          if (flags & _LEFT) {
749
0
            while (width-- > 1) {
750
0
              rv = (*aState->stuff)(aState, &space, 1);
751
0
              if (rv < 0) {
752
0
                return rv;
753
0
              }
754
0
            }
755
0
          }
756
0
        }
757
0
        continue;
758
0
759
0
      case 'p':
760
0
        if (!thisArg->PointerCompatible()) {
761
0
            MOZ_ASSERT(false);
762
0
            break;
763
0
        }
764
0
        static_assert(sizeof(uint64_t) >= sizeof(void*), "pointers are larger than 64 bits");
765
0
        rv = cvt_ll(aState, uintptr_t(thisArg->mValue.mPtr), width, prec, 16, flags | _UNSIGNED,
766
0
                    hexp);
767
0
        if (rv < 0) {
768
0
          return rv;
769
0
        }
770
0
        continue;
771
0
772
0
      case 'n':
773
0
        if (thisArg->mKind != INTPOINTER) {
774
0
          return -1;
775
0
        }
776
0
777
0
        if (thisArg->mValue.mIntPtr != nullptr) {
778
0
          *thisArg->mValue.mIntPtr = aState->cur - aState->base;
779
0
        }
780
0
        continue;
781
0
782
0
      default:
783
0
        /* Not a % token after all... skip it */
784
0
        rv = (*aState->stuff)(aState, percentPointer, aFmt - percentPointer);
785
0
        if (rv < 0) {
786
0
          return rv;
787
0
        }
788
0
        continue;
789
24
    }
790
24
791
24
    // If we get here, we want to handle the argument according to its
792
24
    // actual type; modified by the flags as appropriate.
793
24
    switch (thisArg->mKind) {
794
24
      case INT:
795
24
      case UINT: {
796
24
          int64_t val = thisArg->mValue.mInt;
797
24
          if ((flags & _UNSIGNED) == 0 && val < 0) {
798
0
            val = -val;
799
0
            flags |= _NEG;
800
0
          }
801
24
          rv = cvt_ll(aState, uint64_t(val) & mask, width, prec, radix, flags, hexp);
802
24
        }
803
24
        break;
804
24
      case INTPOINTER:
805
0
      case POINTER:
806
0
        // Always treat these as unsigned hex, no matter the format.
807
0
        static_assert(sizeof(uint64_t) >= sizeof(void*), "pointers are larger than 64 bits");
808
0
        rv = cvt_ll(aState, uintptr_t(thisArg->mValue.mPtr), width, prec, 16, flags | _UNSIGNED,
809
0
                    hexp);
810
0
        break;
811
0
      case DOUBLE:
812
0
        if (c != 'f' && c != 'E' && c != 'e' && c != 'G' && c != 'g') {
813
0
          // Pick some default.
814
0
          c = 'g';
815
0
        }
816
0
        rv = cvt_f(aState, thisArg->mValue.mDouble, width, prec, c, flags);
817
0
        break;
818
0
      case STRING:
819
0
        rv = cvt_s(aState, thisArg->mValue.mString, width, prec, flags);
820
0
        break;
821
0
      case STRING16:
822
0
        rv = cvt_S(aState, thisArg->mValue.mString16, width, prec, flags);
823
0
        break;
824
0
      default:
825
0
        // Can't happen.
826
0
        MOZ_ASSERT(0);
827
24
    }
828
24
829
24
    if (rv < 0) {
830
0
      return rv;
831
0
    }
832
24
  }
833
12
834
12
  return 0;
835
12
}
836
837
/************************************************************************/
838
839
int
840
nsTextFormatter::StringStuff(nsTextFormatter::SprintfStateStr* aState, const char16_t* aStr,
841
                             uint32_t aLen)
842
36
{
843
36
  ptrdiff_t off = aState->cur - aState->base;
844
36
845
36
  nsAString* str = static_cast<nsAString*>(aState->stuffclosure);
846
36
  str->Append(aStr, aLen);
847
36
848
36
  aState->base = str->BeginWriting();
849
36
  aState->cur = aState->base + off;
850
36
851
36
  return 0;
852
36
}
853
854
void
855
nsTextFormatter::vssprintf(nsAString& aOut, const char16_t* aFmt,
856
                           mozilla::Span<BoxedValue> aValues)
857
12
{
858
12
  SprintfStateStr ss;
859
12
  ss.stuff = StringStuff;
860
12
  ss.base = 0;
861
12
  ss.cur = 0;
862
12
  ss.maxlen = 0;
863
12
  ss.stuffclosure = &aOut;
864
12
865
12
  aOut.Truncate();
866
12
  dosprintf(&ss, aFmt, aValues);
867
12
}
868
869
/*
870
** Stuff routine that discards overflow data
871
*/
872
int
873
nsTextFormatter::LimitStuff(SprintfStateStr* aState, const char16_t* aStr, uint32_t aLen)
874
0
{
875
0
  uint32_t limit = aState->maxlen - (aState->cur - aState->base);
876
0
877
0
  if (aLen > limit) {
878
0
    aLen = limit;
879
0
  }
880
0
  while (aLen) {
881
0
    --aLen;
882
0
    *aState->cur++ = *aStr++;
883
0
  }
884
0
  return 0;
885
0
}
886
887
uint32_t
888
nsTextFormatter::vsnprintf(char16_t* aOut, uint32_t aOutLen,
889
                           const char16_t* aFmt, mozilla::Span<BoxedValue> aValues)
890
0
{
891
0
  SprintfStateStr ss;
892
0
893
0
  MOZ_ASSERT((int32_t)aOutLen > 0);
894
0
  if ((int32_t)aOutLen <= 0) {
895
0
    return 0;
896
0
  }
897
0
898
0
  ss.stuff = LimitStuff;
899
0
  ss.base = aOut;
900
0
  ss.cur = aOut;
901
0
  ss.maxlen = aOutLen;
902
0
  int result = dosprintf(&ss, aFmt, aValues);
903
0
904
0
  if (ss.cur == ss.base) {
905
0
    return 0;
906
0
  }
907
0
908
0
  // Append a NUL.  However, be sure not to count it in the returned
909
0
  // length.
910
0
  if (ss.cur - ss.base >= ptrdiff_t(ss.maxlen)) {
911
0
    --ss.cur;
912
0
  }
913
0
  *ss.cur = '\0';
914
0
915
0
  // Check the result now, so that an unterminated string can't
916
0
  // possibly escape.
917
0
  if (result < 0) {
918
0
    return -1;
919
0
  }
920
0
921
0
  return ss.cur - ss.base;
922
0
}