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