MINI MINI MANI MO

Path : /opt/oracle/product/18c/dbhomeXE/lib/
File Upload :
Current File : //opt/oracle/product/18c/dbhomeXE/lib/asmcmdparser.pm

# Copyright (c) 2004, 2014, Oracle and/or its affiliates. All rights reserved.
#
#    NAME
#      asmcmdparser
#
#    DESCRIPTION
#       This module implements the asmcmd command parser.
#
#
#    MODIFIED  (MM/DD/YY)
#    anovelo   10/13/14  - 19783114: Check if command is valid before options
#    adileepk  01/19/12  - Fix for bug-13605324.
#    adileepk  09/12/11  - Removing commented code.
#    adileepk  29/09/10  - creation
#   
#####################################################################

package asmcmdparser;

use asmcmdshare;
use asmcmdglobal;
use XML::Parser;
use Data::Dumper;

require Exporter;
our @ISA    = qw(Exporter);
our @EXPORT = qw(asmcmdparser_parse_issued_command);

  ######################## FUNCTIONS LIST #########################
#
#  asmcmdparser class functions
#     new
#     asmcmdparser_get_parent
#     asmcmdparser_set_parent
#     asmcmdparser_get_content
#     asmcmdparser_get_type
#     asmcmdparser_get_children
#     asmcmdparser_get_number_children
#     asmcmdparser_get_tracecount
#     asmcmdparser_increment_tracecount
#     asmcmdparser_add_child
#     asmcmdparser_check_node
#     asmcmdparser_final_check
#     asmcmdparser_get_parent_type
#     asmcmdparser_mark_ancestors
#     asmcmdparser_match_userip
#
#  Syntax Parsing functions
#     asmcmdparser_trace_enclosing_node
#     asmcmdparser_pipe_present
#     asmcmdparser_get_token_type
#     asmcmdparser_parse_command_token
#     asmcmdparser_determine_edge_type
#     asmcmdparser_parse_options
#     asmcmdparser_parse_rsqua
#     asmcmdparser_parse_rbrace
#     asmcmdparser_parse_pipe
#     
#  Issued Command Parsing functions
#     asmcmdparser_parse_issued_command_token
#     asmcmdparser_parse_issued_command
#     
##############################################################################

####################### ASMCMDPARSER Global Constants & Variables ############
####################### EDGE TYPES ####################                    
use constant   EDGE_COMMAND      => 0;
use constant   EDGE_LONGOPT      => 1;
use constant   EDGE_SHORTOPT     => 2;
use constant   EDGE_MANDATORY    => 3;
use constant   EDGE_ONE_OR_MORE  => 4;
use constant   EDGE_NONE_OR_MORE => 5;
use constant   EDGE_ATMOST_ONE   => 6;
use constant   EDGE_EXACTLY_ONE  => 7;
use constant   EDGE_PIPE         => 8;

####################### NODE TYPES ####################
# Note that all the user-input type nodes should have the highest integer 
# values in the group. This is because the user input nodes are checked 
# for using the condition ($nodeType >= NODE_USER_IP_GEN).
use constant   NODE_COMMAND      => 0;
use constant   NODE_LONGOPT      => 1;
use constant   NODE_SHORTOPT     => 2;
use constant   NODE_SQUA         => 3;
use constant   NODE_SQUA_ONE     => 4;
use constant   NODE_BRACE        => 5;
use constant   NODE_BRACE_ONE    => 6;
use constant   NODE_PIPE         => 7;
use constant   NODE_USER_IP_GEN  => 8;
use constant   NODE_USER_IP_FIXED=> 9;
use constant   NODE_USER_IP_LIST => 10;


####################### TOKEN TYPES ###################                    
# Note that all the user-input type tokens should have the highest integer 
# values in the group. This is because the user input tokens are checked 
# using the condition ($tokeneType >= USER_IP_GEN).
use constant   COMMAND           => 0;
use constant   LONGOPT           => 1;
use constant   SHORTOPT          => 2;
use constant   LSQUA             => 3;
use constant   RSQUA             => 4;
use constant   LBRACE            => 5;
use constant   RBRACE            => 6;
use constant   PIPE              => 7;
use constant   USER_IP_GEN       => 8; # A general user input.
use constant   USER_IP_FIXED     => 9; # Where the user has to pick from a set 
                                       # of values.
                                       # Eg. --striping {coarse | fine}
use constant   USER_IP_LIST      => 10;# Where the user can specify a list of 
                                       # values. It can be comma or space separated
use constant   INVALID           => 11;

#######################################################
my $currentNode;
my $currentIndex;
my $edgeType;
my $currentNodeType;
my $parentNode;
my $rootNode;
my $previousNode;
my $syntax;
my @tokens;
my @userTokens;
my %optionsHash;
my %useripHash;
my $currentToken;
my $cmd;
my $args_ref;
my $optionString;
my $strings_ref;
my $deprecatedOption;
my $matchedUserNode;
my $numUseripToCmd;
my $numMatchedUseripToCmd;
my $token;
my $startNode;
my $trace;
my $startNodeType;
my $previousNodeType;
#  Structure of the asmcmdparser Parse tree.
#  Each node has the following members.
#  _PARENT_    -->   A Reference to the parent node.
#  _CONTENT_   -->   A string characteristic of the node. 
#                    Eg.for the command node, it would be the name of the command
#                       for options it would be "--add" or "-L"
#  _TYPE_      -->   Type of the node, refer to the constants for NODE TYPES.
#  _TRACECOUNT_-->   Count of the no. of times the node has been traversed
#                    successfully as part of parsing.
#  _CHILDREN_  -->   A hash of the children with keys as the string 
#                    characteristic of the child node. Note that this is not a
#                    reference to a hash but an actual hash.
#  _EDGETYPE_  -->   Indicating the type of the edge connecting this node
#                    to its parent. Refer to the EDGE TYPES.
#  _NUMBRACKETS_->   Keeps count of the number of brackets that are children to 
#                    this node.
#  _NUMCHILDREN_->   Keeps count of the number of children.
#  Use the get-set subroutines to access or modify the node.
#  You can add to or remove children from nodes by specifying the string 
#  characteristic of the child.


