Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
8687 turbocat 1
/*
2
        function for format read from any source
3
 
4
Siemargl formats as http://www.cplusplus.com/reference/cstdio/scanf/, no wchar though
5
http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap05.html  is used too
6
 
7
todo:
8
[characters], [^characters]
9
-%n nothing scanned, filled only if good result
10
-%d, i, u, o, x, p read similar - detect base by prefix 0 or 0x
11
-%a
12
-can overflow unsigned as signed
13
-radix point always '.', no LOCALEs
14
*/
15
 
16
#include 
17
#include 
18
#include 
19
#include 
20
#include "format_scan.h"
21
 
22
static int __try_parse_real(long double *real, int ch, const void *src, void *save, virtual_getc vgetc, virtual_ungetc vungetc)
23
// returns 1 if OK, -1 == EOF, -2 parse broken
24
{
25
    int sign = 1, have_digits = 0;
26
    long long div;
27
 
28
    if (ch == '+')
29
    {
30
        ch = vgetc(save, src);
31
        if (ch == EOF) return EOF;
32
    } else
33
    if (ch == '-')
34
    {
35
        sign = -1;
36
        ch = vgetc(save, src);
37
        if (ch == EOF) return EOF;
38
    };
39
    *real = 0.0;
40
    for (;;) // mantissa before point
41
    {
42
        // test ch is valid
43
        if (isdigit(ch))
44
        {
45
            *real = *real * 10 + ch - '0';
46
            have_digits++;
47
            ch = vgetc(save, src);
48
            if (ch == EOF || isspace(ch)) break; // ok, just finish num
49
        } else
50
        if (ch == '.' || ch == 'E' || ch == 'e')
51
        {
52
            break; // ok
53
        }
54
        else
55
        {
56
            vungetc(save, ch, src);
57
            break;
58
        }
59
    }
60
    if (ch != '.' && ch != 'E' && ch != 'e') // ok, just integer part
61
    {
62
        *real *= sign;
63
        if (have_digits)
64
            return 1;
65
        else
66
            return -2;
67
    }
68
 
69
    if(ch == '.')
70
    {
71
        ch = vgetc(save, src);
72
        div = 10; // use as divisor
73
        for (;;) // mantissa after point
74
        {
75
            // test ch is valid
76
            if (isdigit(ch))
77
            {
78
                *real += (double)(ch - '0') / div;
79
                div *= 10;
80
                have_digits++;
81
                ch = vgetc(save, src);
82
                if (ch == EOF || isspace(ch)) break; // ok, just finish num
83
            } else
84
            if (ch == 'E' || ch == 'e')
85
            {
86
                break; // ok
87
            }
88
            else
89
            {
90
                vungetc(save, ch, src);
91
                break;
92
            }
93
        }
94
        if (ch != 'E' && ch != 'e')  // ok, real as XX.YY
95
        {
96
            *real *= sign;
97
            if (have_digits)
98
                return 1;
99
            else
100
                return -2;
101
        }
102
    }
103
 
104
    ch = vgetc(save, src);
105
    *real *= sign;
106
    // exponent
107
    sign = 1;
108
    if (ch == '+')
109
    {
110
        ch = vgetc(save, src);
111
        if (ch == EOF) return EOF;
112
    } else
113
    if (ch == '-')
114
    {
115
        sign = -1;
116
        ch = vgetc(save, src);
117
        if (ch == EOF) return EOF;
118
    };
119
    div = 0;
120
    for (;;)
121
    {
122
        // test ch is valid
123
        if (isdigit(ch))
124
        {
125
            div = div * 10 + ch - '0';
126
            ch = vgetc(save, src);
127
            if (ch == EOF || isspace(ch)) break; // ok, just finish num
128
        }
129
        else
130
        {
131
            vungetc(save, ch, src);
132
            break;
133
        }
134
    }
135
    div *= sign;
136
    *real *= pow(10,div);
137
 
138
    return 1;
139
}
140
 
