Coverage Report

Created: 2026-10-06 06:17

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