1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright (c) 1996, by Sun Microsystems, Inc.
24 * All rights reserved.
25 */
26
27 /*
28 * System V.2 Emulation Stdio Library -- vfscanf
29 *
30 * Copyright 1985, 1992 by Mortice Kern Systems Inc. All rights reserved.
31 *
32 */
33
34 #ifdef M_RCSID
35 #ifndef lint
36 static char rcsID[] = "$Id: vfscanf.c 1.27 1995/09/20 19:07:52 ant Exp $";
37 #endif
38 #endif
39
40 #include <mks.h>
41 #include <ctype.h>
42 #include <limits.h>
43 #include <stdarg.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #ifdef __FLOAT__
47 #include <math.h>
48 #endif
49
50 #define CONVTYPE 1
51 #define STAR 2
52 #define PERCENT 3
53 #define NUMBER 4
54 #define MODCONVL 5
55 #define NSCAN 6
56 #define BRACKET 7
57 #define MODCONVH 8
58
59 #define BASE16 16
60 #define BASE10 10
61 #define BASE8 8
62 #define NOBASE 0
63 #define SIGNED 1
64 #define UNSIGNED 0
65
66 #define CBUFSIZ 100 /* size of character buffer for input conversion */
67
68 struct lexlist {
69 char name;
70 char type;
71 };
72 static struct lexlist *lexp;
73 static struct lexlist lexlist[] ={
74 '*', STAR,
75 '%', PERCENT,
76 'l', MODCONVL,
77 'h', MODCONVH,
78 'n', NSCAN,
79 '[', BRACKET,
80 'd', CONVTYPE,
81 'S', CONVTYPE, /* dummy entry (for multibyte characters) */
82 's', CONVTYPE,
83 'u', CONVTYPE,
84 'c', CONVTYPE,
85 'x', CONVTYPE,
86 'o', CONVTYPE,
87 '0', NUMBER,
88 '1', NUMBER,
89 '2', NUMBER,
90 '3', NUMBER,
91 '4', NUMBER,
92 '5', NUMBER,
93 '6', NUMBER,
94 '7', NUMBER,
95 '8', NUMBER,
96 '9', NUMBER,
97 'i', CONVTYPE,
98 'f', CONVTYPE,
99 'e', CONVTYPE,
100 'g', CONVTYPE,
101 0, 0
102 };
103
104 static int scan(int, const char *, const char *);
105 static int gettoken(void);
106 static void whitespace(void);
107 static int match(const char *, char *);
108 static long unsigned getnum(int, int, int);
109 static int getin(void);
110 static void unget(int);
111 #ifdef __FLOAT__
112 static double lstrtod(void);
113 #endif
114
115 static int ungot; /* getin/unget char */
116 static FILE *fpin; /* input file pointer */
117 static int pflag; /*indicator of conversion description present */
118 static int width; /* field width value */
119 static const char *fmtptr; /* format string pointer */
120 static int charcnt; /* number of characters scanned (for %n) */
121 static int from; /* token type we've come from */
122 static int gfail; /* getnum() fail flag, non-zero for fail */
123
124 /*
125 * Convert formatted input from given input.
126 * This is the workhorse for scanf, sscanf, and fscanf.
127 * Returns the number of matched and assigned input items.
128 */
129 int
mks_vfscanf(FILE * pfin,const char * fmt,va_list ap)130 mks_vfscanf(FILE *pfin, const char *fmt, va_list ap)
131 {
132 int nitems;
133 int ltoken;
134 int c;
135 int modconv; /* flag indicating conversion modifier */
136 int suppression; /* flag to suppress conversion */
137
138 long unsigned number; /* return value from getnumber */
139
140 ungot = EOF;
141 fpin = pfin;
142 fmtptr = fmt;
143 from = 'X';
144 nitems = 0;
145 charcnt = 0;
146
147 for (;;) {
148 if (from == 'X') {
149 pflag = 0;
150 modconv = 0;
151 suppression = 0;
152 width = 0;
153 }
154 ltoken = gettoken();
155
156 switch (ltoken) {
157
158 case 0:
159 goto retitems;
160
161 case MODCONVL:
162 case MODCONVH:
163 switch (from) {
164
165 case 'A':
166 case 'D':
167 case 'P':
168 from = 'E';
169 modconv = ltoken;
170 break;
171 default:
172 from = 'X';
173 break;
174 }
175 break;
176
177 case CONVTYPE:
178 switch (from) {
179
180 int intassign;
181
182 case 'E':
183 case 'P':
184 case 'D':
185 case 'A':
186 from = 'X';
187 intassign = 1;
188 pflag = 0;
189
190 switch (lexp->name) {
191
192 case 'd':
193 number = getnum(BASE10, width, SIGNED);
194 if (gfail)
195 goto retitems;
196 break;
197 case 'u':
198 number = getnum(BASE10, width, UNSIGNED);
199 if (gfail)
200 goto retitems;
201 break;
202 case 'x':
203 number = getnum(BASE16, width, SIGNED);
204 if (gfail)
205 goto retitems;
206 break;
207 case 'o':
208 number = getnum(BASE8, width, SIGNED);
209 if (gfail)
210 goto retitems;
211 break;
212 case 'i':
213 number = getnum(NOBASE, width, SIGNED);
214 if (gfail)
215 goto retitems;
216 break;
217 case 'c':
218 /* 'S' dummy entry (for multibyte characters) */
219 case 'S':
220 case 's': {
221 int gotitem = 0;
222 char *str;
223
224 if (!suppression)
225 str = va_arg(ap, char *);
226
227 /* Input whitespace is not skipped
228 * for %c, which implies that %c
229 * can return whitespace.
230 */
231 if (lexp->name != 'c')
232 whitespace();
233 for (;;) {
234 c = getin();
235
236 /* Only %s and %S stop on
237 * whitespace.
238 */
239 if (lexp->name != 'c' && isspace(c)) {
240 unget(c);
241 break;
242 }
243 if (c == EOF) {
244 if(!gotitem)
245 goto retitems;
246 break;
247 }
248
249 gotitem = 1;
250 if (!suppression)
251 *str++ = c;
252
253 if (width) {
254 if (--width == 0)
255 break;
256 }
257 }
258
259 /*
260 * ANSI C states that %c does not
261 * terminate with a null character.
262 */
263 if (!suppression && lexp->name != 'c')
264 *str = '\0';
265 intassign = 0;
266 break;
267 }
268 #ifdef __FLOAT__
269 case 'f':
270 case 'g':
271 case 'e': {
272 double fresult;
273
274 fresult = lstrtod();
275 if(gfail)
276 goto retitems;
277 if(suppression)
278 break;
279 if (modconv == MODCONVL)
280 *(double *)va_arg(ap, double *) = fresult;
281 else
282 *(float *)va_arg(ap, float *) = (float)fresult;
283 /*FALLTHROUGH*/
284 }
285 #else /* !__FLOAT__ */
286 case 'f':
287 case 'g':
288 case 'e':
289 #endif /* __FLOAT__ */
290 default:
291 intassign = 0;
292 break;
293 }
294
295 if (suppression)
296 break;
297 else
298 nitems++;
299
300 if (intassign == 0)
301 break;
302
303 switch (modconv) {
304
305 case MODCONVH:
306 *(short *)va_arg(ap, short *) = (short)number;
307 break;
308 case MODCONVL:
309 *(long *)va_arg(ap, long *) = (long)number;
310 break;
311 default:
312 *(int *)va_arg(ap, int *) = (int)number;
313 break;
314 }
315 break;
316 default:
317 from = 'X';
318 break;
319 }
320 break;
321
322 case STAR:
323 if (from == 'P') {
324 from = 'A';
325 suppression = 1;
326 } else {
327 from = 'X';
328 }
329 break;
330
331 case PERCENT:
332 if (from == 'P') {
333 from = 'X';
334 pflag = 0;
335 c = getin();
336 if (c != '%')
337 goto retitems;
338 } else {
339 from = 'X';
340 }
341 break;
342
343 case NUMBER:
344 if (from == 'P' || from == 'A') {
345 from = 'D';
346 } else {
347 from = 'X';
348 }
349 break;
350
351 case NSCAN:
352 if (from == 'P') {
353 pflag = 0;
354 if (!suppression) {
355 *(int *)va_arg(ap, int *) = charcnt;
356 }
357 }
358 from = 'X';
359 break;
360
361 case BRACKET:
362 switch (from) {
363
364 case 'A':
365 case 'D':
366 case 'P': {
367 char *ptr;
368
369 pflag = 0;
370 if (width == 0)
371 width = INT_MAX;
372 ptr = suppression ? NULL : va_arg(ap, char *);
373 if (match(fmtptr, ptr) && !feof(fpin)
374 && !suppression)
375 nitems++;
376 while (*fmtptr++ != ']')
377 ;
378 break;
379 }
380 default:
381 break;
382 }
383 from = 'X';
384 break;
385
386 default:
387 c = *(fmtptr-1);
388 if (c == ' ' || c == '\t' || c == '\n' || c == '\f')
389 whitespace();
390 else {
391 c = getin();
392
393 if (c != *(fmtptr-1))
394 goto retitems;
395 }
396 from = 'X';
397 break;
398 }
399 }
400 retitems:
401 if (ungot != EOF) {
402 ungetc(ungot, fpin);
403 ungot = EOF;
404 }
405 return nitems==0 ? EOF : nitems;
406 }
407
408 static int
gettoken()409 gettoken()
410 {
411 char c;
412
413 if (*fmtptr == 0)
414 return 0; /* return 0 for end of string */
415
416 c = *fmtptr++;
417
418 if (pflag) {
419 for(lexp=lexlist; lexp->name != 0; lexp++) {
420 if (c == lexp->name) {
421 if (lexp->type == NUMBER) {
422 width = (int) strtol(fmtptr-1, (char **)0, BASE10);
423 while (*fmtptr >= '0' && *fmtptr <= '9')
424 fmtptr++;
425 } else if (c == 'c') {
426 /* No width specified for %c, default
427 * is one.
428 */
429 width = 1;
430 }
431 return lexp->type;
432 }
433 }
434 return -1;
435 }
436
437 if (c == '%') {
438 pflag = 1;
439 from = 'P';
440 return gettoken();
441 }
442 return -1;
443 }
444
445 static void
whitespace()446 whitespace()
447 {
448 register int c;
449
450 do {
451 c = getin();
452 } while (isspace(c));
453
454 unget(c);
455 }
456
457 static int
scan(int ch,const char * str,const char * estr)458 scan(int ch, const char *str, const char *estr)
459 {
460 for (; str < estr; ++str)
461 if (*str == ch)
462 return 1;
463
464 return 0;
465 }
466
467 static int
match(const char * str,char * outstr)468 match(const char *str, char *outstr)
469 {
470 int complement;
471 int i;
472 char start, end;
473 int c;
474 const char *bscan, *escan;
475
476 if (*str == '^') {
477 complement = 1;
478 str++;
479 } else
480 complement = 0;
481
482 start = *str++;
483 end = 0;
484 if (*str == '-') {
485 if (str[2] == ']')
486 end = str[1];
487 }
488 if (start > end) {
489 bscan = str - 1;
490 while (*str++ != ']')
491 ;
492 escan = str - 1;
493
494 for (i=0; i<width; i++) {
495 if ((c = getin()) == EOF)
496 return 0;
497 if (!scan(c, bscan, escan) ^ complement)
498 break;
499 if (outstr != NULL)
500 *outstr++ = c;
501 }
502 } else {
503 for (i=0; i<width; i++) {
504 c = getin();
505 if (complement) {
506 if (c >= start && c <= end)
507 break;
508 else if (outstr != NULL)
509 *outstr++ = c;
510 } else {
511 if (c < start || c > end)
512 break;
513 else if (outstr != NULL)
514 *outstr++ = c;
515 }
516 }
517 }
518
519 if (i < width)
520 unget(c);
521
522 if (outstr != NULL)
523 *outstr = '\0';
524 return (i > 1);
525 }
526
527 /*
528 * Get a number from the input stream.
529 * The base, if zero, will be determined by the nature of the number.
530 * A leading 0x means hexadecimal, a leading 0 for octal, otherwise decimal.
531 *
532 * if the width is 0 then the max input string length of number is used.
533 *
534 * The sign tell us that a signed number is expected (rather than the
535 * 'u' conversion type which is unsigned).
536 */
537 static long unsigned
getnum(int base,int width,int sign)538 getnum(int base, int width, int sign)
539 {
540 char *s;
541 char cbuf[CBUFSIZ]; /* char buffer for number */
542 int w;
543 register int c;
544 int neg;
545 long ret;
546
547 gfail = 0;
548 whitespace();
549
550 if (width == 0)
551 width = sizeof cbuf;
552
553 neg = 0;
554 if (sign) {
555 c = getin();
556 if (c == '+' || c == '-')
557 neg = c=='-' ? 1 : 0;
558 else
559 unget(c);
560 }
561
562 if (base == 0) {
563 base = 10;
564 c = getin();
565 if (c == '0') {
566 base = 8;
567 c = getin();
568 if (c == 'X' || c == 'x')
569 base = 16;
570 else
571 unget(c);
572 } else
573 unget(c);
574 }
575 if (base == 10) {
576 w = 0;
577 s = cbuf;
578 while (w < width && w < sizeof cbuf) {
579 c = getin();
580 switch (c) {
581
582 case '0':
583 case '1':
584 case '2':
585 case '3':
586 case '4':
587 case '5':
588 case '6':
589 case '7':
590 case '8':
591 case '9':
592 *s++ = c;
593 w++;
594 continue;
595 default:
596 unget(c);
597 w = width; /* force end of loop */
598 break;
599 }
600 }
601 *s = '\0';
602 ret = strtol(cbuf, (char **)0, 10);
603 goto retn;
604 }
605 if (base == 8) {
606 w = 0;
607 s = cbuf;
608 while (w < width && w < sizeof cbuf) {
609 c = getin();
610 switch (c) {
611
612 case '0':
613 case '1':
614 case '2':
615 case '3':
616 case '4':
617 case '5':
618 case '6':
619 case '7':
620 *s++ = c;
621 w++;
622 continue;
623 default:
624 unget(c);
625 w = width; /* force end of loop */
626 break;
627 }
628 }
629 *s = '\0';
630 ret = strtol(cbuf, (char **)0, 8);
631 goto retn;
632 }
633 if (base == 16) {
634 w = 0;
635 s = cbuf;
636 while (w < width && w < sizeof cbuf) {
637 c = getin();
638 c = toupper(c);
639 switch (c) {
640
641 case '0':
642 case '1':
643 case '2':
644 case '3':
645 case '4':
646 case '5':
647 case '6':
648 case '7':
649 case '8':
650 case '9':
651 case 'A':
652 case 'B':
653 case 'C':
654 case 'D':
655 case 'E':
656 case 'F':
657 *s++ = c;
658 w++;
659 continue;
660 default:
661 unget(c);
662 w = width; /* force end of loop */
663 break;
664 }
665 }
666 *s = '\0';
667 ret = strtol(cbuf, (char **)0, 16);
668 goto retn;
669 }
670
671 /*
672 * if we get this far then a bad base was passed.
673 */
674 gfail = -1;
675
676 retn:
677 if (*cbuf == '\0') /* No number at all?? */
678 gfail = -1;
679 if (neg)
680 ret = -ret;
681 return ret;
682 }
683
684 #ifdef __FLOAT__
685 static double
lstrtod()686 lstrtod()
687 {
688 int slen;
689 int neg, eneg;
690 char cbuf[CBUFSIZ];
691 register int c;
692 register char *sp, *s1, *s2, *s3;
693 double total, exp, tens;
694
695 neg = eneg = 1;
696 gfail = 0;
697
698 whitespace();
699
700 c = getin();
701
702 if (c == '-' || c == '+')
703 if (c == '-') {
704 neg = -1;
705 c = getin();
706 }
707
708 sp = s1 = cbuf;
709 while (c >= '0' && c <= '9') {
710 *sp++ = c;
711 c = getin();
712 }
713
714 s2 = sp;
715 if (c == '.') {
716 c = getin();
717 while (c >= '0' && c <= '9') {
718 *sp++ = c;
719 c = getin();
720 }
721 }
722
723 s3 = sp;
724 if (c == 'e' || c == 'E') {
725 c = getin();
726 if (c == '-' || c == '+')
727 if (c == '-') {
728 eneg = -1;
729 c = getin();
730 }
731 while (c >= '0' && c <= '9') {
732 *sp++ = c;
733 c = getin();
734 }
735 }
736 *sp = '\0';
737
738 if (s1 == s2 && s2 == s3) {
739 gfail = -1;
740 return 0.0;
741 }
742 unget(c);
743
744 /*
745 * convert the three strings (integer, fraction, and exponent)
746 * into a floating point number.
747 */
748
749 total = 0.0;
750 tens = 1.0;
751 for (sp=s2-1; sp >= s1; sp--) {
752 total += (*sp -'0') * tens;
753 tens *= 10.0;
754 }
755
756 tens = .1;
757 for (sp=s2; sp < s3; sp++) {
758 total += (*sp - '0') * tens;
759 tens /= 10.0;
760 }
761 total *= (double)neg;
762
763 exp = 0.0;
764 tens = 1.0;
765 if ((slen = strlen(s3)) > 0) {
766 sp = s3 + slen - 1;
767 for ( ; sp >= s3; sp--) {
768 exp += (*sp - '0') * tens;
769 tens *= 10.0;
770 }
771 }
772 *sp = '\0';
773
774 exp *= (double)eneg;
775
776 total *= pow(10.0, exp);
777
778 return total;
779 }
780 #endif /* __FLOAT__ */
781
782 static int
getin()783 getin()
784 {
785 int c;
786
787 if (ungot != EOF) {
788 c = ungot;
789 ungot = EOF;
790 } else
791 c = getc(fpin);
792 charcnt++;
793 return c;
794 }
795
796 static void
unget(int c)797 unget(int c)
798 {
799 /* Dont' use ungetc because it doesn't work with m_fsopen */
800 ungot = c;
801 charcnt--;
802 }
803
804