/* ** Copyright (C) 2001-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@ */ /* ** rwcat.c ** ** 12/13/2002 ** ** Suresh L. Konda ** ** Stream out a bunch of input files--given on the command ** line--and/or stdin to stdout or given file Path ** */ #include "silk.h" RCSIDENT("$SiLK: rwcat.c 6783 2007-04-03 16:26:33Z mthomas $"); #include "rwpack.h" #include "sksite.h" #include "utils.h" /* TYPEDEFS AND DEFINES */ /* file handle for --help output */ #define USAGE_FH stdout /* LOCAL FUNCTIONS */ static void appUsageLong(void); static int appOptionsHandler(clientData cData, int opt_index, char *opt_arg); static void appSetup(int, char**); static void appTeardown(void); static int catFile(const char *inFName); /* LOCAL VARIABLES */ /* index into argv of first filename to process */ static int arg_index; /* whether to read records from stdin */ static int read_from_stdin = 0; /* xargs input stream; currently only reading from stdin is allowed */ static FILE *xargs_stream = NULL; /* output stream */ static rwIOStruct_t *outIOS; /* the compression method to use when writing the file. * sksiteCompmethodOptionsRegister() will set this to the default or * to the value the user specifies. */ static sk_compmethod_t comp_method; /* OPTIONS */ typedef enum { OPT_OUTPUT_PATH, OPT_PRINT_FILENAMES, OPT_XARGS } appOptionsEnum; static struct option appOptions[] = { {"output-path", REQUIRED_ARG, 0, OPT_OUTPUT_PATH}, {"print-filenames", NO_ARG, 0, OPT_PRINT_FILENAMES}, {"xargs", NO_ARG, 0, OPT_XARGS}, {0,0,0,0} /* sentinel entry */ }; static const char *appHelp[] = { "Full path name of the output file. Def: stdout", "Print input filenames while processing", "Treat stdin as a list of files to read, one name per line", (char *)NULL }; static struct { const char *output_path; /* name of output path */ const char *xargs; /* name of stream to read filenames from */ uint8_t print_filenames; /* whether to print file names */ } rwcat_opt = { NULL, NULL, 0 }; /* 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] [FILES] \n" \ "\tReads SiLK Flow records from the FILES named on the comamnd\n" \ "\tline, or from the standard input when no FILES are provided,\n" \ "\tand writes the SiLK records to the specified output file or\n" \ "\tto the standard output if it is not connected to a terminal.\n") FILE *fh = USAGE_FH; skAppStandardUsage(fh, USAGE_MSG, appOptions, appHelp); sksiteOptionsUsage(fh); sksiteCompmethodOptionsUsage(fh); } /* * appTeardown() * * Teardown all modules, close all files, and tidy up all * application state. * * This function is idempotent. */ static void appTeardown(void) { static uint8_t teardownFlag = 0; int rv; if (teardownFlag) { return; } teardownFlag = 1; if (outIOS) { rv = rwioClose(outIOS); if (rv) { rwioPrintLastErr(outIOS, rv, &skAppPrintErr); } rwioDestroy(&outIOS); } 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); /* register the options */ if (optionsRegister(appOptions, (optHandler)appOptionsHandler, NULL) || sksiteCompmethodOptionsRegister(&comp_method) || sksiteOptionsRegister(SK_SITE_FLAG_CONFIG_FILE)) { 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 */ } /* try to load site config file; if it fails, we will not be able * to resolve flowtype and sensor from input file names */ sksiteConfigure(0); /* Make certain we have some input. If xargs was given, stdin is * a list of filenames for input. Else if stdin is not a * terminal, read records from stdin. Else if no files, no xargs, * and stdin is a terminal, complain about no input. */ if ((xargs_stream == NULL) && (arg_index == argc)) { /* no file names were given on command line. We'll try * reading from stdin if it is not a terminal. */ if ( !FILEIsATty(stdin)) { read_from_stdin = 1; } else { skAppPrintErr("no file names given and stdin is a terminal"); skAppUsage(); } } /* stdout is our default output path */ if (NULL == rwcat_opt.output_path) { if (FILEIsATty(stdout)) { skAppPrintErr("stdout is connected to a terminal"); skAppUsage(); } rwcat_opt.output_path = "stdout"; } /* open output and write header */ if ((rv = rwioCreate(&outIOS, rwcat_opt.output_path, SK_RWIO_WRITE)) || (rv = rwioSetCompression(outIOS, comp_method)) || (rv = rwioOpen(outIOS)) || (rv = rwioWriteHeader(outIOS))) { rwioPrintLastErr(outIOS, rv, &skAppPrintErr); skAppPrintErr("Couldn't open output file. Exiting."); 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_OUTPUT_PATH: /* output file path: treat "stdout" as standard output (which is * the default); anything else as file */ if (0 != strcmp(opt_arg, "stdout")) { /* check for multiple output arguments */ if (NULL != rwcat_opt.output_path) { skAppPrintErr("multiple output files '%s', '%s'\n", rwcat_opt.output_path, opt_arg); return 1; } rwcat_opt.output_path = strdup(opt_arg); if (!rwcat_opt.output_path) { skAppPrintErr("appOptionsHandler: strdup failed\n"); exit(EXIT_FAILURE); } } break; case OPT_PRINT_FILENAMES: /* print input file names */ rwcat_opt.print_filenames = 1; break; case OPT_XARGS: rwcat_opt.xargs = "stdin"; xargs_stream = stdin; break; } /* switch */ return 0; /* OK */ } /* * status = catFile(filename); * * Open 'filename' and write all its records to outIOS. Return 0 * on success, or 1 on failure. */ static int catFile(const char *inFName) { rwRec rwrec; rwIOStruct_t *inIOS; uint32_t inC = 0; uint32_t outC = 0; inIOS = rwOpenFile(inFName, NULL); if (NULL == inIOS) { /* some error. the library would have dumped a msg */ return 1; } if (rwcat_opt.print_filenames) { fprintf(stderr, "%s\n", inFName); } #if 1 /* set to 0 for testing */ while (rwRead(inIOS, &rwrec)) { int rv; inC++; rv = rwWrite(outIOS, &rwrec); if (0 != rv) { rwioPrintLastErr(outIOS, rv, &skAppPrintErr); if (LIBRW_ERROR_IS_FATAL(rv)) { break; } } outC++; } #endif rwCloseFile(inIOS); inIOS = (rwIOStruct_t*)NULL; if (rwcat_opt.print_filenames) { fprintf(stderr, "Read %u Wrote %u\n", inC, outC); } return 0; } /* * status = catFilenamesFromStream(stream); * * Treats 'stream' as containing a list of filenames, one per line. * Calls catFile() for each filename (line of input) that is read. */ static int catFilenamesFromStream(FILE *stream) { char line[1024]; char *eol; while (!feof(stream)) { if (!fgets(line, sizeof(line), stream)) { continue; } eol = strchr(line, '\n'); if (eol == line) { /* empty line; ignore */ continue; } if (NULL != eol) { /* expected behavior: read an entire line */ *eol = '\0'; } else if (feof(stream)) { /* okay: last line did not end in newline */ } else { /* bad: line was longer than sizeof(line). read until * newline or eof, then through away line */ skAppPrintErr("--xargs input line too long. ignored"); while (fgets(line, sizeof(line), stream) && !strchr(line,'\n')) ; /* empty */ continue; } /* Don't read binary data from the file we are currently using * as the input stream to xargs */ if (0 == strcmp(line, rwcat_opt.xargs)) { skAppPrintErr("Ignoring filename '%s' in xargs input", line); continue; } catFile(line); } return 0; } /* * status = rwcatMain(argc, argv); * * Call catFile() on each file, whatever its source. */ static int rwcatMain(int argc, char **argv) { if (xargs_stream != NULL) { return catFilenamesFromStream(xargs_stream); } if (read_from_stdin) { catFile("stdin"); } for ( ; arg_index < argc ; arg_index++) { if (catFile(argv[arg_index])) { break; } } return 0; } int main(int argc, char ** argv) { int rv; appSetup(argc, argv); rv = rwcatMain(argc, argv); appTeardown(); return(rv); } /* ** Local variables: ** mode:c ** indent-tabs-mode:nil ** c-basic-offset:4 ** End: */