Coverage Report

Created: 2025-07-01 06:26

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