xref: /titanic_50/usr/src/lib/libbc/libc/stdio/common/doscan.c (revision 8461248208fabd3a8230615f8615e5bf1b4dcdcb)
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) 1988-1995, by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 #pragma	ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*	Copyright (c) 1984 AT&T	*/
30 /*	  All Rights Reserved  	*/
31 
32 
33 /*LINTLIBRARY*/
34 #include <stdio.h>
35 #include <ctype.h>
36 #include <varargs.h>
37 #include <values.h>
38 #include <floatingpoint.h>
39 #include <errno.h>
40 
41 #define NCHARS	(1 << BITSPERBYTE)
42 #define locgetc()	(chcount+=1,getc(iop))
43 #define locungetc(x)	(chcount-=1,ungetc(x,iop))
44 
45 extern char *memset();
46 static int chcount,flag_eof;
47 
48 #ifdef S5EMUL
49 #define	isws(c)		isspace(c)
50 #else
51 /*
52  * _sptab[c+1] is 1 iff 'c' is a white space character according to the
53  * 4.2BSD "scanf" definition - namely, SP, TAB, and NL are the only
54  * whitespace characters.
55  */
56 static char _sptab[1+256] = {
57 	0,				/* EOF - not a whitespace char */
58 	0,0,0,0,0,0,0,0,
59 	0,1,1,0,0,0,0,0,
60 	0,0,0,0,0,0,0,0,
61 	0,0,0,0,0,0,0,0,
62 	1,0,0,0,0,0,0,0,
63 	0,0,0,0,0,0,0,0,
64 	0,0,0,0,0,0,0,0,
65 	0,0,0,0,0,0,0,0,
66 	0,0,0,0,0,0,0,0,
67 	0,0,0,0,0,0,0,0,
68 	0,0,0,0,0,0,0,0,
69 	0,0,0,0,0,0,0,0,
70 	0,0,0,0,0,0,0,0,
71 	0,0,0,0,0,0,0,0,
72 	0,0,0,0,0,0,0,0,
73 	0,0,0,0,0,0,0,0,
74 };
75 
76 #define	isws(c)		((_sptab + 1)[c] != 0)
77 #endif
78 
79 int
80 _doscan(iop, fmt, va_alist)
81 register FILE *iop;
82 register unsigned char *fmt;
83 va_list va_alist;
84 {
85 	extern unsigned char *setup();
86 	char tab[NCHARS];
87 	register int ch;
88 	int nmatch = 0, len, inchar, stow, size;
89 	chcount=0; flag_eof=0;
90 
91 	/*******************************************************
92 	 * Main loop: reads format to determine a pattern,
93 	 *		and then goes to read input stream
94 	 *		in attempt to match the pattern.
95 	 *******************************************************/
96 	for ( ; ; )
97 	{
98 		if ( (ch = *fmt++) == '\0')
99 			return(nmatch); /* end of format */
100 		if (isws(ch))
101 		{
102 		  	if (!flag_eof)
103 			{
104 			   while (isws(inchar = locgetc()))
105 				;
106 			   if (inchar == EOF) {
107 				chcount--;
108 				flag_eof = 1;
109 			   }
110 			   else if (locungetc(inchar) == EOF)
111 				flag_eof = 1;
112 			}
113 		  continue;
114 		}
115 		if (ch != '%' || (ch = *fmt++) == '%')
116                 {
117 			if ( (inchar = locgetc()) == ch )
118 				continue;
119 			if (inchar != EOF) {
120 				if (locungetc(inchar) != EOF)
121 					return(nmatch); /* failed to match input */
122 			} else {
123 				chcount--;
124 			}
125 			break;
126 		}
127 		if (ch == '*')
128 		{
129 			stow = 0;
130 			ch = *fmt++;
131 		}
132 		else
133 			stow = 1;
134 
135 		for (len = 0; isdigit(ch); ch = *fmt++)
136 			len = len * 10 + ch - '0';
137 		if (len == 0)
138 			len = MAXINT;
139 		if ( (size = ch) == 'l' || (size == 'h') || (size == 'L') )
140 			ch = *fmt++;
141 		if (ch == '\0' ||
142 		    ch == '[' && (fmt = setup(fmt, tab)) == NULL)
143 			return(EOF); /* unexpected end of format */
144 		if (isupper(ch))  /* no longer documented */
145 		{
146 			/*
147 			 * The rationale behind excluding the size
148 			 * of 'L' is that the 'L' size specifier was
149 			 * introduced in ANSI/ISO-C.  If the user
150 			 * specifies a format of %LG, it can mean
151 			 * nothing other than "long double", be the
152 			 * code ANSI or not.  Mapping it to "double"
153 			 * makes no sense.
154 			 */
155 			if (size != 'L')
156 				size = 'l';
157 #ifdef S5EMUL
158 			ch = _tolower(ch);
159 #else
160 			ch = tolower(ch);
161 #endif
162 		}
163 		switch(ch)
164 		{
165 		 case 'c':
166 		 case 's':
167 		 case '[':
168 			  if ((size = string(stow,ch,len,tab,iop,&va_alist)) < 0)
169 				goto out;	/* EOF seen, nothing converted */
170 			  break;
171                  case 'n':
172 			  if (stow == 0)
173 				continue;
174 			  if (size == 'h')
175 				*va_arg(va_alist, short *) = (short) chcount;
176 		          else if (size == 'l')
177 				*va_arg(va_alist, long *) = (long) chcount;
178 			  else
179 			  	*va_arg(va_alist, int *) = (int) chcount;
180 			  continue;
181                  default:
182 			 if ((size = number(stow, ch, len, size, iop, &va_alist)) < 0)
183 				goto out;	/* EOF seen, nothing converted */
184 			 break;
185                  }
186 		   if (size)
187 			nmatch += stow;
188 		   else
189 			return((flag_eof && !nmatch) ? EOF : nmatch);
190 		continue;
191 	}
192 out:
193 	return (nmatch != 0 ? nmatch : EOF); /* end of input */
194 }
195 
196 /***************************************************************
197  * Functions to read the input stream in an attempt to match incoming
198  * data to the current pattern from the main loop of _doscan().
199  ***************************************************************/
200 static int
201 number(stow, type, len, size, iop, listp)
202 int stow, type, len, size;
203 register FILE *iop;
204 va_list *listp;
205 {
206 	char numbuf[64], inchar, lookahead;
207 	register char *np = numbuf;
208 	register int c, base;
209 	int digitseen = 0, floater = 0, negflg = 0;
210 	long lcval = 0;
211 	switch(type)
212 	{
213 	case 'e':
214 	case 'f':
215 	case 'g':
216 		floater++;
217 	case 'd':
218 	case 'u':
219 	case 'i':
220 		base = 10;
221 		break;
222 	case 'o':
223 		base = 8;
224 		break;
225 	case 'x':
226 		base = 16;
227 		break;
228 	default:
229 		return(0); /* unrecognized conversion character */
230 	}
231 	if (!flag_eof)
232 	{
233 		while (isws(c = locgetc()))
234 			;
235 	}
236 	else
237 		c = locgetc();
238 	if (c == EOF) {
239 		chcount--;
240 		return(-1);	/* EOF before match */
241 	}
242         if (floater != 0) {     /* Handle floating point with
243                                  * file_to_decimal. */
244                 decimal_mode    dm;
245                 decimal_record  dr;
246                 fp_exception_field_type efs;
247                 enum decimal_string_form form;
248                 char           *echar;
249                 int             nread, ic;
250                 char            buffer[1024];
251                 char           *nb = buffer;
252 
253                 locungetc(c);
254 		if (len > 1024)
255 			len = 1024;
256                 file_to_decimal(&nb, len, 0, &dr, &form, &echar, iop, &nread);
257                 if (stow && (form != invalid_form)) {
258                         dm.rd = fp_direction;
259                         if (size == 'l') {      /* double */
260                                 decimal_to_double((double *) va_arg(*listp, double *), &dm, &dr, &efs);
261                         } else if (size == 'L') {      /* quad */
262                                 decimal_to_quadruple((quadruple *)va_arg(*listp, double *), &dm, &dr, &efs);
263                         } else {/* single */
264                                 decimal_to_single((float *) va_arg(*listp, float *), &dm, &dr, &efs);
265                         }
266 			if ((efs & (1 << fp_overflow)) != 0) {
267 				errno = ERANGE;
268 			}
269 			if ((efs & (1 << fp_underflow)) != 0) {
270 				errno = ERANGE;
271                         }
272                 }
273 		chcount += nread;	/* Count characters read. */
274                 c = *nb;        /* Get first unused character. */
275                 ic = c;
276                 if (c == NULL) {
277                         ic = locgetc();
278                         c = ic;
279                         /*
280                          * If null, first unused may have been put back
281                          * already.
282                          */
283                 }
284                 if (ic == EOF) {
285                         chcount--;
286                         flag_eof = 1;
287                 } else if (locungetc(c) == EOF)
288                         flag_eof = 1;
289                 return ((form == invalid_form) ? 0 : 1);        /* successful match if
290                                                                  * non-zero */
291         }
292 	switch(c) {
293 	case '-':
294 		negflg++;
295 		if (type == 'u')
296 			break;
297 	case '+': /* fall-through */
298 		if (--len <= 0)
299 			break;
300 		if ( (c = locgetc()) != '0')
301 			break;
302         case '0':
303                 if ( (type != 'i') || (len <= 1) )
304 		   break;
305 	        if ( ((inchar = locgetc()) == 'x') || (inchar == 'X') )
306 	        {
307 		      /* If not using sscanf and *
308 		       * at the buffer's end     *
309 		       * then LOOK ahead         */
310 
311                    if ( (iop->_flag & _IOSTRG) || (iop->_cnt != 0) )
312 		      lookahead = locgetc();
313 		   else
314 		   {
315 		      if ( read(fileno(iop),np,1) == 1)
316 		         lookahead = *np;
317                       else
318 		         lookahead = EOF;
319                       chcount += 1;
320                    }
321 		   if ( isxdigit(lookahead) )
322 		   {
323 		       base =16;
324 
325 		       if ( len <= 2)
326 		       {
327 			  locungetc(lookahead);
328 			  len -= 1;            /* Take into account the 'x'*/
329                        }
330 		       else
331 		       {
332 		          c = lookahead;
333 			  len -= 2;           /* Take into account '0x'*/
334 		       }
335                    }
336 	           else
337 	           {
338 	               locungetc(lookahead);
339 	               locungetc(inchar);
340                    }
341 		}
342 	        else
343 	        {
344 		    locungetc(inchar);
345 	            base = 8;
346                 }
347 	}
348 	if (!negflg || type != 'u')
349 	    for (; --len  >= 0 ; *np++ = c, c = locgetc())
350 	    {
351 		if (np > numbuf + 62)
352 		{
353 		    errno = ERANGE;
354 		    return(0);
355                 }
356 		if (isdigit(c))
357 		{
358 			register int digit;
359 			digit = c - '0';
360 			if (base == 8)
361 			{
362 				if (digit >= 8)
363 					break;
364 				if (stow)
365 					lcval = (lcval<<3) + digit;
366 			}
367 			else
368 			{
369 				if (stow)
370 				{
371 					if (base == 10)
372 						lcval = (((lcval<<2) + lcval)<<1) + digit;
373 					else /* base == 16 */
374 						lcval = (lcval<<4) + digit;
375 				}
376 			}
377 			digitseen++;
378 
379 
380 			continue;
381 		}
382 		else if (base == 16 && isxdigit(c))
383 		{
384 			register int digit;
385 			digit = c - (isupper(c) ? 'A' - 10 : 'a' - 10);
386 			if (stow)
387 				lcval = (lcval<<4) + digit;
388 			digitseen++;
389 			continue;
390 		}
391 		break;
392 	    }
393 
394 
395 	if (stow && digitseen)
396 		{
397 	 	/* suppress possible overflow on 2's-comp negation */
398 			if (negflg && lcval != HIBITL)
399 				lcval = -lcval;
400 			if (size == 'l')
401 				*va_arg(*listp, long *) = lcval;
402 			else if (size == 'h')
403 				*va_arg(*listp, short *) = (short)lcval;
404 			else
405 				*va_arg(*listp, int *) = (int)lcval;
406 		}
407 	if (c == EOF) {
408 		chcount--;
409 		flag_eof=1;
410 	} else if (locungetc(c) == EOF)
411 		flag_eof=1;
412 	return (digitseen); /* successful match if non-zero */
413 }
414 
415 static int
416 string(stow, type, len, tab, iop, listp)
417 register int stow, type, len;
418 register char *tab;
419 register FILE *iop;
420 va_list *listp;
421 {
422 	register int ch;
423 	register char *ptr;
424 	char *start;
425 
426 	start = ptr = stow ? va_arg(*listp, char *) : NULL;
427 	if (type == 's')
428 	{
429 		if (!flag_eof)
430 		{
431 			while (isws(ch = locgetc()))
432 				;
433 		}
434 		else
435 			ch = locgetc();
436 		if (ch == EOF)
437 			return(-1);	/* EOF before match */
438 		while (ch != EOF && !isws(ch))
439 		{
440 			if (stow)
441 				*ptr = ch;
442 			ptr++;
443 			if (--len <= 0)
444 				break;
445 			ch = locgetc();
446 		}
447 	} else if (type == 'c') {
448 		if (len == MAXINT)
449 			len = 1;
450 		while ( (ch = locgetc()) != EOF)
451 		{
452 			if (stow)
453 				*ptr = ch;
454 			ptr++;
455 			if (--len <= 0)
456 				break;
457 		}
458 	} else { /* type == '[' */
459 		while ( (ch = locgetc()) != EOF && !tab[ch])
460 		{
461 			if (stow)
462 				*ptr = ch;
463 			ptr++;
464 			if (--len <= 0)
465 				break;
466 		}
467 	}
468 	if (ch == EOF )
469 	{
470 		chcount-=1;
471 		flag_eof = 1;
472 	}
473 	else if (len > 0 && locungetc(ch) == EOF)
474 		flag_eof = 1;
475 	if (ptr == start)
476 		return(0); /* no match */
477 	if (stow && type != 'c')
478 		*ptr = '\0';
479 	return (1); /* successful match */
480 }
481 
482 static unsigned char *
483 setup(fmt, tab)
484 register unsigned char *fmt;
485 register char *tab;
486 {
487 	register int b, c, d, t = 0;
488 
489 	if (*fmt == '^')
490 	{
491 		t++;
492 		fmt++;
493 	}
494 	(void) memset(tab, !t, NCHARS);
495 	if ( (c = *fmt) == ']' || c == '-')  /* first char is special */
496 	{
497 		tab[c] = t;
498 		fmt++;
499 	}
500 	while ( (c = *fmt++) != ']')
501 	{
502 		if (c == '\0')
503 			return(NULL); /* unexpected end of format */
504 		if (c == '-' && (d = *fmt) != ']' && (b = fmt[-2]) < d)
505 		{
506 			(void) memset(&tab[b], t, d - b + 1);
507 			fmt++;
508 		}
509 		else
510 			tab[c] = t;
511 	}
512 	return (fmt);
513 }
514