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