Subversion Repositories Kolibri OS

Rev

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

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