141
static int __try_parse_int(long long *digit, int ch, const void *src, void *save, virtual_getc vgetc, virtual_ungetc vungetc)
142
{
143
    int sign = 1, base = 10, have_digits = 0;
144
 
145
    if (ch == '+')
146
    {
147
        ch = vgetc(save, src);
148
        if (ch == EOF) return EOF;
149
    } else
150
    if (ch == '-')
151
    {
152
        sign = -1;
153
        ch = vgetc(save, src);
154
        if (ch == EOF) return EOF;
155
    };
156
 
157
    if (ch == '0')  // octal or hex, read next
158
    {
159
        base = 8;
160
        ch = vgetc(save, src);
161
        if (ch == EOF || isspace(ch))
162
            have_digits++;
163
        else
164
        if (ch == 'x' || ch == 'X')
165
        {
166
            base = 16;
167
            ch = vgetc(save, src);
168
            if (ch == EOF) return EOF;
169
        }
170
    }
171
    *digit = 0;
172
    for (;;)
173
    {
174
        // test ch is valid
175
        if ((isdigit(ch) && base == 10) ||
176
            (isdigit(ch) && base == 8 && ch < '8') ||
177
            (isxdigit(ch) && base == 16))
178
        {
179
            if (base == 16)
180
            {
181
                if (ch <= '9') ch-= '0';
182
                else
183
                if (ch <= 'F') ch = 10 + ch - 'A';
184
                else
185
                    ch = 10 + ch - 'a';
186
            }
187
            else
188
                ch -= '0';
189
            *digit = *digit * base + ch;
190
            have_digits++;
191
            ch = vgetc(save, src);
192
            if (ch == EOF || isspace(ch)) break; // ok, just finish num
193
        }
194
        else if (ch == EOF || isspace(ch))
195
            break;
196
        else
197
        {
198
            vungetc(save, ch, src);
199
            break;
200
        }
201
    }
202
    *digit *= sign;
203
    if (have_digits)
204
        return 1;
205
    else
206
        return -2;
207
}
208
 
209
 
210
 
