Graphviz 12.0.1~dev.20240716.0800
Loading...
Searching...
No Matches
sfvscanf.c
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (c) 2011 AT&T Intellectual Property
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11#include <assert.h>
12#include <cgraph/gv_ctype.h>
13#include <inttypes.h>
14#include <limits.h>
15#include <sfio/sfhdr.h>
16#include <stdbool.h>
17#include <stddef.h>
18#include <stdio.h>
19
20/* The main engine for reading formatted data
21**
22** Written by Kiem-Phong Vo.
23*/
24
25#define MAXWIDTH INT_MAX // max amount to scan
26
31static const unsigned char *setclass(const unsigned char *form, bool *accept) {
32 int fmt, c;
33 bool yes;
34
35 if ((fmt = *form++) == '^') { /* we want the complement of this set */
36 yes = false;
37 fmt = *form++;
38 } else
39 yes = true;
40
41 for (c = 0; c <= UCHAR_MAX; ++c)
42 accept[c] = !yes;
43
44 if (fmt == ']' || fmt == '-') { /* special first char */
45 accept[fmt] = yes;
46 fmt = *form++;
47 }
48
49 for (; fmt != ']'; fmt = *form++) { /* done */
50 if (!fmt)
51 return (form - 1);
52
53 /* interval */
54 if (fmt != '-' || form[0] == ']' || form[-2] > form[0])
55 accept[fmt] = yes;
56 else
57 for (c = form[-2] + 1; c < form[0]; ++c)
58 accept[c] = yes;
59 }
60
61 return form;
62}
63
68int sfvscanf(FILE *f, Sffmt_t *ft) {
69 int inp, shift, base, width;
70 ssize_t size;
71 int fmt, flags, dot, n_assign, v, n, n_input;
72 char *sp;
73 char accept[SF_MAXDIGITS];
74
75 Argv_t argv;
76
77 int argp, argn;
78
79 void *value; /* location to assign scanned value */
80 const char *t_str;
81 ssize_t n_str;
82
83#define SFGETC(f,c) (((c) = getc(f)) < 0 ? c : (++n_input, c))
84#define SFUNGETC(f,c) do { \
85 ungetc((c), (f)); \
86 --n_input; \
87} while (0)
88
89 assert(f != NULL);
90
91 n_assign = n_input = 0;
92
93 inp = -1;
94
95 const char *form;
96 argv.ft = ft;
97
98 form = argv.ft->form;
99 argn = -1;
100
101 assert(ft != NULL && ft->extf != NULL);
102
103 loop_fmt:
104 while ((fmt = *form++)) {
105 if (fmt != '%') {
106 if (gv_isspace(fmt)) {
107 if (fmt != '\n')
108 fmt = -1;
109 for (;;) {
110 if (SFGETC(f, inp) < 0 || inp == fmt)
111 goto loop_fmt;
112 else if (!gv_isspace(inp)) {
113 SFUNGETC(f, inp);
114 goto loop_fmt;
115 }
116 }
117 } else {
118 match_1:
119 if (SFGETC(f, inp) != fmt) {
120 if (inp >= 0)
121 SFUNGETC(f, inp);
122 goto done;
123 }
124 }
125 continue;
126 }
127
128 if (*form == '%') {
129 form += 1;
130 goto match_1;
131 }
132
133 if (*form == '\0')
134 goto done;
135
136 if (*form == '*') {
138 form += 1;
139 } else
140 flags = 0;
141
142 /* matching some pattern */
143 base = 10;
144 size = -1;
145 width = dot = 0;
146 t_str = NULL;
147 n_str = 0;
148 value = NULL;
149 argp = -1;
150
151 loop_flags: /* LOOP FOR FLAGS, WIDTH, BASE, TYPE */
152 switch ((fmt = *form++)) {
153 case LEFTP: /* get the type which is enclosed in balanced () */
154 t_str = form;
155 for (v = 1;;) {
156 switch (*form++) {
157 case 0: /* not balanceable, retract */
158 form = t_str;
159 t_str = NULL;
160 n_str = 0;
161 goto loop_flags;
162 case LEFTP: /* increasing nested level */
163 v += 1;
164 continue;
165 case RIGHTP: /* decreasing nested level */
166 if ((v -= 1) != 0)
167 continue;
168 if (*t_str != '*')
169 n_str = (form - 1) - t_str;
170 else {
171 t_str = _Sffmtintf(t_str + 1, &n);
172 FP_SET(-1, argn);
173
174 FMTSET(ft, form, LEFTP, 0, 0, 0, 0, 0, NULL, 0);
175 n = ft->extf(&argv, ft);
176 if (n < 0)
177 goto done;
178 assert(ft->flags & SFFMT_VALUE);
179 if ((t_str = argv.s) && (n_str = (int)ft->size) < 0)
180 n_str = (ssize_t)strlen(t_str);
181 }
182 goto loop_flags;
183 default:
184 // skip over
185 break;
186 }
187 }
188
189 case '#': /* alternative format */
191 goto loop_flags;
192
193 case '.': /* width & base */
194 dot += 1;
195 if (gv_isdigit(*form)) {
196 fmt = *form++;
197 goto dot_size;
198 } else if (*form == '*') {
199 form = _Sffmtintf(form + 1, &n);
200 n = FP_SET(-1, argn);
201
202 FMTSET(ft, form, '.', dot, 0, 0, 0, 0, NULL, 0);
203 if (ft->extf(&argv, ft) < 0)
204 goto done;
205 assert(ft->flags & SFFMT_VALUE);
206 v = argv.i;
207 if (v < 0)
208 v = 0;
209 goto dot_set;
210 } else
211 goto loop_flags;
212
213 case '0':
214 case '1':
215 case '2':
216 case '3':
217 case '4':
218 case '5':
219 case '6':
220 case '7':
221 case '8':
222 case '9':
223 dot_size:
224 for (v = fmt - '0'; gv_isdigit(*form); ++form)
225 v = v * 10 + (*form - '0');
226
227 dot_set:
228 if (dot == 0 || dot == 1)
229 width = v;
230 else if (dot == 2)
231 base = v;
232 goto loop_flags;
233
234 case 'I': /* object size */
235 size = 0;
236 flags = (flags & ~SFFMT_TYPES) | SFFMT_IFLAG;
237 if (gv_isdigit(*form)) {
238 for (n = *form; gv_isdigit(n); n = *++form)
239 size = size * 10 + (n - '0');
240 } else if (*form == '*') {
241 form = _Sffmtintf(form + 1, &n);
242 n = FP_SET(-1, argn);
243
244 FMTSET(ft, form, 'I', sizeof(int), 0, 0, 0, 0, NULL, 0);
245 if (ft->extf(&argv, ft) < 0)
246 goto done;
247 assert(ft->flags & SFFMT_VALUE);
248 size = argv.i;
249 }
250 goto loop_flags;
251
252 case 'l':
253 size = -1;
254 flags &= ~SFFMT_TYPES;
255 if (*form == 'l') {
256 form += 1;
258 } else
259 flags |= SFFMT_LONG;
260 goto loop_flags;
261 case 'h':
262 size = -1;
263 flags &= ~SFFMT_TYPES;
264 if (*form == 'h') {
265 form += 1;
267 } else
269 goto loop_flags;
270 case 'L':
271 size = -1;
272 flags = (flags & ~SFFMT_TYPES) | SFFMT_LDOUBLE;
273 goto loop_flags;
274 case 'j':
275 size = -1;
276 flags = (flags & ~SFFMT_TYPES) | SFFMT_JFLAG;
277 goto loop_flags;
278 case 'z':
279 size = -1;
280 flags = (flags & ~SFFMT_TYPES) | SFFMT_ZFLAG;
281 goto loop_flags;
282 case 't':
283 size = -1;
284 flags = (flags & ~SFFMT_TYPES) | SFFMT_TFLAG;
285 goto loop_flags;
286 default: // continue with logic below
287 break;
288 }
289
290 /* set object size */
291 if (flags & (SFFMT_TYPES & ~SFFMT_IFLAG)) {
292 if ((_Sftype[fmt] & (SFFMT_INT | SFFMT_UINT)) || fmt == 'n') {
293 size = (flags & SFFMT_LLONG) ? (ssize_t)sizeof(long long) :
294 (flags & SFFMT_LONG) ? (ssize_t)sizeof(long) :
295 (flags & SFFMT_SHORT) ? (ssize_t)sizeof(short) :
296 (flags & SFFMT_SSHORT) ? (ssize_t)sizeof(char) :
297 (flags & SFFMT_JFLAG) ? (ssize_t)sizeof(long long) :
298 (flags & SFFMT_TFLAG) ? (ssize_t)sizeof(ptrdiff_t) :
299 (flags & SFFMT_ZFLAG) ? (ssize_t)sizeof(size_t) : -1;
300 } else if (_Sftype[fmt] & SFFMT_FLOAT) {
301 size = (flags & SFFMT_LDOUBLE) ? (ssize_t)sizeof(long double) :
302 (flags & (SFFMT_LONG | SFFMT_LLONG)) ? (ssize_t)sizeof(double) : -1;
303 }
304 }
305
306 argp = FP_SET(argp, argn);
307 FMTSET(ft, form, fmt, size, flags, width, 0, base, t_str, n_str);
308 v = ft->extf(&argv, ft);
309
310 if (v < 0)
311 goto done;
312 else if (v == 0) { // extf did not use input stream
313 FMTGET(ft, form, fmt, size, flags, width, n, base);
314 if ((ft->flags & SFFMT_VALUE) && !(ft->flags & SFFMT_SKIP))
315 value = argv.vp;
316 } else { // v > 0: number of input bytes consumed
317 n_input += v;
318 if (!(ft->flags & SFFMT_SKIP))
319 n_assign += 1;
320 continue;
321 }
322
323 if (_Sftype[fmt] == 0) /* unknown pattern */
324 continue;
325
326 assert(!(!value && !(flags & SFFMT_SKIP)));
327
328 if (fmt == 'n') { /* return length of consumed input */
329 if (sizeof(long) > sizeof(int) && FMTCMP(size, long, long long))
330 *((long *) value) = (long)n_input;
331 else if (sizeof(short) < sizeof(int) && FMTCMP(size, short, long long))
332 *((short *) value) = (short)n_input;
333 else if (size == sizeof(char))
334 *((char *) value) = (char)n_input;
335 else
336 *((int *) value) = (int)n_input;
337 continue;
338 }
339
340 /* if get here, start scanning input */
341 if (width == 0)
342 width = fmt == 'c' ? 1 : MAXWIDTH;
343
344 /* define the first input character */
345 if (fmt == 'c' || fmt == '[')
346 SFGETC(f, inp);
347 else {
348 do {
349 SFGETC(f, inp);
350 }
351 while (gv_isspace(inp)) // skip starting blanks
352 ;
353 }
354 if (inp < 0)
355 goto done;
356
357 if (_Sftype[fmt] == SFFMT_FLOAT) {
358 char *val;
359
360 val = accept;
361 if (width >= 0 && (size_t)width >= SF_MAXDIGITS)
362 width = SF_MAXDIGITS - 1;
363 int exponent = 0;
364 bool seen_dot = false;
365 do {
366 if (gv_isdigit(inp))
367 *val++ = inp;
368 else if (inp == '.') { /* too many dots */
369 if (seen_dot)
370 break;
371 seen_dot = true;
372 *val++ = '.';
373 } else if (inp == 'e' || inp == 'E') { /* too many e,E */
374 if (exponent++ > 0)
375 break;
376 *val++ = inp;
377 if (--width <= 0 || SFGETC(f, inp) < 0 ||
378 (inp != '-' && inp != '+' && !gv_isdigit(inp)))
379 break;
380 *val++ = inp;
381 } else if (inp == '-' || inp == '+') { /* too many signs */
382 if (val > accept)
383 break;
384 *val++ = inp;
385 } else
386 break;
387
388 } while (--width > 0 && SFGETC(f, inp) >= 0);
389
390 if (value) {
391 *val = '\0';
392 argv.d = strtod(accept, NULL);
393
394 n_assign += 1;
395 if (FMTCMP(size, double, long double))
396 *((double *) value) = argv.d;
397 else
398 *((float *) value) = (float) argv.d;
399 }
400 } else if (_Sftype[fmt] == SFFMT_UINT || fmt == 'p') {
401 if (inp == '-') {
402 SFUNGETC(f, inp);
403 goto done;
404 } else
405 goto int_cvt;
406 } else if (_Sftype[fmt] == SFFMT_INT) {
407 int_cvt:
408 if (inp == '-' || inp == '+') {
409 if (inp == '-')
411 while (--width > 0 && SFGETC(f, inp) >= 0)
412 if (!gv_isspace(inp))
413 break;
414 }
415 if (inp < 0)
416 goto done;
417
418 if (fmt == 'o')
419 base = 8;
420 else if (fmt == 'x' || fmt == 'p')
421 base = 16;
422 else if (fmt == 'i' && inp == '0') { /* self-described data */
423 base = 8;
424 if (width > 1) { /* peek to see if it's a base-16 */
425 if (SFGETC(f, inp) >= 0) {
426 if (inp == 'x' || inp == 'X')
427 base = 16;
428 SFUNGETC(f, inp);
429 }
430 inp = '0';
431 }
432 }
433
434 /* now convert */
435 argv.lu = 0;
436 if (base == 16) {
437 sp = (char *) _Sfcv36;
438 shift = 4;
439 if (sp[inp] >= 16) {
440 SFUNGETC(f, inp);
441 goto done;
442 }
443 if (inp == '0' && --width > 0) { /* skip leading 0x or 0X */
444 if (SFGETC(f, inp) >= 0 &&
445 (inp == 'x' || inp == 'X') && --width > 0)
446 SFGETC(f, inp);
447 }
448 if (inp >= 0 && sp[inp] < 16)
449 goto base_shift;
450 } else if (base == 10) { /* fast base 10 conversion */
451 if (inp < '0' || inp > '9') {
452 SFUNGETC(f, inp);
453 goto done;
454 }
455
456 do {
457 argv.lu =
458 (argv.lu << 3) + (argv.lu << 1) + (inp - '0');
459 } while (--width > 0 && SFGETC(f, inp) >= '0'
460 && inp <= '9');
461
462 if (fmt == 'i' && inp == '#' && !(flags & SFFMT_ALTER)) {
463 base = (int) argv.lu;
464 if (base < 2 || base > SF_RADIX)
465 goto done;
466 argv.lu = 0;
467 sp = base <= 36 ? (char *) _Sfcv36 : (char *) _Sfcv64;
468 if (--width > 0 &&
469 SFGETC(f, inp) >= 0 && sp[inp] < base)
470 goto base_conv;
471 }
472 } else { /* other bases */
473 sp = base <= 36 ? (char *) _Sfcv36 : (char *) _Sfcv64;
474 if (base < 2 || base > SF_RADIX || sp[inp] >= base) {
475 SFUNGETC(f, inp);
476 goto done;
477 }
478
479 base_conv: /* check for power of 2 conversions */
480 if ((base & ~(base - 1)) == base) {
481 if (base < 8)
482 shift = base < 4 ? 1 : 2;
483 else if (base < 32)
484 shift = base < 16 ? 3 : 4;
485 else
486 shift = base < 64 ? 5 : 6;
487
488 base_shift:do {
489 argv.lu = (argv.lu << shift) + sp[inp];
490 } while (--width > 0 &&
491 SFGETC(f, inp) >= 0 && sp[inp] < base);
492 } else {
493 do {
494 argv.lu = (argv.lu * base) + sp[inp];
495 } while (--width > 0 &&
496 SFGETC(f, inp) >= 0 && sp[inp] < base);
497 }
498 }
499
500 if (flags & SFFMT_MINUS)
501 argv.ll = -argv.ll;
502
503 if (value) {
504 n_assign += 1;
505
506 if (fmt == 'p') {
507 *((void **) value) = (void *)(uintptr_t)argv.lu;
508 } else if (sizeof(long) > sizeof(int) && FMTCMP(size, long, long long)) {
509 if (fmt == 'd' || fmt == 'i')
510 *((long *) value) = (long) argv.ll;
511 else
512 *((ulong *) value) = (ulong) argv.lu;
513 } else if (sizeof(short) < sizeof(int) && FMTCMP(size, short, long long)) {
514 if (fmt == 'd' || fmt == 'i')
515 *((short *) value) = (short) argv.ll;
516 else
517 *((ushort *) value) = (ushort) argv.lu;
518 } else if (size == sizeof(char)) {
519 if (fmt == 'd' || fmt == 'i')
520 *((char *) value) = (char) argv.ll;
521 else
522 *((uchar *) value) = (uchar) argv.lu;
523 } else {
524 if (fmt == 'd' || fmt == 'i')
525 *((int *) value) = (int) argv.ll;
526 else
527 *((unsigned*)value) = (unsigned)argv.lu;
528 }
529 }
530 } else if (fmt == 's' || fmt == 'c' || fmt == '[') {
531 if (size < 0)
532 size = MAXWIDTH;
533 if (value) {
534 argv.s = (char *) value;
535 if (fmt != 'c')
536 size -= 1;
537 } else
538 size = 0;
539
540 n = 0;
541 if (fmt == 's') {
542 do {
543 if (gv_isspace(inp))
544 break;
545 if ((n += 1) <= size)
546 *argv.s++ = inp;
547 } while (--width > 0 && SFGETC(f, inp) >= 0);
548 } else if (fmt == 'c') {
549 do {
550 if ((n += 1) <= size)
551 *argv.s++ = inp;
552 } while (--width > 0 && SFGETC(f, inp) >= 0);
553 } else { /* if(fmt == '[') */
554 bool accepted[UCHAR_MAX + 1];
555 form = (const char*)setclass((const unsigned char*)form, accepted);
556 do {
557 if (!accepted[inp]) {
558 if (n > 0 || (flags & SFFMT_ALTER))
559 break;
560 else {
561 SFUNGETC(f, inp);
562 goto done;
563 }
564 }
565 if ((n += 1) <= size)
566 *argv.s++ = inp;
567 } while (--width > 0 && SFGETC(f, inp) >= 0);
568 }
569
570 if (value && (n > 0 || fmt == '[')) {
571 n_assign += 1;
572 if (fmt != 'c' && size >= 0)
573 *argv.s = '\0';
574 }
575 }
576
577 if (width > 0 && inp >= 0)
578 SFUNGETC(f, inp);
579 }
580
581 done:
582
583 if (n_assign == 0 && inp < 0)
584 n_assign = -1;
585
586 return n_assign;
587}
static int flags
Definition gc.c:61
#define dot(v, w)
Definition geom.c:228
node NULL
Definition grammar.y:149
replacements for ctype.h functions
static bool gv_isdigit(int c)
Definition gv_ctype.h:41
static bool gv_isspace(int c)
Definition gv_ctype.h:55
#define RIGHTP
Definition sfhdr.h:86
#define FMTCMP(sz, type, maxtype)
Definition sfhdr.h:99
#define LEFTP
Definition sfhdr.h:85
#define FMTGET(ft, frm, fv, sz, flgs, wid, pr, bs)
Definition sfhdr.h:95
#define _Sfcv36
Definition sfhdr.h:134
#define FP_SET(fp, fn)
Definition sfhdr.h:66
#define SF_MAXDIGITS
Definition sfhdr.h:125
#define _Sfcv64
Definition sfhdr.h:135
#define _Sftype
Definition sfhdr.h:136
#define ushort
Definition sfhdr.h:47
#define ulong
Definition sfhdr.h:44
#define _Sffmtintf
Definition sfhdr.h:133
#define SF_RADIX
Definition sfhdr.h:119
#define SFFMT_TYPES
Definition sfhdr.h:107
#define uchar
Definition sfhdr.h:41
#define FMTSET(ft, frm, fv, sz, flgs, wid, pr, bs, ts, ns)
Definition sfhdr.h:89
#define SFFMT_MINUS
Definition sfhdr.h:105
#define SFFMT_INT
Definition sfhdr.h:112
#define SFFMT_UINT
Definition sfhdr.h:113
#define SFFMT_FLOAT
Definition sfhdr.h:114
#define SFFMT_ZFLAG
Definition sfio.h:52
#define SFFMT_ALTER
Definition sfio.h:58
#define SFFMT_LONG
Definition sfio.h:62
#define SFFMT_JFLAG
Definition sfio.h:68
#define SFFMT_SSHORT
Definition sfio.h:50
#define SFFMT_SHORT
Definition sfio.h:61
#define SFFMT_IFLAG
Definition sfio.h:67
#define SFFMT_LDOUBLE
Definition sfio.h:64
#define SFFMT_VALUE
Definition sfio.h:65
#define SFFMT_TFLAG
Definition sfio.h:51
#define SFFMT_SKIP
Definition sfio.h:60
#define SFFMT_LLONG
Definition sfio.h:63
static const unsigned char * setclass(const unsigned char *form, bool *accept)
Definition sfvscanf.c:31
#define SFUNGETC(f, c)
#define MAXWIDTH
Definition sfvscanf.c:25
int sfvscanf(FILE *f, Sffmt_t *ft)
Definition sfvscanf.c:68
#define SFGETC(f, c)
char * form
Definition sfio.h:37
int flags
Definition sfio.h:41
ssize_t size
Definition sfio.h:40
Sffmtext_f extf
Definition sfio.h:35
Definition sfhdr.h:68
unsigned long long lu
Definition sfhdr.h:76
Sffmt_t * ft
Definition sfhdr.h:82
void * vp
Definition sfhdr.h:81
long long ll
Definition sfhdr.h:75
double d
Definition sfhdr.h:78
char * s
Definition sfhdr.h:80
int i
Definition sfhdr.h:69