Subversion Repositories Kolibri OS

Rev

Rev 300 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
300 serge 1
#include "kolibc.h"
2
 
3
#define NULL (void*)0
4
 
5
static int isspeciall(long double d, char *bufp);
6
static char * exponentl(char *p, int expv, unsigned char fmtch);
7
 
8
 
9
/***
10
struct __FILE {
11
  int   _cnt;
12
  char *_ptr;
13
  char *_base;
14
  int   _bufsiz;
15
  int   _flag;
16
  int   _file;
17
  int   _fillsize;
18
};
19
 
20
typedef struct __FILE FILE;
21
*****/
22
 
23
static char decimal = '.';
24
 
25
/* 11-bit exponent (VAX G floating point) is 308 decimal digits */
26
#define	MAXEXP		308
27
#define MAXEXPLD        4952 /* this includes subnormal numbers */
28
/* 128 bit fraction takes up 39 decimal digits; max reasonable precision */
29
#define	MAXFRACT	39
30
 
31
#define	DEFPREC		6
32
#define	DEFLPREC	6
33
 
34
#define	BUF		(MAXEXPLD+MAXFRACT+1)	/* + decimal point */
35
 
36
#define	PUTC(ch)	(void) putc(ch, fp)
37
 
38
#define ARG(basetype) \
39
	_ulong = flags&LONGINT ? va_arg(argp, long basetype) : \
40
	    flags&SHORTINT ? (short basetype)va_arg(argp, int) : \
41
	    va_arg(argp, int)
42
 
43
static int nan2 = 0;
44
 
45
static int todigit(char c)
46
{
47
  if (c<='0') return 0;
48
  if (c>='9') return 9;
49
  return c-'0';
50
}
51
static char tochar(int n)
52
{
53
  if (n>=9) return '9';
54
  if (n<=0) return '0';
55
  return n+'0';
56
}
57
 
58
/* have to deal with the negative buffer count kludge */
59
 
60
#define	LONGINT		0x01		/* long integer */
61
#define	LONGDBL		0x02		/* long double */
62
#define	SHORTINT	0x04		/* short integer */
63
#define	ALT		0x08		/* alternate form */
64
#define	LADJUST		0x10		/* left adjustment */
65
#define	ZEROPAD		0x20		/* zero (as opposed to blank) pad */
66
#define	HEXPREFIX	0x40		/* add 0x or 0X prefix */
67
 
68
static cvtl(long double number, int prec, int flags, char *signp,
69
	    unsigned char fmtch, char *startp, char *endp);
70
static char *roundl(long double fract, int *expv, char *start, char *end,
71
		    char ch, char *signp);
72
static char *exponentl(char *p, int expv, unsigned char fmtch);
73
 
74
static char NULL_REP[] = "(null)";
324 serge 75
static char buf[BUF];		/* space for %c, %[diouxX], %[eEfgG] */
300 serge 76
 
