Subversion Repositories Kolibri OS

Rev

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

Rev Author Line No. Line
647 andrew_pro 1
/*
2
        function for format output to the string
6424 siemargl 3
 
4
Siemargl update formats as http://www.cplusplus.com/reference/cstdio/printf/, no wchar though
5
http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap05.html  is used too
6
%g explain https://support.microsoft.com/en-us/kb/43392
7
 
8
todo:
9
-%e
10
-simplify justifying
11
-fix %o, %x
12
-fix precision in %g
13
-%a
14
-NAN, INF
15
-%n nothing printed
16
-%17.18f digits maximum format
17
%C as w_char L'x'
647 andrew_pro 18
*/
19
 
6424 siemargl 20
 
21
//#include 
647 andrew_pro 22
#include 
23
#include 
6424 siemargl 24
#include 
647 andrew_pro 25
#include 
6424 siemargl 26
#include 
647 andrew_pro 27
 
6424 siemargl 28
enum flags_t
647 andrew_pro 29
{
6424 siemargl 30
        flag_unsigned   = 0x02,
31
        flag_register   = 0x04,
32
        flag_plus       = 0x08,
33
        flag_left_just  = 0x10,
34
        flag_lead_zeros = 0x20,
35
        flag_space_plus = 0x40,
36
        flag_hash_sign  = 0x80,
37
        flag_point      = 0x100
38
};
647 andrew_pro 39
 
6424 siemargl 40
int formatted_double_to_string_scientific(long double number, int format1, int format2, char *s, int flags)
41
{
42
    strcpy(s, "%e not implemented yet, sorry");
43
    return strlen(s);
44
}
647 andrew_pro 45
 
6424 siemargl 46
int formatted_double_to_string(long double number, int format1, int format2, char *s, int flags)
47
{
48
        long double	nafter, beforpointdigit;
49
        long long	intdigit, mul;
50
        int	    div;
51
        int     i;
52
        char*   size;
53
        int     fmt1;
54
        int     fmt2;
55
        char    buf[100], *pbuf = buf;
56
        char    buf_low[50], *pbuf_lo = buf_low;
647 andrew_pro 57
 
6424 siemargl 58
        if((flags & flag_point) == 0) format2 = 6;  // default prec if no point spec
647 andrew_pro 59
 
6424 siemargl 60
        size = s;
61
        if (number < 0) {*s++ = '-'; number = -number; }
62
        else
63
        {
64
            if (flags & flag_plus) *s++ = '+';  else
65
            if (flags & flag_space_plus) *s++ = ' ';
66
        }
647 andrew_pro 67
 
6424 siemargl 68
        fmt1 = 1;
69
        fmt2 = format2;
70
        if (fmt2 > 18) fmt2 = 18; //maximum size of long long type
647 andrew_pro 71
 
6424 siemargl 72
        beforpointdigit = floor(number + 0.00000000000001);
73
        nafter = number - beforpointdigit;
74
 
75
        //print part of number before point
76
        mul = 1;
77
        for(i = 0; i < sizeof buf - 1; i++)
78
        {
79
            mul *= 10;
80
            if ((beforpointdigit/mul) < 1.0) { fmt1 = i + 1; break; }
647 andrew_pro 81
        }
6424 siemargl 82
        if (i == sizeof buf - 1 || fmt1 > 17)
83
        {
84
            strcpy(s, "[too big number for %f, %a]");
85
            return strlen(s);
86
        }
647 andrew_pro 87
 
6424 siemargl 88
        mul /= 10;
89
        while(mul > 1)
90
        {
91
            div = beforpointdigit / mul;
92
            *pbuf++ = (char)div + '0';
93
            beforpointdigit = beforpointdigit - div * mul;
94
            mul /= 10;
95
        }
96
        *pbuf++=(char)beforpointdigit + '0';
647 andrew_pro 97
 
6424 siemargl 98
        //print part of number after point
99
        mul = 1;
100
        for(i = 0; i < fmt2; i++)
101
        {
102
            nafter = nafter*10;
103
            mul *= 10;
104
        }
647 andrew_pro 105
 
6424 siemargl 106
        intdigit = roundl(nafter);
647 andrew_pro 107
 
6424 siemargl 108
        mul /= 10;
109
        for(i = 0; i < fmt2 - 1; i++)
110
        {
111
                div = intdigit / mul;
112
                *pbuf_lo++=(char)div + '0';
113
                intdigit = intdigit - div * mul;
114
                mul /= 10;
115
                if (mul == 1) break;
116
        }
117
        *pbuf_lo++ = (char)intdigit + '0';
647 andrew_pro 118
 
119
 
6424 siemargl 120
        memcpy(s, buf, pbuf - buf);  s += pbuf - buf;
121
        if (roundl(nafter) != 0 || fmt2 != 0)
122
        {
123
            *s++ = '.';
124
            memcpy(s, buf_low, pbuf_lo - buf_low);  s += pbuf_lo - buf_low;
125
        } else if (flags & flag_hash_sign)
126
            *s++ = '.';
647 andrew_pro 127
 
6424 siemargl 128
        // right justifiyng and forward zeros
129
        div = (s - size);
130
        if ((flags & flag_left_just) == 0 && div < format1)
131
        {
132
            pbuf = size;
133
            if ((flags & flag_lead_zeros) != 0)
134
                if (*pbuf == '+' || *pbuf == '-' || *pbuf == ' ') { pbuf++; div--; } // sign already at place
135
            for (i = 0; i < div; i++)
136
                size[format1 - i - 1] = pbuf[div - 1 - i];
137
            for (i = 0; i < format1 - div - (pbuf - size); i++)
138
                if (flags & flag_lead_zeros)
139
                    pbuf[i] = '0';
140
                else
141
                    pbuf[i] = ' ';
142
 
143
            return format1;
647 andrew_pro 144
        }
6424 siemargl 145
 
146
        return s - size;
647 andrew_pro 147
}
148
 
