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