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 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 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 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 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 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 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 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 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 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