77
int _doprnt(char *dest, size_t maxlen, const char *fmt0, va_list argp)
78
{
79
  const char *fmt;		/* format string */
80
  int ch;			/* character from fmt */
81
  int cnt;			/* return value accumulator */
82
  int n;			/* random handy integer */
83
  char *t;			/* buffer pointer */
84
  long double _ldouble;		/* double and long double precision arguments
85
				   %L.[eEfgG] */
86
  unsigned long _ulong;		/* integer arguments %[diouxX] */
87
  int base;			/* base for [diouxX] conversion */
88
  int dprec;			/* decimal precision in [diouxX] */
89
  int fieldsz;			/* field size expanded by sign, etc */
90
  int flags;			/* flags as above */
91
  int fpprec;			/* `extra' floating precision in [eEfgG] */
92
  int prec;			/* precision from format (%.3d), or -1 */
93
  int realsz;			/* field size expanded by decimal precision */
94
  int size;			/* size of converted field or string */
95
  int width;			/* width from format (%8d), or 0 */
96
  char sign;			/* sign prefix (' ', '+', '-', or \0) */
97
  char softsign;		/* temporary negative sign for floats */
98
  const char *digs;		/* digits for [diouxX] conversion */
99
  char *p;
324 serge 100
  digs = "0123456789abcdef";
300 serge 101
 
102
  fmt = fmt0;
103
  p = dest;
104
 
105
  for (cnt = 0;; ++fmt)
106
  {
107
    while((ch=*fmt)&&(ch!='%'))
108
	{ *p++ = ch;
109
	  cnt++;
110
	  fmt++;
111
	  maxlen--;
112
	};
113
 
114
    n = maxlen;
115
    if (!ch)
116
    { *p=0;
117
      return cnt;
118
    };
119
    flags = 0; dprec = 0; fpprec = 0; width = 0;
120
    prec = -1;
121
    sign = '\0';
122
rflag:
123
    switch (*++fmt)
124
    { case ' ':
125
        if (!sign)
126
	      sign = ' ';
127
          goto rflag;
128
      case '#':
129
        flags |= ALT;
130
        goto rflag;
131
      case '*':
132
        if ((width = va_arg(argp, int)) >= 0)
133
	      goto rflag;
134
          width = -width;
135
      /* FALLTHROUGH */
136
      case '-':
137
        flags |= LADJUST;
138
        goto rflag;
139
      case '+':
140
        sign = '+';
141
        goto rflag;
142
      case '.':
143
        if (*++fmt == '*')
144
	    n = va_arg(argp, int);
145
        else
146
        { n = 0;
147
	      while (isascii(*fmt) && isdigit(*fmt))
148
	      n = 10 * n + todigit(*fmt++);
149
	      --fmt;
150
        }
151
        prec = n < 0 ? -1 : n;
152
        goto rflag;
153
      case '0':
154
        flags |= ZEROPAD;
155
        goto rflag;
156
      case '1': case '2': case '3': case '4':
157
      case '5': case '6': case '7': case '8': case '9':
158
        n = 0;
159
        do
160
        { n = 10 * n + todigit(*fmt);
161
        } while (isascii(*++fmt) && isdigit(*fmt));
162
        width = n;
163
        --fmt;
164
        goto rflag;
165
      case 'L':
166
        flags |= LONGDBL;
167
        goto rflag;
168
      case 'h':
169
        flags |= SHORTINT;
170
        goto rflag;
171
      case 'l':
172
        flags |= LONGINT;
173
        goto rflag;
174
      case 'c':
175
        *(t = buf) = va_arg(argp, int);
176
        size = 1;
177
        sign = '\0';
178
        goto pforw;
179
      case 'D':
180
        flags |= LONGINT;
181
      /*FALLTHROUGH*/
182
      case 'd':
183
      case 'i':
184
        ARG(int);
185
        if ((long)_ulong < 0)
186
        { _ulong = -_ulong;
187
	      sign = '-';
188
        }
189
        base = 10;
190
        goto number;
191
      case 'e':
192
      case 'E':
193
      case 'f':
194
      case 'g':
195
      case 'G':
196
        if (flags & LONGDBL)
197
	      _ldouble = va_arg(argp, long double);
198
        else
199
	   _ldouble = (long double)va_arg(argp, double);
200
       if (prec > MAXFRACT)
201
      {
202
     	if (*fmt != 'g' && (*fmt != 'G' || (flags&ALT)))
203
	    fpprec = prec - MAXFRACT;
204
	    prec = MAXFRACT;
205
      }
206
      else if (prec == -1)
207
      {	if (flags&LONGINT)
208
	      prec = DEFLPREC;
209
	    else
210
	      prec = DEFPREC;
211
      }
212
      if (_ldouble < 0)
213
      {
214
	    softsign = '-';
215
	    _ldouble = -_ldouble;
216
      }
217
      else
218
	    softsign = 0;
219
        *buf = 0;
220
        size = cvtl(_ldouble, prec, flags, &softsign, *fmt, buf,
221
		            buf + sizeof(buf));
222
        if (softsign && !nan2)
223
	    sign = '-';
224
        nan2 = 0;
225
        t = *buf ? buf : buf + 1;
226
        goto pforw;
227
      case 'n':
228
        if (flags & LONGINT)
229
	      *va_arg(argp, long *) = cnt;
230
        else if (flags & SHORTINT)
231
	      *va_arg(argp, short *) = cnt;
232
        else
233
	      *va_arg(argp, int *) = cnt;
234
          break;
235
      case 'O':
236
        flags |= LONGINT;
237
      /*FALLTHROUGH*/
238
      case 'o':
239
        ARG(unsigned);
240
        base = 8;
241
        goto nosign;
242
      case 'p':
243
      /* NOSTRICT */
244
        _ulong = (unsigned long)va_arg(argp, void *);
245
        base = 16;
246
        goto nosign;
247
      case 's':
248
        if (!(t = va_arg(argp, char *)))
249
	      t = NULL_REP;
250
        if (prec >= 0)
251
        { char *p			/*, *memchr() */;
252
	      if ((p = memchr(t, 0, prec)))
253
	      { size = p - t;
254
	        if (size > prec)
255
	          size = prec;
256
	      }
257
	      else
258
	        size = prec;
259
         }
260
         else
261
	       size = strlen(t);
262
           sign = '\0';
263
          goto pforw;
264
      case 'U':
265
        flags |= LONGINT;
266
      /*FALLTHROUGH*/
267
      case 'u':
268
        ARG(unsigned);
269
        base = 10;
270
        goto nosign;
271
      case 'X':
272
        digs = "0123456789ABCDEF";
273
      /* FALLTHROUGH */
274
      case 'x':
275
        ARG(unsigned);
276
        base = 16;
277
      /* leading 0x/X only if non-zero */
278
        if (flags & ALT && _ulong != 0)
279
	      flags |= HEXPREFIX;
280
 
281
      /* unsigned conversions */
282
nosign:			sign = '\0';
283
number:			if ((dprec = prec) >= 0)
284
        flags &= ~ZEROPAD;
285
 
286
        t = buf + BUF;
287
        if (_ulong != 0 || prec != 0)
288
        {
289
	      do
290
	      {
291
	        *--t = digs[_ulong % base];
292
	        _ulong /= base;
293
	      } while (_ulong);
294
	      digs = "0123456789abcdef";
295
	      if (flags & ALT && base == 8 && *t != '0')
296
	        *--t = '0';		/* octal leading 0 */
297
        }
298
        size = buf + BUF - t;
299
 
300
pforw:
301
        fieldsz = size + fpprec;
302
        realsz = dprec > fieldsz ? dprec : fieldsz;
303
        if (sign)
304
	      realsz++;
305
        if (flags & HEXPREFIX)
306
	      realsz += 2;
307
 
308
      /* right-adjusting blank padding */
309
        if ((flags & (LADJUST|ZEROPAD)) == 0 && width)
310
	      for (n = realsz; n < width; n++)
311
	        *p++ = ' ';
312
      /* prefix */
313
        if (sign)
314
 	      *p++ = sign;
315
        if (flags & HEXPREFIX)
316
        {
317
	      *p++ = '0';
318
	      *p++ = (char)*fmt;
319
        }
320
      /* right-adjusting zero padding */
321
      if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
322
	  for (n = realsz; n < width; n++)
323
	      *p++ = '0';
324
      /* leading zeroes from decimal precision */
325
      for (n = fieldsz; n < dprec; n++)
326
	      *p++ = '0';
327
 
328
      /* the string or number proper */
329
      n = size;
330
//      if (fp->_cnt - n >= 0 )
331
//      {
332
//	    fp->_cnt -= n;
333
        maxlen-= n;
334
	    memcpy(p, t, n);
335
	    p+=n;
336
//	    fp->_ptr += n;
337
//      }
338
//      else
339
//	    while (--n >= 0)
340
//	     PUTC(*t++);
341
      /* trailing f.p. zeroes */
342
      while (--fpprec >= 0)
343
	    *p++= '0';
344
      /* left-adjusting padding (always blank) */
345
      if (flags & LADJUST)
346
	  for (n = realsz; n < width; n++)
347
	    *p++ = ' ';
348
      /* finally, adjust cnt */
349
        cnt += width > realsz ? width : realsz;
350
        break;
351
      case '\0':			/* "%?" prints ?, unless ? is NULL */
352
        return cnt;
353
      default:
354
        *p++ = (char)*fmt;
355
        cnt++;
356
     }
357
  }
358
  /* NOTREACHED */
359
}
360
 
