#include "defs.h" /* * A list (in the form of a string constant) of valid options * * One thing in particular to notice about this program is that all * variable declarations and internal processing (loops and whatnot) * are dependent on the contents of this string, and nothing else. * * The list of valid options can be modified simply by changing the * contents of the VALID_OPTS string and recompiling the program. * No additional modifications need to be made. * * This is a very important consideration when writing programs, as * it isolates and reduces the number of things that need to be changed * to modify a program's behavior. This is a common technique in C * programming, and in general, the fewer "hard" dependencies you * have in a program, the easier it will be to maintain. */ #define VALID_OPTS "ceisxvH" /* * Define what action is to be taken if an unrecoverable error occurs. * This macro relies on the constant string concatenation feature of C. * Also, note the comma and backslash at the end of the first line - * these are used to continue the definition across several lines. */ #define FATAL(p) fprintf( stderr, "%s: " p "\n" , Argv[ 0 ] ), \ exit( EXIT_FAILURE ); /* * An option can be either a letter (in which case OptFlag is SET or * UNSET) or a list of arguments (in which case we need to store them * as an OptList). Since only one choice is possible for any given * option, we use a union to store the relevant information. */ union optun { int OptFlag; char **OptList; }; /* * This structure stores a single option letter in OptChoice, and then * an OptFlag setting or OptList (as appropriate) for that option */ typedef struct { char OptChoice; union optun OptVal; } OptInfo; /* * This defines an array of structures, one for each letter in the * VALID_OPTS string. There is one extra slot for the "actual" argument * list (i.e. the program name in argv[ 0 ], plus the real arguments). * * For the sake of convenience and ease of access, we will store the * main argument list in OptArgs[ 0 ], and the individual option * choices in the remaining OptInfo structures. */ static OptInfo OptArgs[ sizeof( VALID_OPTS ) ]; /* * The following few function definitions are included here to * simplify the grading of this project. A couple of notes: * * (1). Do not remove or modify any function definitions for * functions that you are not able to implement. If you * cannot implement one of the assigned functions, then * LEAVE THE EXISTING DEFINITION AS-IS. This will allow * your program to be compiled with the grader's main() * function. * * If any of these function definitions are modified IN * ANY WAY, then that will be accepted by the grader as * a submittal of that function for grading. * * (2). Functions that are submitted for grading are subject * to the "no compile" rule described in the description * file for this project ("shell-project", which is * available via "printdoc"). Programs that do not * compile receive a WORKING grade of 0 (zero). * * (3). The comment lines that have "LINTED" in them are * required to keep "lint" from complaining about various * things being "unused". You will need to remove these * lines as you begin work on each of the functions, so * that you can use "lint" to check your program before * compiling it. If you are not able to implement a * function for any reason, then be sure and put the * "LINTED" line back before turning in your program * (see notes (1) and (2) above). */ void /* LINTED - remove this line before writing SetOpt() */ SetOpt( char OptChar ) { /* replace the following line with your code */ return; } void /* LINTED - remove this line before writing UnsetOpt() */ UnsetOpt( char OptChar ) { /* replace the following line with your code */ return; } int /* LINTED - remove this line before writing GetOptState() */ GetOptState( char OptChar ) { /* replace the following line with your code */ return UNSET; } char * GetOptString( void ) { /* replace the following line with your code */ return ""; } char * /* LINTED - remove this line before writing GetArgString() */ GetArgString( int ArgNum ) { /* replace the following line with your code */ return ""; } char * GetArgCount( void ) { /* replace the following line with your code */ return ""; } static void /* LINTED - remove this line before writing InitOpts() */ InitOpts( void ) { /* replace the following line with your code */ return; } /* * ProcessOpts( ) * * Processes an argument string and sets appropriate flags * in one of the structures in the OptArgs array * * Argc - number of arguments to scan * Argv - array of pointers to arguments * */ void ProcessOpts( int Argc, char *Argv[ ] ) { extern int optind, opterr; /* these are required for getopt() */ int NArgs, c, i = 0; optind = 1; /* re-start the option processing */ opterr = 0; /* disable default error messages */ /* * Use the getopt() library call to read and parse the options */ while( ( c = getopt( Argc, Argv, VALID_OPTS ) ) != EOF ) { if( strchr( VALID_OPTS, c ) == NULL ) fprintf( stderr, "%s: Bad option(s)\n" , Argv[ 0 ] ); /* * This is bad - it does not deal correctly with * multiple instances of the same option letter */ OptArgs[ ++i ].OptChoice = c; OptArgs[ i ].OptVal.OptFlag = SET; } if( ( NArgs = Argc - optind ) >= 0 ) { /* * Here we use an automatic variable as a place-holder * for the spot in OptArgs where the command line * arguments are stored. This makes the "for" loop * that immediately follows a little less grotty. */ char **TmpArgs = OptArgs[ 0 ].OptVal.OptList; /* * Now we are going to check and see if there are already * some comand line arguments stored in OptArgs. If so, * then we free(3c) the space to make room for the new * arguments that we are putting in. * * There is actually a more efficient way to do this using * realloc(3c), but the resulting code would be so horrible * and convoluted that I cannot bring myself to write it. */ if( TmpArgs != NULL ) { for( i = 0; TmpArgs[ i ] != NULL; i++ ) free( TmpArgs[ i ] ); free( TmpArgs ); } /* * Now that everything is freed, we allocate space for * the array of string pointers - later we will allocate * additional space for the actual argument strings. */ TmpArgs = malloc( sizeof( *TmpArgs ) * ( NArgs + 2 ) ); /* * Check the return value from malloc(), and then * copy it to the OptList pointer in OptArgs[ 0 ] */ if( ( OptArgs[ 0 ].OptVal.OptList = TmpArgs ) == NULL ) FATAL( "Could not create arg space" ); /* * Now duplicate Argv[ 0 ] and copy it to the first * pointer element in the OptList array */ if( ( TmpArgs[ 0 ] = strdup( Argv[ 0 ] ) ) == NULL ) FATAL( "Could not create arg space" ); /* * Duplicate the remaining arguments from the rest * of the command line */ for( i = 1; i <= NArgs; i++, optind++ ) if( ( TmpArgs[ i ] = strdup( Argv[ optind ] ) ) == NULL ) FATAL( "Could not create arg space" ); /* * Since the number of arguments can vary, the last * entry gets a NULL pointer, so we can easily scan * the pointer array with a while loop */ TmpArgs[ i ] = NULL; } } /* * PrintOpts( ) * * This is a convenience function that is used to print out * all arguments and options that were processed. It should not * be needed after this assignment has been graded and returned. * */ void PrintOpts( void ) { int i; /* a dummy integer for looping */ char **s; /* a dummy pointer for looping */ /* * Print the arguments from the OptList of the first structure * in OptArgs[ ]. The structure of the "for" loop here is the * same as the one in ProcessOpts( ), except we access the * elements in OptArgs[ ] with pointers to the structures. */ for( s = OptArgs[ 0 ].OptVal.OptList; *s != NULL; s++ ) printf( "Args: %s\n", *s ); /* * Now print out the other options that were given. Only those * options that are SET will be printed */ for( i = 1; i <= strlen( VALID_OPTS ); i++ ) if( OptArgs[ i ].OptVal.OptFlag == SET ) printf( "Opts: %c\n", OptArgs[ i ].OptChoice ); printf( "Done.\n\n" ); }