211
int _format_scan(const void *src, const char *fmt, va_list argp, virtual_getc vgetc, virtual_ungetc vungetc)
212
{
213
    int         i;
214
    int         length;
215
    int         fmt1, fmt2;  // width, precision
216
    size_t      pos, posc;
217
    const char  *fmtc;  // first point to %, fmtc points to specifier
218
    int         ch;
219
    int         format_flag;
220
    int         flag_long;        // 2 = long double or long long int or wchar
221
    int         *point_to_n = NULL, nread = 0;
222
    int         flags;  // parsed flags
223
    int         save = 0;
224
    char        *arg_str;
225
    int	        *arg_int;
226
    long        *arg_long;
227
    long long	*arg_longlong;
228
    float       *arg_float;
229
    double      *arg_double;
230
    long double *arg_longdouble;
231
    long long  digit;
232
    long double real;
233
 
234
 
235
    pos = 0;
236
    while(*fmt)
237
    {
238
        while (*fmt && isspace(*fmt)) fmt++; // skip paces in format str
239
 
240
        if (*fmt != '%')  // usual char
241
        {
242
            ch = vgetc(&save, src);
243
            if (ch != *fmt++) // char not match format
244
            {
245
                vungetc(&save, ch, src);
246
                break;
247
            }
248
            pos++;
249
            continue;
250
        }
251
 
252
        if (*(fmt + 1) == '%') // %%
253
        {
254
            ch = vgetc(&save, src);
255
            if (ch != '%') // char not match format
256
            {
257
                vungetc(&save, ch, src);
258
                break;
259
            }
260
            pos++;
261
            fmt += 2;
262
            continue;
263
        }
264
        //checking to containg format in the string
265
        fmtc = fmt;
266
        posc = pos;
267
 
268
        flags = 0;
269
        format_flag = 0;
270
        flag_long = 0;  // 2 = long double or long long int or wchar
271
 
272
        while(*fmtc != '\0' && !format_flag)    // searching end of format
273
        {
274
                fmtc++; posc++;
275
                switch( *fmtc )
276
                {
277
                case 'a':
278
                    format_flag = 1;
279
                    flags |= flag_unsigned;
280
                    break;
281
                case 'c':   case 'd':   case 'i':   case 'e':   case 'f':   case 'g':   case 's':   case 'n':
282
                    format_flag = 1;
283
                    break;
284
                case 'l':
285
                    flag_long  = flag_long ? 2 : 1;  // ll.eq.L
286
                    break;
287
                case 'L':
288
                    flag_long = 2;
289
                    break;
290
                case 'o':   case 'u':   case 'x':   case 'p':
291
                    format_flag = 1;
292
                    flags |= flag_unsigned;
293
                    break;
294
                case '*':   case '.':  // just skip
295
                    break;
296
                default:
297
                    if(isdigit(*fmtc))  break;
298
                    goto exit_me;  // non format char found - user error
299
                }
300
        }
301
 
302
        if (format_flag == 0)
303
        {
304
            goto exit_me;  // format char not found - user error
305
        }
306
 
307
        fmt1 = 0;
308
        fmt2 = 0;
309
        if (posc - pos > 1)  // try to read width, precision
310
        {
311
            fmt++;
312
            for(i = pos + 1; i < posc; i++)
313
            {
314
                switch(*fmt)
315
                {
316
                case '0':
317
                    if(fmt1 == 0 && (flags & flag_point) == 0)    flags |= flag_lead_zeros;
318
                case '1':   case '2':   case '3':   case '4':
319
                case '5':   case '6':   case '7':   case '8':   case '9':
320
                    if ((flags & flag_point) == 0)
321
                        fmt1 = fmt1 * 10 + (*fmt -'0');
322
                    else
323
                        fmt2 = fmt2 * 10 + (*fmt -'0');
324
                    break;
325
                case '*': // ignoring
326
                    break;
327
                case '.':
328
                    flags |= flag_point;
329
                    break;
330
                case 'l':   case 'L':      // valid chars - skip
331
                    break;
332
                default: // must be error
333
                    goto exit_me;  // format char not found - user error
334
                }
335
                fmt++;
336
            }
337
        }
338
 
339
        // do real work - format arguments values
340
        // skip input spaces
341
        do {
342
            ch = vgetc(&save, src);
343
            if (ch == EOF) goto exit_me;
344
        } while (isspace(ch));
345
 
346
        switch(*fmtc)
347
        {
348
        case 'n':
349
            point_to_n = va_arg(argp, int*);
350
            vungetc(&save, ch, src);
351
            break;
352
        case 'c':  // read width chars, ever spaces
353
            arg_str = va_arg(argp, char*);
354
            if (fmt1 == 0) length = 1;
355
            else length = fmt1;
356
            for (i = 0; i < length;)
357
            {
358
                *arg_str++ = ch; i++;
359
                ch = vgetc(&save, src);
360
                if (ch == EOF) break;
361
            }
362
            if (i < length) goto exit_me; // not enough chars
363
            break;
364
        case 's':
365
            arg_str = va_arg(argp, char*);
366
            if (fmt1 == 0) length = STDIO_MAX_MEM;   // max string scan 4096
367
            else length = fmt1;
368
            for (i = 0; i < length; i++)
369
            {
370
                *arg_str++ = ch;
371
                ch = vgetc(&save, src);
372
                if (ch == EOF || isspace(ch)) break; // ok, just finish string
373
            }
374
            *arg_str++ = '\0';
375
            break;
376
        case 'd':   case 'i':   case 'u':
377
        case 'o':   case 'p':   case 'x':
378
            i = __try_parse_int(&digit, ch, src, &save, vgetc, vungetc);
379
            if (i < 0) goto exit_me;
380
 
381
            if (flag_long == 0) { arg_int = va_arg(argp, int*); *arg_int = (int)digit; } else
382
            if (flag_long == 1) { arg_long = va_arg(argp, long*); *arg_long = (long)digit; } else
383
            if (flag_long == 2) { arg_longlong = va_arg(argp, long long*); *arg_longlong = digit; }
384
            break;
385
        case 'a':   case 'A':   case 'f':   case 'F':
386
        case 'e':   case 'E':
387
        case 'g':   case 'G':
388
            i = __try_parse_real(&real, ch, src, &save, vgetc, vungetc);
389
            if (i < 0) goto exit_me;
390
 
391
            if (flag_long == 0) { arg_float = va_arg(argp, float*); *arg_float = (float)real; } else
392
            if (flag_long == 1) { arg_double = va_arg(argp, double*); *arg_double = (double)real; } else
393
            if (flag_long == 2) { arg_longdouble = va_arg(argp, long double*); *arg_longdouble = real; }
394
            break;
395
        }
396
 
397
        fmt = fmtc + 1;
398
        nread++;
399
    }
400
exit_me:
401
    if (point_to_n) *point_to_n = nread;
402
    return  nread;
403
}