########
# NAME
#   new
#
# DESCRIPTION
#   This routine creates a new node with the 
#   parameters passed and returns a reference to the new node.
#
# PARAMETERS
#   class  (IN) - class type.
#   parent (IN) - reference tot he parent node.
#   content(IN) - string equivalent of the current node.
#                 for brackets it would be "{<bracketnumber>" or 
#                 "[<bracketnumber"
#   edgetype(IN)- The type of the edge through which it is connected to the 
#                 parent node.
# RETURNS
#   Returns a reference to the newly created node if successful.
#   If unsuccessful, it returns undef.
########
sub new
{
   my $class = shift;
   my $self = {_PARENT_ => shift,
               _CONTENT_ => shift,
               _TYPE_ => shift,
               _TRACECOUNT_ => 0,
               _EDGETYPE_ => shift,
               _NUMBRACKETS_ => 0,
               _NUMCHILDREN_ => 0,
               _CHILDREN_ => {},
              };
   $parentReference = $self->{_PARENT_};
   if (defined $parentReference)
   {
      $childHash = $parentReference->{_CHILDREN_};
      if (defined $childHash->{$self->{_CONTENT_}})
      {
         return undef;
      }
      else
      {
         $childHash->{$self->{_CONTENT_}} = $self;
      }
   }
   bless $self, $class;
   return $self;
}

########
# NAME
#   asmcmdparser_get_parent 
#
# DESCRIPTION
#   This routine returns a reference to the parent node of this node.
#
# PARAMETERS
#   self   (IN) - reference to this node.
#
# RETURNS
#   Returns a reference to the parent node of this node.
#
########
sub asmcmdparser_get_parent
{
   my ($self) = @_;
   return $self->{_PARENT_};
}

########
# NAME
#   asmcmdparser_set_parent 
#
# DESCRIPTION
#   This routine sets the parent node of this node.
#
# PARAMETERS
#   self   (IN) - reference to this node.
#   parent (IN) - reference to new parent node of this node.
# RETURNS
#   Returns a reference to the parent node of this node.
#
########
sub asmcmdparser_set_parent
{
   my ($self, $parent) = @_;
   $self->{_PARENT_} = $parent if defined $parent;
   return $self->{_PARENT_};
}

########
# NAME
#   asmcmdparser_get_content 
#
# DESCRIPTION
#   This routine returns the content string of this node.
#
# PARAMETERS
#   self   (IN) - reference to this node.
#
# RETURNS
#   Returns the content string of this node.
#
########
sub asmcmdparser_get_content
{
   my ($self) = @_;
   return $self->{_CONTENT_};
}

########
# NAME
#   asmcmdparser_get_type 
#
# DESCRIPTION
#   This routine returns the type of this node.
#
# PARAMETERS
#   self   (IN) - reference to this node.
#
# RETURNS
#   Returns the type of this node.
#
########
sub asmcmdparser_get_type
{
   my ($self) = @_;
   return $self->{_TYPE_};
}

########
# NAME
#   asmcmdparser_get_children 
#
# DESCRIPTION
#   This routine returns a reference to the hash of children of this node.
#
# PARAMETERS
#   self   (IN) - reference to this node.
#
# RETURNS
#   Returns a reference to the hash of children of this node.
#
########
sub asmcmdparser_get_children
{
   my ($self) = @_;
   return $self->{_CHILDREN_};
}

########
# NAME
#   asmcmdparser_get_number_children 
#
# DESCRIPTION
#   This routine returns the count of children of this node.
#
# PARAMETERS
#   self   (IN) - reference to this node.
#
# RETURNS
#  Returns the count of children of this node.
#
########
sub asmcmdparser_get_number_children
{
   my ($self) = @_;
   return $self->{_NUMCHILDREN_};
}

########
# NAME
#   asmcmdparser_get_tracecount 
#
# DESCRIPTION
#   This routine returns the tracecount of this node.
#
# PARAMETERS
#   self   (IN) - reference to this node.
#
# RETURNS
#  Returns the tracecount of this node.
#
########
sub asmcmdparser_get_tracecount
{
   my ($self) = @_;
   return $self->{_TRACECOUNT_};
}

########
# NAME
#   asmcmdparser_increment_tracecount 
#
# DESCRIPTION
#   This routine increments the tracecount of this node and returns it.
#
# PARAMETERS
#   self   (IN) - reference to this node.
#
# RETURNS
#  Returns the incremented tracecount of this node.
#
########
sub asmcmdparser_increment_tracecount
{
   my ($self) = @_;
   $self->{_TRACECOUNT_}++;
   return $self->{_TRACECOUNT_};
}

########
# NAME
#   asmcmdparser_add_child 
#
# DESCRIPTION
#   This routine adds a child node to this node. Also returns a reference to 
#   the newly created child node. 
#
# PARAMETERS
#   self   (IN) - reference to this node.
#   childContent (IN) - The string equivalent of the node. 
#   childType  (IN) - The type of the node. 
#   edgeType   (IN) - The edge type which records the relationship the node has 
#                     with its parent node.
#
# RETURNS
#   Returns a reference to the newly created node.
#
########
sub asmcmdparser_add_child
{
   my ($self, $childContent, $childType, $edgeType) = @_;
   if ($childContent eq "[" || $childContent eq "{" || $childContent eq "|")
   {
      $childContent = $self->{_NUMBRACKETS_};
      $self->{_NUMBRACKETS_}++;
   }
   my $newNode = new asmcmdparser($self, $childContent, $childType, $edgeType);
   $self->{_CHILDREN_}{$childContent} = $newNode;  
   $self->{_NUMCHILDREN_}++;
   return $newNode;
}