361
static long double pten[] =
362
{
363
  1e1, 1e2, 1e4, 1e8, 1e16, 1e32, 1e64, 1e128, 1e256};
364
 
365
static long double ptenneg[] =
366
{
367
  1e-1L, 1e-2L, 1e-4L, 1e-8L, 1e-16L, 1e-32L, 1e-64L, 1e-128L, 1e-256L,
368
  1e-512L, 1e-1024L, 1e-2048L, 1e-4096L
369
};
370
 
371
#define MAXP 4096
372
#define NP   12
373
#define P    (4294967296.0L * 4294967296.0L * 2.0L)   /* 2^65 */
374
static long double INVPREC = P;
375
static long double PREC = 1.0L/P;
376
#undef P
377
/*
378
 * Defining FAST_LDOUBLE_CONVERSION results in a little bit faster
379
 * version, which might be less accurate (about 1 bit) for long
380
 * double. For 'normal' double it doesn't matter.
381
 */
382
/* #define FAST_LDOUBLE_CONVERSION */
383
 
384
static int
385
cvtl(long double number, int prec, int flags, char *signp, unsigned char fmtch,
386
     char *startp, char *endp)
387
{
388
  char *p, *t;
389
  long double fract;
390
  int dotrim, expcnt, gformat;
391
  long double integer, tmp;
392
 
393
  if ((expcnt = isspeciall(number, startp)))
394
    return(expcnt);
395
 
396
  dotrim = expcnt = gformat = 0;
397
  /* fract = modfl(number, &integer); */
398
  integer = number;
399
 
400
  /* get an extra slot for rounding. */
401
  t = ++startp;
402
 
403
  p = endp - 1;
404
  if (integer)
405
  {
406
    int i, lp=NP, pt=MAXP;
407
#ifndef FAST_LDOUBLE_CONVERSION
408
    long double oint = integer, dd=1.0L;
409
#endif
410
    if (integer > INVPREC)
411
    {
412
      integer *= PREC;
413
      while(lp >= 0) {
414
	if (integer >= pten[lp])
415
	{
416
	  expcnt += pt;
417
	  integer *= ptenneg[lp];
418
#ifndef FAST_LDOUBLE_CONVERSION
419
	  dd *= pten[lp];
420
#endif
421
	}
422
	pt >>= 1;
423
	lp--;
424
      }
425
#ifndef FAST_LDOUBLE_CONVERSION
426
      integer = oint/dd;
427
#else
428
      integer *= INVPREC;
429
#endif
430
    }
431
    /*
432
     * Do we really need this ?
433
     */
434
    for (i = 0; i < expcnt; i++)
435
      *p-- = '0';
436
  }
437
  number = integer;
438
 // fract = modfl(number, &integer);
439
  /*
440
   * get integer portion of number; put into the end of the buffer; the
441
   * .01 is added for modf(356.0 / 10, &integer) returning .59999999...
442
   */
443
  for (; integer; ++expcnt)
444
  {
445
//    tmp = modfl(integer * 0.1L , &integer);
446
    *p-- = tochar((int)((tmp + .01L) * 10));
447
  }
448
  switch(fmtch)
449
  {
450
  case 'f':
451
    /* reverse integer into beginning of buffer */
452
    if (expcnt)
453
      for (; ++p < endp; *t++ = *p);
454
    else
455
      *t++ = '0';
456
    /*
457
     * if precision required or alternate flag set, add in a
458
     * decimal point.
459
     */
460
    if (prec || flags&ALT)
461
      *t++ = decimal;
462
    /* if requires more precision and some fraction left */
463
    if (fract)
464
    {
465
      if (prec)
466
	do {
467
//	  fract = modfl(fract * 10.0L, &tmp);
468
	  *t++ = tochar((int)tmp);
469
	} while (--prec && fract);
470
      if (fract)
471
	startp = roundl(fract, (int *)NULL, startp,
472
			t - 1, (char)0, signp);
473
    }
474
    for (; prec--; *t++ = '0');
475
    break;
476
  case 'e':
477
  case 'E':
478
  eformat:
479
    if (expcnt)
480
    {
481
      *t++ = *++p;
482
      if (prec || flags&ALT)
483
	*t++ = decimal;
484
      /* if requires more precision and some integer left */
485
      for (; prec && ++p < endp; --prec)
486
	*t++ = *p;
487
      /*
488
       * if done precision and more of the integer component,
489
       * round using it; adjust fract so we don't re-round
490
       * later.
491
       */
492
      if (!prec && ++p < endp)
493
      {
494
	fract = 0;
495
	startp = roundl((long double)0.0L, &expcnt,
496
			startp, t - 1, *p, signp);
497
      }
498
      /* adjust expcnt for digit in front of decimal */
499
      --expcnt;
500
    }
501
    /* until first fractional digit, decrement exponent */
502
    else if (fract)
503
    {
504
      int lp=NP, pt=MAXP;
505
#ifndef FAST_LDOUBLE_CONVERSION
506
      long double ofract = fract, dd=1.0L;
507
#endif
508
      expcnt = -1;
509
      if (fract < PREC)
510
      {
511
	fract *= INVPREC;
512
	while(lp >= 0)
513
	{
514
	  if (fract <= ptenneg[lp])
515
	  {
516
	    expcnt -= pt;
517
	    fract *= pten[lp];
518
#ifndef FAST_LDOUBLE_CONVERSION
519
	    dd *= pten[lp];
520
#endif
521
	  }
522
	  pt >>= 1;
523
	  lp--;
524
	}
525
#ifndef FAST_LDOUBLE_CONVERSION
526
	fract = ofract*dd;
527
#else
528
	fract *= PREC;
529
#endif
530
      }
531
      /* adjust expcnt for digit in front of decimal */
532
      for (			/* expcnt = -1 */ ;; --expcnt)
533
      {
534
//	fract = modfl(fract * 10.0L, &tmp);
535
	if (tmp)
536
	  break;
537
      }
538
      *t++ = tochar((int)tmp);
539
      if (prec || flags&ALT)
540
	*t++ = decimal;
541
    }
542
    else
543
    {
544
      *t++ = '0';
545
      if (prec || flags&ALT)
546
	*t++ = decimal;
547
    }
548
    /* if requires more precision and some fraction left */
549
    if (fract)
550
    {
551
      if (prec)
552
	do {
553
//	  fract = modfl(fract * 10.0L, &tmp);
554
	  *t++ = tochar((int)tmp);
555
	} while (--prec && fract);
556
      if (fract)
557
	startp = roundl(fract, &expcnt, startp,
558
			t - 1, (char)0, signp);
559
    }
560
    /* if requires more precision */
561
    for (; prec--; *t++ = '0');
562
 
563
    /* unless alternate flag, trim any g/G format trailing 0's */
564
    if (gformat && !(flags&ALT))
565
    {
566
      while (t > startp && *--t == '0');
567
      if (*t == decimal)
568
	--t;
569
      ++t;
570
    }
571
    t = exponentl(t, expcnt, fmtch);
572
    break;
573
  case 'g':
574
  case 'G':
575
    /* a precision of 0 is treated as a precision of 1. */
576
    if (!prec)
577
      ++prec;
578
    /*
579
     * ``The style used depends on the value converted; style e
580
     * will be used only if the exponent resulting from the
581
     * conversion is less than -4 or greater than the precision.''
582
     *	-- ANSI X3J11
583
     */
584
    if (expcnt > prec || (!expcnt && fract && fract < .0001))
585
    {
586
      /*
587
       * g/G format counts "significant digits, not digits of
588
       * precision; for the e/E format, this just causes an
589
       * off-by-one problem, i.e. g/G considers the digit
590
       * before the decimal point significant and e/E doesn't
591
       * count it as precision.
592
       */
593
      --prec;
594
      fmtch -= 2;		/* G->E, g->e */
595
      gformat = 1;
596
      goto eformat;
597
    }
598
    /*
599
     * reverse integer into beginning of buffer,
600
     * note, decrement precision
601
     */
602
    if (expcnt)
603
      for (; ++p < endp; *t++ = *p, --prec);
604
    else
605
      *t++ = '0';
606
    /*
607
     * if precision required or alternate flag set, add in a
608
     * decimal point.  If no digits yet, add in leading 0.
609
     */
610
    if (prec || flags&ALT)
611
    {
612
      dotrim = 1;
613
      *t++ = decimal;
614
    }
615
    else
616
      dotrim = 0;
617
    /* if requires more precision and some fraction left */
618
    while (prec && fract)
619
    {
620
//      fract = modfl(fract * 10.0L, &tmp);
621
      *t++ = tochar((int)tmp);
622
      prec--;
623
    }
624
    if (fract)
625
      startp = roundl(fract, (int *)NULL, startp, t - 1,
626
		      (char)0, signp);
627
    /* alternate format, adds 0's for precision, else trim 0's */
628
    if (flags&ALT)
629
      for (; prec--; *t++ = '0');
630
    else if (dotrim)
631
    {
632
      while (t > startp && *--t == '0');
633
      if (*t != decimal)
634
	++t;
635
    }
636
  }
637
  return t - startp;
638
}
639
 
