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 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <stdio.h> 30 #include <fcntl.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <sys/types.h> 34 #include <sys/stat.h> 35 #include <dirent.h> 36 #include <sys/param.h> 37 #include <errno.h> 38 #include <unistd.h> 39 #include <ftw.h> 40 41 #include "list.h" 42 #include "protocmp.h" 43 #include "proto_list.h" 44 #include "protodir.h" 45 #include "exception_list.h" 46 #include "stdusers.h" 47 48 #define MAX_PROTO_REFS 5 49 #define MAX_EXCEPTION_FILES 5 50 #define MAX_DEPTH 50 51 52 /* 53 * default flag values 54 */ 55 static int check_group = 1; 56 static int set_group = 0; 57 static int check_user = 1; 58 static int set_user = 0; 59 static int check_perm = 1; 60 static int set_perm = 0; 61 static int check_link = 1; 62 static int check_sym = 1; 63 static int check_majmin = 1; 64 65 static elem_list first_list; 66 static char *first_file_name; 67 68 static elem_list second_list; 69 static char *second_file_name; 70 71 static FILE *need_add_fp; 72 static char *need_add_file; 73 static FILE *need_rm_fp; 74 static char *need_rm_file; 75 static FILE *differ_fp; 76 static char *differ_file; 77 78 static char *myname; 79 80 /* 81 * default flag values 82 */ 83 static int verbose = 0; 84 85 static void 86 usage(void) 87 { 88 (void) fputs("usage: protocmp [-gupGUPlmsLv] " 89 "[-e <exception-list> ...] " 90 "-d <protolist|pkg dir>\n\t[-d <protolist|pkg dir> ...] " 91 "[<protolist|pkg dir>...]|<root>]\n", 92 stderr); 93 (void) fputs(" where:\n", stderr); 94 (void) fputs("\t-g : don't compare group\n", stderr); 95 (void) fputs("\t-u : don't compare owner\n", stderr); 96 (void) fputs("\t-p : don't compare permissions\n", stderr); 97 (void) fputs("\t-G : set group\n", stderr); 98 (void) fputs("\t-U : set owner\n", stderr); 99 (void) fputs("\t-P : set permissions\n", stderr); 100 (void) fputs("\t-l : don't compare link counts\n", stderr); 101 (void) fputs("\t-m : don't compare major/minor numbers\n", 102 stderr); 103 (void) fputs("\t-s : don't compare symlink values\n", stderr); 104 (void) fputs("\t-d <protolist|pkg dir>:\n", stderr); 105 (void) fputs("\t proto list or packaging to check\n", stderr); 106 (void) fputs("\t-e <file>: exceptions file\n", stderr); 107 (void) fputs("\t-L : list filtered exceptions\n", stderr); 108 (void) fputs("\t-v : verbose output\n", stderr); 109 (void) fputs("\n" 110 "If any of the -[GUP] flags are given, then the final argument must be the\n" 111 "proto root directory itself on which to set permissions according to the\n" 112 "packaging data specified via -d options.\n", stderr); 113 } 114 115 116 static void 117 open_output_files(void) 118 { 119 if ((need_add_fp = 120 fopen((need_add_file = tempnam(NULL, "add")), "w")) == NULL) { 121 perror(need_add_file); 122 exit(1); 123 } 124 125 if ((need_rm_fp = 126 fopen((need_rm_file = tempnam(NULL, "rm")), "w")) == NULL) { 127 perror(need_rm_file); 128 exit(1); 129 } 130 131 if ((differ_fp = 132 fopen((differ_file = tempnam(NULL, "diff")), "w")) == NULL) { 133 perror(differ_file); 134 exit(1); 135 } 136 } 137 138 static void 139 close_output_files(void) 140 { 141 (void) fclose(need_add_fp); 142 (void) fclose(need_rm_fp); 143 (void) fclose(differ_fp); 144 } 145 146 static void 147 print_file(char *file) 148 { 149 FILE *fp; 150 int count; 151 char buff[BUF_SIZE]; 152 153 if ((fp = fopen(file, "r")) == NULL) { 154 perror(need_add_file); 155 } 156 157 while (count = fread(buff, sizeof (char), BUF_SIZE, fp)) 158 (void) fwrite(buff, sizeof (char), count, stdout); 159 (void) fclose(fp); 160 } 161 162 static void 163 print_header(void) 164 { 165 (void) printf("%c %-30s %-20s %-4s %-5s %-5s %-5s %-2s %2s %2s %-9s\n", 166 'T', "File Name", "Reloc/Sym name", "perm", "owner", "group", 167 "inode", "lnk", "maj", "min", "package(s)"); 168 (void) puts("-------------------------------------------------------" 169 "-----------------------------------------------------"); 170 } 171 172 static void 173 print_results(void) 174 { 175 (void) puts("*******************************************************"); 176 (void) puts("*"); 177 (void) printf("* Entries found in %s, but not found in %s\n", 178 first_file_name, second_file_name); 179 (void) puts("*"); 180 (void) puts("*******************************************************"); 181 print_header(); 182 print_file(need_add_file); 183 (void) puts("*******************************************************"); 184 (void) puts("*"); 185 (void) printf("* Entries found in %s, but not found in %s\n", 186 second_file_name, first_file_name); 187 (void) puts("*"); 188 (void) puts("*******************************************************"); 189 print_header(); 190 print_file(need_rm_file); 191 (void) puts("*******************************************************"); 192 (void) puts("*"); 193 (void) printf("* Entries that differ between %s and %s\n", 194 first_file_name, second_file_name); 195 (void) puts("*"); 196 (void) printf("* filea == %s\n", first_file_name); 197 (void) printf("* fileb == %s\n", second_file_name); 198 (void) puts("*"); 199 (void) puts("*******************************************************"); 200 (void) fputs("Unit ", stdout); 201 print_header(); 202 print_file(differ_file); 203 } 204 205 static void 206 clean_up(void) 207 { 208 (void) unlink(need_add_file); 209 (void) unlink(need_rm_file); 210 (void) unlink(differ_file); 211 } 212 213 /* 214 * elem_compare(a,b) 215 * 216 * Args: 217 * a - element a 218 * b - element b 219 * different_types - 220 * value = 0 -> comparing two elements of same 221 * type (eg: protodir elem vs. protodir elem). 222 * value != 0 -> comparing two elements of different type 223 * (eg: protodir elem vs. protolist elem). 224 * 225 * Returns: 226 * 0 - elements are identical 227 * >0 - elements differ 228 * check flags to see which fields differ. 229 */ 230 static int 231 elem_compare(elem *a, elem *b, int different_types) 232 { 233 int res = 0; 234 elem *i, *j; 235 236 /* 237 * if these are hard links to other files - those are the 238 * files that should be compared. 239 */ 240 i = a->link_parent ? a->link_parent : a; 241 j = b->link_parent ? b->link_parent : b; 242 243 /* 244 * We do not compare inodes - they always differ. 245 * We do not compare names because we assume that was 246 * checked before. 247 */ 248 249 /* 250 * Special rules for comparison: 251 * 252 * 1) if directory - ignore ref_cnt. 253 * 2) if sym_link - only check file_type & symlink 254 * 3) elem type of FILE_T, EDIT_T, & VOLATILE_T are equivilant when 255 * comparing a protodir entry to a protolist entry. 256 */ 257 if (i->file_type != j->file_type) { 258 if (different_types) { 259 /* 260 * Check to see if filetypes are FILE_T vs. 261 * EDIT_T/VOLATILE_T/LINK_T comparisons. 262 */ 263 if ((i->file_type == FILE_T) && 264 ((j->file_type == EDIT_T) || 265 (j->file_type == VOLATILE_T) || 266 (j->file_type == LINK_T))) { 267 /*EMPTY*/ 268 } else if ((j->file_type == FILE_T) && 269 ((i->file_type == EDIT_T) || 270 (i->file_type == VOLATILE_T) || 271 (i->file_type == LINK_T))) { 272 /*EMPTY*/ 273 } else 274 res |= TYPE_F; 275 } else 276 res |= TYPE_F; 277 } 278 279 /* 280 * if symlink - check the symlink value and then 281 * return. symlink is the only field of concern 282 * in SYMLINKS. 283 */ 284 if (check_sym && ((res == 0) && (i->file_type == SYM_LINK_T))) { 285 if ((!i->symsrc) || (!j->symsrc)) 286 res |= SYM_F; 287 else { 288 /* 289 * if either symlink starts with a './' strip it off, 290 * its irrelevant. 291 */ 292 if ((i->symsrc[0] == '.') && (i->symsrc[1] == '/')) 293 i->symsrc += 2; 294 if ((j->symsrc[0] == '.') && (j->symsrc[1] == '/')) 295 j->symsrc += 2; 296 297 if (strncmp(i->symsrc, j->symsrc, MAXNAME) != 0) 298 res |= SYM_F; 299 } 300 return (res); 301 } 302 303 if ((i->file_type != DIR_T) && check_link && 304 (i->ref_cnt != j->ref_cnt)) 305 res |= REF_F; 306 if (check_user && (strncmp(i->owner, j->owner, TYPESIZE) != 0)) 307 res |= OWNER_F; 308 if (check_group && (strncmp(i->group, j->group, TYPESIZE) != 0)) 309 res |= GROUP_F; 310 if (check_perm && (i->perm != j->perm)) 311 res |= PERM_F; 312 if (check_majmin && ((i->major != j->major) || (i->minor != j->minor))) 313 res |= MAJMIN_F; 314 315 return (res); 316 } 317 318 static void 319 print_elem(FILE *fp, elem *e) 320 { 321 elem p; 322 pkg_list *l; 323 char maj[TYPESIZE], min[TYPESIZE]; 324 char perm[12], ref_cnt[12]; 325 326 /* 327 * If this is a LINK to another file, then adopt 328 * the permissions of that file. 329 */ 330 if (e->link_parent) { 331 p = *((elem *)e->link_parent); 332 (void) strcpy(p.name, e->name); 333 p.symsrc = e->symsrc; 334 p.file_type = e->file_type; 335 e = &p; 336 } 337 338 if (!check_majmin || e->major == -1) { 339 maj[0] = '-'; 340 maj[1] = '\0'; 341 } else { 342 (void) sprintf(maj, "%d", e->major); 343 } 344 345 if (!check_majmin || e->minor == -1) { 346 min[0] = '-'; 347 min[1] = '\0'; 348 } else { 349 (void) sprintf(min, "%d", e->minor); 350 } 351 352 if (!check_perm) { 353 perm[0] = '-'; 354 perm[1] = '\0'; 355 } else { 356 (void) snprintf(perm, sizeof (perm), "%o", e->perm); 357 } 358 359 if (!check_link) { 360 ref_cnt[0] = '-'; 361 ref_cnt[1] = '\0'; 362 } else { 363 (void) snprintf(ref_cnt, sizeof (ref_cnt), "%d", e->ref_cnt); 364 } 365 366 (void) fprintf(fp, "%c %-30s %-20s %4s %-5s %-5s %6d %2s %2s %2s ", 367 e->file_type, e->name, 368 check_sym && e->symsrc != NULL ? e->symsrc : "-", perm, 369 check_user ? e->owner : "-", 370 check_group ? e->group : "-", 371 e->inode, ref_cnt, maj, min); 372 /* 373 * dump package list - if any. 374 */ 375 if (!e->pkgs) 376 (void) fputs(" proto", fp); 377 378 for (l = e->pkgs; l; l = l->next) { 379 (void) fputc(' ', fp); 380 (void) fputs(l->pkg_name, fp); 381 } 382 (void) fputc('\n', fp); 383 } 384 385 /* 386 * do_compare(a,b) 387 * 388 * Args: 389 * different_types - see elem_compare() for explanation. 390 */ 391 static void 392 do_compare(elem *a, elem *b, int different_types) 393 { 394 int rc; 395 396 if ((rc = elem_compare(a, b, different_types)) != 0) { 397 (void) fputs("filea: ", differ_fp); 398 print_elem(differ_fp, a); 399 (void) fputs("fileb: ", differ_fp); 400 print_elem(differ_fp, b); 401 (void) fputs(" differ: ", differ_fp); 402 403 if (rc & SYM_F) 404 (void) fputs("symlink", differ_fp); 405 if (rc & PERM_F) 406 (void) fputs("perm ", differ_fp); 407 if (rc & REF_F) 408 (void) fputs("ref_cnt ", differ_fp); 409 if (rc & TYPE_F) 410 (void) fputs("file_type ", differ_fp); 411 if (rc & OWNER_F) 412 (void) fputs("owner ", differ_fp); 413 if (rc & GROUP_F) 414 (void) fputs("group ", differ_fp); 415 if (rc & MAJMIN_F) 416 (void) fputs("major/minor ", differ_fp); 417 (void) putc('\n', differ_fp); 418 (void) putc('\n', differ_fp); 419 } 420 } 421 422 static void 423 check_second_vs_first(int verbose) 424 { 425 int i; 426 elem *cur; 427 428 for (i = 0; i < second_list.num_of_buckets; i++) { 429 for (cur = second_list.list[i]; cur; cur = cur->next) { 430 if (!(cur->flag & VISITED_F)) { 431 if ((first_list.type != second_list.type) && 432 find_elem(&exception_list, cur, 433 FOLLOW_LINK)) { 434 /* 435 * this entry is filtered, we don't 436 * need to do any more processing. 437 */ 438 if (verbose) { 439 (void) printf( 440 "Filtered: Need Deletion " 441 "of:\n\t"); 442 print_elem(stdout, cur); 443 } 444 continue; 445 } 446 /* 447 * It is possible for arch specific files to be 448 * found in a protodir but listed as arch 449 * independent in a protolist file. If this is 450 * a protodir vs. a protolist we will make 451 * that check. 452 */ 453 if ((second_list.type == PROTODIR_LIST) && 454 (cur->arch != P_ISA) && 455 (first_list.type != PROTODIR_LIST)) { 456 /* 457 * do a lookup for same file, but as 458 * type ISA. 459 */ 460 elem *e; 461 462 e = find_elem_isa(&first_list, cur, 463 NO_FOLLOW_LINK); 464 if (e) { 465 do_compare(e, cur, 466 first_list.type - 467 second_list.type); 468 continue; 469 } 470 } 471 472 print_elem(need_rm_fp, cur); 473 } 474 } 475 } 476 } 477 478 static void 479 check_first_vs_second(int verbose) 480 { 481 int i; 482 elem *e; 483 elem *cur; 484 485 for (i = 0; i < first_list.num_of_buckets; i++) { 486 for (cur = first_list.list[i]; cur; cur = cur->next) { 487 if ((first_list.type != second_list.type) && 488 find_elem(&exception_list, cur, FOLLOW_LINK)) { 489 /* 490 * this entry is filtered, we don't need to do 491 * any more processing. 492 */ 493 if (verbose) { 494 (void) printf("Filtered: Need " 495 "Addition of:\n\t"); 496 print_elem(stdout, cur); 497 } 498 continue; 499 } 500 501 /* 502 * Search package database for file. 503 */ 504 e = find_elem(&second_list, cur, NO_FOLLOW_LINK); 505 506 /* 507 * It is possible for arch specific files to be found 508 * in a protodir but listed as arch independent in a 509 * protolist file. If this is a protodir vs. a 510 * protolist we will make that check. 511 */ 512 if (!e && (first_list.type == PROTODIR_LIST) && 513 (cur->arch != P_ISA) && 514 (second_list.type != PROTODIR_LIST)) { 515 /* 516 * do a lookup for same file, but as type ISA. 517 */ 518 e = find_elem_isa(&second_list, cur, 519 NO_FOLLOW_LINK); 520 } 521 522 if (!e && (first_list.type != PROTODIR_LIST) && 523 (cur->arch == P_ISA) && 524 (second_list.type == PROTODIR_LIST)) { 525 /* 526 * do a lookup for same file, but as any 527 * type but ISA 528 */ 529 e = find_elem_mach(&second_list, cur, 530 NO_FOLLOW_LINK); 531 } 532 533 if (e == NULL) 534 print_elem(need_add_fp, cur); 535 else { 536 do_compare(cur, e, 537 first_list.type - second_list.type); 538 e->flag |= VISITED_F; 539 } 540 } 541 } 542 } 543 544 static int 545 read_in_file(const char *file_name, elem_list *list) 546 { 547 struct stat st_buf; 548 int count = 0; 549 550 if (stat(file_name, &st_buf) == 0) { 551 if (S_ISREG(st_buf.st_mode)) { 552 if (verbose) { 553 (void) printf("file(%s): trying to process " 554 "as protolist...\n", file_name); 555 } 556 count = read_in_protolist(file_name, list, verbose); 557 } else if (S_ISDIR(st_buf.st_mode)) { 558 if (verbose) 559 (void) printf("directory(%s): trying to " 560 "process as protodir...\n", file_name); 561 count = read_in_protodir(file_name, list, verbose); 562 } else { 563 (void) fprintf(stderr, 564 "%s not a file or a directory.\n", file_name); 565 usage(); 566 exit(1); 567 } 568 } else { 569 perror(file_name); 570 usage(); 571 exit(1); 572 } 573 574 return (count); 575 } 576 577 /* ARGSUSED */ 578 static int 579 set_values(const char *fname, const struct stat *sbp, int otype, 580 struct FTW *ftw) 581 { 582 elem *ep; 583 uid_t uid; 584 gid_t gid; 585 elem keyelem; 586 mode_t perm; 587 588 if (fname[0] == '\0' || fname[1] == '\0' || fname[2] == '\0') 589 return (0); 590 /* skip leading "./" */ 591 fname += 2; 592 switch (otype) { 593 case FTW_F: 594 case FTW_D: 595 case FTW_DP: 596 if (strlcpy(keyelem.name, fname, sizeof (keyelem.name)) >= 597 sizeof (keyelem.name)) { 598 (void) fprintf(stderr, "%s: %s: name too long\n", 599 myname, fname); 600 return (1); 601 } 602 keyelem.arch = P_ISA; 603 ep = find_elem(&first_list, &keyelem, NO_FOLLOW_LINK); 604 if (ep == NULL) { 605 ep = find_elem_mach(&first_list, &keyelem, 606 NO_FOLLOW_LINK); 607 } 608 /* 609 * Do nothing if this is a hard or symbolic link, 610 * since links don't have this information. 611 * 612 * Assume it's a file on the exception list if it's 613 * not found in the packaging. Those are root:bin 755. 614 */ 615 if (ep != NULL && 616 (ep->file_type == SYM_LINK_T || ep->file_type == LINK_T)) { 617 return (0); 618 } 619 if (!set_group) { 620 gid = -1; 621 } else if (ep == NULL) { 622 gid = 0; 623 } else if ((gid = stdfind(ep->group, groupnames)) == -1) { 624 (void) fprintf(stderr, "%s: %s: group '%s' unknown\n", 625 myname, fname, ep->group); 626 return (1); 627 } 628 if (!set_user) { 629 uid = -1; 630 } else if (ep == NULL) { 631 uid = 2; 632 } else if ((uid = stdfind(ep->owner, usernames)) == -1) { 633 (void) fprintf(stderr, "%s: %s: user '%s' unknown\n", 634 myname, fname, ep->owner); 635 return (1); 636 } 637 if ((set_group && gid != -1 && gid != sbp->st_gid) || 638 (set_user && uid != -1 && uid != sbp->st_uid)) { 639 if (verbose) { 640 const char *owner, *group; 641 642 owner = ep == NULL ? "root" : ep->owner; 643 group = ep == NULL ? "bin" : ep->group; 644 if (set_group && set_user) { 645 (void) printf("chown %s:%s %s\n", 646 owner, group, fname); 647 } else if (set_user) { 648 (void) printf("chown %s %s\n", owner, 649 fname); 650 } else { 651 (void) printf("chgrp %s %s\n", group, 652 fname); 653 } 654 } 655 if (lchown(fname, uid, gid) == -1) { 656 perror(fname); 657 return (1); 658 } 659 } 660 perm = ep == NULL ? 0755 : ep->perm; 661 if (set_perm && ((perm ^ sbp->st_mode) & ~S_IFMT) != 0) { 662 if (verbose) 663 (void) printf("chmod %lo %s\n", perm, fname); 664 if (chmod(fname, perm) == -1) { 665 perror(fname); 666 return (1); 667 } 668 } 669 return (0); 670 case FTW_DNR: 671 case FTW_NS: 672 (void) fprintf(stderr, "%s: %s: permission denied\n", 673 myname, fname); 674 return (1); 675 case FTW_SL: 676 case FTW_SLN: 677 return (0); 678 default: 679 return (1); 680 } 681 } 682 683 int 684 main(int argc, char **argv) 685 { 686 int errflg = 0; 687 int i, c; 688 int list_filtered_exceptions = NULL; 689 int n_proto_refs = 0; 690 int n_exception_files = 0; 691 char *proto_refs[MAX_PROTO_REFS]; 692 char *exception_files[MAX_EXCEPTION_FILES]; 693 struct stat st_buf; 694 695 if ((myname = argv[0]) == NULL) 696 myname = "protocmp"; 697 698 while ((c = getopt(argc, argv, "gupGUPlmsLe:vd:")) != EOF) { 699 switch (c) { 700 case 's': 701 check_sym = 0; 702 break; 703 case 'm': 704 check_majmin = 0; 705 break; 706 case 'g': 707 check_group = 0; 708 break; 709 case 'G': 710 set_group = 1; 711 break; 712 case 'u': 713 check_user = 0; 714 break; 715 case 'U': 716 set_user = 1; 717 break; 718 case 'l': 719 check_link = 0; 720 break; 721 case 'p': 722 check_perm = 0; 723 break; 724 case 'P': 725 set_perm = 1; 726 break; 727 case 'e': 728 if (n_exception_files >= MAX_EXCEPTION_FILES) { 729 errflg++; 730 (void) fprintf(stderr, 731 "Only %d exception files supported\n", 732 MAX_EXCEPTION_FILES); 733 } else { 734 exception_files[n_exception_files++] = optarg; 735 } 736 break; 737 case 'L': 738 list_filtered_exceptions++; 739 break; 740 case 'v': 741 verbose++; 742 break; 743 case 'd': 744 if (n_proto_refs >= MAX_PROTO_REFS) { 745 errflg++; 746 (void) fprintf(stderr, 747 "Only %d proto references supported\n", 748 MAX_PROTO_REFS); 749 } else { 750 proto_refs[n_proto_refs++] = optarg; 751 } 752 break; 753 case '?': 754 default: 755 errflg++; 756 break; 757 } 758 } 759 760 if (argc == optind || n_proto_refs == 0) { 761 usage(); 762 exit(1); 763 } 764 765 if (set_group || set_user || set_perm) { 766 if (optind != argc - 1) { 767 usage(); 768 exit(1); 769 } 770 if (stat(argv[optind], &st_buf) == -1) { 771 perror(argv[optind]); 772 exit(1); 773 } 774 if (!S_ISDIR(st_buf.st_mode)) { 775 (void) fprintf(stderr, "%s: %s: not a directory\n", 776 myname, argv[optind]); 777 exit(1); 778 } 779 } 780 781 init_list(&first_list, HASH_SIZE); 782 init_list(&second_list, HASH_SIZE); 783 init_list(&exception_list, HASH_SIZE); 784 785 for (i = 0; i < n_exception_files; i++) { 786 (void) read_in_exceptions(exception_files[i], verbose); 787 } 788 789 for (i = 0; i < n_proto_refs; i++) { 790 first_file_name = proto_refs[i]; 791 (void) read_in_file(first_file_name, &first_list); 792 } 793 794 if (set_group || set_user || set_perm) { 795 if (chdir(argv[optind]) == -1) { 796 perror(argv[optind]); 797 exit(1); 798 } 799 i = nftw(".", set_values, MAX_DEPTH, FTW_PHYS|FTW_DEPTH); 800 if (i == -1) { 801 perror("nftw"); 802 i = 1; 803 } 804 exit(i); 805 } 806 807 for (i = optind; i < argc; i++) { 808 second_file_name = argv[i]; 809 (void) read_in_file(second_file_name, &second_list); 810 } 811 812 open_output_files(); 813 814 if (verbose) 815 (void) puts("comparing build to packages..."); 816 817 check_first_vs_second(list_filtered_exceptions); 818 819 if (verbose) 820 (void) puts("checking over packages..."); 821 check_second_vs_first(list_filtered_exceptions); 822 823 close_output_files(); 824 825 print_results(); 826 827 clean_up(); 828 829 return (0); 830 } 831