Subversion Repositories Kolibri OS

Rev

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