6424 siemargl 149
int formatted_long_to_string(long long number, int format1, int prec, char *s, int flags)
647 andrew_pro 150
{
6424 siemargl 151
        int         i;
152
        int         fmt;
153
        char*       size = s;
154
        long long   digit;
155
        long long   mul;
156
        int         div;
157
        char        buf[100], *pbuf = buf;
647 andrew_pro 158
 
6424 siemargl 159
        if (number == -9223372036854775807LL - 1)  // overflow all our math, cant minus this
160
        {
161
            strcpy(buf, "9223372036854775808");
162
            pbuf += 19;
163
            *s++ = '-';
164
            goto verybig;
165
        }
647 andrew_pro 166
 
6424 siemargl 167
        if (flags & flag_point) flags &= ~flag_lead_zeros;  // conflicting flags
647 andrew_pro 168
 
6424 siemargl 169
        if (number < 0) {*s++ = '-'; number = -number; }
170
        else
171
        {
172
            if (flags & flag_plus) *s++ = '+';  else
173
            if (flags & flag_space_plus) *s++ = ' ';
174
        }
647 andrew_pro 175
 
6424 siemargl 176
        digit = number;
647 andrew_pro 177
 
6424 siemargl 178
        mul = (digit < 0) ? -1 : 1;
647 andrew_pro 179
 
6424 siemargl 180
        for(i = 0; i < sizeof buf - 2; i++)
181
        {
182
            if (digit / mul < 10) { fmt = i + 1; break; }
183
            mul *= 10;
184
        }
185
 
186
        // add leading zeros
187
        for(i = 0; i < prec - fmt; i++) *pbuf++ = '0';
188
 
189
        for(i = 0; i < fmt - 1; i++)
190
        {
191
            div = digit / mul;
192
            *pbuf++ = (char)div + '0';
193
            digit = digit - div * mul;
194
            mul /= 10;
195
            if (mul == 1 || mul == -1) break;
196
        }
197
        *pbuf++ = (char)digit + '0';
198
 
199
verybig:
200
        memcpy(s, buf, pbuf - buf);  s += pbuf - buf;
201
 
202
        // right justifiyng and forward zeros
203
        div = (s - size);
204
        if ((flags & flag_left_just) == 0 && div < format1)
205
        {
206
            pbuf = size;
207
            if ((flags & flag_lead_zeros) != 0)
208
                if (*pbuf == '+' || *pbuf == '-' || *pbuf == ' ') { pbuf++; div--; } // sign already at place
209
            for (i = 0; i < div; i++)
210
                size[format1 - i - 1] = pbuf[div - 1 - i];
211
            for (i = 0; i < format1 - div - (pbuf - size); i++)
212
                if (flags & flag_lead_zeros)
213
                    pbuf[i] = '0';
647 andrew_pro 214
                else
6424 siemargl 215
                    pbuf[i] = ' ';
647 andrew_pro 216
 
6424 siemargl 217
            return format1;
218
        }
219
 
220
        return s - size;
647 andrew_pro 221
}
222
 
