/*
 * args.c -- parse command-line arguments
 * Part of the tcpick project
 *
 * Author: Francesco Stablum <duskdruid @ despammed.com>
 *
 * Copyright (C) 2003, 2004  Francesco Stablum
 * Licensed under the GPL
 *
 */

/* 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at you option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111,
 * USA.
 *
 * Please read the COPYING file for the license details!
 */

/* 
 * tcpick project needs somebody suggesting me bugs and new features!
 * If you want to contribute sending patches, finding bugs, compilation
 * errors, platform-specific incompatibilies you are invited to the tcpick
 * mailing-list:
 *
 * email address: 
 *   <tcpick-project@lists.sourceforge.net>
 * Archive:
 *   http://sourceforge.net/mailarchive/forum.php?forum=tcpick-project
 * Subscribe:
 *   http://lists.sourceforge.net/lists/listinfo/tcpick-project
 */

#include "tcpick.h"
#include "extern.h"

#define GNU_OPTIONS    "ab::c:C::D::e:E::F:hi:npP:r:St::T::v::w::X:y::"
#define NONGNU_OPTIONS  "ab:c:CD:e:E:F:hi:npP:r:St:T:v:w:X:y:"

int 
usage()
{
	color(c_USAGE, stderr, USAGE);
	exit( TCPICK_SUCCESS );
}

int
parse_flav ( char c, enum FLAVOUR * flav )
{
	switch( c ) {
	case 'H':
	case 'P':
	case 'R':
	case 's':
	case 'U':
	case 'x':
	case 'X':
		if ( * flav == SILENT )
			* flav = c;
		return 1;
		break;
	default:
		return 0;
	}
}

void 
parse_write ( char * s )
{
#define WRITE_SIDE flags.writer.side
#define WRITE_FLAV flags.writer.flavour

	int i;
	char c;
	for(i = 0; i < strlen( s ) ; i++) {
		switch( c = *( s + i ) ) {
		case 'C':
			WRITE_SIDE = ( WRITE_SIDE == SERVER ) ? BOTH : CLIENT;
			break;
		case 'S':
			WRITE_SIDE = ( WRITE_SIDE == CLIENT ) ? BOTH : SERVER;
			break;
		case 's':
			flags.writer.type = SEPARATE;
			break;
		case 'u':
			flags.writer.type = UNIQUE;
			break;
		case 'b':
			flags.writer.banner = 1;
			break;
		default:
			if (! parse_flav( c, &(flags.writer.flavour) ) ) {
				ERR_FLAV( c );
			}
			break;
		}
	}

	if ( WRITE_FLAV == SILENT )
		WRITE_FLAV = RAW;
	if ( WRITE_SIDE == NOTHING )
		WRITE_SIDE = BOTH;
}

void 
parse_display_rebuild ( char * s )
{
#define REBUILD_SIDE flags.display_rebuild.side
#define REBUILD_FLAV flags.display_rebuild.flavour

	int i;
	char c;
	for(i = 0; i < strlen( s ) ; i++) {
		c = *( s + i );
		switch( c )	{
		case 'C':
			REBUILD_SIDE = ( REBUILD_SIDE == SERVER ) ? BOTH : CLIENT;
			break;
		case 'S':
			REBUILD_SIDE = ( REBUILD_SIDE == CLIENT ) ? BOTH : SERVER;
			break;
		default:
			if (! parse_flav( c, &(flags.display_rebuild.flavour) ) ) {
				ERR_FLAV( c );
			}
			break;
			
		}
	}

	if ( REBUILD_FLAV == SILENT )
		REBUILD_FLAV = RAW;
	if ( REBUILD_SIDE == NOTHING )
		REBUILD_SIDE = BOTH;
}

