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 2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * Portions of this source code were derived from Berkeley 4.3 BSD 32 * under license from the Regents of the University of California. 33 */ 34 35 #pragma ident "%Z%%M% %I% %E% SMI" 36 37 /* 38 * ff -- obtain file names from reading filesystem 39 */ 40 41 #define NB 500 42 #define MAXNINDIR (MAXBSIZE / sizeof (daddr32_t)) 43 44 #include <sys/param.h> 45 #include <sys/types.h> 46 #include <sys/mntent.h> 47 #include <sys/vnode.h> 48 #include <sys/fs/ufs_inode.h> 49 #include <sys/stat.h> 50 #include <sys/fs/ufs_fs.h> 51 #include <sys/fs/ufs_fsdir.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <strings.h> 55 #include <errno.h> 56 #include <fcntl.h> 57 #include <unistd.h> 58 #include <pwd.h> 59 #include "roll_log.h" 60 61 #define MIN_PHYS_READ BBSIZE 62 #define DAY (24*60*60) 63 64 65 union { 66 struct fs sblk; 67 char xxx[SBSIZE]; /* because fs is variable length */ 68 } real_fs; 69 #define sblock real_fs.sblk 70 71 struct dinode *itab; /* = (struct dinode *)itab; */ 72 73 struct dinode *gip; 74 75 struct ilist { 76 ino_t ino; 77 ushort_t mode; 78 uid_t uid; 79 gid_t gid; 80 } ilist[NB]; 81 82 struct htab 83 { 84 ino_t h_ino; 85 ino_t h_pino; 86 int h_name_index; /* index into string table */ 87 } *htab; 88 char *strngtab; 89 long hsize; 90 int strngloc; 91 int strngtab_size; 92 #define STRNGTAB_INCR (1024*16) /* amount to grow strngtab */ 93 #define MAX_STRNGTAB_INDEX() (strngtab_size - 1) 94 #define AVG_PATH_LEN 30 /* average (?) length of name */ 95 96 struct dirstuff { 97 int loc; 98 struct dinode *ip; 99 char dbuf[MAXBSIZE]; 100 }; 101 int Aflg = 0; /* accessed in n days */ 102 int Mflg = 0; /* modified in n days */ 103 int Nflg = 0; /* modified more recently than 'file' */ 104 int Cflg = 0; /* changed within n days */ 105 int aflg = 0; /* print the names `.' and `..' */ 106 int sflg = 0; /* print only special files and files with set-user-ID mode */ 107 int Sflg = 0; /* print file size */ 108 int iflg = 0; /* number of inodes being searched for */ 109 int Iflg = 0; /* do not print i-number */ 110 int Lflg = 0; /* supplementary list of multiply linked files */ 111 int mflg = 0; 112 int pflg = 0; /* a prefix exists */ 113 int uflg = 0; /* print the owner's login name */ 114 int fi; 115 ino_t ino; 116 int nhent; 117 int nxfile; 118 int imax; /* highest inode number */ 119 int inode_reads; 120 int passwd_lookups; 121 int Adelay; /* Access delay */ 122 int Asign; /* Access sign */ 123 int Mdelay; /* Modify delay */ 124 int Msign; /* Modify sign */ 125 int Cdelay; /* change delay */ 126 int Csign; /* change sign */ 127 time_t Nage; /* Last modification time of the file */ 128 char *Lname; /* filename for supplementary list */ 129 FILE *Lfile; /* file for supplementary list */ 130 131 /* 132 * Function prototypes 133 */ 134 void check(char *file); 135 void pass1(struct dinode *ip); 136 void pass2(struct dinode *ip); 137 void pass3(struct dinode *ip); 138 struct direct *dreaddir(struct dirstuff *dirp); 139 int dotname(struct direct *dp); 140 void pname(FILE *stream, ino_t i, int lev); 141 struct htab *lookup(ino_t i, int ef); 142 void bread(diskaddr_t bno, char *buf, int cnt); 143 diskaddr_t bmap(diskaddr_t i); 144 struct dinode *ginode(ino_t inumber); 145 char *user_name(int uid); 146 int cmp(int a, int b, int s); 147 time_t mod_time(char *file); 148 void out_multilinks(); 149 void usage(); 150 int extend_strngtab(unsigned int size); 151 152 long atol(); 153 offset_t llseek(); 154 char *strcpy(); 155 156 char *prefix; 157 time_t Today; 158 int nerror; 159 160 161 extern int optind; 162 extern char *optarg; 163 164 char *subopts [] = { 165 #define A_FLAG 0 166 "a", 167 #define M_FLAG 1 168 "m", 169 #define S_FLAG 2 170 "s", 171 NULL 172 }; 173 174 int 175 main(argc, argv) 176 int argc; 177 char *argv[]; 178 { 179 long n; 180 int opt; 181 char *suboptions, *value; 182 char *p; 183 int first = 0; 184 185 Today = time((time_t *)0); 186 while ((opt = getopt(argc, argv, "Ia:c:i:lm:n:o:p:su")) != EOF) { 187 switch (opt) { 188 189 case 'a': 190 Aflg++; 191 Adelay = atoi(optarg); 192 Asign = optarg[0]; 193 break; 194 195 case 'I': 196 Iflg++; 197 break; 198 199 case 'c': 200 Cflg++; 201 Cdelay = atoi(optarg); 202 Csign = optarg[0]; 203 break; 204 205 case 'l': 206 Lflg++; 207 Lname = tmpnam((char *)0); 208 if ((Lfile = fopen(Lname, "w+")) == NULL) { 209 perror("open"); 210 (void) fprintf(stderr, 211 "ff: unable to open temp file, -l ignored\n"); 212 Lflg = 0; 213 } 214 break; 215 216 case 'm': 217 Mflg++; 218 Mdelay = atoi(optarg); 219 Msign = optarg[0]; 220 break; 221 222 case 'n': 223 Nflg++; 224 Nage = mod_time(optarg); 225 break; 226 227 case 'o': 228 /* 229 * ufs specific options. 230 */ 231 suboptions = optarg; 232 233 if (*suboptions == '\0') 234 usage(); 235 while (*suboptions != '\0') { 236 switch ((getsubopt(&suboptions, 237 subopts, &value))) { 238 239 case A_FLAG: 240 aflg++; 241 break; 242 243 case M_FLAG: 244 mflg++; 245 break; 246 247 case S_FLAG: 248 sflg++; 249 break; 250 251 default: 252 usage(); 253 } 254 } 255 break; 256 257 case 'i': 258 while ((p = (char *)strtok(((first++ == 0) ? 259 optarg: ((char *)0)), ", ")) != NULL) { 260 if ((n = atoi(p)) == 0) 261 break; 262 ilist[iflg].ino = n; 263 nxfile = iflg; 264 iflg++; 265 } 266 break; 267 268 case 'p': 269 prefix = optarg; 270 pflg++; 271 break; 272 273 case 's': 274 Sflg++; 275 break; 276 277 case 'u': 278 uflg++; 279 break; 280 281 case '?': 282 usage(); 283 } 284 } 285 argc -= optind; 286 argv = &argv[optind]; 287 while (argc--) { 288 check(*argv); 289 argv++; 290 } 291 if (Lflg) { 292 out_multilinks(); 293 } 294 if (nerror) 295 return (32); 296 return (0); 297 } 298 299 void 300 check(char *file) 301 { 302 register int i, j, c; 303 304 fi = open64(file, 0); 305 if (fi < 0) { 306 (void) fprintf(stderr, "ff: cannot open %s\n", file); 307 nerror++; 308 return; 309 } 310 nhent = 0; 311 (void) printf("%s:\n", file); 312 sync(); 313 bread(SBLOCK, (char *)&sblock, SBSIZE); 314 if ((sblock.fs_magic != FS_MAGIC) && 315 (sblock.fs_magic != MTB_UFS_MAGIC)) { 316 (void) fprintf(stderr, "%s: not a ufs file system\n", file); 317 nerror++; 318 return; 319 } 320 321 if (sblock.fs_magic == MTB_UFS_MAGIC && 322 (sblock.fs_version > MTB_UFS_VERSION_1 || 323 sblock.fs_version < MTB_UFS_VERSION_MIN)) { 324 (void) fprintf(stderr, "%s: unrecognized version of UFS: %d\n", 325 file, sblock.fs_version); 326 nerror++; 327 return; 328 } 329 330 /* If fs is logged, roll the log. */ 331 if (sblock.fs_logbno) { 332 switch (rl_roll_log(file)) { 333 case RL_SUCCESS: 334 /* 335 * Reread the superblock. Rolling the log may have 336 * changed it. 337 */ 338 bread(SBLOCK, (char *)&sblock, SBSIZE); 339 break; 340 case RL_SYSERR: 341 (void) printf("Warning: Cannot roll log for %s. %s\n", 342 file, strerror(errno)); 343 break; 344 default: 345 (void) printf("Warning: Cannot roll log for %s.\n ", 346 file); 347 break; 348 } 349 } 350 351 352 itab = (struct dinode *)calloc(sblock.fs_ipg, sizeof (struct dinode)); 353 imax = sblock.fs_ncg * sblock.fs_ipg; 354 355 hsize = sblock.fs_ipg * sblock.fs_ncg - sblock.fs_cstotal.cs_nifree + 1; 356 htab = (struct htab *)calloc(hsize, sizeof (struct htab)); 357 358 if (!extend_strngtab(AVG_PATH_LEN * hsize)) { 359 (void) printf("not enough memory to allocate tables\n"); 360 nerror++; 361 return; 362 } 363 strngloc = 0; 364 365 if ((itab == NULL) || (htab == NULL)) { 366 (void) printf("not enough memory to allocate tables\n"); 367 nerror++; 368 return; 369 } 370 ino = 0; 371 for (c = 0; c < sblock.fs_ncg; c++) { 372 bread(fsbtodb(&sblock, cgimin(&sblock, c)), (char *)itab, 373 (int)(sblock.fs_ipg * sizeof (struct dinode))); 374 for (j = 0; j < sblock.fs_ipg; j++) { 375 if (itab[j].di_smode != 0) { 376 itab[j].di_mode = itab[j].di_smode; 377 if (itab[j].di_suid != (o_uid_t)UID_LONG) 378 itab[j].di_uid = (unsigned int)itab[j].di_suid; 379 if (itab[j].di_sgid != GID_LONG) 380 itab[j].di_gid = (unsigned int)itab[j].di_sgid; 381 pass1(&itab[j]); 382 } 383 ino++; 384 } 385 } 386 ilist[nxfile+1].ino = 0; 387 ino = 0; 388 for (c = 0; c < sblock.fs_ncg; c++) { 389 bread(fsbtodb(&sblock, cgimin(&sblock, c)), (char *)itab, 390 (int)(sblock.fs_ipg * sizeof (struct dinode))); 391 for (j = 0; j < sblock.fs_ipg; j++) { 392 if (itab[j].di_smode != 0) { 393 itab[j].di_mode = itab[j].di_smode; 394 pass2(&itab[j]); 395 } 396 ino++; 397 } 398 } 399 ino = 0; 400 for (c = 0; c < sblock.fs_ncg; c++) { 401 bread(fsbtodb(&sblock, cgimin(&sblock, c)), (char *)itab, 402 (int)(sblock.fs_ipg * sizeof (struct dinode))); 403 for (j = 0; j < sblock.fs_ipg; j++) { 404 if (itab[j].di_smode != 0) { 405 itab[j].di_mode = itab[j].di_smode; 406 pass3(&itab[j]); 407 } 408 ino++; 409 } 410 } 411 (void) close(fi); 412 for (i = iflg; i < NB; i++) 413 ilist[i].ino = 0; 414 nxfile = iflg; 415 free(itab); 416 free(htab); 417 free(strngtab); 418 } 419 420 void 421 pass1(struct dinode *ip) 422 { 423 int i; 424 425 if (mflg) 426 for (i = 0; i < iflg; i++) 427 if (ino == ilist[i].ino) { 428 ilist[i].mode = ip->di_mode; 429 ilist[i].uid = ip->di_uid; 430 ilist[i].gid = ip->di_gid; 431 } 432 if ((ip->di_mode & IFMT) != IFDIR) { 433 if (sflg == 0 || nxfile >= NB) 434 return; 435 if ((ip->di_mode&IFMT) == IFBLK || 436 (ip->di_mode&IFMT) == IFCHR || ip->di_mode&(ISUID|ISGID)) { 437 ilist[nxfile].ino = ino; 438 ilist[nxfile].mode = ip->di_mode; 439 ilist[nxfile].uid = ip->di_uid; 440 ilist[nxfile++].gid = ip->di_gid; 441 return; 442 } 443 } 444 (void) lookup(ino, 1); 445 } 446 447 void 448 pass2(struct dinode *ip) 449 { 450 register struct direct *dp; 451 struct dirstuff dirp; 452 struct htab *hp; 453 454 if ((ip->di_mode&IFMT) != IFDIR) 455 return; 456 dirp.loc = 0; 457 dirp.ip = ip; 458 gip = ip; 459 for (dp = dreaddir(&dirp); dp != NULL; dp = dreaddir(&dirp)) { 460 int nmlen; 461 462 if (dp->d_ino == 0) 463 continue; 464 hp = lookup(dp->d_ino, 0); 465 if (hp == 0) 466 continue; 467 if (dotname(dp)) 468 continue; 469 hp->h_pino = ino; 470 nmlen = strlen(dp->d_name); 471 472 if (strngloc + nmlen + 1 > MAX_STRNGTAB_INDEX()) { 473 if (!extend_strngtab(STRNGTAB_INCR)) { 474 perror("ncheck: can't grow string table\n"); 475 exit(32); 476 } 477 } 478 479 hp->h_name_index = strngloc; 480 (void) strcpy(&strngtab[strngloc], dp->d_name); 481 strngloc += nmlen + 1; 482 } 483 } 484 485 void 486 pass3(struct dinode *ip) 487 { 488 register struct direct *dp; 489 struct dirstuff dirp; 490 struct dinode *dip; 491 int k; 492 493 if ((ip->di_mode&IFMT) != IFDIR) 494 return; 495 dirp.loc = 0; 496 dirp.ip = ip; 497 gip = ip; 498 for (dp = dreaddir(&dirp); dp != NULL; dp = dreaddir(&dirp)) { 499 if (aflg == 0 && dotname(dp)) 500 continue; 501 if (sflg == 0 && iflg == 0) 502 goto pr; 503 for (k = 0; ilist[k].ino != 0; k++) 504 if (ilist[k].ino == dp->d_ino) 505 break; 506 if (ilist[k].ino == 0) 507 continue; 508 if (mflg) 509 (void) printf("mode %-6o uid %-5ld gid %-5ld ino ", 510 ilist[k].mode, ilist[k].uid, ilist[k].gid); 511 pr: 512 if (Sflg || uflg || Aflg || Mflg || Cflg || Nflg || Lflg) 513 dip = ginode(dp->d_ino); 514 if ((!Aflg || 515 cmp((Today - dip->di_un.di_icom.ic_atime)/DAY, Adelay, 516 Asign)) && 517 (!Mflg || cmp((Today - dip->di_un.di_icom.ic_mtime)/DAY, 518 Mdelay, Msign)) && 519 (!Cflg || cmp((Today - dip->di_un.di_icom.ic_mtime)/DAY, 520 Cdelay, Csign)) && 521 (!Nflg || cmp(dip->di_un.di_icom.ic_mtime, Nage, '+'))) { 522 if (Iflg == 0) 523 (void) printf("%-5u\t", dp->d_ino); 524 pname(stdout, ino, 0); 525 (void) printf("/%s", dp->d_name); 526 if (lookup(dp->d_ino, 0)) 527 (void) printf("/."); 528 if (Sflg) 529 (void) printf("\t%6lld", 530 dip->di_un.di_icom.ic_lsize); 531 if (uflg) 532 (void) printf("\t%s", 533 user_name(dip->di_un.di_icom.ic_uid)); 534 (void) printf("\n"); 535 if (Lflg && (dip->di_un.di_icom.ic_nlink > 1)) { 536 (void) fprintf(Lfile, "%-5u\t", 537 dp->d_ino); 538 (void) fprintf(Lfile, "%-5u\t", 539 dip->di_un.di_icom.ic_nlink); 540 pname(Lfile, ino, 0); 541 (void) fprintf(Lfile, "/%s\n", dp->d_name); 542 } 543 } 544 } 545 } 546 547 548 549 /* 550 * get next entry in a directory. 551 */ 552 struct direct * 553 dreaddir(struct dirstuff *dirp) 554 { 555 register struct direct *dp; 556 diskaddr_t lbn, d; 557 558 for (;;) { 559 if (dirp->loc >= (int)dirp->ip->di_size) 560 return (NULL); 561 if (blkoff(&sblock, dirp->loc) == 0) { 562 lbn = lblkno(&sblock, dirp->loc); 563 d = bmap(lbn); 564 if (d == 0) 565 return (NULL); 566 bread(fsbtodb(&sblock, d), dirp->dbuf, 567 (int)dblksize(&sblock, dirp->ip, (int)lbn)); 568 } 569 dp = (struct direct *) 570 (dirp->dbuf + blkoff(&sblock, dirp->loc)); 571 dirp->loc += dp->d_reclen; 572 if (dp->d_ino == 0) 573 continue; 574 return (dp); 575 } 576 } 577 578 int 579 dotname(struct direct *dp) 580 { 581 582 if (dp->d_name[0] == '.') 583 if (dp->d_name[1] == 0 || 584 (dp->d_name[1] == '.' && dp->d_name[2] == 0)) 585 return (1); 586 return (0); 587 } 588 589 void 590 pname(FILE *stream, ino_t i, int lev) 591 { 592 register struct htab *hp; 593 594 if (i == UFSROOTINO) 595 return; 596 if ((hp = lookup(i, 0)) == 0) { 597 (void) fprintf(stream, "???"); 598 return; 599 } 600 if (lev > 10) { 601 (void) fprintf(stream, "..."); 602 return; 603 } 604 pname(stream, hp->h_pino, ++lev); 605 if (pflg) 606 (void) fprintf(stream, "%s/%s", prefix, 607 &(strngtab[hp->h_name_index])); 608 else 609 (void) fprintf(stream, "/%s", 610 &(strngtab[hp->h_name_index])); 611 } 612 613 struct htab * 614 lookup(ino_t i, int ef) 615 { 616 register struct htab *hp; 617 618 for (hp = &htab[(int)i%hsize]; hp->h_ino; ) { 619 if (hp->h_ino == i) 620 return (hp); 621 if (++hp >= &htab[hsize]) 622 hp = htab; 623 } 624 if (ef == 0) 625 return (0); 626 if (++nhent >= hsize) { 627 (void) fprintf(stderr, 628 "ff: hsize of %ld is too small\n", hsize); 629 exit(32); 630 } 631 hp->h_ino = i; 632 return (hp); 633 } 634 635 void 636 bread(diskaddr_t bno, char *buf, int cnt) 637 { 638 register i; 639 int got; 640 offset_t offset; 641 642 offset = (offset_t)bno * DEV_BSIZE; 643 if (llseek(fi, offset, 0) == (offset_t)-1) { 644 (void) fprintf(stderr, 645 "ff: llseek error %lx %lx\n", 646 ((long *)&offset)[0], ((long *)&offset)[1]); 647 for (i = 0; i < cnt; i++) 648 buf[i] = 0; 649 return; 650 } 651 652 got = read((int)fi, buf, cnt); 653 if (got != cnt) { 654 perror("read"); 655 (void) fprintf(stderr, 656 "ff: (wanted %d got %d blk %lld)\n", cnt, got, bno); 657 for (i = 0; i < cnt; i++) 658 buf[i] = 0; 659 } 660 } 661 662 diskaddr_t 663 bmap(diskaddr_t i) 664 { 665 daddr32_t ibuf[MAXNINDIR]; 666 667 if (i < NDADDR) 668 return ((diskaddr_t)gip->di_db[i]); 669 i -= NDADDR; 670 if (i > NINDIR(&sblock)) { 671 (void) fprintf(stderr, "ff : %lu - huge directory\n", ino); 672 return ((diskaddr_t)0); 673 } 674 bread(fsbtodb(&sblock, gip->di_ib[0]), (char *)ibuf, sizeof (ibuf)); 675 return ((diskaddr_t)ibuf[i]); 676 } 677 678 struct dinode * 679 ginode(ino_t inumber) 680 { 681 diskaddr_t iblk; 682 diskaddr_t dblk; 683 int ioff; 684 static diskaddr_t curr_dblk; 685 static char buf[MIN_PHYS_READ]; 686 struct dinode *ibuf; 687 688 if (inumber < UFSROOTINO || (int)inumber > imax) { 689 (void) fprintf(stderr, 690 "bad inode number %ld to ginode\n", inumber); 691 exit(32); 692 } 693 iblk = itod(&sblock, (int)inumber); 694 dblk = fsbtodb(&sblock, iblk); 695 ioff = itoo(&sblock, (int)inumber); 696 if (dblk != curr_dblk) { 697 bread(dblk, &buf[0], sizeof (buf)); 698 curr_dblk = dblk; 699 inode_reads++; 700 } 701 ibuf = (struct dinode *)&buf[0]; 702 ibuf += ioff; 703 return (ibuf); 704 } 705 706 #define HASHNAMESIZE 16 707 708 struct name_ent { 709 struct name_ent *name_nxt; 710 int name_uid; 711 char *name_string; 712 }; 713 struct name_ent *hashtable[HASHNAMESIZE]; 714 715 char * 716 user_name(int uid) 717 { 718 int h_index; 719 struct name_ent *hp; 720 struct passwd *pwent; 721 722 h_index = uid % HASHNAMESIZE; 723 for (hp = hashtable[h_index]; hp != NULL; hp = hp->name_nxt) { 724 if (hp->name_uid == uid) { 725 return (hp->name_string); 726 } 727 } 728 hp = (struct name_ent *)calloc(1, sizeof (struct name_ent)); 729 hp->name_nxt = hashtable[h_index]; 730 hp->name_uid = uid; 731 hashtable[h_index] = hp; 732 if ((pwent = getpwuid(uid)) == NULL) { 733 hp->name_string = "unknown"; 734 } else { 735 hp->name_string = (char *)strdup(pwent->pw_name); 736 } 737 passwd_lookups++; 738 739 return (hp->name_string); 740 } 741 742 int 743 cmp(int a, int b, int s) 744 { 745 if (s == '+') 746 return (a > b); 747 if (s == '-') 748 return (a < -(b)); 749 return (a == b); 750 } 751 752 /* 753 * We can't do this one by reading the disk directly, since there 754 * is no guarantee that the file is even on a local disk. 755 */ 756 time_t 757 mod_time(char *file) 758 { 759 struct stat64 stat_buf; 760 761 if (stat64(file, &stat_buf) < 0) { 762 (void) fprintf(stderr, "ff: can't stat '%s' - ignored\n", file); 763 return (0); 764 } 765 return (stat_buf.st_mtime); 766 } 767 768 void 769 out_multilinks() 770 { 771 int length; 772 773 if ((length = fseek(Lfile, 0L, 2)) < 0) { 774 perror("fseek"); 775 exit(32); 776 } else 777 if ((length = ftell(Lfile)) > 0) { 778 (void) fprintf(stdout, 779 "\nmultilink files\nIno\tLinks\tPathname\n\n"); 780 rewind(Lfile); 781 while (length-- > 0) 782 (void) putc(getc(Lfile), stdout); 783 } else 784 (void) fprintf(stdout, "No multilink files\n"); 785 (void) fclose(Lfile); 786 } 787 788 void 789 usage() 790 { 791 (void) fprintf(stderr, 792 "ufs usage: ff [-F ufs] [generic options] [-o a,m,s] special\n"); 793 exit(32); 794 } 795 796 /* 797 * Extend or create the string table. 798 * Preserves contents. 799 * Return non-zero for success. 800 */ 801 int 802 extend_strngtab(unsigned int size) 803 { 804 strngtab_size += size; 805 strngtab = (char *)realloc(strngtab, strngtab_size); 806 807 return ((int)strngtab); 808 } 809