6424 siemargl 223
int formatted_hex_to_string(long long number, int fmt1, char *s, int flags)
647 andrew_pro 224
{
225
        long    n;
226
        int             i,pos;
6424 siemargl 227
//        int             fmt;
647 andrew_pro 228
        long    size;
229
        int             difference_pos;
230
        char            xdigs_lower[16]="0123456789abcdef";
231
        char            xdigs_upper[16]="0123456789ABCDEF";
232
        char            buf[200];
233
 
234
        n=(long)number;
235
        size=(int)s;
236
        if (n<0) {*s='-';s++;n=-n;}
237
 
238
        if (n==0) {*s='0';s++;goto end;}
239
        for(i=0;i<200;i++) {buf[i]=0;}
240
 
241
        i=0;
242
        if (flag_register==0)
243
        {
244
                while (n>0)
245
                {
246
                        buf[i]=xdigs_lower[n & 15];
247
                        n=n>>4;
248
                        i++;
249
                }
250
        }
251
        else
252
        {
253
                while (n>0)
254
                {
255
                        buf[i]=xdigs_upper[n & 15];
256
                        n=n>>4;
257
                        i++;
258
                }
259
        }
260
 
261
        pos=i;
262
        difference_pos=i;
263
 
264
        for(i=pos-1;i>=0;i--)
265
        {
266
                *s=buf[i];
267
                s++;
268
        }
269
 
270
        if (fmt1-difference_pos>0)
271
        {
272
                for(i=difference_pos+1;i<=fmt1;i++)
273
                {
274
                        *s=' ';
275
                        s++;
276
                }
277
        }
278
        end:size=(int)s-size;
279
        return(size);
280
}
281
 
6424 siemargl 282
int formatted_octa_to_string(long long number, int fmt1, char *s, int flags)
647 andrew_pro 283
{
284
        long    n;
285
        int             i,pos;
6424 siemargl 286
//        int             fmt;
647 andrew_pro 287
        long   	size;
288
        int             difference_pos;
289
        char            xdigs_lower[16]="012345678";
290
        char            buf[200];
291
 
292
        n=number;
293
        size=(int)s;
294
        if (n<0) {*s='-';s++;n=-n;}
295
 
296
        if (n==0) {*s='0';s++;goto end;}
297
        for(i=0;i<200;i++) {buf[i]=0;}
298
 
299
        i=0;
300
        if (flag_register==0)
301
        {
302
                while (n>0)
303
                {
304
                        buf[i]=xdigs_lower[n & 7];
305
                        n=n>>3;
306
                        i++;
307
                }
308
        }
309
 
310
        pos=i;
311
        difference_pos=i;
312
 
313
        for(i=pos-1;i>=0;i--)
314
        {
315
                *s=buf[i];
316
                s++;
317
        }
318
 
319
        if (fmt1-difference_pos>0)
320
        {
321
                for(i=difference_pos+1;i<=fmt1;i++)
322
                {
323
                        *s=' ';
324
                        s++;
325
                }
326
        }
327
        end:size=(int)s-size;
328
        return(size);
329
}
330
 
