1 #ifndef lint 2 static char sccsid[] = "@(#)lpass2.c 1.12 (Berkeley) 05/12/90"; 3 #endif lint 4 5 # include "macdefs.h" 6 # include "manifest.h" 7 # include "lmanifest.h" 8 9 # define USED 01 10 # define VUSED 02 11 # define EUSED 04 12 # define RVAL 010 13 # define VARARGS 0100 14 15 # define NSZ 4096 16 # define TYSZ 3500 17 # define FSZ 500 18 # define NTY 50 19 20 typedef struct sty STYPE; 21 struct sty { ATYPE t; STYPE *next; }; 22 23 typedef struct sym { 24 #ifndef FLEXNAMES 25 char name[LCHNM]; 26 #else 27 char *name; 28 #endif 29 short nargs; 30 int decflag; 31 int fline; 32 STYPE symty; 33 int fno; 34 int use; 35 } STAB; 36 37 STAB stab[NSZ]; 38 STAB *find(); 39 40 STYPE tary[TYSZ]; 41 STYPE *tget(); 42 43 #ifndef FLEXNAMES 44 char fnm[FSZ][LFNM]; 45 #else 46 char *fnm[FSZ]; 47 #endif 48 49 #ifdef FLEXNAMES 50 char *getstr(); 51 #endif 52 53 int tfree; /* used to allocate types */ 54 int ffree; /* used to save filenames */ 55 56 struct ty atyp[NTY]; 57 /* r is where all the input ends up */ 58 union rec r; 59 60 int hflag = 0; 61 int pflag = 0; 62 int xflag = 0; 63 int uflag = 1; 64 int ddddd = 0; 65 int zflag = 0; 66 int Pflag = 0; 67 68 int cfno; /* current file number */ 69 70 main( argc, argv ) char *argv[]; { 71 register char *p; 72 73 /* first argument is intermediate file */ 74 /* second argument is - options */ 75 76 for( ; argc>2 && argv[argc-1][0] == '-' ; --argc ){ 77 for( p=argv[argc-1]; *p; ++p ){ 78 switch( *p ){ 79 80 case 'h': 81 hflag = 1; 82 break; 83 84 case 'p': 85 pflag = 1; 86 break; 87 88 case 'x': 89 xflag = 1; 90 break; 91 92 case 'X': 93 ddddd = 1; 94 break; 95 96 case 'u': 97 uflag = 0; 98 break; 99 100 case 'z': 101 zflag = 1; 102 break; 103 104 case 'P': 105 Pflag = 1; 106 break; 107 108 } 109 } 110 } 111 112 if( argc < 2 || !freopen( argv[1], "r", stdin ) ){ 113 error( "cannot open intermediate file" ); 114 exit( 1 ); 115 } 116 if( Pflag ){ 117 pfile(); 118 return( 0 ); 119 } 120 mloop( LDI|LIB|LST ); 121 rewind( stdin ); 122 mloop( LDC|LDX ); 123 rewind( stdin ); 124 mloop( LRV|LUV|LUE|LUM ); 125 cleanup(); 126 return(0); 127 } 128 129 mloop( m ){ 130 /* do the main loop */ 131 register STAB *q; 132 133 while( lread(m) ){ 134 q = find(); 135 if( q->decflag ) chkcompat(q); 136 else setuse(q); 137 } 138 } 139 140 lread(m){ /* read a line into r.l */ 141 142 register n; 143 144 for(;;) { 145 if( fread( (char *)&r, sizeof(r), 1, stdin ) <= 0 ) return(0); 146 if( r.l.decflag & LFN ){ 147 /* new filename */ 148 #ifdef FLEXNAMES 149 r.f.fn = getstr(0); 150 #endif 151 if( Pflag ) return( 1 ); 152 setfno( r.f.fn ); 153 continue; 154 } 155 #ifdef FLEXNAMES 156 r.l.name = getstr(1); 157 #else /* !FLEXNAMES */ 158 portify(r.l.name); 159 #endif /* !FLEXNAMES */ 160 n = r.l.nargs; 161 if( n<0 ) n = ~n; 162 if( n>=NTY ) error( "more than %d args?", n ); 163 fread( (char *)atyp, sizeof(ATYPE), n, stdin ); 164 if( ( r.l.decflag & m ) ) return( 1 ); 165 } 166 } 167 168 setfno( s ) char *s; { 169 /* look up current file names */ 170 /* first, strip backwards to the beginning or to the first / */ 171 int i; 172 173 /* now look up s */ 174 for( i=0; i<ffree; ++i ){ 175 #ifndef FLEXNAMES 176 if( !strncmp( s, fnm[i], LFNM ) ) 177 #else 178 if (fnm[i] == s) 179 #endif 180 { 181 cfno = i; 182 return; 183 } 184 } 185 /* make a new entry */ 186 if( ffree >= FSZ ) error( "more than %d files", FSZ ); 187 #ifndef FLEXNAMES 188 strncpy( fnm[ffree], s, LFNM ); 189 #else 190 fnm[ffree] = s; 191 #endif 192 cfno = ffree++; 193 } 194 195 /* VARARGS */ 196 error( s, a ) char *s; { 197 198 #ifndef FLEXNAMES 199 fprintf( stderr, "pass 2 error:(file %.*s) ", LFNM, fnm[cfno] ); 200 #else 201 fprintf( stderr, "pass 2 error:(file %s) ", fnm[cfno] ); 202 #endif 203 fprintf( stderr, s, a ); 204 fprintf( stderr, "\n" ); 205 exit(1); 206 } 207 208 STAB * 209 find(){ 210 register h=0; 211 #ifndef FLEXNAMES 212 h = hashstr(r.l.name, LCHNM) % NSZ; 213 #else 214 h = (int)r.l.name % NSZ; 215 #endif 216 { register STAB *p, *q; 217 for( p=q= &stab[h]; q->decflag; ){ 218 #ifndef FLEXNAMES 219 if( !strncmp( r.l.name, q->name, LCHNM)) 220 #else 221 if (r.l.name == q->name) 222 #endif 223 if( ((q->decflag|r.l.decflag)&LST)==0 || q->fno==cfno ) 224 return(q); 225 if( ++q >= &stab[NSZ] ) q = stab; 226 if( q == p ) error( "too many names defined" ); 227 } 228 #ifndef FLEXNAMES 229 strncpy( q->name, r.l.name, LCHNM ); 230 #else 231 q->name = r.l.name; 232 #endif 233 return( q ); 234 } 235 } 236 237 STYPE * 238 tget(){ 239 if( tfree >= TYSZ ){ 240 error( "too many types needed" ); 241 } 242 return( &tary[tfree++] ); 243 } 244 245 chkcompat(q) STAB *q; { 246 /* are the types, etc. in r.l and q compatible */ 247 register int i; 248 STYPE *qq; 249 250 setuse(q); 251 252 /* argument check */ 253 254 if( q->decflag & (LDI|LIB|LUV|LUE|LST) ){ 255 if( r.l.decflag & (LUV|LIB|LUE) ){ 256 if( q->nargs != r.l.nargs ){ 257 if( !(q->use&VARARGS) ){ 258 #ifndef FLEXNAMES 259 printf( "%.8s: variable # of args.", q->name ); 260 #else 261 printf( "%s: variable # of args.", q->name ); 262 #endif 263 viceversa(q); 264 } 265 if( r.l.nargs > q->nargs ) r.l.nargs = q->nargs; 266 if( !(q->decflag & (LDI|LIB|LST) ) ) { 267 q->nargs = r.l.nargs; 268 q->use |= VARARGS; 269 } 270 } 271 for( i=0,qq=q->symty.next; i<r.l.nargs; ++i,qq=qq->next){ 272 if( chktype( &qq->t, &atyp[i], q->fno ) ){ 273 #ifndef FLEXNAMES 274 printf( "%.8s, arg. %d used inconsistently", 275 #else 276 printf( "%s, arg. %d used inconsistently", 277 #endif 278 q->name, i+1 ); 279 viceversa(q); 280 } 281 } 282 } 283 } 284 285 if( (q->decflag&(LDI|LIB|LUV|LST)) && r.l.decflag==LUV ){ 286 if( chktype( &r.l.type, &q->symty.t, q->fno ) ){ 287 #ifndef FLEXNAMES 288 printf( "%.8s value used inconsistently", q->name ); 289 #else 290 printf( "%s value used inconsistently", q->name ); 291 #endif 292 viceversa(q); 293 } 294 } 295 296 /* check for multiple declaration */ 297 298 if( (q->decflag&(LDI|LST)) && (r.l.decflag&(LDI|LIB|LST)) ){ 299 #ifndef FLEXNAMES 300 printf( "%.8s multiply declared", q->name ); 301 #else 302 printf( "%s multiply declared", q->name ); 303 #endif 304 viceversa(q); 305 } 306 307 /* do a bit of checking of definitions and uses... */ 308 309 if( (q->decflag & (LDI|LIB|LDX|LDC|LUM|LST)) && (r.l.decflag & (LDX|LDC|LUM)) && q->symty.t.aty != r.l.type.aty ){ 310 #ifndef FLEXNAMES 311 printf( "%.8s value declared inconsistently", q->name ); 312 #else 313 printf( "%s value declared inconsistently", q->name ); 314 #endif 315 viceversa(q); 316 } 317 318 /* better not call functions which are declared to be structure or union returning */ 319 320 if( (q->decflag & (LDI|LIB|LDX|LDC|LST)) && (r.l.decflag & LUE) && q->symty.t.aty != r.l.type.aty ){ 321 /* only matters if the function returns union or structure */ 322 TWORD ty; 323 ty = q->symty.t.aty; 324 if( ISFTN(ty) && ((ty = DECREF(ty))==STRTY || ty==UNIONTY ) ){ 325 #ifndef FLEXNAMES 326 printf( "%.8s function value type must be declared before use", q->name ); 327 #else 328 printf( "%s function value type must be declared before use", q->name ); 329 #endif 330 viceversa(q); 331 } 332 } 333 334 if( pflag && q->decflag==LDX && r.l.decflag == LUM && !ISFTN(q->symty.t.aty) ){ 335 /* make the external declaration go away */ 336 /* in effect, it was used without being defined */ 337 } 338 } 339 340 viceversa(q) STAB *q; { 341 /* print out file comparison */ 342 #ifndef FLEXNAMES 343 printf( " %.*s(%d) :: %.*s(%d)\n", 344 LFNM, fnm[q->fno], q->fline, 345 LFNM, fnm[cfno], r.l.fline ); 346 #else 347 printf( " %s(%d) :: %s(%d)\n", 348 fnm[q->fno], q->fline, 349 fnm[cfno], r.l.fline ); 350 #endif 351 } 352 353 /* messages for defintion/use */ 354 char * 355 mess[2][2] ={ 356 "", 357 #ifndef FLEXNAMES 358 "%.8s used( %.*s(%d) ), but not defined\n", 359 "%.8s defined( %.*s(%d) ), but never used\n", 360 "%.8s declared( %.*s(%d) ), but never used or defined\n" 361 #else 362 "%s used( %s(%d) ), but not defined\n", 363 "%s defined( %s(%d) ), but never used\n", 364 "%s declared( %s(%d) ), but never used or defined\n" 365 #endif 366 }; 367 368 lastone(q) STAB *q; { 369 370 register nu, nd, uses; 371 372 if( ddddd ) pst(q); 373 374 nu = nd = 0; 375 uses = q->use; 376 377 if( !(uses&USED) && q->decflag != LIB ) { 378 #ifndef FLEXNAMES 379 if( strncmp(q->name,"main",7) ) 380 #else 381 if (strcmp(q->name, "main")) 382 #endif 383 nu = 1; 384 } 385 386 if( !ISFTN(q->symty.t.aty) ){ 387 switch( q->decflag ){ 388 389 case LIB: 390 nu = nd = 0; /* don't complain about uses on libraries */ 391 break; 392 case LDX: 393 if( !xflag ) break; 394 case LUV: 395 case LUE: 396 /* 01/04/80 */ case LUV | LUE: 397 case LUM: 398 nd = 1; 399 } 400 } 401 if( uflag && ( nu || nd ) ) 402 #ifndef FLEXNAMES 403 printf( mess[nu][nd], q->name, LFNM, fnm[q->fno], q->fline ); 404 #else 405 printf( mess[nu][nd], q->name, fnm[q->fno], q->fline ); 406 #endif 407 408 if( (uses&(RVAL+EUSED)) == (RVAL+EUSED) ){ 409 /* if functions is static, then print the file name too */ 410 if( q->decflag & LST ) 411 #ifndef FLEXNAMES 412 printf( "%.*s(%d):", LFNM, fnm[q->fno], q->fline ); 413 #else 414 printf( "%s(%d):", fnm[q->fno], q->fline ); 415 #endif 416 #ifndef FLEXNAMES 417 printf( "%.*s returns value which is %s ignored\n", 418 LCHNM, q->name, uses&VUSED ? "sometimes" : "always" ); 419 #else 420 printf( "%s returns value which is %s ignored\n", 421 q->name, uses&VUSED ? "sometimes" : "always" ); 422 #endif 423 } 424 425 if( (uses&(RVAL+VUSED)) == (VUSED) && (q->decflag&(LDI|LIB|LST)) ){ 426 if( q->decflag & LST ) 427 #ifndef FLEXNAMES 428 printf( "%.*s(%d):", LFNM, fnm[q->fno], q->fline ); 429 #else 430 printf( "%s(%d):", fnm[q->fno], q->fline ); 431 #endif 432 #ifndef FLEXNAMES 433 printf( "%.*s value is used, but none returned\n", 434 LCHNM, q->name); 435 #else 436 printf( "%s value is used, but none returned\n", q->name); 437 #endif 438 } 439 } 440 441 cleanup(){ /* call lastone and die gracefully */ 442 STAB *q; 443 for( q=stab; q< &stab[NSZ]; ++q ){ 444 if( q->decflag ) lastone(q); 445 } 446 exit(0); 447 } 448 449 setuse(q) STAB *q; { /* check new type to ensure that it is used */ 450 451 if( !q->decflag ){ /* new one */ 452 q->decflag = r.l.decflag; 453 q->symty.t = r.l.type; 454 if( r.l.nargs < 0 ){ 455 q->nargs = ~r.l.nargs; 456 q->use = VARARGS; 457 } 458 else { 459 q->nargs = r.l.nargs; 460 q->use = 0; 461 } 462 q->fline = r.l.fline; 463 q->fno = cfno; 464 if( q->nargs ){ 465 int i; 466 STYPE *qq; 467 for( i=0,qq= &q->symty; i<q->nargs; ++i,qq=qq->next ){ 468 qq->next = tget(); 469 qq->next->t = atyp[i]; 470 } 471 } 472 } 473 474 switch( r.l.decflag ){ 475 476 case LRV: 477 q->use |= RVAL; 478 return; 479 case LUV: 480 q->use |= VUSED+USED; 481 return; 482 case LUE: 483 q->use |= EUSED+USED; 484 return; 485 /* 01/04/80 */ case LUV | LUE: 486 case LUM: 487 q->use |= USED; 488 return; 489 490 } 491 } 492 493 chktype( pt1, pt2, fno ) register ATYPE *pt1, *pt2; { 494 TWORD t; 495 496 /* check the two type words to see if they are compatible */ 497 /* for the moment, enums are turned into ints, and should be checked as such */ 498 if( pt1->aty == ENUMTY ) pt1->aty = INT; 499 if( pt2->aty == ENUMTY ) pt2->aty = INT; 500 501 if( (t=BTYPE(pt1->aty)==STRTY) || t==UNIONTY ){ 502 if( pt1->aty != pt2->aty || pt1->extra1 != pt2->extra1 ) 503 return 1; 504 /* if -z then don't worry about undefined structures, 505 as long as the names match */ 506 if( zflag && (pt1->extra == 0 || pt2->extra == 0) ) return 0; 507 /* if -p and pt1 is "too big" and 508 ** pt1 came from a llib-l file, we can't pass judgment on it. 509 */ 510 if ( pflag && pt1->extra > pt2->extra && 511 strncmp(fnm[fno], "llib-l", 6) == 0) 512 return 0; 513 return pt1->extra != pt2->extra; 514 } 515 516 if( pt2->extra ){ /* constant passed in */ 517 if( pt1->aty == UNSIGNED && pt2->aty == INT ) return( 0 ); 518 else if( pt1->aty == ULONG && pt2->aty == LONG ) return( 0 ); 519 } 520 else if( pt1->extra ){ /* for symmetry */ 521 if( pt2->aty == UNSIGNED && pt1->aty == INT ) return( 0 ); 522 else if( pt2->aty == ULONG && pt1->aty == LONG ) return( 0 ); 523 } 524 525 return( pt1->aty != pt2->aty ); 526 } 527 528 struct tb { int m; char *nm; }; 529 530 struct tb dfs[] = { 531 LDI, "LDI", 532 LIB, "LIB", 533 LDC, "LDC", 534 LDX, "LDX", 535 LRV, "LRV", 536 LUV, "LUV", 537 LUE, "LUE", 538 LUM, "LUM", 539 LST, "LST", 540 LFN, "LFN", 541 0, "" }; 542 543 struct tb us[] = { 544 USED, "USED", 545 VUSED, "VUSED", 546 EUSED, "EUSED", 547 RVAL, "RVAL", 548 VARARGS, "VARARGS", 549 0, "" }; 550 551 ptb( v, tp ) struct tb *tp; { 552 /* print a value from the table */ 553 int flag; 554 flag = 0; 555 for( ; tp->m; ++tp ){ 556 if( v&tp->m ){ 557 if( flag++ ) putchar( '|' ); 558 printf( "%s", tp->nm ); 559 } 560 } 561 } 562 563 pst( q ) STAB *q; { 564 /* give a debugging output for q */ 565 566 #ifndef FLEXNAMES 567 printf( "%.8s (", q->name ); 568 #else 569 printf( "%s (", q->name ); 570 #endif 571 ptb( q->decflag, dfs ); 572 printf( "), use= " ); 573 ptb( q->use, us ); 574 printf( ", line %d, nargs=%d\n", q->fline, q->nargs ); 575 } 576 577 pfile() { 578 /* print the input file in readable form */ 579 while( lread( LDI|LIB|LDC|LDX|LRV|LUV|LUE|LUM|LST|LFN ) ) 580 prc(); 581 } 582 583 prc() { 584 /* print out 'r' for debugging */ 585 register i, j, k; 586 587 printf( "decflag\t" ); 588 ptb( r.l.decflag, dfs ); 589 putchar( '\n' ); 590 if( r.l.decflag & LFN ){ 591 #ifdef FLEXNAMES 592 printf( "fn\t\t%s\n", r.f.fn ); 593 #else 594 printf( "fn\t%\t.*s\n", LFNM, r.f.fn ); 595 #endif 596 } 597 else { 598 #ifdef FLEXNAMES 599 printf( "name\t%s\n", r.l.name ); 600 #else 601 printf( "name\t%.*s\n", LCHNM, r.l.name ); 602 #endif 603 printf( "nargs\t%d\n", r.l.nargs ); 604 printf( "fline\t%d\n", r.l.fline ); 605 printf( "type.aty\t0%o (", r.l.type.aty ); 606 pty( r.l.type.aty, r.l.name ); 607 printf( ")\ntype.extra\t%d\n", r.l.type.extra ); 608 j = r.l.type.extra1; 609 printf( "type.extra1\t0x%x (%d,%d)\n", 610 j, j & X_NONAME ? 1 : 0, j & ~X_NONAME ); 611 k = r.l.nargs; 612 if( k < 0 ) k = ~k; 613 for( i = 0; i < k; i++ ){ 614 printf( "atyp[%d].aty\t0%o (", i, atyp[i].aty ); 615 pty( atyp[i].aty, "" ); 616 printf( ")\natyp[%d].extra\t%d\n", i, atyp[i].extra); 617 j = atyp[i].extra1; 618 printf( "atyp[%d].extra1\t0x%x (%d,%d)\n", 619 i, j, j & X_NONAME ? 1 : 0, j & ~X_NONAME ); 620 } 621 } 622 putchar( '\n' ); 623 } 624 625 pty( t, name ) TWORD t; { 626 static char * tnames[] = { 627 "void", "farg", "char", "short", 628 "int", "long", "float", "double", 629 "struct xxx", "union %s", "enum", "moety", 630 "unsigned char", "unsigned short", "unsigned", "unsigned long", 631 "?", "?" 632 }; 633 634 printf( "%s ", tnames[BTYPE(t)] ); 635 pty1( t, name, (8 * sizeof (int) - BTSHIFT) / TSHIFT ); 636 } 637 638 pty1( t, name, level ) TWORD t; { 639 register TWORD u; 640 641 if( level < 0 ){ 642 printf( "%s", name ); 643 return; 644 } 645 u = t >> level * TSHIFT; 646 if( ISPTR(u) ){ 647 printf( "*" ); 648 pty1( t, name, level-1 ); 649 } 650 else if( ISFTN(u) ){ 651 if( level > 0 && ISPTR(u << TSHIFT) ){ 652 printf( "(" ); 653 pty1( t, name, level-1 ); 654 printf( ")()" ); 655 } 656 else { 657 pty1( t, name, level-1 ); 658 printf( "()" ); 659 } 660 } 661 else if( ISARY(u) ){ 662 if( level > 0 && ISPTR(u << TSHIFT) ){ 663 printf( "(" ); 664 pty1( t, name, level-1 ); 665 printf( ")[]" ); 666 } 667 else { 668 pty1( t, name, level-1 ); 669 printf( "[]" ); 670 } 671 } 672 else { 673 pty1( t, name, level-1 ); 674 } 675 } 676 677 #ifdef FLEXNAMES 678 char * 679 getstr(doport) 680 { 681 char buf[BUFSIZ]; 682 register char *cp = buf; 683 register int c; 684 685 if (feof(stdin) || ferror(stdin)) 686 return(""); 687 while ((c = getchar()) > 0) 688 *cp++ = c; 689 if (c < 0) { 690 error("intermediate file format error (getstr)"); 691 exit(1); 692 } 693 *cp++ = 0; 694 if (doport) 695 portify(buf); 696 return (hash(buf)); 697 } 698 699 #define NSAVETAB 4096 700 char *savetab; 701 int saveleft; 702 703 char * 704 savestr(cp) 705 register char *cp; 706 { 707 register int len; 708 709 len = strlen(cp) + 1; 710 if (len > saveleft) { 711 saveleft = NSAVETAB; 712 if (len > saveleft) 713 saveleft = len; 714 savetab = (char *)malloc(saveleft); 715 if (savetab == 0) { 716 error("ran out of memory (savestr)"); 717 exit(1); 718 } 719 } 720 strncpy(savetab, cp, len); 721 cp = savetab; 722 savetab += len; 723 saveleft -= len; 724 return (cp); 725 } 726 727 /* 728 * The definition for the segmented hash tables. 729 */ 730 #define MAXHASH 20 731 #define HASHINC 1013 732 struct ht { 733 char **ht_low; 734 char **ht_high; 735 int ht_used; 736 } htab[MAXHASH]; 737 738 char * 739 hash(s) 740 char *s; 741 { 742 register char **h; 743 register i; 744 register char *cp; 745 struct ht *htp; 746 int sh; 747 748 sh = hashstr(s) % HASHINC; 749 cp = s; 750 /* 751 * There are as many as MAXHASH active 752 * hash tables at any given point in time. 753 * The search starts with the first table 754 * and continues through the active tables 755 * as necessary. 756 */ 757 for (htp = htab; htp < &htab[MAXHASH]; htp++) { 758 if (htp->ht_low == 0) { 759 register char **hp = 760 (char **) calloc(sizeof (char **), HASHINC); 761 if (hp == 0) { 762 error("ran out of memory (hash)"); 763 exit(1); 764 } 765 htp->ht_low = hp; 766 htp->ht_high = htp->ht_low + HASHINC; 767 } 768 h = htp->ht_low + sh; 769 /* 770 * quadratic rehash increment 771 * starts at 1 and incremented 772 * by two each rehash. 773 */ 774 i = 1; 775 do { 776 if (*h == 0) { 777 if (htp->ht_used > (HASHINC * 3)/4) 778 break; 779 htp->ht_used++; 780 *h = savestr(cp); 781 return (*h); 782 } 783 if (**h == *cp && strcmp(*h, cp) == 0) 784 return (*h); 785 h += i; 786 i += 2; 787 if (h >= htp->ht_high) 788 h -= HASHINC; 789 } while (i < HASHINC); 790 } 791 error("ran out of hash tables"); 792 exit(1); 793 } 794 char *tstrbuf[1]; 795 #endif 796 797 #include "ctype.h" 798 799 portify(cp) 800 register char * cp; 801 { 802 register int i; 803 804 if (!pflag) 805 return; 806 for (i = 0; i < 6; ++i) 807 if (cp[i] == '\0') 808 return; 809 else if (isascii(cp[i]) && isupper(cp[i])) 810 cp[i] = tolower(cp[i]); 811 cp[i] = '\0'; 812 } 813