/*++

  ENUM.C

  Option Enumerated Types

  Copyright (C) 1997 Microsoft Corporation, all rights reserved

  Created, 7/29/1997 by DavidCHR

  --*/


#include "private.h"

BOOL
PrintEnumValues( FILE          *out,
		 PCHAR          header,
		 optEnumStruct *pStringTable ) {

    ULONG index;

    for( index = 0 ; pStringTable[index].UserField != NULL ; index++ ) {

      if ( pStringTable[index].DescriptionField ) {
	fprintf( stderr, "%hs%10hs : %hs \n",
		 header,
		 pStringTable[index].UserField,
		 pStringTable[index].DescriptionField );

      }
    }

    return TRUE;

}

BOOL
IsMaskChar( IN CHAR ch ) {

    return ( ch == '|' ) || ( ch == ',' );
	
}


BOOL
ResolveEnumFromStrings( ULONG         cStrings,
			PCHAR        *strings, // remaining args.
			optionStruct *opt,
			PULONG        pcArgsUsed ) {

    optEnumStruct *pStringTable;
    ULONG          TableIndex;
    ULONG          StringIndex;
    ULONG          cArgsUsed  = 0;
    BOOL           wasFound;
    BOOL           moreComing = TRUE;

    pStringTable = ( optEnumStruct *) opt->optData;

#if 1

    for ( StringIndex = 0 ;
	  (StringIndex < cStrings) && moreComing ;
	  StringIndex++ ) {

      PCHAR theString;    // points to the current argument

      theString  = strings[ StringIndex ];

      do {

	OPTIONS_DEBUG( "Start of maskable loop.  theString = %s\n",
		       theString );

	wasFound   = FALSE;
	moreComing = FALSE;

	for( TableIndex = 0 ;
	     pStringTable[ TableIndex ].UserField != NULL;
	     TableIndex ++ ) {
	
	  ULONG StringLength; // set to the string length of the option cmd

	  StringLength = strlen( pStringTable[ TableIndex ].UserField );

	  // string-compare up to the StringLength.

	  if ( STRNCASECMP( pStringTable[ TableIndex ].UserField,
			    theString, StringLength ) != 0  ) {

	    continue; // this entry doesn't match.

	  } // else...

	  // found a partial match!  Verify the remainder if there is any.

	  if ( theString [ StringLength ] != '\0' ) {

	    if ( opt->flags & OPT_ENUM_IS_MASK ) {
		
	      if ( IsMaskChar( theString[ StringLength ] ) ) {
		
		// more are coming.
		moreComing = TRUE;

	      } else continue; // inexact match.
	    } else continue;   // inexact match.
	  }

	  wasFound = TRUE;

	  if ( cArgsUsed ) {
	
	    *(POPTU_CAST( *opt )->integer) |= ( ( ULONG )((ULONG_PTR)
						pStringTable[ TableIndex ].
						VariableField ));

	  } else {

	    *(POPTU_CAST( *opt )->raw_data) = ( pStringTable[ TableIndex ].
						VariableField );

	  }

	  if ( theString == strings[ StringIndex ] ) {
	
	    /* we modify theString if it includes multiple mask values.
	       So, this way we only increase the number of used arguments
	       ONCE per actual argument.  */

	    cArgsUsed++;
	  }
	
	  if ( opt->flags & OPT_ENUM_IS_MASK ) {

	    if ( moreComing ) {

	      // check to see if the user input "xxx|yyy", or just "xxx|"

	      ASSERT( StringLength > 0 );

	      // theString[ StringLength ] == '|' or something.

	      for ( theString += StringLength+1; // +1 to go past '|'
		    theString != NULL ;
		    theString ++ ) {
		
		if ( *theString == '\0' ) {

		  OPTIONS_DEBUG( "Mask is of the form 'XXX|'\n" );
		
		  // case = xxx| -- no more coming.

		  theString = NULL; //
		  break;

		}

		if ( isspace( *theString ) ) {
		  continue;
		}

		OPTIONS_DEBUG( "nonspace character '%c' hit.\n"
			       "mask component is of the form XXX|YYY.\n",
			
			       *theString );
		break;

	      }

	      ASSERT( !theString || ( (*theString != '\0') &&
				      !isspace(*theString) ) );

	      break;

	    } else { // !moreComing

	      theString = NULL;  // no more args in *this* string.

	      /* check to see if the mask character is or is in the NEXT
		 argument: "xxx" "|yyy" or "xxx" "|" "yyy" */
	
	      if ( strings[ StringIndex+1 ] ) {

		if ( IsMaskChar( strings[ StringIndex+1 ][0] ) ) {
		
		  moreComing = TRUE;
		
		  if ( strings[ StringIndex+1 ][1] == '\0' ) {

		    // xxx | yyy

		    cArgsUsed++;
		    StringIndex++;

		  } else {

		    // xxx |yyy

		    strings[ StringIndex +1 ]++;

		  }

		}

	      } // strings[ StringIndex +1 ]

	    } // !moreComing

	    break; // found what we wanted.  stop checking the table.

	  } else { // !OPT_ENUM_IS_MASK

	    // found the only argument we were expecting.  Just return.

	    *pcArgsUsed = cArgsUsed;
	    return TRUE;

	  } // moreComing check

	} // for each table entry

      } while ( ( theString != NULL ) && wasFound );

      if ( !wasFound ) { // option was not recognized.

	fprintf( stderr,
		 "%s: enum value '%s' is not known.\n",
		 opt->cmd, strings[ StringIndex ] );
	break;
		
      }
    }

#else

    for( index = 0 ; pStringTable[index].UserField != NULL; index++ ) {
      if ( STRCASECMP( pStringTable[index].UserField, string ) == 0 ) {
	
	// found a match!
	*(POPTU_CAST( *opt )->raw_data) = pStringTable[index].VariableField;

	OPTIONS_DEBUG( "Enum resolves to #%d, \"%s\" = 0x%x \n",
		       index,
		       pStringTable[index].DescriptionField,
		       pStringTable[index].VariableField  );

	return TRUE;
      }
    }


#endif

    if ( wasFound ) {

      *pcArgsUsed = cArgsUsed;

    } else {

      fprintf( stderr, "Error: argument for option \"%hs\" must be %s:\n",
	       opt->cmd,
	       ( opt->flags & OPT_ENUM_IS_MASK ) ?
	       "one or more of the\n following, separated by '|' or ','" :
	       "one of the following values" );

      PrintEnumValues( stderr, "", pStringTable );

    }

    return wasFound;

}