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