MINI MINI MANI MO
# 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