########
# NAME
#   asmcmdparser_check_node 
#
# DESCRIPTION
#   This routine, together with asmcmdparser_final_check() recursively 
#   traverses the Nary tree validating the nodes. Verifies that the 
#   conditions of the syntax are satisfied.
#
# PARAMETERS
#   self   (IN) - reference to this node.
#
# RETURNS
#   0 if any syntax check fails.
#   1 if every check succeeds.
########
sub asmcmdparser_check_node
{
   my ($self) = @_;
   my @eargs;
   asmcmdshare_trace(4, 'Inside asmcmdparser_check_node()', 'y', 'n');
   $currentNodeType = $self->asmcmdparser_get_type();
   $trace = $self->asmcmdparser_get_tracecount();
   # If the node has been traced atleast once then validate its children 
   # by calling asmcmdparser_final_check().
   if ($currentNodeType == NODE_SQUA)
   {
      if ($trace > 0)
      {
         if (!($self->asmcmdparser_final_check()))
         {
            return 0;
         }
      }
   }
   # If the node has been traced more than once then report error
   # else validate its children by calling asmcmdparser_final_check().
   elsif ($currentNodeType == NODE_SQUA_ONE)
   {
      if ($trace > 1)
      {
         # Multiple options specified inside a square bracket where at most 
         # one option is expected
         return 0;
      }
      elsif ($trace == 1)
      {
         if (!($self->asmcmdparser_final_check()))
         {
            return 0;
         }
      }
   }
   # If the node hasn't been traversed then error out.
   # else if the node has been traced atleast once then validate its children 
   # by calling asmcmdparser_final_check().
   elsif ($currentNodeType == NODE_BRACE)
   {
      if ($trace == 0)
      {
         # No options specified inside a brace where at least
         # one option is expected
         return 0;
      }
      else
      {
         if (!($self->asmcmdparser_final_check()))
         {
            return 0;
         }
      }
   }
   # If the node hasn't been traversed then error out.
   # else if the node has been traced exactly once then validate its children 
   # by calling asmcmdparser_final_check().
   elsif ($currentNodeType == NODE_BRACE_ONE)
   {
      if ($trace != 1)
      {
         # Multiple options specified  or no option specified at all, inside a
         # brace where exactly one option is expected
         return 0;
      }
      else
      {
         if (!($self->asmcmdparser_final_check()))
         {
            return 0;
         }
      }
   }
   # Check if the option is a mandatory one. If so then it should have been
   # traced. If the node has been traced atleast once then validate its children
   # by calling asmcmdparser_final_check().
   elsif ($currentNodeType == NODE_LONGOPT || $currentNodeType == NODE_SHORTOPT)
   {
      if ((($self->{_EDGETYPE_} == EDGE_MANDATORY) || 
           ($self->{_EDGETYPE_} == EDGE_COMMAND)  ) && ($trace == 0))
      {
         # A long option is expected, but not found.
         return 0;
      }
      if (!($self->asmcmdparser_final_check()))
      {
         return 0;
      }
   }
   elsif ($currentNodeType == NODE_PIPE)
   {
      if ($trace != 0)
      {
         if (!($self->asmcmdparser_final_check()))
         {
            return 0;
         }
      }
   }
   # The control will not reach this point unless a user input is expected.
   # Hence error out if no user input is found.
   else
   {
      # If trace is zero and one of the following conditions is true, 
      # then error out. (Mandatory user input not found)
      # 1.) The user input expected is associated with the command itself,
      #     and the number of inputs that have been matched to the command
      #     is less than the expected number of user inputs.
      # 2.) The User input expecetd is assocated with either a short or long
      #     option.
      if ($trace == 0 && 
          (($self->asmcmdparser_get_parent_type() == NODE_COMMAND && 
            $numMatchedUseripToCmd < $numUseripToCmd
           ) ||
           ($self->asmcmdparser_get_parent_type() == NODE_SHORTOPT ||
            $self->asmcmdparser_get_parent_type() == NODE_LONGOPT
           )
          )
         )
      {
         # A user input is expected, but not found.
         return 0;
      }
   }
   return 1;
}

########
# NAME
#   asmcmdparser_final_check 
#
# DESCRIPTION
#   This routine, together with asmcmdparser_check_node() recursively traverses
#   the Nary tree validating the nodes. Verifies that the conditions of the 
#   syntax are satisfied.
#
# PARAMETERS
#   self   (IN) - reference to this node.
#
# RETURNS
#   (Nothing)
#
########
sub asmcmdparser_final_check
{
   my ($self) = @_;
   my $temp = $self->asmcmdparser_get_children();
   my %children = %$temp;
   my $key;
   my $childNode;
   asmcmdshare_trace(4, "Inside asmcmdparser_final_check()", 'y', 'n');
   # Run checks on each of the child node.
   while(($key, $childNode) = each(%children))
   {
      if (!($childNode->asmcmdparser_check_node()))
      {
         return 0;
      }
   }
   return 1;
}

########
# NAME
#   asmcmdparser_get_parent_type 
#
# DESCRIPTION
#   This routine returns the type of the parent node.
#
# PARAMETERS
#   self   (IN) - reference to this node.
#
# RETURNS
#   This routine returns the type of the parent node.
#
########
sub asmcmdparser_get_parent_type
{
   my ($self) = @_;
   my $parent = $self->{_PARENT_};
   return $parent->{_TYPE_};
}

