2012-07-09 20:32:02 +02:00
/******************************************************************************
* Icinga 2 *
2015-01-22 12:00:23 +01:00
* Copyright ( C ) 2012 - 2015 Icinga Development Team ( http : //www.icinga.org) *
2012-07-09 20:32:02 +02:00
* *
* This program is free software ; you can redistribute it and / or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation ; either version 2 *
* of the License , or ( at your option ) any later version . *
* *
* This program is distributed in the hope that it will be useful , *
* but WITHOUT ANY WARRANTY ; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the *
* GNU General Public License for more details . *
* *
* You should have received a copy of the GNU General Public License *
* along with this program ; if not , write to the Free Software Foundation *
* Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 , USA . *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2014-05-25 16:23:35 +02:00
# include "icinga/macroprocessor.hpp"
# include "icinga/macroresolver.hpp"
# include "icinga/customvarobject.hpp"
# include "base/array.hpp"
# include "base/objectlock.hpp"
2014-10-19 14:21:12 +02:00
# include "base/logger.hpp"
2014-05-25 16:23:35 +02:00
# include "base/context.hpp"
2015-08-15 20:28:05 +02:00
# include "base/configobject.hpp"
2015-01-27 13:40:05 +01:00
# include "base/scriptframe.hpp"
2015-08-27 08:22:35 +02:00
# include "base/convert.hpp"
# include "base/exception.hpp"
2015-09-22 18:18:29 +02:00
# include <boost/assign.hpp>
2013-03-16 21:18:53 +01:00
# include <boost/foreach.hpp>
2014-04-08 13:23:24 +02:00
# include <boost/algorithm/string/split.hpp>
# include <boost/algorithm/string/join.hpp>
# include <boost/algorithm/string/classification.hpp>
2012-06-13 13:42:55 +02:00
using namespace icinga ;
2014-04-08 13:23:24 +02:00
Value MacroProcessor : : ResolveMacros ( const Value & str , const ResolverList & resolvers ,
2014-04-26 13:16:08 +02:00
const CheckResult : : Ptr & cr , String * missingMacro ,
2014-11-13 11:23:57 +01:00
const MacroProcessor : : EscapeCallback & escapeFn , const Dictionary : : Ptr & resolvedMacros ,
2015-08-27 08:22:35 +02:00
bool useResolvedMacros , int recursionLevel )
2013-02-13 20:08:09 +01:00
{
Value result ;
2013-09-11 14:59:49 +02:00
if ( str . IsEmpty ( ) )
return Empty ;
2013-07-16 17:11:18 +02:00
if ( str . IsScalar ( ) ) {
2014-11-13 11:23:57 +01:00
result = InternalResolveMacros ( str , resolvers , cr , missingMacro , escapeFn ,
2015-08-27 08:22:35 +02:00
resolvedMacros , useResolvedMacros , recursionLevel + 1 ) ;
2013-07-16 17:11:18 +02:00
} else if ( str . IsObjectType < Array > ( ) ) {
2014-11-08 21:17:16 +01:00
Array : : Ptr resultArr = new Array ( ) ;
2013-07-16 17:11:18 +02:00
Array : : Ptr arr = str ;
2013-02-13 20:08:09 +01:00
2013-03-14 12:17:46 +01:00
ObjectLock olock ( arr ) ;
2013-02-28 10:27:20 +01:00
2013-03-14 12:17:46 +01:00
BOOST_FOREACH ( const Value & arg , arr ) {
2013-03-22 10:58:47 +01:00
/* Note: don't escape macros here. */
2015-03-26 09:29:52 +01:00
Value value = InternalResolveMacros ( arg , resolvers , cr , missingMacro ,
2015-08-27 08:22:35 +02:00
EscapeCallback ( ) , resolvedMacros , useResolvedMacros , recursionLevel + 1 ) ;
2015-03-26 09:29:52 +01:00
if ( value . IsObjectType < Array > ( ) )
resultArr - > Add ( Utility : : Join ( value , ' ; ' ) ) ;
else
resultArr - > Add ( value ) ;
2013-02-13 20:08:09 +01:00
}
2013-03-14 12:17:46 +01:00
result = resultArr ;
2015-02-11 15:58:59 +01:00
} else if ( str . IsObjectType < Dictionary > ( ) ) {
Dictionary : : Ptr resultDict = new Dictionary ( ) ;
Dictionary : : Ptr dict = str ;
ObjectLock olock ( dict ) ;
BOOST_FOREACH ( const Dictionary : : Pair & kv , dict ) {
/* Note: don't escape macros here. */
resultDict - > Set ( kv . first , InternalResolveMacros ( kv . second , resolvers , cr , missingMacro ,
2015-08-27 08:22:35 +02:00
EscapeCallback ( ) , resolvedMacros , useResolvedMacros , recursionLevel + 1 ) ) ;
2015-02-11 15:58:59 +01:00
}
result = resultDict ;
2015-01-29 10:09:53 +01:00
} else if ( str . IsObjectType < Function > ( ) ) {
2015-08-27 08:58:25 +02:00
result = EvaluateFunction ( str , resolvers , cr , escapeFn , resolvedMacros , useResolvedMacros , 0 ) ;
2013-02-17 19:14:34 +01:00
} else {
2015-01-29 10:09:53 +01:00
BOOST_THROW_EXCEPTION ( std : : invalid_argument ( " Macro is not a string or array. " ) ) ;
2013-02-13 20:08:09 +01:00
}
return result ;
}
2014-04-08 13:23:24 +02:00
bool MacroProcessor : : ResolveMacro ( const String & macro , const ResolverList & resolvers ,
2014-11-26 20:43:42 +01:00
const CheckResult : : Ptr & cr , Value * result , bool * recursive_macro )
2013-03-22 14:40:55 +01:00
{
2013-11-19 07:49:41 +01:00
CONTEXT ( " Resolving macro ' " + macro + " ' " ) ;
2014-04-17 17:38:02 +02:00
* recursive_macro = false ;
2014-04-10 08:46:36 +02:00
2014-04-08 13:23:24 +02:00
std : : vector < String > tokens ;
boost : : algorithm : : split ( tokens , macro , boost : : is_any_of ( " . " ) ) ;
String objName ;
if ( tokens . size ( ) > 1 ) {
objName = tokens [ 0 ] ;
tokens . erase ( tokens . begin ( ) ) ;
}
BOOST_FOREACH ( const ResolverSpec & resolver , resolvers ) {
if ( ! objName . IsEmpty ( ) & & objName ! = resolver . first )
continue ;
if ( objName . IsEmpty ( ) ) {
2014-05-12 16:45:25 +02:00
CustomVarObject : : Ptr dobj = dynamic_pointer_cast < CustomVarObject > ( resolver . second ) ;
2014-04-08 13:23:24 +02:00
if ( dobj ) {
Dictionary : : Ptr vars = dobj - > GetVars ( ) ;
if ( vars & & vars - > Contains ( macro ) ) {
2014-11-26 20:43:42 +01:00
* result = vars - > Get ( macro ) ;
2014-04-17 17:38:02 +02:00
* recursive_macro = true ;
2014-04-08 13:23:24 +02:00
return true ;
}
}
}
2014-11-08 21:17:16 +01:00
MacroResolver * mresolver = dynamic_cast < MacroResolver * > ( resolver . second . get ( ) ) ;
2014-04-08 13:23:24 +02:00
if ( mresolver & & mresolver - > ResolveMacro ( boost : : algorithm : : join ( tokens , " . " ) , cr , result ) )
2013-03-22 14:40:55 +01:00
return true ;
2014-04-08 13:23:24 +02:00
Value ref = resolver . second ;
bool valid = true ;
BOOST_FOREACH ( const String & token , tokens ) {
if ( ref . IsObjectType < Dictionary > ( ) ) {
Dictionary : : Ptr dict = ref ;
if ( dict - > Contains ( token ) ) {
ref = dict - > Get ( token ) ;
continue ;
} else {
valid = false ;
break ;
}
} else if ( ref . IsObject ( ) ) {
Object : : Ptr object = ref ;
2014-11-03 00:44:04 +01:00
Type : : Ptr type = object - > GetReflectionType ( ) ;
2014-04-08 13:23:24 +02:00
if ( ! type ) {
valid = false ;
break ;
}
int field = type - > GetFieldId ( token ) ;
if ( field = = - 1 ) {
valid = false ;
break ;
}
ref = object - > GetField ( field ) ;
}
}
if ( valid ) {
2014-04-17 17:38:02 +02:00
if ( tokens [ 0 ] = = " vars " | |
tokens [ 0 ] = = " action_url " | |
tokens [ 0 ] = = " notes_url " | |
tokens [ 0 ] = = " notes " )
* recursive_macro = true ;
2014-04-10 08:46:36 +02:00
2014-04-08 13:23:24 +02:00
* result = ref ;
return true ;
}
2013-03-22 14:40:55 +01:00
}
return false ;
}
2015-01-28 14:14:48 +01:00
Value MacroProcessor : : InternalResolveMacrosShim ( const std : : vector < Value > & args , const ResolverList & resolvers ,
2015-08-27 08:58:25 +02:00
const CheckResult : : Ptr & cr , const MacroProcessor : : EscapeCallback & escapeFn , const Dictionary : : Ptr & resolvedMacros ,
2015-01-28 14:14:48 +01:00
bool useResolvedMacros , int recursionLevel )
{
if ( args . size ( ) < 1 )
BOOST_THROW_EXCEPTION ( std : : invalid_argument ( " Too few arguments for function " ) ) ;
2015-08-27 08:58:25 +02:00
String missingMacro ;
return MacroProcessor : : InternalResolveMacros ( args [ 0 ] , resolvers , cr , & missingMacro , escapeFn ,
2015-01-28 14:14:48 +01:00
resolvedMacros , useResolvedMacros , recursionLevel ) ;
}
2015-08-27 08:22:35 +02:00
Value MacroProcessor : : InternalResolveArgumentsShim ( const std : : vector < Value > & args , const ResolverList & resolvers ,
const CheckResult : : Ptr & cr , const Dictionary : : Ptr & resolvedMacros ,
bool useResolvedMacros , int recursionLevel )
{
if ( args . size ( ) < 2 )
BOOST_THROW_EXCEPTION ( std : : invalid_argument ( " Too few arguments for function " ) ) ;
return MacroProcessor : : ResolveArguments ( args [ 0 ] , args [ 1 ] , resolvers , cr ,
resolvedMacros , useResolvedMacros , recursionLevel ) ;
}
2015-01-29 10:09:53 +01:00
Value MacroProcessor : : EvaluateFunction ( const Function : : Ptr & func , const ResolverList & resolvers ,
2015-08-27 08:58:25 +02:00
const CheckResult : : Ptr & cr , const MacroProcessor : : EscapeCallback & escapeFn ,
const Dictionary : : Ptr & resolvedMacros , bool useResolvedMacros , int recursionLevel )
2015-01-29 10:09:53 +01:00
{
Dictionary : : Ptr resolvers_this = new Dictionary ( ) ;
BOOST_FOREACH ( const ResolverSpec & resolver , resolvers ) {
resolvers_this - > Set ( resolver . first , resolver . second ) ;
}
resolvers_this - > Set ( " macro " , new Function ( boost : : bind ( & MacroProcessor : : InternalResolveMacrosShim ,
2015-08-27 08:58:25 +02:00
_1 , boost : : cref ( resolvers ) , cr , MacroProcessor : : EscapeCallback ( ) , resolvedMacros , useResolvedMacros ,
2015-04-17 17:23:08 +02:00
recursionLevel + 1 ) ) ) ;
2015-08-27 08:22:35 +02:00
resolvers_this - > Set ( " resolve_arguments " , new Function ( boost : : bind ( & MacroProcessor : : InternalResolveArgumentsShim ,
_1 , boost : : cref ( resolvers ) , cr , resolvedMacros , useResolvedMacros ,
recursionLevel + 1 ) ) ) ;
2015-01-29 10:09:53 +01:00
ScriptFrame frame ( resolvers_this ) ;
2015-02-03 14:02:32 +01:00
return func - > Invoke ( ) ;
2015-01-29 10:09:53 +01:00
}
2014-11-26 20:43:42 +01:00
Value MacroProcessor : : InternalResolveMacros ( const String & str , const ResolverList & resolvers ,
2014-04-26 13:16:08 +02:00
const CheckResult : : Ptr & cr , String * missingMacro ,
2014-11-13 11:23:57 +01:00
const MacroProcessor : : EscapeCallback & escapeFn , const Dictionary : : Ptr & resolvedMacros ,
bool useResolvedMacros , int recursionLevel )
2012-06-13 13:42:55 +02:00
{
2013-11-19 07:49:41 +01:00
CONTEXT ( " Resolving macros for string ' " + str + " ' " ) ;
2014-04-08 13:23:24 +02:00
if ( recursionLevel > 15 )
BOOST_THROW_EXCEPTION ( std : : runtime_error ( " Infinite recursion detected while resolving macros " ) ) ;
2013-02-28 10:27:20 +01:00
size_t offset , pos_first , pos_second ;
2012-06-13 13:42:55 +02:00
offset = 0 ;
2015-01-27 13:40:05 +01:00
Dictionary : : Ptr resolvers_this ;
2012-08-02 09:38:08 +02:00
String result = str ;
while ( ( pos_first = result . FindFirstOf ( " $ " , offset ) ) ! = String : : NPos ) {
pos_second = result . FindFirstOf ( " $ " , pos_first + 1 ) ;
2012-06-13 13:42:55 +02:00
2012-08-02 09:38:08 +02:00
if ( pos_second = = String : : NPos )
2013-03-16 21:18:53 +01:00
BOOST_THROW_EXCEPTION ( std : : runtime_error ( " Closing $ not found in macro format string. " ) ) ;
2012-06-13 13:42:55 +02:00
2012-08-02 09:38:08 +02:00
String name = result . SubStr ( pos_first + 1 , pos_second - pos_first - 1 ) ;
2014-11-26 20:43:42 +01:00
Value resolved_macro ;
2014-04-17 17:38:02 +02:00
bool recursive_macro ;
2014-11-13 11:23:57 +01:00
bool found ;
if ( useResolvedMacros ) {
recursive_macro = false ;
found = resolvedMacros - > Contains ( name ) ;
if ( found )
resolved_macro = resolvedMacros - > Get ( name ) ;
} else
found = ResolveMacro ( name , resolvers , cr , & resolved_macro , & recursive_macro ) ;
2013-02-09 23:02:33 +01:00
2013-03-22 14:40:55 +01:00
/* $$ is an escape sequence for $. */
if ( name . IsEmpty ( ) ) {
resolved_macro = " $ " ;
found = true ;
}
2013-02-28 10:27:20 +01:00
2015-01-27 13:40:05 +01:00
if ( resolved_macro . IsObjectType < Function > ( ) ) {
2015-08-27 08:58:25 +02:00
resolved_macro = EvaluateFunction ( resolved_macro , resolvers , cr , escapeFn ,
2015-08-27 08:22:35 +02:00
resolvedMacros , useResolvedMacros , recursionLevel + 1 ) ;
2015-01-27 13:40:05 +01:00
}
2014-04-26 13:16:08 +02:00
if ( ! found ) {
if ( ! missingMacro )
2014-10-19 17:52:17 +02:00
Log ( LogWarning , " MacroProcessor " )
< < " Macro ' " < < name < < " ' is not defined. " ;
2014-04-26 13:16:08 +02:00
else
* missingMacro = name ;
}
2013-02-09 23:02:33 +01:00
2014-04-10 08:46:36 +02:00
/* recursively resolve macros in the macro if it was a user macro */
2014-11-26 20:43:42 +01:00
if ( recursive_macro ) {
if ( resolved_macro . IsObjectType < Array > ( ) ) {
Array : : Ptr arr = resolved_macro ;
2015-01-28 15:48:08 +01:00
Array : : Ptr resolved_arr = new Array ( ) ;
2014-11-26 20:43:42 +01:00
ObjectLock olock ( arr ) ;
2015-01-28 15:48:08 +01:00
BOOST_FOREACH ( const Value & value , arr ) {
if ( value . IsScalar ( ) ) {
resolved_arr - > Add ( InternalResolveMacros ( value ,
resolvers , cr , missingMacro , EscapeCallback ( ) , Dictionary : : Ptr ( ) ,
false , recursionLevel + 1 ) ) ;
} else
resolved_arr - > Add ( value ) ;
2014-11-26 20:43:42 +01:00
}
2015-01-28 15:48:08 +01:00
resolved_macro = resolved_arr ;
2015-07-13 08:25:19 +02:00
} else if ( resolved_macro . IsString ( ) ) {
2014-11-26 20:43:42 +01:00
resolved_macro = InternalResolveMacros ( resolved_macro ,
resolvers , cr , missingMacro , EscapeCallback ( ) , Dictionary : : Ptr ( ) ,
false , recursionLevel + 1 ) ;
2015-07-13 08:25:19 +02:00
}
2014-11-26 20:43:42 +01:00
}
2014-11-13 11:23:57 +01:00
if ( ! useResolvedMacros & & found & & resolvedMacros )
resolvedMacros - > Set ( name , resolved_macro ) ;
2014-04-08 13:23:24 +02:00
2014-04-06 10:45:50 +02:00
if ( escapeFn )
resolved_macro = escapeFn ( resolved_macro ) ;
2013-08-30 16:52:13 +02:00
2015-07-13 08:25:19 +02:00
/* we're done if this is the only macro and there are no other non-macro parts in the string */
if ( pos_first = = 0 & & pos_second = = str . GetLength ( ) - 1 )
return resolved_macro ;
else if ( resolved_macro . IsObjectType < Array > ( ) )
BOOST_THROW_EXCEPTION ( std : : invalid_argument ( " Mixing both strings and non-strings in macros is not allowed. " ) ) ;
2014-11-26 20:43:42 +01:00
if ( resolved_macro . IsObjectType < Array > ( ) ) {
/* don't allow mixing strings and arrays in macro strings */
if ( pos_first ! = 0 | | pos_second ! = str . GetLength ( ) - 1 )
BOOST_THROW_EXCEPTION ( std : : invalid_argument ( " Mixing both strings and non-strings in macros is not allowed. " ) ) ;
return resolved_macro ;
}
String resolved_macro_str = resolved_macro ;
2014-12-01 15:53:23 +01:00
result . Replace ( pos_first , pos_second - pos_first + 1 , resolved_macro_str ) ;
offset = pos_first + resolved_macro_str . GetLength ( ) ;
2013-02-09 23:02:33 +01:00
}
return result ;
}
2015-02-11 16:08:02 +01:00
bool MacroProcessor : : ValidateMacroString ( const String & macro )
{
if ( macro . IsEmpty ( ) )
return true ;
size_t pos_first , pos_second , offset ;
offset = 0 ;
2015-03-21 10:04:14 +01:00
while ( ( pos_first = macro . FindFirstOf ( " $ " , offset ) ) ! = String : : NPos ) {
2015-02-11 16:08:02 +01:00
pos_second = macro . FindFirstOf ( " $ " , pos_first + 1 ) ;
if ( pos_second = = String : : NPos )
return false ;
offset = pos_second + 1 ;
}
return true ;
}
2015-08-27 08:22:35 +02:00
2015-09-22 18:18:29 +02:00
void MacroProcessor : : ValidateCustomVars ( const ConfigObject : : Ptr & object , const Dictionary : : Ptr & value )
{
if ( ! value )
return ;
/* string, array, dictionary */
ObjectLock olock ( value ) ;
BOOST_FOREACH ( const Dictionary : : Pair & kv , value ) {
const Value & varval = kv . second ;
if ( varval . IsObjectType < Dictionary > ( ) ) {
/* only one dictonary level */
Dictionary : : Ptr varval_dict = varval ;
ObjectLock xlock ( varval_dict ) ;
BOOST_FOREACH ( const Dictionary : : Pair & kv_var , varval_dict ) {
2015-11-04 19:04:33 +01:00
if ( ! kv_var . second . IsString ( ) )
2015-09-22 18:18:29 +02:00
continue ;
if ( ! ValidateMacroString ( kv_var . second ) )
BOOST_THROW_EXCEPTION ( ValidationError ( object . get ( ) , boost : : assign : : list_of < String > ( " vars " ) ( kv . first ) ( kv_var . first ) , " Closing $ not found in macro format string ' " + kv_var . second + " '. " ) ) ;
}
} else if ( varval . IsObjectType < Array > ( ) ) {
/* check all array entries */
Array : : Ptr varval_arr = varval ;
ObjectLock ylock ( varval_arr ) ;
BOOST_FOREACH ( const Value & arrval , varval_arr ) {
2015-11-04 19:04:33 +01:00
if ( ! arrval . IsString ( ) )
2015-09-22 18:18:29 +02:00
continue ;
if ( ! ValidateMacroString ( arrval ) ) {
BOOST_THROW_EXCEPTION ( ValidationError ( object . get ( ) , boost : : assign : : list_of < String > ( " vars " ) ( kv . first ) , " Closing $ not found in macro format string ' " + arrval + " '. " ) ) ;
}
}
} else {
2015-11-04 19:04:33 +01:00
if ( ! varval . IsString ( ) )
2015-09-22 18:18:29 +02:00
continue ;
2015-11-04 19:04:33 +01:00
if ( ! ValidateMacroString ( varval ) )
BOOST_THROW_EXCEPTION ( ValidationError ( object . get ( ) , boost : : assign : : list_of < String > ( " vars " ) ( kv . first ) , " Closing $ not found in macro format string ' " + varval + " '. " ) ) ;
2015-09-22 18:18:29 +02:00
}
}
}
2015-08-27 08:22:35 +02:00
void MacroProcessor : : AddArgumentHelper ( const Array : : Ptr & args , const String & key , const String & value ,
bool add_key , bool add_value )
{
if ( add_key )
args - > Add ( key ) ;
if ( add_value )
args - > Add ( value ) ;
}
Value MacroProcessor : : EscapeMacroShellArg ( const Value & value )
{
String result ;
if ( value . IsObjectType < Array > ( ) ) {
Array : : Ptr arr = value ;
ObjectLock olock ( arr ) ;
BOOST_FOREACH ( const Value & arg , arr ) {
if ( result . GetLength ( ) > 0 )
result + = " " ;
result + = Utility : : EscapeShellArg ( arg ) ;
}
} else
result = Utility : : EscapeShellArg ( value ) ;
return result ;
}
struct CommandArgument
{
int Order ;
bool SkipKey ;
bool RepeatKey ;
bool SkipValue ;
String Key ;
Value AValue ;
CommandArgument ( void )
: Order ( 0 ) , SkipKey ( false ) , RepeatKey ( true ) , SkipValue ( false )
{ }
bool operator < ( const CommandArgument & rhs ) const
{
return Order < rhs . Order ;
}
} ;
Value MacroProcessor : : ResolveArguments ( const Value & command , const Dictionary : : Ptr & arguments ,
const MacroProcessor : : ResolverList & resolvers , const CheckResult : : Ptr & cr ,
const Dictionary : : Ptr & resolvedMacros , bool useResolvedMacros , int recursionLevel )
{
Value resolvedCommand ;
if ( ! arguments | | command . IsObjectType < Array > ( ) | | command . IsObjectType < Function > ( ) )
resolvedCommand = MacroProcessor : : ResolveMacros ( command , resolvers , cr , NULL ,
EscapeMacroShellArg , resolvedMacros , useResolvedMacros , recursionLevel + 1 ) ;
else {
Array : : Ptr arr = new Array ( ) ;
arr - > Add ( command ) ;
resolvedCommand = arr ;
}
if ( arguments ) {
std : : vector < CommandArgument > args ;
ObjectLock olock ( arguments ) ;
BOOST_FOREACH ( const Dictionary : : Pair & kv , arguments ) {
const Value & arginfo = kv . second ;
CommandArgument arg ;
arg . Key = kv . first ;
bool required = false ;
Value argval ;
if ( arginfo . IsObjectType < Dictionary > ( ) ) {
Dictionary : : Ptr argdict = arginfo ;
if ( argdict - > Contains ( " key " ) )
arg . Key = argdict - > Get ( " key " ) ;
argval = argdict - > Get ( " value " ) ;
if ( argdict - > Contains ( " required " ) )
required = argdict - > Get ( " required " ) ;
arg . SkipKey = argdict - > Get ( " skip_key " ) ;
if ( argdict - > Contains ( " repeat_key " ) )
arg . RepeatKey = argdict - > Get ( " repeat_key " ) ;
arg . Order = argdict - > Get ( " order " ) ;
Value set_if = argdict - > Get ( " set_if " ) ;
if ( ! set_if . IsEmpty ( ) ) {
String missingMacro ;
Value set_if_resolved = MacroProcessor : : ResolveMacros ( set_if , resolvers ,
cr , & missingMacro , MacroProcessor : : EscapeCallback ( ) , resolvedMacros ,
useResolvedMacros , recursionLevel + 1 ) ;
if ( ! missingMacro . IsEmpty ( ) )
continue ;
int value ;
if ( set_if_resolved = = " true " )
value = 1 ;
else if ( set_if_resolved = = " false " )
value = 0 ;
else {
try {
value = Convert : : ToLong ( set_if_resolved ) ;
} catch ( const std : : exception & ex ) {
/* tried to convert a string */
Log ( LogWarning , " PluginUtility " )
< < " Error evaluating set_if value ' " < < set_if_resolved < < " ': " < < ex . what ( ) ;
continue ;
}
}
if ( ! value )
continue ;
}
}
else
argval = arginfo ;
if ( argval . IsEmpty ( ) )
arg . SkipValue = true ;
String missingMacro ;
arg . AValue = MacroProcessor : : ResolveMacros ( argval , resolvers ,
cr , & missingMacro , MacroProcessor : : EscapeCallback ( ) , resolvedMacros ,
useResolvedMacros , recursionLevel + 1 ) ;
if ( ! missingMacro . IsEmpty ( ) ) {
if ( required ) {
BOOST_THROW_EXCEPTION ( ScriptError ( " Non-optional macro ' " + missingMacro + " ' used in argument ' " +
arg . Key + " ' is missing. " ) ) ;
}
continue ;
}
args . push_back ( arg ) ;
}
std : : sort ( args . begin ( ) , args . end ( ) ) ;
Array : : Ptr command_arr = resolvedCommand ;
BOOST_FOREACH ( const CommandArgument & arg , args ) {
if ( arg . AValue . IsObjectType < Dictionary > ( ) ) {
Log ( LogWarning , " PluginUtility " , " Tried to use dictionary in argument " ) ;
continue ;
} else if ( arg . AValue . IsObjectType < Array > ( ) ) {
bool first = true ;
Array : : Ptr arr = static_cast < Array : : Ptr > ( arg . AValue ) ;
ObjectLock olock ( arr ) ;
BOOST_FOREACH ( const Value & value , arr ) {
bool add_key ;
if ( first ) {
first = false ;
add_key = ! arg . SkipKey ;
} else
add_key = ! arg . SkipKey & & arg . RepeatKey ;
AddArgumentHelper ( command_arr , arg . Key , value , add_key , ! arg . SkipValue ) ;
}
} else
AddArgumentHelper ( command_arr , arg . Key , arg . AValue , ! arg . SkipKey , ! arg . SkipValue ) ;
}
}
return resolvedCommand ;
}