640
static char *
641
roundl(long double fract, int *expv, char *start, char *end, char ch,
642
       char *signp)
643
{
644
  long double tmp;
645
 
646
  if (fract)
647
  {
648
    if (fract == 0.5L)
649
    {
650
      char *e = end;
651
      if (*e == '.')
652
	e--;
653
      if (*e == '0' || *e == '2' || *e == '4'
654
	  || *e == '6' || *e == '8')
655
      {
656
	tmp = 3.0;
657
	goto start;
658
      }
659
    }
660
//    (void)modfl(fract * 10.0L, &tmp);
661
  }
662
  else
663
    tmp = todigit(ch);
664
 start:
665
  if (tmp > 4)
666
    for (;; --end)
667
    {
668
      if (*end == decimal)
669
	--end;
670
      if (++*end <= '9')
671
	break;
672
      *end = '0';
673
      if (end == start)
674
      {
675
	if (expv)
676
	{		/* e/E; increment exponent */
677
	  *end = '1';
678
	  ++*expv;
679
	}
680
	else
681
	{			/* f; add extra digit */
682
	  *--end = '1';
683
	  --start;
684
	}
685
	break;
686
      }
687
    }
688
  /* ``"%.3f", (double)-0.0004'' gives you a negative 0. */
689
  else if (*signp == '-')
690
    for (;; --end)
691
    {
692
      if (*end == decimal)
693
	--end;
694
      if (*end != '0')
695
	break;
696
      if (end == start)
697
	*signp = 0;
698
    }
699
  return start;
700
}
701
 