########
# NAME
#   asmcmdparser_mark_ancestors 
#
# DESCRIPTION
#   This routine marks the trace count for the ancestors of each node 
#   that has been matched. This helps in the validation step later.
#   Also updates the trace of the node being matched.
#
# PARAMETERS
#   $self (IN) - The node from which to mark the ancestors.
#
# RETURNS
#   1 on successful marking.
#   0 on any failure. This error condition would bubble up to the 
#   function asmcmdparser_parse_issued_command().
#   NOTE Detailed error messages can be enabled from within this function.
#
########
sub asmcmdparser_mark_ancestors
{
   my ($self) = @_;
   my @eargs;
   $startNode = $self;
   # Get the trace count for the leaf node.
   $trace = $startNode->asmcmdparser_get_tracecount();
   if (($trace != 0) && 
       ($startNode->asmcmdparser_get_type() != NODE_USER_IP_LIST))
   {
      # Token has been matched already.
      return 0;
   }
   # If this leaf node has not already been matched, then increment the trace
   # count.
   $trace = $startNode->asmcmdparser_increment_tracecount();
   # Now proceed to the parent node of the leaf node.
   $startNode = $startNode->asmcmdparser_get_parent();
   $startNodeType = $startNode->asmcmdparser_get_type();
   # Walk up the tree till you reach the root node (Command node)
   while($startNodeType != NODE_COMMAND)
   {
      $trace = $startNode->asmcmdparser_get_tracecount();
      # Make sure that atmost one child of the SQUA_ONE node is active. 
      # ie. trace count was 0 before 
      # we encountered the leaf node.
      if ($startNodeType == NODE_SQUA_ONE && $trace == 1)
      {
         # Too many options used.
         return 0;
      }
      elsif ($startNodeType == NODE_BRACE_ONE && $trace != 0)
      {
         # exactly one option should be used.
         return 0;
      }
      # If you encounter an option node, that means that this option should 
      # have been encountered before we encountered the leaf node. Hence the
      # trace count of the option should have already been set to 1.
      # Or, the parser should look forward and find the option is still used.
      # Eg. for dropdg command -f should be present only if -r is present,
      # but the order of these options can be overlooked.
      # If, even after the incrementation, the trace count is 1 that means 
      # the option node was never encountered, which is an error.
      elsif (($startNodeType == NODE_LONGOPT) || 
             ($startNodeType == NODE_SHORTOPT))
      {
         return 1 if $trace == 1; # ALL IS WELL.
         my $parentOption = $startNode->asmcmdparser_get_content();
         # If option has not been matched, look forward and check if it 
         # occurs later on.
         my $i = $currentIndex + 1;
         while($i <= $#ARGV)
         {
            return 1 if ($ARGV[$i] eq $parentOption);
            $i++;
         }
         # A long or short option is expected, but not found.
         return 0;
      }
      # If this is the first time this particular pipe has been seen, mark it.
      # if this pipe has been matched before, no need to walk further up the 
      # tree.
      elsif (($startNodeType == NODE_PIPE) && ($trace == 1))
      {
         return 1;
      }
      
      # Successful tracing of this current node. Now walk up to the node's parent
      $trace = $startNode->asmcmdparser_increment_tracecount();
      $startNode = $startNode->asmcmdparser_get_parent();
      $startNodeType = $startNode->asmcmdparser_get_type();
         
   }
   # If control reaches this point, then there were no errors.
   return 1;
}

########
# NAME
#   asmcmdparser_match_userip 
#
# DESCRIPTION
#   This routine recursively explores the tree rooted at the previous matched
#   node (the command or an option) trying to match user input.
#   Also sets $matchedUserip as the node in the parse tree that was successfully
#   matched.
#
# PARAMETERS
#   $self (IN) - a reference to the current node.
# 
# GLOBAL VARIABLES USED
#   $previousNode
#   $matchedUserip
#   $type
# 
# RETURNS
#   1 - if successfully matched a user input
#   0 - if couldn't match a user input.
#  -1 - if the routine encountered an error condition.
#   2 - if the routine expected a possible FIXED INPUT but didn't find it.
#
########
sub asmcmdparser_match_userip
{
   my ($self) = @_;
   my $numChildren = $self->asmcmdparser_get_number_children();
   my $type = $self->asmcmdparser_get_type();
   my $result;
   my $fixedipFlag = 0;
   asmcmdshare_trace(4, "Inside asmcmdparser_match_userip()", 'y', 'n');
   # Check if this node is a type of user input node.
   if ($type >= NODE_USER_IP_GEN)
   {
      # If it is a fixed user input node, check if the current token matches
      # the contents of the node.
      if ($type == NODE_USER_IP_FIXED)
      {
         # Check if the contents match the current token.
         if ($self->asmcmdparser_get_content() eq $token)
         {
            # Try to mark the ancestors of the node if it was a successful match
            $result = $self->asmcmdparser_mark_ancestors();
            if ($result == 1)
            {
               # Set $matchedUserip as this node and return 1.
               $matchedUserip = $self;
               return 1;
            }
            # If mark_ancestors failed, then some condition of the syntax
            # was not satisfied, so return -1.
            return -1;
         }
         else
         {
            return 2; # Indicate that fixed input is expected.
         }
      }
      # If it is a general user input or a list
      else
      {
         # If the $previousNode is the command node and this current userip node
         # is not a userip list and it has been matched already, then return 0.
         if ($previousNode->asmcmdparser_get_type() == NODE_COMMAND && 
             $self->asmcmdparser_get_type() != NODE_USER_IP_LIST && 
             $self->asmcmdparser_get_tracecount() > 0
            )
         {
          return 0;
         }
         # Else, try to match this node and mark the ancestors
         $result  = $self->asmcmdparser_mark_ancestors();
         # If match is successful, set the $matchedUserip as this node and 
         # return 1.
         if ($result == 1)
         {
            $matchedUserip = $self;
            return 1;
         }
         # Else return failuer to match.
         return -1;
      }
   }
   # Else if this node is a non-userip node, then traverse recursively till a 
   # matching userip node is found
   else
   { 
      
      my $temp = $self->asmcmdparser_get_children();
      my %children = %$temp;
      my $key;
      my $childNode;
      # Run checks on each of the child node.
      while(($key, $childNode) = each(%children))
      {
         $type = $childNode->asmcmdparser_get_type();
         # If this node is a bracket type or a userip node recursively call
         # this function.
         if ($type == NODE_SQUA || $type == NODE_SQUA_ONE || 
             $type == NODE_BRACE || $type == NODE_BRACE_ONE || 
             $type == NODE_PIPE || $type >= NODE_USER_IP_GEN)
         {
            $result = $childNode->asmcmdparser_match_userip();
            # Bubble up the results at this level to higher levels.
            return -1 if $result == -1;
            return  1 if $result ==  1;
            $fixedipFlag = 1 if $result == 2;
         }
      }
      # If a fixedip is expecetd and not found return 2
      # Else return 0.
      return 2 if $fixedipFlag == 1;
      return 0;
   }
}
#################################################

########
# NAME
#   asmcmdparser_trace_enclosing_node 
#
# DESCRIPTION
#   This routine finds the node with which the current node should be
#   associated. But not necessarily with a parent-child relationship.
#   It sets the value of $previousNode as a reference to the enclosing node.
#
# PARAMETERS
#   self   (IN) - reference to this node.
#
# GLOBAL VARIABLES USED
#   $previousNode
#     
# RETURNS
#   (Nothing)
#
# NOTES
#   This subroutine is invoked only when the current token (from the command
#   syntax) is an option (short / long).
#
########
sub asmcmdparser_trace_enclosing_node
{
   my $type = $previousNode->asmcmdparser_get_type();
   while(($type != NODE_COMMAND) && ($type != NODE_SQUA) && 
         ($type != NODE_BRACE)   && ($type != NODE_SQUA_ONE) && 
         ($type != NODE_BRACE_ONE) && ($type != NODE_PIPE)
        )
   {
      $previousNode = $previousNode->asmcmdparser_get_parent();
      $type = $previousNode->asmcmdparser_get_type();
   }
}

########
# NAME
#   asmcmdparser_pipe_present 
#
# DESCRIPTION
#   Looks ahead into the remaining tokens and detects pipe token
#   associated with the nearest enclosing bracket.
#
# PARAMETERS
#   self   (IN) - reference to this node.
#
# GLOBAL VARIABLES USED
#   $currentIndex
#   @tokens
#     
# RETURNS
#   0 if there are no pipe tokens associated with the nearest bracket.
#   1 if it detects a pipe token.
########
sub asmcmdparser_pipe_present
{
   my $bracketDepth = 1;
   my $index = $currentIndex + 1;
   for($i = $index; $i <= $#tokens; $i++)
   {
      if ($bracketDepth == 0)
      {
         return 0;
      }
      
      if (($tokens[$i] eq "|") && ($bracketDepth == 1))
      {
         return 1;
      }
      elsif (($tokens[$i] eq "}") || ($tokens[$i] eq "]"))
      {
         $bracketDepth--;
      }
      elsif (($tokens[$i] eq "{") || ($tokens[$i] eq "["))
      {
         $bracketDepth++;
      }
   }
   return 0;
}

########
# NAME
#   asmcmdparser_get_token_type 
#
# DESCRIPTION
#   This routine finds the type of the token.
#   Uses pattern matching to deteremine the type.
#
# PARAMETERS
#   token   (IN) - token to be classified.
#
# GLOBAL VARIABLES USED
#   (None)
# 
# RETURNS
#   Returns a constant associated with the token type.
#
########
sub asmcmdparser_get_token_type
{
   my $token = shift; 
   if ($token =~ /^\-\-/)
   {
      return LONGOPT;
   }
   elsif ($token =~ /^\-\w/)
   {
      return SHORTOPT;
   }
   elsif ($token =~ /^\[$/)
   {
      return LSQUA;
   }
   elsif ($token =~ /^\]$/)
   {
      return RSQUA;
   }
   elsif ($token =~ /^\{$/)
   {
      return LBRACE;
   }
   elsif ($token =~ /^\}$/)
   {
      return RBRACE;
   }
   elsif ($token =~ /^\|$/)
   {
      return PIPE;
   }
   elsif ($token =~ /^<[^,\.]*,?\.\.\.>$/)
   {
      return USER_IP_LIST;
   }
   elsif ($token =~ /^<[^>]*>$/)
   {
      return USER_IP_GEN;
   }
   elsif ($token =~ /^\w*$/)
   {
      return USER_IP_FIXED;
   }
   else
   {
     return undef;
   }
}

########
# NAME
#   asmcmdparser_parse_command_token 
#
# DESCRIPTION
#   This routine parses the next token
#
# PARAMETERS
#   (Nothing)
#
# GLOBAL VARIABLES USED
#   $currentToken
#   $currentNodeType
#   $currentIndex
#   @tokens
# 
# RETURNS
#   0 - on failure
#   1 - on success
#
########
sub asmcmdparser_parse_command_token
{
   # Determine the type of the next token. Make it the current token.
   # Long and short options, user input, brackets are handled by the subroutine
   # asmcmdparser_parse_options().
   # Others are handled by other specific subroutines.
   $currentToken = &asmcmdparser_get_token_type($tokens[$currentIndex]);
   if ($currentToken == LONGOPT)
   {
      $currentNodeType = NODE_LONGOPT;
      asmcmdparser_trace_enclosing_node();
      if (!asmcmdparser_parse_options())
      {
         return 0;
      }
   }
   elsif ($currentToken == SHORTOPT)
   {
      $currentNodeType = NODE_SHORTOPT;
      asmcmdparser_trace_enclosing_node();
      if (!asmcmdparser_parse_options())
      {
         return 0;
      }
   }
   elsif ($currentToken == LSQUA)
   {
      if (asmcmdparser_pipe_present())
      {
         $currentNodeType = NODE_SQUA_ONE;
      }
      else
      {
         $currentNodeType = NODE_SQUA;
      }
      if (!asmcmdparser_parse_options())
      {
         return 0;
      }
   }
   elsif ($currentToken == RSQUA)
   {
      asmcmdparser_parse_rsqua();
   }
   elsif ($currentToken == LBRACE)
   {
      if (asmcmdparser_pipe_present())
      {
         $currentNodeType = NODE_BRACE_ONE;
      }
      else
      {
         $currentNodeType = NODE_BRACE;
      }
      if (!asmcmdparser_parse_options())
      {
         return 0;
      }
   }
   elsif ($currentToken == RBRACE)
   {
      asmcmdparser_parse_rbrace();
   }
   elsif ($currentToken == PIPE)
   {
      $currentNodeType = NODE_PIPE;
      if (!asmcmdparser_parse_pipe())
      {
         return 0;
      }
   }
   elsif ($currentToken == USER_IP_GEN)
   {
      $currentNodeType = NODE_USER_IP_GEN;
      if (!asmcmdparser_parse_options())
      {
         return 0;
      }
   }
   elsif ($currentToken == USER_IP_FIXED)
   {
      $currentNodeType = NODE_USER_IP_FIXED;
      if (!asmcmdparser_parse_options())
      {
         return 0;
      }
   }
   elsif ($currentToken == USER_IP_LIST)
   {
      $currentNodeType = NODE_USER_IP_LIST;
      if (!asmcmdparser_parse_options())
      {
         return 0;
      }
   }
   elsif ($currentToken == INVALID)
   {
      return 0;
   }
   return 1;
}

########
# NAME
#   asmcmdparser_determine_edge_type 
#
# DESCRIPTION
#   This routine determines the type of parent-child relationship for the 
#   current node.
#
# PARAMETERS
#   (Nothing)
#
# GLOBAL VARIABLES USED
#   $edgeType
#   $parentNode
# 
# RETURNS
#   Returns the constant corresponding to the edge type.
#
########
sub asmcmdparser_determine_edge_type
{
   if ($parentNode->asmcmdparser_get_type() == NODE_COMMAND)
   {
      $edgeType = EDGE_COMMAND;
   }
   elsif ($parentNode->asmcmdparser_get_type() == NODE_LONGOPT)
   {
      $edgeType = EDGE_LONGOPT;
   }
   elsif ($parentNode->asmcmdparser_get_type() == NODE_SHORTOPT)
   {
      $edgeType = EDGE_SHORTOPT;
   }
   elsif ($parentNode->asmcmdparser_get_type() == NODE_SQUA)
   {
      $edgeType = EDGE_NONE_OR_MORE;
   }
   elsif ($parentNode->asmcmdparser_get_type() == NODE_SQUA_ONE)
   {
      $edgeType = EDGE_ATMOST_ONE;
   }
   elsif ($parentNode->asmcmdparser_get_type() == NODE_BRACE)
   {
      $edgeType = EDGE_ONE_OR_MORE;
   }
   elsif ($parentNode->asmcmdparser_get_type() == NODE_BRACE_ONE)
   {
      $edgeType = EDGE_EXACTLY_ONE;
   }
   elsif ($parentNode->asmcmdparser_get_type() == NODE_PIPE)
   {
      $edgeType = EDGE_PIPE;
   }
   else
   {
   }
}

########
# NAME
#   asmcmdparser_parse_options 
#
# DESCRIPTION
#   This routine parses long and short options, user inputs, and brackets
#
# PARAMETERS
#   (Nothing)
#
# GLOBAL VARIABLES USED
#   @tokens
#   $previousNode
#   $parentNode
#   $currentIndex
#   %optionsHash
#   %useripHash
#   $edgeType
# 
# RETURNS
#   (Nothing)
#
########
sub asmcmdparser_parse_options
{
   my $previousNodeType = $previousNode->asmcmdparser_get_type();
   my $lookAhead;
   my $newNode;
   # Determine the parent node.
   # If the enclosing node is the Command then the parent node is the Command
   # node itself.
   if ($previousNodeType == NODE_COMMAND)
   {
      $parentNode = $previousNode;
   }
   # If the enclosing node is a long option, then there are special cases.
   elsif ($previousNodeType == NODE_LONGOPT || 
          $previousNodeType == NODE_SHORTOPT
         )
   {  
      my $parentOfPreviousNode = $previousNode->asmcmdparser_get_parent();
      # If the parent of the enclosing node is the Command, then we need to 
      # lookahead to the next tokens to determine the correct relationship 
      # between the previousNode and the currentNode.
      # Eg.1 somecommand --striping {         coarse | fine }
      #      ^           ^          ^         ^
      #      Command previousNode currentNode nextNode
      # Eg.2 anothercommand --discovery [         --redundancy ...
      #      ^              ^           ^         ^
      #      Command    previousNode currentNode nextNode
      #
      # Here in the first case the parent of current node is the long option 
      # '--striping'
      # whereas in the second case, the parent of the square brace is the
      # command and not the long option preceding it. Hence in such special 
      # case we need to look ahead into the contents of the brace to determine
      # the parent.
      if ($parentOfPreviousNode->asmcmdparser_get_type() == NODE_COMMAND)
      {
         $lookAhead = $tokens[$currentIndex + 1];
         if (($currentNodeType == NODE_SQUA) || 
             ($currentNodeType == NODE_BRACE) ||
             ($currentNodeType == NODE_SQUA_ONE) || 
             ($currentNodeType == NODE_BRACE_ONE)
            )
         {
            my $lookAheadType = &asmcmdparser_get_token_type($lookAhead);
            if ($lookAheadType >= USER_IP_GEN)
            {
               $parentNode = $previousNode;
            }
            else
            {
               $parentNode = $previousNode->asmcmdparser_get_parent();
            }
         }
         elsif ($currentNodeType >= NODE_USER_IP_GEN)
         {
            $parentNode = $previousNode;
         }
         else
         {
            $parentNode = $previousNode->asmcmdparser_get_parent();
         }
      }
      else
      {
         $parentNode = $previousNode;
      }
   }
   elsif (($previousNodeType == NODE_SQUA) ||
          ($previousNodeType == NODE_SQUA_ONE) || 
          ($previousNodeType == NODE_BRACE) || 
          ($previousNodeType == NODE_BRACE_ONE) ||
          ($previousNodeType == NODE_PIPE)
         )
   {
      $parentNode = $previousNode;
   }
   else
   {
      $parentNode = $previousNode->asmcmdparser_get_parent();
   }
   asmcmdparser_determine_edge_type();

   $newNode = $parentNode->asmcmdparser_add_child($tokens[$currentIndex],
                                                  $currentNodeType, 
                                                  $edgeType);
   return 0 if (!defined $newNode);
   
   if ($currentNodeType < NODE_USER_IP_GEN)
   {
      $previousNode = $newNode;
      if (($currentNodeType == NODE_BRACE_ONE) || 
          ($currentNodeType == NODE_SQUA_ONE)
         )
      {
         $newNode = $previousNode->asmcmdparser_add_child("|", 
                                                          NODE_PIPE, 
                                                          EDGE_EXACTLY_ONE) 
                                    if ($currentNodeType == NODE_BRACE_ONE);
         $newNode = $previousNode->asmcmdparser_add_child("|", 
                                                          NODE_PIPE, 
                                                          EDGE_ATMOST_ONE) 
                                    if ($currentNodeType == NODE_SQUA_ONE);
         return 0 if (!defined $newNode);
         $previousNode = $newNode;
      }
   }
   else 
   {
      if ($parentNode->asmcmdparser_get_type() == NODE_COMMAND)
      {
         $numUseripToCmd++;
      }
      $previousNode = $previousNode->asmcmdparser_get_parent() 
      if ($previousNode->asmcmdparser_get_type() != NODE_COMMAND);
   }
   # Store the references to nodes in a hash for easy access.
   if (($currentNodeType == NODE_LONGOPT) || 
       ($currentNodeType == NODE_SHORTOPT))
   {
      $optionsHash{$tokens[$currentIndex]} = $newNode;
   }
   # Store the references in a hash for easy access.
   if ($currentNodeType >= NODE_USER_IP_GEN)
   {
      if (exists $useripHash{$tokens[$currentIndex]})
      {
         $useripHash{$tokens[$currentIndex] . 
                     $parentNode->asmcmdparser_get_content()} = $newNode;
      }
      else
      {
         $useripHash{$tokens[$currentIndex]} = $newNode;
      }
   }
   $currentIndex++;
   return 1;
}

########
# NAME
#   asmcmdparser_parse_rsqua 
#
# DESCRIPTION
#   This routine parses closing brackets.
#
# PARAMETERS
#   (Nothing)
#
# GLOBAL VARIABLES USED
#   $previousNode
#   $currentIndex
# 
# RETURNS
#   (Nothing)
#
########
sub asmcmdparser_parse_rsqua
{
   if ($previousNode->asmcmdparser_get_type() == NODE_COMMAND)
   {
      $currentIndex++;
      return;
   }
   while(($previousNode->asmcmdparser_get_type() != NODE_SQUA) && 
         ($previousNode->asmcmdparser_get_type() != NODE_SQUA_ONE)
        )
   {
      $previousNode = $previousNode->asmcmdparser_get_parent();  
   }
   $previousNode = $previousNode->asmcmdparser_get_parent();
   $currentIndex++;
}

########
# NAME
#   asmcmdparser_parse_rbrace 
#
# DESCRIPTION
#   This routine parses closing brackets.
#
# PARAMETERS
#   (Nothing)
#
# GLOBAL VARIABLES USED
#   $previousNode
#   $currentIndex
# 
# RETURNS
#   (Nothing)
#
########
sub asmcmdparser_parse_rbrace
{
   while(($previousNode->asmcmdparser_get_type() != NODE_BRACE) && 
         ($previousNode->asmcmdparser_get_type() != NODE_BRACE_ONE)
        )
   {
      $previousNode = $previousNode->asmcmdparser_get_parent();  
   }
   $previousNode = $previousNode->asmcmdparser_get_parent();
   $currentIndex++;
}

########
# NAME
#   asmcmdparser_parse_pipe 
#
# DESCRIPTION
#   This routine parses pipes. Adds new children under the brackets.
#
# PARAMETERS
#   (Nothing)
#
# GLOBAL VARIABLES USED
#   $previousNode
#   $currentIndex
# 
# RETURNS
#   (Nothing)
#
########
sub asmcmdparser_parse_pipe
{
   my $temp = $previousNode->asmcmdparser_get_type();
   my $newNode;
   while(($temp != NODE_SQUA_ONE) && ($temp != NODE_BRACE_ONE))
   {
      $previousNode = $previousNode->asmcmdparser_get_parent();  
      $temp = $previousNode->asmcmdparser_get_type();
   }
   $newNode = $previousNode->asmcmdparser_add_child("|", 
                                                    NODE_PIPE, 
                                                    EDGE_EXACTLY_ONE) 
                                    if ($temp == NODE_BRACE_ONE);
   $newNode = $previousNode->asmcmdparser_add_child("|", 
                                                    NODE_PIPE, 
                                                    EDGE_ATMOST_ONE) 
                                    if ($temp == NODE_SQUA_ONE);
   return 0 if (!defined $newNode);
   $previousNode = $newNode;
   $currentIndex++;
   return 1;
}

######################################################



########
# NAME
#   asmcmdparser_parse_issued_command_token 
#
# DESCRIPTION
#   This routine processes each of the tokens from the user issued command 
#   string. 
#   it would detect any deprecated options and finds the new option to be used
#   it will set $token as the new option (replacing the deprecated option)
#   The function should be called again to process the new option.
#
# PARAMETERS
#   (None)
#
# GLOBAL VARIABLES USED
#   $startNode
#   $optionsHash
#   $token 
#   $previousNode
#   $rootNode
#   $args_ref
#   $optionString
#   $
#   $currentIndex
# 
# RETURNS
#   1 - If parsing was successful.
#   0 - If parsing failed.
#   2 - if a deprecated option is found.
#
########
sub asmcmdparser_parse_issued_command_token
{
   my $key;
   my $value;
   my $foundFlag = 0;
   my $result;
   my $fixedUserInputFlag = 0;
   my @eargs;
   # If the option is found to exist as a key in the hash, then it is an option
   if (defined $optionsHash{$token})
   {
      $startNode = $optionsHash{$token};
      # Since the option has been successfully matched,
      # remember it for later when we might need to match a user input
      # as this node's child.
      # If this node has no child then if we encounter a user input right
      # after this token, it would have to be associated with the root node.
      if ( $startNode->asmcmdparser_get_number_children() != 0)
      {
         $previousNode = $startNode;
      }
      else 
      {
         $previousNode = $rootNode;
      }
      # Try to match the token with the node bt marking the ancestors.
      $result = $startNode->asmcmdparser_mark_ancestors();
      return 0 if ($result == 0);
      # If matched successfully, set the key in the args hash.
      $foundFlag = 1;
      $token =~ m/-+(\w+)/;
      $optionString = $1;
      $args_ref->{$optionString} = 1;
      return 1;
   }
   # If the key exists, but is not defined (ie, it doesn't point to a 
   # parse tree node, then we've found a deprecated option.
   elsif (exists $optionsHash{$token})
   {
      $foundFlag = 1;
      $token =~ m/-+(\w+)/;
      # Set the key in the args hash (Since this is expected by the modules
      # for displayin the Warning and to set the correct new option.
      $optionString = $1;
      $deprecatedOption = $optionString;
      $args_ref->{$optionString} = 1;
      # Find the new option from the list of deprecated option in asmcmdglobal
      my $newOption = $asmcmdglobal_deprecated_options{$cmd}{$optionString}[1]; 
      my @newOptions = ();
      # Handle cases where the string is "NULL" or contains "|"
      # If the string for new option is null, then just report success.
      # There might not be a replacement for this deprecated option.
      # hence just set the flag and return 1.
      if (!defined $newOption || $newOption eq "NULL")
      {
         return 1;
      }
      # There are scenarios where one deprecated option has been replaced by
      # a set of new options. For such cases, there will be a "|" symbol in 
      # the string $newOption.
      elsif ($newOption =~ /\|/)
      {
         # Extract the new options into a array
         @newOptions = split('\|', $newOption);
         # These kind of deprecated option have been ungraded in the following
         # manner:
         # Deprecaetd use: --deprecatedOption { option1 | option2 | option3 }
         # Current use   : { --option1 | --option2 | --option3 }
         # Hence look ahead at the token following the deprecated option and 
         # set it as the new option.
         foreach(@newOptions)
         {
            if ($currentIndex < $#ARGV && $_ eq $ARGV[$currentIndex+1])
            {
               $newOption = $_;
               $currentIndex++;
            }
         }
         # If a correct new option cannot be found in this scenario, 
         # report failure.
         return 0 if ($newOption eq $optionString);
      }
      # Append the hyphens suitably once a new option has been determined
      $token = "-" . $newOption if ($newOption =~ /^\w$/);
      $token = "--" . $newOption if ($newOption =~ /^\w\w+$/);
      return 2;
   }
   # Else it could be a user input or an invalid option.
   else
   {
      # If the string matches the format of an option, report invalid option.
      if ($token =~ /^-+\w+$/)
      {
         $token =~ s/^-+//;
         @eargs = ($token);
         # Invalid option specifed.
         asmcmdshare_error_msg(9412, \@eargs);
         return 0;
      }
      if ($token =~ /^-+$/)
      {
         return 0;
      }
      # Else try to find a userip node in the parse tree that matches this input
      $result = $previousNode->asmcmdparser_match_userip();
      if ($result == 2)
      {
         # Invalid user input. A fixed user input expected here.
         return 0;
      }
      # Successful matching to a userip node in the tree.
      elsif ($result == 1)
      {
         $optionString = $previousNode->asmcmdparser_get_content();
         if ($previousNode->asmcmdparser_get_type() != NODE_COMMAND)
         {
            $optionString =~ m/-*(\w+)/;
            $optionString = $1;
         }
         if ($matchedUserip->asmcmdparser_get_type() == NODE_USER_IP_LIST ||
             $previousNode->asmcmdparser_get_type() == NODE_COMMAND
            )
         {
            if (($previousNode->asmcmdparser_get_type() == NODE_COMMAND && 
                 $numMatchedUseripToCmd == 0) ||
                ($previousNode->asmcmdparser_get_type() != NODE_COMMAND && 
                 $matchedUserip->asmcmdparser_get_tracecount() == 1)
               )
            {
               $args_ref->{$optionString} = ();

            }
            my @tempArray = split(/,/,$token);
            push(@{$args_ref->{$optionString}}, @tempArray);
            if ($previousNode->asmcmdparser_get_type() == NODE_COMMAND)
            {
               $numMatchedUseripToCmd++;
            }

            #Bug-19783114: If specified command is invalid, throw ASMCMD-8022
            if ($previousNode->asmcmdparser_get_content() eq "asmcmd" &&
                $matchedUserip->asmcmdparser_get_content() eq "<command>" &&
                !defined($asmcmdglobal_cmds{$token}))
            {
               @eargs = ($token);
               asmcmdshare_error_msg(8022, \@eargs);
               return 0;
            }
         }
         else
         {
            $args_ref->{$optionString} = $token;
            if (defined($deprecatedOption))
            {
               $args_ref->{$deprecatedOption} = $token;
               $deprecatedOption = undef;
            }
         }
         $previousNode = $rootNode;
         return 1;
      }
      elsif ($result == 0)
      {  
         $previousNode = $rootNode;
         $result = $previousNode->asmcmdparser_match_userip();
         if ($result == 2)
         {
            $previousnodecontent = $previousNode->asmcmdparser_get_content();
            $previousnodecontent =~ s/-*//;
            $token =~ s/-*//;
            @eargs = ($token, $previousnodecontent);
            # Invalid user input. A fixed user input expected here.
            return 0;
         }
         elsif ($result == 1)
         {
            $optionString = $previousNode->asmcmdparser_get_content();
            $optionString =~ m/-*(\w+)/;
            $optionString = $1;
            if ($matchedUserip->asmcmdparser_get_type() == NODE_USER_IP_LIST ||
                $previousNode->asmcmdparser_get_type() == NODE_COMMAND
               )
            {
               if (($previousNode->asmcmdparser_get_type() == NODE_COMMAND && 
                    $numMatchedUseripToCmd == 0) ||
                   ($previousNode->asmcmdparser_get_type() != NODE_COMMAND && 
                    $matchedUserip->asmcmdparser_get_tracecount() == 1)
                  )
               {
                  $args_ref->{$optionString} = ();
               }
               my @tempArray = split(/,/,$token);
               push(@{$args_ref->{$optionString}}, @tempArray);
               if ($previousNode->asmcmdparser_get_type() == NODE_COMMAND)
               {
                  $numMatchedUseripToCmd++;
               }
               #push(@{$args_ref->{$optionString}}, $token);
            }
            else
            {
               $args_ref->{$optionString} = $token;
               if (defined($deprecatedOption))
               {
                  $args_ref->{$deprecatedOption} = $token;
                  $deprecatedOption = undef;
               }
            }
            $previousNode = $rootNode;
            return 1;
         }
         $token =~ s/-*//;
         @eargs = ($token);
         # Token could not be matched.
         return 0;
      }
      return 0;
   }
}

########
# NAME
#   asmcmdparser_parse_issued_command 
#
# DESCRIPTION
#   This routine is the one that should be called to initiate parsing of the 
#   user issued command. 
#   It expects the tokens to be in the array @ARGV. (Tokens expcept the name 
#   of the command.)
#   Constructs the parse tree for the command.
#   Runs routine preprocessing steps like expanding the short options that have
#   been clubbed together.
#   Then processes each of the tokens from the user issued command string. 
#
# PARAMETERS
#   $cmd - The name of the command issued.
#   $args_ref - A reference to the hash in which the options used in the
#               command (issued) are stored.
#   $strings_ref - A reference to an array containing flags associated with
#                  the current command. This will contain the list of deprecated
#                  options as well.
#
# RETURNS
#   1 on successful parsing of the command.
#   0 if parsing fails at any stage. 
#
########
sub asmcmdparser_parse_issued_command
{
   $cmd = shift @_;
   $args_ref = shift @_;
   $strings_ref = shift @_;
   my $result;
   %{$args_ref} = ();
   %optionsHash = ();
   $deprecatedOption = undef;
   $syntax = asmcmdshare_get_help_syntax($cmd);
   # Add spaces between each token so that they can be extracted easily.
   $syntax =~ s/^\s+//;
   $syntax =~ s/\s+$//;
   $syntax =~ s/(\S)([\[\]\{\}\|])/$1 $2/g;
   $syntax =~ s/(\S)([\[\]\{\}\|])/$1 $2/g;
   $syntax =~ s/([\[\]\{\}\|])(\S)/$1 $2/g;
   $syntax =~ s/\s+/ /g;
   while($syntax =~ /<[^\s>]*\s[^>]*>/)
   {
      $syntax =~ s/(<[^\s>]*)\s([^>]*>)/$1$2/g;
   }
   # Expand the short options that have been clubbed together in the syntax
   # specification.
   while($syntax =~ /\s(-\w\w+)/)
   {
      my $shortOpts = $1;
      my @shortOptions = split(//, $shortOpts);
      shift @shortOptions;
      my $newShortOpts = join(" -", @shortOptions);
      $syntax =~ s/$shortOpts/-$newShortOpts/;
   }
   # Split the syntax into tokens. The syntax specification should not have any
   # quotes.
   @tokens = split(/ /, $syntax);
       
   # Create the root node with the command.
   $newNode = new asmcmdparser(undef, $tokens[0], NODE_COMMAND, undef);
   return 0 if (!defined $newNode);
   $previousNode = $newNode;
   $newNode->asmcmdparser_set_parent($newNode);
   
   $rootNode = $newNode;
   $numUseripToCmd = 0;
   $numMatchedUseripToCmd = 0;
   $currentIndex = 1;
   # Parse the syntax and populate the parse tree.
   while($currentIndex <= $#tokens)
   {
      if (!asmcmdparser_parse_command_token())
      {
         asmcmdshare_error_msg(9401, undef); 
         return 0;
      }
   }
   # Add the deprecated options to the %optionsHash.
   foreach (@$strings_ref)
   {
      $_ =~ s/=.*$//;
      if (length($_) == 1)
      {
         $_ =~ s/^/-/;
      }
      else
      {
         $_ =~ s/^/--/;
      }
      if (exists($optionsHash{$_}))
      {
         next;
      }
      else
      {
         $optionsHash{$_} = undef;
      }
   }
  
   # Matching #
   # Now parse the command issued by the user.
   # Process each token in the command.
   $currentIndex = 0;
   while($currentIndex <= $#ARGV)
   {
      $token = $ARGV[$currentIndex];
      # If the token is a group of short options clubbed together, then expand
      # them.
      if ($token =~ /^-\w\w+$/)
      {
         my $tempBuffer = $token;
         # Split the token into the individual characters.
         my @tempArray  = split(//, $token);
         shift @tempArray;
         # Handle each short option.
         foreach (@tempArray)
         {
            # Construct the short option from the character.
            $token = "-" . $_;
            # Handle the current short option.
            $result = asmcmdparser_parse_issued_command_token();
            if (!$result)
            {
               return 0;
            }
            elsif ($result == 2)
            {
               if (!asmcmdparser_parse_issued_command_token())
               {
                  return 0;
               }
            }
         }
      }
      # If it is not a bunch of short options, then handle the token the usual
      # way.
      else
      {
         $result = asmcmdparser_parse_issued_command_token();
         if (!$result)
         {
            return 0;
         }
         elsif ($result == 2)
         {
            if (!asmcmdparser_parse_issued_command_token())
            {
               return 0;
            }
         }
      }
      $currentIndex++;
   }
   if (!$rootNode->asmcmdparser_final_check())
   {
      return 0;
   }
   
   return 1;
}

1;

OHA YOOOO