void
parse_args(int argc, char ** argv)
{
	char opt;

#ifdef HAVE_GETOPT_LONG
	static struct option long_options[]=
		{
			{"version",           no_argument,       &(flags.versionandquit),  1   },
			{"help",              no_argument,       &(flags.helpandquit),     1   },
			{"show-header",       no_argument,       0,                        'h' },
			{"separator",         no_argument,       &(flags.separator),       1   },
			{"interface",         required_argument, 0,                        'i' },
			{"readfile",          required_argument, 0,                        'r' },
			{"dirs",              required_argument, 0,                        'D' },
			{"filenaming",        required_argument, 0,                        'F' },
			{"show-offsets",      no_argument,       &(flags.displayoffsets),  1   },
			{"resolv-names",      no_argument,       0,                        'a' },
			{"numerical-services",no_argument,       0,                        'n' },
			{"colors",            no_argument,       0,                        'C' },
			{"pipe",              required_argument, 0,                        'P' },
			{0,0,0,0} /* end */
		};
#endif

	int option_index = 0;

	/* default values */
	memset( &flags, 0, sizeof(struct FLAGS) );

	flags.writer.flavour  = SILENT;
	flags.display_payload = SILENT;
	flags.display_rebuild.side = NOTHING;
	flags.display_rebuild.flavour = SILENT;
	flags.verbosity = 1;
	flags.trackonly = -1; /* infinite (-1) is default; -T0 means "don't track
				  anything" 
			       */
	flags.filenaming = 1;
  
	if ( argc <= 1 )
		printf("important: `man 8 tcpick' explains all options available\n");

#if HAVE_GETOPT_LONG /* GNU/Linux systems */
	while (( opt = getopt_long(argc,argv,
				   GNU_OPTIONS,
				   long_options, &option_index) ) != -1 )
#else /* BSD systems */
		printf("Your system doesn't support getopt_long() function; you will be "
		       "unable to use the long options\n"
		       " i.e. `% tcpick --interface ppp0' DOESN'T WORK! \n");
	while ( ( opt = getopt( argc, argv, NONGNU_OPTIONS ) ) !=-1 )
#endif
    
		switch(opt) {

		case 'a':
			flags.lookup = 1;
			break;
		case 'b':
			if( optarg != NULL )
				parse_display_rebuild ( optarg );
			else
				parse_display_rebuild ( "R" );
			break;
		case 'c':
			if (( flags.maxconns = atoi(optarg)) == 0)
				suicide( "parse_args", "%s: invalid number of maximum connections",optarg );
			else
				flags.maxconns = 1;
			break;
		case 'C':
			flags.color = 1;
			break;
		case 'D':
			if( optarg != NULL ) {
				flags.dirs = atoi(optarg);
				if( flags.dirs == 0 )
					suicide ("parse_args", "%s: invalid number of files per connection", optarg );
			} else {
				flags.dirs = 100;
			}
							       
			break;
		case 'e':
			if( optarg != NULL ) {
				flags.exitpackets = atoi(optarg);
				if ( flags.exitpackets == 0 ) {
					suicide ("parse_args",
						 "%s: invalid number of packets to exit", 
						 optarg);
				}
			}
			break;
			
		case 'E': /* WARNING: if you modify this section, you
			     should modify section case 'T' too */
			flags.exitclosed = 1; /* default value */

			if( optarg != NULL ) {
				if ( *optarg == 'f' ) {
					flags.exitclosed_first = 1; /* -Ef */
					if ( strlen(optarg) == 1 )  
                                        /* -Ef without number */
						break;
					else
						optarg++;           
                                        /* -Ef with number */
				}

				if (! ( flags.exitclosed = atoi(optarg) ) )
					suicide( "parse_args", 
						 "%s: invalid number of closed connection"
						 " to track before exiting", optarg );
			}
			break;
		case 'F': 
			if( optarg != NULL ) {
				flags.filenaming = atoi(optarg);
				if(flags.filenaming == 0) {
					suicide( "parse_args",
						 "%s: invalid file naming system", optarg );
				}
			} else {
				suicide( "parse_args",
					 "-F option needs a number\n"
					 "i.e. -F1 or -F2" );
			}
			
		case 'h':
			flags.header = 1;
			break;
		case 'i':
			dev = optarg;
			break;
		case 'n':
			flags.numericalports = 1;
			break;
		case 'p':
			flags.notpromisc = 1;
			break;
		case 'P':
			switch( *optarg ) {
			case 'c':
			case 'C':
				parse_display_rebuild ( "RC" ); /* -bRC */
				break;
			case 's':
			case 'S':
				parse_display_rebuild ( "RS" ); /* -bRS */
				break;
			default:
				suicide("parse_args", "%s: invalid --pipe (-P) option",optarg);
			}
			flags.notstatus        = 1; /* -S  */ 
			flags.verbosity        = 0; /* -v0 */
			flags.trackonly        = 1; /* -T1 */
			flags.trackonly_first  = 1; /* -Tf */
			flags.exitclosed       = 1; /* -E1 */
			flags.exitclosed_first = 1; /* -Ef */

			break;
		case 'r':
			readfile = optarg;
			break;
		case 'S':
			flags.notstatus = 1;
			break;
		case 't': /* timestamp */
			if( optarg ) {
				if( *optarg == 'd' ) {
					flags.displaytime = TIMEDATE;
				} else {
					suicide( "parse_args",
						 "%s: invalid timestamp option", optarg );
				}
			} else {
				flags.displaytime = ONLYTIME;
			}

			break;
				
				
					
		case 'T':
			/* WARNING: if you modify this section, you
			   should modify section case 'E' too */
			flags.trackonly = 1; /* default value */

			if( optarg ) {
				if ( *optarg == 'f' ) {
					flags.trackonly_first = 1; /* -Tf */
					if ( strlen(optarg) == 1 )  
                                        /* -Tf without number */
						break;
					else
						optarg++;           
                                        /* -Tf with number */
				}

				if (! ( flags.trackonly = atoi(optarg) ) ) {
					suicide( "parse_args",
						 "%s: invalid number of connections to track", optarg );
				}
			}
			break;
		case 'v':
			if ( optarg ) {
				flags.verbosity = strtol(optarg,(char **)NULL, 10);
				if ( flags.verbosity < 0 || flags.verbosity > 5 )
					suicide( "%s: invalid verbosity number",optarg );
			} else
				flags.verbosity = 1;
			break;
		case 'w':
			if( optarg )
				parse_write( optarg );
			else
				parse_write( "R" );
			break;
		case 'y':
			if( optarg ) { 
				if (! parse_flav( *optarg, &(flags.display_payload) ) )
					ERR_FLAV( *optarg );
			} else
				parse_flav( 'P', &(flags.display_payload) );
			break;
		case 'X':
			flags.timeout = atoi(optarg);
			break;

		default:
			; /* UNREACHED */
		}

	filter = argv[optind];

	if(flags.versionandquit) {
		color( c_USAGE, stdout, PACKAGE_STRING "\n"
			     " Author: " AUTHOR "\n"
			     " for other info try `man 1 tcpick', or `%s --help'\n\n"
			     TERMS ,TCPICK_NAME);
		exit( TCPICK_SUCCESS );
	}

	if(flags.helpandquit) {
		usage();
		exit( TCPICK_SUCCESS );
	}
}


syntax highlighted by Code2HTML, v. 0.9.1