702
static char * exponentl(char *p, int expv, unsigned char fmtch)
703
{
704
  char *t;
705
  char expbuf[MAXEXPLD];
706
 
707
  *p++ = fmtch;
708
  if (expv < 0)
709
  {
710
    expv = -expv;
711
    *p++ = '-';
712
  }
713
  else
714
    *p++ = '+';
715
  t = expbuf + MAXEXPLD;
716
  if (expv > 9)
717
  {
718
    do {
719
      *--t = tochar(expv % 10);
720
    } while ((expv /= 10) > 9);
721
    *--t = tochar(expv);
722
    for (; t < expbuf + MAXEXPLD; *p++ = *t++);
723
  }
724
  else
725
  {
726
    *p++ = '0';
727
    *p++ = tochar(expv);
728
  }
729
  return p;
730
}
731
 
732
static int isspeciall(long double d, char *bufp)
733
{
734
  struct IEEExp {
735
    unsigned manl:32;
736
    unsigned manh:32;
737
    unsigned exp:15;
738
    unsigned sign:1;
739
  } *ip = (struct IEEExp *)&d;
740
 
741
  nan2 = 0;  /* don't assume the static is 0 (emacs) */
742
  if (ip->exp != 0x7fff)
743
    return(0);
744
  if ((ip->manh & 0x7fffffff) || ip->manl)
745
  {
746
    strcpy(bufp, "NaN");
747
    nan2 = 1;			/* kludge: we don't need the sign,  it's not nice
748
				   but it should work */
749
  }
750
  else
751
    (void)strcpy(bufp, "Inf");
752
  return(3);
753
}
754
 
324 serge 755
static unsigned int strlen(const char* string)
756
{
757
	int i;
758
	i=0;
759
	while (*string++) i++;
760
	return i;
761
}
300 serge 762
 
324 serge 763
static char* strcpy(char* dst,const char* src)
764
{
765
	char* res= dst;
766
	while(*dst++ = *src++) ;
767
	return res;
768
}
300 serge 769