6424 siemargl 331
//int vsnprintf (char * s, size_t n, const char * format, va_list arg );
332
int format_print(char *dest, size_t maxlen, const char *fmt0, va_list argp)
647 andrew_pro 333
{
6424 siemargl 334
    int                     i;
335
    int                     length;
336
    int                     fmt1, fmt2;  // width, precision
337
    size_t                  pos, posc;
338
    long	long	        intdigit;
339
    long	double          doubledigit;
340
//        float                   floatdigit;
341
    const   char            *fmt, *fmtc;  // first point to %, fmtc points to specifier
342
    char                    *s; // pointer to current dest char
343
    char                    *str;
344
    char                    buf[200];  // buffer for current argument value print representation
345
    int                     format_flag;
346
    int                     flag_long;        // 2 = long double or long long int or wchar
347
    int                    *point_to_n = NULL;
348
    int                     flags;  // parsed flags
647 andrew_pro 349
 
6424 siemargl 350
    fmt = fmt0;
351
    s = dest;
352
    pos = 0;
353
    while(pos < maxlen)
354
    {
355
        if (*fmt != '%')  // usual char
647 andrew_pro 356
        {
6424 siemargl 357
            if ('\0' == (*s++ = *fmt++)) break;
358
            pos++;
359
            continue;
360
        }
647 andrew_pro 361
 
6424 siemargl 362
        if (*(fmt + 1) == '%') // %%
363
        {
364
                *s++ = '%'; pos++;
365
                fmt += 2;
366
                continue;
367
        }
368
        //checking to containg format in the string
369
        fmtc = fmt;
370
        posc = pos;
647 andrew_pro 371
 
6424 siemargl 372
        flags = 0;
373
        format_flag = 0;
374
        flag_long = 0;  // 2 = long double or long long int or wchar
647 andrew_pro 375
 
6424 siemargl 376
        while(*fmtc != '\0' && !format_flag)    // searching end of format
377
        {
378
                fmtc++; posc++;
379
                switch( *fmtc )
380
                {
381
                case 'a':
382
                    format_flag = 1;
383
                    flags |= flag_unsigned;
384
                    break;
385
                case 'A':
386
                    format_flag = 1;
387
                    flags |= flag_unsigned | flag_register;
388
                    break;
389
                case 'c':   case 'd':   case 'i':   case 'e':   case 'f':   case 'g':   case 's':   case 'n':
390
                    format_flag = 1;
391
                    break;
392
                case 'E':   case 'F':   case 'G':
393
                    format_flag = 1;
394
                    flags |= flag_register;
395
                    break;
396
                case 'l':
397
                    flag_long  = flag_long ? 2 : 1;  // ll.eq.L
398
                    break;
399
                case 'L':
400
                    flag_long = 2;
401
                    break;
402
                case 'o':   case 'u':   case 'x':   case 'p':
403
                    format_flag = 1;
404
                    flags |= flag_unsigned;
405
                    break;
406
                case 'X':   case 'P':
407
                    format_flag = 1;
408
                    flags |= flag_unsigned | flag_register;
409
                    break;
410
                case '+':
411
                    flags |= flag_plus;
412
                    break;
413
                case '-':
414
                    flags |= flag_left_just;
415
                    break;
416
                case ' ': // space
417
                    flags |= flag_space_plus;
418
                    break;
419
                case '#':
420
                    flags |= flag_hash_sign;
421
                    break;
422
                case '*':   case '.':  // just skip
423
                    break;
424
                default:
425
                    if(isdigit(*fmtc))  break;
426
                    strncpy(dest, "print format error - in % invalid char found", maxlen);
427
                    return -1;  // non format char found - user error
428
                }
429
        }
647 andrew_pro 430
 
6424 siemargl 431
        if (format_flag == 0)
432
        {
433
            strncpy(dest, "print format error - % without format specifier", maxlen);
434
            return -1;  // format char not found - user error
435
        }
647 andrew_pro 436
 
6424 siemargl 437
        fmt1 = 0;
438
        fmt2 = 0;
439
        if (posc - pos > 1)  // try to read width, precision
440
        {
441
            fmt++;
442
            for(i = pos + 1; i < posc; i++)
443
            {
444
                switch(*fmt)
445
                {
446
                case '0':
447
                    if(fmt1 == 0 && (flags & flag_point) == 0)    flags |= flag_lead_zeros;
448
                case '1':   case '2':   case '3':   case '4':
449
                case '5':   case '6':   case '7':   case '8':   case '9':
450
                    if ((flags & flag_point) == 0)
451
                        fmt1 = fmt1 * 10 + (*fmt -'0');
452
                    else
453
                        fmt2 = fmt2 * 10 + (*fmt -'0');
454
                    break;
455
                case '*':
456
                    if (flag_point == 0)
457
                        fmt1 = va_arg(argp, int);
458
                    else
459
                        fmt2 = va_arg(argp, int);
460
                    break;
461
                case '.':
462
                    flags |= flag_point;
463
                    break;
464
                case 'l':   case 'L':   case '+':   // valid chars - skip
465
                case '-':   case ' ':   case '#':
466
                    break;
467
                default: // must be error
468
                    strncpy(dest, "print format error - %width.precision", maxlen);
469
                    return -1;  // format char not found - user error
470
                }
471
                fmt++;
472
            }
473
        }
647 andrew_pro 474
 
6424 siemargl 475
        // do real work - format arguments values
647 andrew_pro 476
 
6424 siemargl 477
        length = 0;
478
        switch(*fmtc)
479
        {
480
        case 'n':
481
            point_to_n = va_arg(argp, int*);
482
            break;
483
        case 'c':
484
            if (pos + 1 <= maxlen)
485
            {
486
                buf[0] = (char)va_arg(argp, int);
487
                length = 1;
488
            }
489
            break;
490
        case 's':   // special case - without buf
491
            str = va_arg(argp, char*);
492
            length = strlen(str);
493
            if ((flags & flag_point) && (length > fmt2)) length = fmt2;  // cut by precision
494
            if (pos + length > maxlen) length = maxlen - pos;
495
            memcpy(s, str ,length);
496
            s += length;
497
            pos += length;
498
            break;
499
        case 'd':   case 'i':   case 'u':   case 'U':
500
            if (flag_long == 0) intdigit = va_arg(argp, int); else
501
            if (flag_long == 1) intdigit = va_arg(argp, long); else
502
            if (flag_long == 2) intdigit = va_arg(argp, long long);
503
            length = formatted_long_to_string(intdigit, fmt1, fmt2, buf, flags);
504
            break;
505
        case 'o':
506
            if (flag_long == 0) intdigit = va_arg(argp, int); else
507
            if (flag_long == 1) intdigit = va_arg(argp, long); else
508
            if (flag_long == 2) intdigit = va_arg(argp, long long);
509
            length = formatted_octa_to_string(intdigit, fmt1, buf, flags);
510
            break;
511
        case 'p':   case 'P':   case 'x':   case 'X':
512
            if (flag_long == 0) intdigit = va_arg(argp, int); else
513
            if (flag_long == 1) intdigit = va_arg(argp, long); else
514
            if (flag_long == 2) intdigit = va_arg(argp, long long);
515
            length=formatted_hex_to_string(intdigit, fmt1, buf, flags);
516
            break;
517
        case 'a':   case 'A':   case 'f':   case 'F':
518
            if (flag_long <= 1) doubledigit = va_arg(argp, double); else
519
            if (flag_long == 2) doubledigit = va_arg(argp, long double);
520
            length = formatted_double_to_string(doubledigit, fmt1, fmt2, buf, flags);
521
            break;
522
        case 'e':   case 'E':
523
            if (flag_long <= 1) doubledigit = va_arg(argp, double); else
524
            if (flag_long == 2) doubledigit = va_arg(argp, long double);
525
            length = formatted_double_to_string_scientific(doubledigit, fmt1, fmt2, buf, flags);
526
            break;
527
        case 'g':   case 'G':
528
 //prec special case, this is just workaround
529
            if (flag_long <= 1) doubledigit = va_arg(argp, double); else
530
            if (flag_long == 2) doubledigit = va_arg(argp, long double);
531
            length = formatted_double_to_string(doubledigit, fmt1, fmt2, buf, flags);
532
            i = formatted_double_to_string_scientific(doubledigit, fmt1, fmt2, buf + sizeof buf / 2, flags);
533
            if(length > i)
534
            {
535
                memcpy(buf, buf + sizeof buf / 2, i);
536
                length = i;
537
            }
538
            break;
539
        }
540
        if (*fmtc != 's' && length > 0) // skip multiple string copying
541
        {
542
            if (pos + length > maxlen)  length = maxlen - pos;
543
            memcpy(s, buf, length);
544
            s += length;
545
            pos += length;
546
        }
547
        fmt = fmtc + 1;
548
    }
647 andrew_pro 549
 
6424 siemargl 550
    if (point_to_n) *point_to_n = pos;
551
    return(pos);
647 andrew_pro 552
}