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