1/* 2 * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23#include <CoreFoundation/CoreFoundation.h> 24#include <CoreFoundation/CFBundlePriv.h> 25 26#include <IOKit/kext/OSKext.h> 27#include <IOKit/kext/OSKextPrivate.h> 28 29#include "kextfind_main.h" 30#include "kextfind_tables.h" 31#include "kextfind_query.h" 32#include "kextfind_commands.h" 33#include "kextfind_report.h" 34#include "QEQuery.h" 35 36/******************************************************************************* 37* Misc. macros. 38*******************************************************************************/ 39 40#define kKextSuffix ".kext" 41 42/******************************************************************************* 43* Global variables (non-static referenced by utility.c). 44*******************************************************************************/ 45 46const char * progname = "(unknown)"; 47 48#pragma mark Main Routine 49/******************************************************************************* 50* Global variables. 51*******************************************************************************/ 52int main(int argc, char * const *argv) 53{ 54 int result = EX_OSERR; 55 56 CFIndex count, i; 57 58 QEQueryRef query = NULL; 59 struct querySetup * queryCallback = queryCallbackList; 60 61 QEQueryRef reportQuery = NULL; 62 struct querySetup * reportCallback = reportCallbackList; 63 uint32_t reportStartIndex; 64 65 QueryContext queryContext; 66 Boolean queryStarted = false; 67 uint32_t numArgsUsed = 0; 68 69 OSKextRef theKext = NULL; // don't release 70 CFArrayRef allKexts = NULL; // must release 71 72 bzero(&queryContext, sizeof(queryContext)); 73 74 /***** 75 * Find out what the program was invoked as. 76 */ 77 progname = rindex(argv[0], '/'); 78 if (progname) { 79 progname++; // go past the '/' 80 } else { 81 progname = (char *)argv[0]; 82 } 83 84 /* Set the OSKext log callback right away. 85 */ 86 OSKextSetLogOutputFunction(&tool_log); 87 88 result = readArgs(argc, argv, &queryContext); 89 if (result != EX_OK) { 90 if (result == kKextfindExitHelp) { 91 result = EX_OK; 92 } 93 goto finish; 94 } 95 96 result = checkArgs(&queryContext); 97 if (result != EX_OK) { 98 goto finish; 99 } 100 101 if (queryContext.defaultArch) { 102 OSKextSetArchitecture(queryContext.defaultArch); 103 } 104 105 /***** 106 * Set up the query. 107 */ 108 query = QEQueryCreate(&queryContext); 109 if (!query) { 110 OSKextLog(/* kext */ NULL, 111 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 112 "Can't create query"); 113 goto finish; 114 } 115 116 while (queryCallback->longName) { 117 if (queryCallback->parseCallback) { 118 QEQuerySetParseCallbackForPredicate(query, queryCallback->longName, 119 queryCallback->parseCallback); 120 if (queryCallback->shortName) { 121 QEQuerySetSynonymForPredicate(query, queryCallback->shortName, 122 queryCallback->longName); 123 } 124 } 125 if (queryCallback->evalCallback) { 126 QEQuerySetEvaluationCallbackForPredicate(query, 127 queryCallback->longName, 128 queryCallback->evalCallback); 129 } 130 queryCallback++; 131 } 132 QEQuerySetSynonymForPredicate(query, CFSTR("!"), CFSTR(kQEQueryTokenNot)); 133 134 numArgsUsed = optind; 135 136 /* If we're not immediately doing a report spec, parse the query. 137 */ 138 if (argv[numArgsUsed] && strcmp(argv[numArgsUsed], kKeywordReport)) { 139 while (QEQueryAppendElementFromArgs(query, argc - numArgsUsed, 140 &argv[numArgsUsed], &numArgsUsed)) { 141 142 queryStarted = true; 143 if (argv[numArgsUsed] && !strcmp(argv[numArgsUsed], kKeywordReport)) { 144 break; 145 } 146 } 147 148 } 149 150 if (QEQueryLastError(query) != kQEQueryErrorNone) { 151 switch (QEQueryLastError(query)) { 152 case kQEQueryErrorNoMemory: 153 OSKextLogMemError(); 154 break; 155 case kQEQueryErrorEmptyGroup: 156 OSKextLog(/* kext */ NULL, 157 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 158 "Empty group near arg #%d.", numArgsUsed); 159 break; 160 case kQEQueryErrorSyntax: 161 OSKextLog(/* kext */ NULL, 162 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 163 "Query syntax error near '%s' (arg #%d).", 164 argv[numArgsUsed], numArgsUsed); 165 break; 166 case kQEQueryErrorNoParseCallback: 167 if (queryStarted) { 168 OSKextLog(/* kext */ NULL, 169 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 170 "Expected query predicate, found '%s' (arg #%d).", 171 argv[numArgsUsed], numArgsUsed); 172 } else { 173 OSKextLog(/* kext */ NULL, 174 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 175 "Unknown option/query predicate '%s' (arg #%d).", 176 argv[numArgsUsed], numArgsUsed); 177 usage(kUsageLevelBrief); 178 } 179 break; 180 case kQEQueryErrorInvalidOrMissingArgument: 181 OSKextLog(/* kext */ NULL, 182 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 183 "Invalid/missing option or argument for '%s' (arg #%d).", 184 argv[numArgsUsed], numArgsUsed); 185 break; 186 case kQEQueryErrorParseCallbackFailed: 187 OSKextLog(/* kext */ NULL, 188 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 189 "Query parsing callback failed."); 190 break; 191 default: 192 break; 193 } 194 goto finish; 195 } 196 197 if (!QEQueryIsComplete(query)) { 198 OSKextLog(/* kext */ NULL, 199 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 200 "Unbalanced groups or trailing operator."); 201 goto finish; 202 } 203 204 /**************************************** 205 */ 206 if (argv[numArgsUsed] && !strcmp(argv[numArgsUsed], kKeywordReport)) { 207 208 numArgsUsed++; 209 210 if (argv[numArgsUsed] && !strcmp(argv[numArgsUsed], kNoReportHeader)) { 211 numArgsUsed++; 212 queryContext.reportStarted = true; // cause header to be skipped 213 } 214 215 if (queryContext.commandSpecified) { 216 OSKextLog(/* kext */ NULL, 217 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 218 "Can't do report; query has commands."); 219 goto finish; 220 } 221 222 reportQuery = QEQueryCreate(&queryContext); 223 if (!reportQuery) { 224 OSKextLog(/* kext */ NULL, 225 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 226 "Can't create report engine."); 227 goto finish; 228 } 229 QEQuerySetShortCircuits(reportQuery, false); 230 231 while (reportCallback->longName) { 232 if (reportCallback->parseCallback) { 233 QEQuerySetParseCallbackForPredicate(reportQuery, 234 reportCallback->longName, 235 reportCallback->parseCallback); 236 if (reportCallback->shortName) { 237 QEQuerySetSynonymForPredicate(reportQuery, 238 reportCallback->shortName, 239 reportCallback->longName); 240 } 241 } 242 if (reportCallback->evalCallback) { 243 QEQuerySetEvaluationCallbackForPredicate(reportQuery, 244 reportCallback->longName, 245 reportCallback->evalCallback); 246 } 247 reportCallback++; 248 } 249 250 reportStartIndex = numArgsUsed; 251 252 while (QEQueryAppendElementFromArgs(reportQuery, argc - numArgsUsed, 253 &argv[numArgsUsed], &numArgsUsed)) { 254 } 255 256 if (reportStartIndex == numArgsUsed) { 257 OSKextLog(/* kext */ NULL, 258 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 259 "No report predicates specified."); 260 usage(kUsageLevelBrief); 261 goto finish; 262 } 263 264 if (QEQueryLastError(reportQuery) != kQEQueryErrorNone) { 265 switch (QEQueryLastError(reportQuery)) { 266 case kQEQueryErrorNoMemory: 267 OSKextLogMemError(); 268 break; 269 case kQEQueryErrorEmptyGroup: 270 OSKextLog(/* kext */ NULL, 271 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 272 "Empty group near arg #%d.", numArgsUsed); 273 break; 274 case kQEQueryErrorSyntax: 275 OSKextLog(/* kext */ NULL, 276 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 277 "Report syntax error near '%s' (arg #%d).", 278 argv[numArgsUsed], numArgsUsed); 279 break; 280 case kQEQueryErrorNoParseCallback: 281 if (1) { 282 OSKextLog(/* kext */ NULL, 283 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 284 "Expected report predicate, found '%s' (arg #%d).", 285 argv[numArgsUsed], numArgsUsed); 286 } else { 287 OSKextLog(/* kext */ NULL, 288 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 289 "Unknown option/report predicate '%s' (arg #%d).", 290 argv[numArgsUsed], numArgsUsed); 291 } 292 break; 293 case kQEQueryErrorInvalidOrMissingArgument: 294 OSKextLog(/* kext */ NULL, 295 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 296 "Invalid/missing option or argument for '%s' (arg #%d).", 297 argv[numArgsUsed], numArgsUsed); 298 break; 299 case kQEQueryErrorParseCallbackFailed: 300 OSKextLog(/* kext */ NULL, 301 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 302 "Query parsing callback failed."); 303 break; 304 default: 305 break; 306 } 307 goto finish; 308 } 309 310 if (!QEQueryIsComplete(reportQuery)) { 311 OSKextLog(/* kext */ NULL, 312 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 313 "Unbalanced groups or trailing operator."); 314 goto finish; 315 } 316 } 317 318 if ((int)numArgsUsed < argc) { 319 OSKextLog(/* kext */ NULL, 320 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 321 "Leftover elements '%s'... (arg #%d).", 322 argv[numArgsUsed], numArgsUsed); 323 goto finish; 324 } 325 326 /***** 327 * Create the set of kexts we'll be searching/reporting. 328 */ 329 OSKextSetRecordsDiagnostics(kOSKextDiagnosticsFlagAll); 330 OSKextSetUsesCaches(false); 331 allKexts = OSKextCreateKextsFromURLs(kCFAllocatorDefault, 332 queryContext.searchURLs); 333 if (!allKexts || !CFArrayGetCount(allKexts)) { 334 // see comment below about clang not understanding exit() :P 335 OSKextLog(/* kext */ NULL, 336 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 337 "No kernel extensions found."); 338 result = EX_SOFTWARE; 339 goto finish; 340 } 341 342 if (queryContext.checkLoaded) { 343 if (kOSReturnSuccess != OSKextReadLoadedKextInfo( 344 /* kextIdentifiers (all kexts) */ NULL, 345 /* flushDependencies? */ false)) { 346 347 result = EX_OSERR; 348 goto finish; 349 } 350 } 351 352 if (result != EX_OK) { 353 goto finish; 354 } 355 356 /***** 357 * Run the query! 358 */ 359 count = CFArrayGetCount(allKexts); 360 for (i = 0; i < count; i++) { 361 362 theKext = (OSKextRef)CFArrayGetValueAtIndex(allKexts, i); 363 364 if (QEQueryEvaluate(query, theKext)) { 365 if (!queryContext.commandSpecified) { 366 if (!reportQuery) { 367 printKext(theKext, queryContext.pathSpec, 368 queryContext.extraInfo, '\n'); 369 } else { 370 if (!queryContext.reportStarted) { 371 queryContext.reportRowStarted = false; 372 QEQueryEvaluate(reportQuery, theKext); 373 printf("\n"); 374 if ((QEQueryLastError(reportQuery) != kQEQueryErrorNone)) { 375 OSKextLog(/* kext */ NULL, 376 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 377 "Report evaluation error; aborting."); 378 goto finish; 379 } 380 queryContext.reportStarted = true; 381 } 382 queryContext.reportRowStarted = false; 383 QEQueryEvaluate(reportQuery, theKext); 384 printf("\n"); 385 if ((QEQueryLastError(reportQuery) != kQEQueryErrorNone)) { 386 OSKextLog(/* kext */ NULL, 387 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 388 "Report evaluation error; aborting."); 389 goto finish; 390 } 391 } 392 } 393 } else if (QEQueryLastError(query) != kQEQueryErrorNone) { 394 OSKextLog(/* kext */ NULL, 395 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 396 "Query evaluation error; aborting."); 397 goto finish; 398 } 399 } 400 401 result = EX_OK; 402 403finish: 404 // clang's analyzer now knows exit() never returns but doesn't realize 405 // it frees resources. :P 406 exit(result); // we don't need to do the cleanup when exiting. 407 408 if (query) QEQueryFree(query); 409 if (allKexts) CFRelease(allKexts); 410 411 exit(result); 412 return result; 413} 414 415#pragma mark Major Subroutines 416/******************************************************************************* 417* Major Subroutines 418*******************************************************************************/ 419ExitStatus readArgs( 420 int argc, 421 char * const * argv, 422 QueryContext * toolArgs) 423{ 424 ExitStatus result = EX_USAGE; 425 int opt_char = 0; 426 int last_optind; // for recovering from getopt failures 427 Boolean readingOptions = true; 428 CFURLRef scratchURL = NULL; // must release 429 uint32_t i; 430 431 bzero(toolArgs, sizeof(*toolArgs)); 432 433 /***** 434 * Allocate collection objects needed for command line argument processing. 435 */ 436 if (!createCFMutableArray(&toolArgs->searchURLs, &kCFTypeArrayCallBacks)) { 437 OSKextLogMemError(); 438 result = EX_OSERR; 439 goto finish; 440 } 441 442 toolArgs->assertiveness = kKextfindPicky; 443 444 /***** 445 * Process command-line arguments. 446 */ 447 opterr = 0; 448 last_optind = optind; 449 while (readingOptions && 450 -1 != (opt_char = getopt_long_only(argc, argv, kOPT_CHARS, 451 opt_info, NULL))) { 452 453 switch (opt_char) { 454 455 case kOptHelp: 456 usage(kUsageLevelFull); 457 result = kKextfindExitHelp; 458 goto finish; 459 break; 460 461 case kOptCaseInsensitive: 462 toolArgs->caseInsensitive = true; 463 break; 464 465 case kOptSearchItem: 466 if (!checkSearchItem(optarg, /* log? */ true)) { 467 goto finish; 468 } 469 470 SAFE_RELEASE_NULL(scratchURL); 471 scratchURL = CFURLCreateFromFileSystemRepresentation( 472 kCFAllocatorDefault, 473 (const UInt8 *)optarg, strlen(optarg), true); 474 if (!scratchURL) { 475 result = EX_OSERR; 476 OSKextLogMemError(); 477 goto finish; 478 } 479 CFArrayAppendValue(toolArgs->searchURLs, scratchURL); 480 break; 481 482 case kOptSubstring: 483 toolArgs->substrings = true; 484 break; 485 486 case kOptSystemExtensions: 487 { 488 CFArrayRef sysExtFolders = 489 OSKextGetSystemExtensionsFolderURLs(); 490 CFArrayAppendArray(toolArgs->searchURLs, 491 sysExtFolders, RANGE_ALL(sysExtFolders)); 492 } 493 break; 494 495 case 0: 496 switch (longopt) { 497 498 case kLongOptQueryPredicate: 499 optind = last_optind; 500 readingOptions = false; 501 if (argv[last_optind] && (argv[last_optind][0] != '-')) { 502 // probably a directory; stupid getopt_long_only() 503 break; 504 } 505 break; 506 507#ifdef EXTRA_INFO 508 case kLongOptExtraInfo: 509 toolArgs->extraInfo = true; 510 break; 511#endif 512 case kLongOptRelativePaths: 513 /* Last one specified wins! */ 514 toolArgs->pathSpec = kPathsRelative; 515 break; 516 517 case kLongOptDefaultArch: 518 /* Last one specified wins! */ 519 toolArgs->defaultArch = NXGetArchInfoFromName(optarg); 520 if (!toolArgs->defaultArch) { 521 OSKextLog(/* kext */ NULL, 522 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 523 "Unknown architecture %s.", optarg); 524 goto finish; 525 } 526 break; 527 528 case kLongOptNoPaths: 529 /* Last one specified wins! */ 530 toolArgs->pathSpec = kPathsNone; 531 break; 532 533#ifdef MEEK_PICKY 534 case kLongOptMeek: 535 toolArgs->assertiveness = kKextfindMeek; 536 break; 537 538 case kLongOptPicky: 539 toolArgs->assertiveness = kKextfindPicky; 540 break; 541#endif 542 543 default: 544 OSKextLog(/* kext */ NULL, 545 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 546 "Internal argument processing error."); 547 result = EX_SOFTWARE; 548 goto finish; 549 break; 550 551 } 552 longopt = 0; 553 break; 554 555 default: 556 /* getopt_long() gives us '?' if we turn off errors, so just 557 * move on to query parsing. Sometimes optind jumps ahead too 558 * far, so we restore it to the value before the call to 559 * getopt_long_only() -- we can't just decrement it. 560 */ 561 optind = last_optind; 562 readingOptions = false; 563 break; 564 } 565 566 last_optind = optind; 567 } 568 569 /***** 570 * Record the kext & directory names from the command line. 571 */ 572 for (i = optind; (int)i < argc; i++) { 573 SAFE_RELEASE_NULL(scratchURL); 574 575 /* If the arg isn't a directory, break from the loop, and we'll 576 * process remaining args as query elements. 577 */ 578 if (!checkSearchItem(argv[i], /* log? */ false)) { 579 break; 580 } 581 scratchURL = CFURLCreateFromFileSystemRepresentation( 582 kCFAllocatorDefault, 583 (const UInt8 *)argv[i], strlen(argv[i]), true); 584 if (!scratchURL) { 585 result = EX_OSERR; 586 OSKextLogMemError(); 587 goto finish; 588 } 589 CFArrayAppendValue(toolArgs->searchURLs, scratchURL); 590 optind++; 591 } 592 593 if (!CFArrayGetCount(toolArgs->searchURLs)) { 594 CFArrayRef sysExtFolders = 595 OSKextGetSystemExtensionsFolderURLs(); 596 CFArrayAppendArray(toolArgs->searchURLs, 597 sysExtFolders, RANGE_ALL(sysExtFolders)); 598 } 599 600 result = EX_OK; 601 602finish: 603 SAFE_RELEASE(scratchURL); 604 605 if (result == EX_USAGE) { 606 usage(kUsageLevelBrief); 607 } 608 return result; 609} 610 611/******************************************************************************* 612*******************************************************************************/ 613ExitStatus checkArgs(QueryContext * toolArgs __unused) 614{ 615 ExitStatus result = EX_USAGE; 616 617 result = EX_OK; 618 619 if (result == EX_USAGE) { 620 usage(kUsageLevelBrief); 621 } 622 return result; 623} 624 625/******************************************************************************* 626* checkSearchItem() 627* 628* This function makes sure that a given directory exists, and is writeable. 629*******************************************************************************/ 630Boolean checkSearchItem(const char * pathname, Boolean logFlag) 631{ 632 int result = false; 633 struct stat stat_buf; 634 635 if (stat(pathname, &stat_buf) != 0) { 636 if (logFlag) { 637 OSKextLog(/* kext */ NULL, 638 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 639 "Can't stat %s - %s.", pathname, strerror(errno)); 640 } 641 goto finish; 642 } 643 644 if ((stat_buf.st_mode & S_IFMT) != S_IFDIR) { 645 if (logFlag) { 646 OSKextLog(/* kext */ NULL, 647 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 648 "%s - not a kext or directory.", 649 pathname); 650 } 651 goto finish; 652 } 653 654 result = true; 655 656finish: 657 return result; 658} 659 660/******************************************************************************* 661*******************************************************************************/ 662fat_iterator createFatIteratorForKext(OSKextRef aKext) 663{ 664 fat_iterator result = NULL; 665 CFURLRef kextURL = NULL; // do not release 666 CFURLRef executableURL = NULL; // must release 667 char executablePath[PATH_MAX]; 668 669 kextURL = OSKextGetURL(aKext); 670 if (!kextURL) { 671 // xxx - log it? 672 goto finish; 673 } 674 executableURL = _CFBundleCopyExecutableURLInDirectory(kextURL); 675 if (!executableURL) { 676 goto finish; 677 } 678 if (!CFURLGetFileSystemRepresentation(executableURL, 679 /* resolveToBase? */ true, (UInt8 *)executablePath, 680 sizeof(executablePath))) { 681 682 OSKextLogStringError(aKext); 683 goto finish; 684 } 685 result = fat_iterator_open(executablePath, /* macho_only? */ true); 686 687finish: 688 SAFE_RELEASE(executableURL); 689 return result; 690} 691 692/******************************************************************************* 693* usage() 694*******************************************************************************/ 695void usage(UsageLevel usageLevel) 696{ 697 FILE * stream = stderr; 698 699 fprintf(stream, 700 "usage: %s [options] [directory or extension ...] [query]\n" 701 " [-report [-no-header] report_predicate...]" 702 "\n", 703 progname); 704 705 if (usageLevel == kUsageLevelBrief) { 706 fprintf(stream, "use %s -%s for a list of options\n", 707 progname, kOptNameHelp); 708 return; 709 } 710 711 fprintf(stream, "Options\n"); 712 713 fprintf(stream, " -%s -%s\n", 714 kOptNameHelp, kOptNameCaseInsensitive); 715#ifdef EXTRA_INFO 716 fprintf(stream, " -%s -%s\n", 717 kOptNameExtraInfo, kOptNameNulTerminate); 718#endif 719 fprintf(stream, " -%s -%s\n", 720 kOptNameRelativePaths, kOptNameSubstring); 721 fprintf(stream, " -%s\n", 722 kOptNameNoPaths); 723 724 fprintf(stream, "\n"); 725 726 fprintf(stream, "Handy Query Predicates\n"); 727 728 fprintf(stream, " %s [-s] [-i] id\n", kPredNameBundleID); 729 fprintf(stream, " %s [-s] [-i] id\n", kPredNameBundleName); 730 fprintf(stream, " %s [-s] [-i] name value\n", kPredNameMatchProperty); 731 fprintf(stream, " %s [-s] [-i] name value\n", kPredNameProperty); 732 733 fprintf(stream, "\n"); 734 735 fprintf(stream, " %s %s\n", 736 kPredNameLoaded, kPredNameNonloadable); 737 fprintf(stream, " %s %s\n", 738 kPredNameInvalid, kPredNameInauthentic); 739 fprintf(stream, " %s %s\n", 740 kPredNameDependenciesMissing, kPredNameWarnings); 741 742 fprintf(stream, "\n"); 743 744 fprintf(stream, " %s arch1[,arch2...] %s arch1[,arch2...]\n", 745 kPredNameArch, kPredNameArchExact); 746 fprintf(stream, " %s %s\n", 747 kPredNameExecutable, kPredNameIsLibrary); 748 fprintf(stream, " %s symbol %s symbol\n", 749 kPredNameDefinesSymbol, kPredNameReferencesSymbol); 750 751 fprintf(stream, "\n"); 752 753 fprintf(stream, "See the man page for the full list.\n"); 754 755 return; 756} 757