/* ** Copyright (C) 2003-2007 by Carnegie Mellon University. ** ** @OPENSOURCE_HEADER_START@ ** ** Use of the SILK system and related source code is subject to the terms ** of the following licenses: ** ** GNU Public License (GPL) Rights pursuant to Version 2, June 1991 ** Government Purpose License Rights (GPLR) pursuant to DFARS 252.225-7013 ** ** NO WARRANTY ** ** ANY INFORMATION, MATERIALS, SERVICES, INTELLECTUAL PROPERTY OR OTHER ** PROPERTY OR RIGHTS GRANTED OR PROVIDED BY CARNEGIE MELLON UNIVERSITY ** PURSUANT TO THIS LICENSE (HEREINAFTER THE "DELIVERABLES") ARE ON AN ** "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY ** KIND, EITHER EXPRESS OR IMPLIED AS TO ANY MATTER INCLUDING, BUT NOT ** LIMITED TO, WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE, ** MERCHANTABILITY, INFORMATIONAL CONTENT, NONINFRINGEMENT, OR ERROR-FREE ** OPERATION. CARNEGIE MELLON UNIVERSITY SHALL NOT BE LIABLE FOR INDIRECT, ** SPECIAL OR CONSEQUENTIAL DAMAGES, SUCH AS LOSS OF PROFITS OR INABILITY ** TO USE SAID INTELLECTUAL PROPERTY, UNDER THIS LICENSE, REGARDLESS OF ** WHETHER SUCH PARTY WAS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. ** LICENSEE AGREES THAT IT WILL NOT MAKE ANY WARRANTY ON BEHALF OF ** CARNEGIE MELLON UNIVERSITY, EXPRESS OR IMPLIED, TO ANY PERSON ** CONCERNING THE APPLICATION OF OR THE RESULTS TO BE OBTAINED WITH THE ** DELIVERABLES UNDER THIS LICENSE. ** ** Licensee hereby agrees to defend, indemnify, and hold harmless Carnegie ** Mellon University, its trustees, officers, employees, and agents from ** all claims or demands made against them (and any related losses, ** expenses, or attorney's fees) arising out of, or relating to Licensee's ** and/or its sub licensees' negligent use or willful misuse of or ** negligent conduct or willful misconduct regarding the Software, ** facilities, or other rights or assistance granted by Carnegie Mellon ** University under this License, including, but not limited to, any ** claims of product liability, personal injury, death, damage to ** property, or violation of any laws or regulations. ** ** Carnegie Mellon University Software Engineering Institute authored ** documents are sponsored by the U.S. Department of Defense under ** Contract F19628-00-C-0003. Carnegie Mellon University retains ** copyrights in all material produced under this contract. The U.S. ** Government retains a non-exclusive, royalty-free license to publish or ** reproduce these documents, or allow others to do so, for U.S. ** Government purposes only pursuant to the copyright license under the ** contract clause at 252.227.7013. ** ** @OPENSOURCE_HEADER_END@ */ /* ** Quick application to print the IPs in a set file ** */ #include "silk.h" RCSIDENT("$SiLK: rwsetcat.c 8269 2007-08-03 18:54:48Z mthomas $"); #include "utils.h" #include "iptree.h" #include "skprintnets.h" /* LOCAL DEFINES AND TYPEDEFS */ /* Where to print usage (--help) */ #define USAGE_FH stdout /* Isolate octets */ #define OCTET_A(x) (0xFF000000 & ((uint32_t)(x))) #define OCTET_B(x) (0x00FF0000 & ((uint32_t)(x))) #define OCTET_C(x) (0x0000FF00 & ((uint32_t)(x))) #define OCTET_X(x) (0x000000E0 & ((uint32_t)(x))) /* LOCAL FUNCTIONS */ /* main routine to process a file */ static void readsetPrintNetwork(skstream_t *outstream,const skIPTree_t *ipset); static int readsetProcessFile(skstream_t *outstream, const char *fileName); static void appUsageLong(void); /* never returns */ static void appTeardown(void); static void appSetup(int argc, char **argv); /* never returns */ static int appOptionsHandler(clientData cData, int opt_index, char *opt_arg); /* LOCAL VARIABLES */ /* where to send output */ static skstream_t *stream_out; /* index of first option that is not handled by the options handler. * If this is equal to argc, then input is from stdin. */ static int arg_index = 0; /* output delimiter for network structure */ static char output_delimiter = '|'; /* type of network structure to print */ static const char *net_structure = NULL; /* paging program to use for output */ static const char *pager = NULL; /* option flags */ static struct { /* how to print IPs from iptree.h: enum skIPTreeFormats; def. dot-quad */ int ip_format; /* whether user explicitly gave the print-ips option */ int print_ips; /* whether to count IPs: default no */ int count_ips; /* whether to print statistics; default no */ int statistics; /* output network breakdown of set contents */ int network_structure; /* whether to print IP ranges as LOW|HIGH| */ int ip_ranges; /* whether to surpress fixed-width columnar network structure output */ int no_columns; } optFlags = { SKIP_IPF_DOT, 0, 0, 0, 0, 0, 0 }; /* OPTIONS SETUP */ typedef enum _appOptionsEnum { OPT_COUNT_IPS, OPT_PRINT_IPS, OPT_NETWORK_STRUCTURE, OPT_PRINT_STATISTICS, OPT_CIDR_BLOCKS, OPT_IP_RANGES, OPT_INTEGER_IPS, OPT_ZERO_PAD_IPS, OPT_NO_COLUMNS, OPT_COLUMN_SEPARATOR, OPT_DELIMITED, OPT_PAGER } appOptionsEnum; static struct option appOptions[] = { {"count-ips", NO_ARG, 0, OPT_COUNT_IPS}, {"print-ips", NO_ARG, 0, OPT_PRINT_IPS}, {"network-structure", OPTIONAL_ARG, 0, OPT_NETWORK_STRUCTURE}, {"print-statistics", NO_ARG, 0, OPT_PRINT_STATISTICS}, {"cidr-blocks", NO_ARG, 0, OPT_CIDR_BLOCKS}, {"ip-ranges", NO_ARG, 0, OPT_IP_RANGES}, {"integer-ips", NO_ARG, 0, OPT_INTEGER_IPS}, {"zero-pad-ips", NO_ARG, 0, OPT_ZERO_PAD_IPS}, {"no-columns", NO_ARG, 0, OPT_NO_COLUMNS}, {"column-separator", REQUIRED_ARG, 0, OPT_COLUMN_SEPARATOR}, {"delimited", OPTIONAL_ARG, 0, OPT_DELIMITED}, {"pager", REQUIRED_ARG, 0, OPT_PAGER}, {0,0,0,0} /* sentinel entry */ }; static const char *appHelp[] = { "Print the number of IPs; disables default printing of IPs", "Also print IPs when count or statistics switch is given", ("Print the network structure of the set. Def. TS\n" "\tChoose from TABCXHS for counts by Total address space, /8, /16, /24,\n" "\t/27, and/or /32; with S provides roll-up summaries."), "Print set statistics (min-/max-ip, etc). Def. No", "Print IPs in CIDR block notation. Def. No", "Print IPs as ranges of count|low|high|. Def. No", "Print IP numbers as integers. Def. Dotted decimal", "Print IP numbers as zero-padded dotted-decimal. Def. No", ("When printing network-structure, disable fixed-width\n" "\tcolumnar output. Def. Columnar"), ("When printing network-structure, use specified\n" "\tcharacter between columns. Def. '|'"), "Shortcut for --no-columns --column-sep=CHAR", "Program to invoke to page output. Def. $SILK_PAGER or $PAGER", (char *)NULL /* sentinel entry */ }; /* FUNCTION DEFINITIONS */ /* * appUsageLong(); * * Print complete usage information to USAGE_FH. Pass this * function to skOptionsSetUsageCallback(); optionsParse() will * call this funciton and then exit the program when the --help * option is given. */ static void appUsageLong(void) { #define USAGE_MSG \ ("[SWITCHES] [IPSET_FILES]\n" \ "\tBy default, prints the IPs in the specified IPSET_FILES. Use\n" \ "\tswitches to control format of the outout and to optionally or\n" \ "\tadditionally print the number of IPs in the file, the network\n" \ "\tstructure, or other statistics. If no IPSET_FILEs are given on\n" \ "\tthe command line, the IPset will be read from the standard input.\n") FILE *fh = USAGE_FH; skAppStandardUsage(fh, USAGE_MSG, appOptions, appHelp); } /* * appTeardown() * * Teardown all modules, close all files, and tidy up all * application state. * * This function is idempotent. */ static void appTeardown(void) { static int teardownFlag = 0; if (teardownFlag) { return; } teardownFlag = 1; skStreamDestroy(&stream_out); skAppUnregister(); } /* * appSetup(argc, argv); * * Perform all the setup for this application include setting up * required modules, parsing options, etc. This function should be * passed the same arguments that were passed into main(). * * Returns to the caller if all setup succeeds. If anything fails, * this function will cause the application to exit with a FAILURE * exit status. */ static void appSetup(int argc, char **argv) { int rv; /* verify same number of options and help strings */ assert((sizeof(appHelp)/sizeof(char *)) == (sizeof(appOptions)/sizeof(struct option))); /* register the application */ skAppRegister(argv[0]); skOptionsSetUsageCallback(&appUsageLong); /* initialize globals */ stream_out = NULL; /* register the options */ if (optionsRegister(appOptions, (optHandler)appOptionsHandler, NULL)) { skAppPrintErr("unable to register options"); exit(EXIT_FAILURE); } /* parse options */ arg_index = optionsParse(argc, argv); assert(arg_index <= argc); if (arg_index < 0) { skAppUsage(); /* never returns */ } /* either need name of set file(s) after options or a set file on stdin */ if ((arg_index == argc) && (FILEIsATty(stdin))) { skAppPrintErr("No files on the command line and" " stdin is connected to a terminal"); skAppUsage(); } /* network structure output conflicts with most other output */ if (optFlags.network_structure) { if (optFlags.ip_format & SKIP_IPF_CIDR) { skAppPrintErr("Cannot combine the --%s and --%s switches.", appOptions[OPT_NETWORK_STRUCTURE].name, appOptions[OPT_CIDR_BLOCKS].name); skAppUsage(); } if (optFlags.print_ips) { skAppPrintErr("Cannot combine the --%s and --%s switches.", appOptions[OPT_NETWORK_STRUCTURE].name, appOptions[OPT_PRINT_IPS].name); skAppUsage(); } if (optFlags.count_ips) { skAppPrintErr("Cannot combine the --%s and --%s switches.", appOptions[OPT_NETWORK_STRUCTURE].name, appOptions[OPT_COUNT_IPS].name); skAppUsage(); } if (optFlags.ip_ranges) { skAppPrintErr("Cannot combine the --%s and --%s switches.", appOptions[OPT_NETWORK_STRUCTURE].name, appOptions[OPT_IP_RANGES].name); skAppUsage(); } } if (optFlags.ip_ranges) { if (optFlags.ip_format & SKIP_IPF_CIDR) { skAppPrintErr("Cannot combine the --%s and --%s switches.", appOptions[OPT_IP_RANGES].name, appOptions[OPT_CIDR_BLOCKS].name); skAppUsage(); } optFlags.print_ips = 0; } /* If no output was specified, print the ips */ if (!optFlags.statistics && !optFlags.count_ips && !optFlags.network_structure && !optFlags.print_ips && !optFlags.ip_ranges) { optFlags.print_ips = 1; } /* Create the output stream */ if ((rv = skStreamCreate(&stream_out, SK_IO_WRITE, SK_CONTENT_TEXT)) || (rv = skStreamBind(stream_out, "stdout")) || (rv = skStreamPageOutput(stream_out, pager)) || (rv = skStreamOpen(stream_out))) { skStreamPrintLastErr(stream_out, rv, &skAppPrintErr); exit(EXIT_FAILURE); } if (atexit(appTeardown) < 0) { skAppPrintErr("unable to register appTeardown() with atexit()"); appTeardown(); exit(EXIT_FAILURE); } return; /* OK */ } /* * status = appOptionsHandler(cData, opt_index, opt_arg); * * This function is passed to optionsRegister(); it will be called * by optionsParse() for each user-specified switch that the * application has registered; it should handle the switch as * required---typically by setting global variables---and return 1 * if the switch processing failed or 0 if it succeeded. Returning * a non-zero from from the handler causes optionsParse() to return * a negative value. * * The clientData in 'cData' is typically ignored; 'opt_index' is * the index number that was specified as the last value for each * struct option in appOptions[]; 'opt_arg' is the user's argument * to the switch for options that have a REQUIRED_ARG or an * OPTIONAL_ARG. */ static int appOptionsHandler( clientData UNUSED(cData), int opt_index, char *opt_arg) { switch ((appOptionsEnum)opt_index) { case OPT_PRINT_STATISTICS: optFlags.statistics = 1; break; case OPT_COUNT_IPS: optFlags.count_ips = 1; break; case OPT_NETWORK_STRUCTURE: net_structure = opt_arg; optFlags.network_structure = 1; break; case OPT_PRINT_IPS: optFlags.print_ips = 1; break; case OPT_CIDR_BLOCKS: optFlags.print_ips = 1; optFlags.ip_format = (optFlags.ip_format | SKIP_IPF_CIDR); break; case OPT_IP_RANGES: optFlags.ip_ranges = 1; break; case OPT_INTEGER_IPS: if ((optFlags.ip_format & ~SKIP_IPF_CIDR) != SKIP_IPF_DOT) { skAppPrintErr("Warning: overriding previous output format;\n" "\t will print IPs as integers."); } optFlags.ip_format = (SKIP_IPF_DEC | (optFlags.ip_format & SKIP_IPF_CIDR)); break; case OPT_ZERO_PAD_IPS: if ((optFlags.ip_format & ~SKIP_IPF_CIDR) != SKIP_IPF_DOT) { skAppPrintErr("Warning: overriding previous output format;\n" "\t will print IPs as three-digit dotted decimal."); } optFlags.ip_format = (SKIP_IPF_ZERO | (optFlags.ip_format & SKIP_IPF_CIDR)); break; case OPT_NO_COLUMNS: optFlags.no_columns = 1; break; case OPT_COLUMN_SEPARATOR: output_delimiter = opt_arg[0]; break; case OPT_DELIMITED: optFlags.no_columns = 1; if (opt_arg) { output_delimiter = opt_arg[0]; } break; case OPT_PAGER: pager = opt_arg; break; } return 0; /* OK */ } /* * readsetPrintRanges(outstream, ipset); * * Print IPset in three output columns; where the first is the * number of IPs in the range, the second is the starting IP, the * third is the ending IP. * * COUNT| LOW| HIGH| */ static void readsetPrintRanges( skstream_t *outstream, const skIPTree_t *ipset) { skIPTreeIterator_t iter; uint32_t ipaddr; uint32_t start = 0; uint64_t count = 0; char ip1[SK_NUM2DOT_STRLEN+1]; char ip2[SK_NUM2DOT_STRLEN+1]; int widths[3]; if (optFlags.no_columns) { memset(widths, 0, sizeof(widths)); } else { widths[0] = 10; switch (optFlags.ip_format & ~SKIP_IPF_CIDR) { case SKIP_IPF_DEC: case SKIP_IPF_HEX: widths[1] = widths[2] = 10; break; case SKIP_IPF_ZERO: case SKIP_IPF_DOT: default: widths[1] = widths[2] = 15; break; } } skIPTreeIteratorBind(&iter, ipset); while (skIPTreeIteratorNext(&ipaddr, &iter) == SK_ITERATOR_OK) { if (count == 0) { start = ipaddr; ++count; continue; } if (ipaddr == start+count) { ++count; continue; } switch (optFlags.ip_format & ~SKIP_IPF_CIDR) { case SKIP_IPF_DEC: skStreamPrint(outstream, ("%*" PRIu64 "%c%*" PRIu32 "%c%*" PRIu64 "%c\n"), widths[0], count, output_delimiter, widths[1], start, output_delimiter, widths[2], start+count-1, output_delimiter); break; case SKIP_IPF_HEX: skStreamPrint(outstream, ("%*" PRIu64 "%c%*" PRIx32 "%c%*" PRIx64 "%c\n"), widths[0], count, output_delimiter, widths[1], start, output_delimiter, widths[2], start+count-1, output_delimiter); break; case SKIP_IPF_ZERO: skStreamPrint(outstream, ("%*" PRIu64 "%c%*s%c%*s%c\n"), widths[0], count, output_delimiter, widths[1], num2dot0_r(start, ip1), output_delimiter, widths[2], num2dot0_r(start+count-1, ip2), output_delimiter); break; case SKIP_IPF_DOT: default: skStreamPrint(outstream, ("%*" PRIu64 "%c%*s%c%*s%c\n"), widths[0], count, output_delimiter, widths[1], num2dot_r(start, ip1), output_delimiter, widths[2], num2dot_r(start+count-1, ip2), output_delimiter); break; } start = ipaddr; count = 1; } if (count) { switch (optFlags.ip_format & ~SKIP_IPF_CIDR) { case SKIP_IPF_DEC: skStreamPrint(outstream, ("%*" PRIu64 "%c%*" PRIu32 "%c%*" PRIu64 "%c\n"), widths[0], count, output_delimiter, widths[1], start, output_delimiter, widths[2], start+count-1, output_delimiter); break; case SKIP_IPF_HEX: skStreamPrint(outstream, ("%*" PRIu64 "%c%*" PRIx32 "%c%*" PRIx64 "%c\n"), widths[0], count, output_delimiter, widths[1], start, output_delimiter, widths[2], start+count-1, output_delimiter); break; case SKIP_IPF_ZERO: skStreamPrint(outstream, ("%*" PRIu64 "%c%*s%c%*s%c\n"), widths[0], count, output_delimiter, widths[1], num2dot0_r(start, ip1), output_delimiter, widths[2], num2dot0_r(start+count-1, ip2), output_delimiter); break; case SKIP_IPF_DOT: default: skStreamPrint(outstream, ("%*" PRIu64 "%c%*s%c%*s%c\n"), widths[0], count, output_delimiter, widths[1], num2dot_r(start, ip1), output_delimiter, widths[2], num2dot_r(start+count-1, ip2), output_delimiter); break; } } } /* * readsetPrintNetwork(outstream, ipset); * * Print information about the strucuture of the IPset. */ static void readsetPrintNetwork( skstream_t *outstream, const skIPTree_t *ipset) { skIPTreeIterator_t iter; uint32_t ipaddr; netStruct_t *ns; /* Set up the netStruct */ if (netStructureCreate(&ns, 0 /*no counts*/)) { skAppPrintErr("Error creating network-structure"); } if (netStructureParse(ns, net_structure)) { return; } netStructureSetOutputStream(ns, outstream); netStructureSetDelimiter(ns, output_delimiter); if ( optFlags.no_columns) { netStructureSetNoColumns(ns); } netStructureSetIpFormat(ns, optFlags.ip_format); skIPTreeIteratorBind(&iter, ipset); while (skIPTreeIteratorNext(&ipaddr, &iter) == SK_ITERATOR_OK) { netStructurePrintIP(ipaddr, NULL, ns); } /* * set the last key flag and call it once more, for good measure. * (that way, it closes out blocks after the last key.) */ netStructurePrintFinalize(ns); netStructureDestroy(&ns); } /* * readsetPrintStatistics * * Prints, to outF, statistics of the IPTree ipset. Statistics * printed are the minimum IP, the maximum IP, a count of the class * A blocks, class B blocks (number of nodes), class C blocks, and * a count of the addressBlocks (/27's) used. If integerIP is 0, * the min and max IPs are printed in dotted-quad form; otherwise * they are printed as integers. */ static void readsetPrintStatistics( skstream_t *outstream, const skIPTree_t *ipset, int ip_format) { skIPTreeIterator_t iter; uint32_t ipaddr; uint32_t old_addr; uint32_t num_slash_8; uint32_t num_slash_16; uint32_t num_slash_24; uint32_t num_slash_27; uint32_t min_ip; uint32_t max_ip; uint64_t num_hosts; char ip1[SK_NUM2DOT_STRLEN+1]; char ip2[SK_NUM2DOT_STRLEN+1]; if (skIPTreeIteratorBind(&iter, ipset)) { return; } /* Get first IP */ if (skIPTreeIteratorNext(&ipaddr, &iter) != SK_ITERATOR_OK) { /* empty ipset */ return; } /* first IP */ min_ip = ipaddr; old_addr = ipaddr; num_hosts = 1; num_slash_8 = 1; num_slash_16 = 1; num_slash_24 = 1; num_slash_27 = 1; /* Handle remaining IPs */ while (skIPTreeIteratorNext(&ipaddr, &iter) == SK_ITERATOR_OK) { if (OCTET_A(old_addr) != OCTET_A(ipaddr)) { ++num_slash_8; ++num_slash_16; ++num_slash_24; ++num_slash_27; } else if (OCTET_B(old_addr) != OCTET_B(ipaddr)) { ++num_slash_16; ++num_slash_24; ++num_slash_27; } else if (OCTET_C(old_addr) != OCTET_C(ipaddr)) { ++num_slash_24; ++num_slash_27; } else if (OCTET_X(old_addr) != OCTET_X(ipaddr)) { ++num_slash_27; } ++num_hosts; old_addr = ipaddr; } /* Get the max */ max_ip = ipaddr; skStreamPrint(outstream, "Network Summary\n"); switch (ip_format & ~SKIP_IPF_CIDR) { case SKIP_IPF_DEC: skStreamPrint(outstream, ("\tminimumIP = %10" PRIu32 "\n" "\tmaximumIP = %10" PRIu32 "\n"), min_ip, max_ip); break; case SKIP_IPF_HEX: skStreamPrint(outstream, ("\tminimumIP = %08" PRIx32 "\n" "\tmaximumIP = %08" PRIx32 "\n"), min_ip, max_ip); break; case SKIP_IPF_ZERO: skStreamPrint(outstream, "\tminimumIP = %s\n\tmaximumIP = %s\n", num2dot0_r(min_ip, ip1), num2dot0_r(max_ip, ip2)); break; case SKIP_IPF_DOT: default: skStreamPrint(outstream, "\tminimumIP = %s\n\tmaximumIP = %s\n", num2dot_r(min_ip, ip1), num2dot_r(max_ip, ip2)); break; } skStreamPrint(outstream, ("\t%10" PRIu64 " host%s %10.6f%% of 2^32\n"), num_hosts, ((num_hosts > 1) ? "s (/32s)," : " (/32), "), (100.0 *((double)num_hosts) /(((double)UINT32_MAX) + 1.0))); skStreamPrint(outstream, "\t%10u occupied /8%s %10.6f%% of 2^8\n", num_slash_8, ((num_slash_8 > 1) ? "s," : ", "), (100.0 * ((double)num_slash_8) / ((double)(1 << 8)))); skStreamPrint(outstream, "\t%10u occupied /16%s %10.6f%% of 2^16\n", num_slash_16, ((num_slash_16 > 1) ? "s," : ", "), (100.0 * ((double)num_slash_16) / ((double)(1 << 16)))); skStreamPrint(outstream, "\t%10u occupied /24%s %10.6f%% of 2^24\n", num_slash_24, ((num_slash_24 > 1) ? "s," : ", "), (100.0 * ((double)num_slash_24) / ((double)(1 << 24)))); skStreamPrint(outstream, "\t%10u occupied /27%s %10.6f%% of 2^27\n", num_slash_27, ((num_slash_27 > 1) ? "s," : ", "), (100.0 * ((double)num_slash_27) / ((double)(1 << 27)))); return; } /* * readsetProcessFile(fh, fileName); * * Open the binary set-file given in 'fileName', read the ipTree * from it; then print the output requested by the user to the * stream 'fh'. Return 0 on success, or 1 if the ipTree cannot be * read from 'fileName'. */ static int readsetProcessFile(skstream_t *outstream, const char *fileName) { uint64_t count; skIPTree_t *ipset = NULL; /* Read ipTree from file */ if (0 != skIPTreeLoad(fileName, &ipset)) { skAppPrintErr("Error reading binary IPset from '%s'", fileName); return 1; } if (optFlags.count_ips) { count = skIPTreeCountIPs(ipset); skStreamPrint(outstream, ("%" PRIu64 "\n"), count); } if (optFlags.print_ips) { skIPTreePrint(outstream, ipset, optFlags.ip_format); } if (optFlags.network_structure) { readsetPrintNetwork(outstream, ipset); } if (optFlags.ip_ranges) { readsetPrintRanges(outstream, ipset); } if (optFlags.statistics) { readsetPrintStatistics(outstream, ipset, optFlags.ip_format); } skIPTreeDelete(&ipset); return 0; } int main(int argc, char **argv) { int i; appSetup(argc, argv); /* never returns on error */ if (arg_index == argc) { (void)readsetProcessFile(stream_out, "stdin"); } else { for (i = arg_index; i < argc; ++i) { (void)readsetProcessFile(stream_out, argv[i]); } } /* done */ appTeardown(); return 0; } /* ** Local Variables: ** mode:c ** indent-tabs-mode:nil ** c-basic-offset:4 ** End: */