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