2017-01-08 10:17:07 -05:00
/ *
* MIT License
*
* Copyright ( c ) 2017 Michael VanOverbeek and ShiftOS devs
*
* Permission is hereby granted , free of charge , to any person obtaining a copy
* of this software and associated documentation files ( the "Software" ) , to deal
* in the Software without restriction , including without limitation the rights
* to use , copy , modify , merge , publish , distribute , sublicense , and / or sell
* copies of the Software , and to permit persons to whom the Software is
* furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software .
*
* THE SOFTWARE IS PROVIDED "AS IS" , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
* LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM ,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE .
* /
using System ;
2017-01-08 09:57:10 -05:00
using System.Collections.Generic ;
2017-05-24 16:41:49 -04:00
using System.Diagnostics ;
2017-01-08 09:57:10 -05:00
using System.Linq ;
using System.Reflection ;
using System.Text ;
using System.Text.RegularExpressions ;
using System.Threading.Tasks ;
using Newtonsoft.Json ;
using static ShiftOS . Engine . SaveSystem ;
2017-04-30 20:28:31 -04:00
namespace ShiftOS.Engine
{
2017-05-20 15:58:04 -04:00
/// <summary>
/// Backend for the ShiftOS terminal.
/// </summary>
2017-04-30 20:28:31 -04:00
public static class TerminalBackend
{
2017-07-29 11:01:32 -04:00
private static string _shellOverrideString = "" ;
/// <summary>
/// Gets the current shell prompt override string.
/// </summary>
public static string ShellOverride
{
get
{
return ( string . IsNullOrWhiteSpace ( _shellOverrideString ) | | SaveSystem . CurrentSave = = null ) ? $"{SaveSystem.CurrentSave.Username}@{SaveSystem.CurrentSave.SystemName}:~$ " : _shellOverrideString ;
}
}
/// <summary>
/// Sets the shell override string to the specified value. Empty string or <see cref="null"/> to use the default ShiftOS string.
/// </summary>
/// <param name="value">The string to use as a shell prompt.</param>
public static void SetShellOverride ( string value )
{
_shellOverrideString = value ;
}
2017-05-20 15:58:04 -04:00
/// <summary>
/// Occurs when a command is processed.
/// </summary>
2017-01-18 10:01:37 -05:00
public static event Action < string , string > CommandProcessed ;
2017-05-20 15:58:04 -04:00
/// <summary>
/// Gets or sets whether the current command is elevated.
/// </summary>
2017-01-08 09:57:10 -05:00
public static bool Elevated { get ; set ; }
2017-05-20 15:58:04 -04:00
/// <summary>
/// Parses command-line arguments using the ShiftOS syntax and stores them in a <see cref="Dictionary{string, string}"/>, removing the parsed text from the original string.
/// </summary>
/// <param name="text">The text to parse.</param>
/// <returns><see cref="Dictionary{string, string}"/> containing the parsed arguments.</returns>
2017-04-30 20:28:31 -04:00
public static Dictionary < string , string > GetArgs ( ref string text )
{
2017-01-08 09:57:10 -05:00
bool shouldParse = false ;
int argStart = 0 ;
2017-04-30 20:28:31 -04:00
if ( text . Contains ( "{" ) )
{
2017-01-08 09:57:10 -05:00
shouldParse = true ;
argStart = text . IndexOf ( '{' ) ;
}
2017-04-30 20:28:31 -04:00
if ( shouldParse = = false )
{
2017-01-08 09:57:10 -05:00
string replacement = Regex . Replace ( text , @"\t|\n|\r" , "" ) ;
text = replacement + "{}" ;
shouldParse = true ;
argStart = text . IndexOf ( '{' ) ;
}
string args = text . Substring ( argStart , text . Length - argStart ) ;
text = text . Remove ( argStart , text . Length - argStart ) . Replace ( " " , "" ) ;
2017-03-12 09:29:17 -07:00
return JsonConvert . DeserializeObject < Dictionary < string , string > > ( args ) ;
2017-01-08 09:57:10 -05:00
}
2017-05-20 15:58:04 -04:00
/// <summary>
/// String representing the last command entered by the user.
/// </summary>
2017-01-08 09:57:10 -05:00
public static string LastCommand = "" ;
2017-05-26 17:06:38 -04:00
/// <summary>
/// Gets the output of the last command.
/// </summary>
public static string LastCommandBuffer { get ; private set ; }
2017-05-20 15:58:04 -04:00
/// <summary>
/// Invokes a ShiftOS terminal command.
/// </summary>
/// <param name="command">The command name.</param>
/// <param name="arguments">The command arguments.</param>
/// <param name="isRemote">Whether the command should be sent through Remote Terminal Session (RTS).</param>
2017-06-17 21:03:27 -04:00
public static void InvokeCommand ( string command , Dictionary < string , string > arguments , bool isRemote = false )
2017-04-30 20:28:31 -04:00
{
try
{
2017-06-17 21:03:27 -04:00
bool commandWasClient = RunClient ( command , arguments , isRemote ) ;
2017-03-12 09:29:17 -07:00
2017-06-17 21:03:27 -04:00
if ( ! commandWasClient )
2017-04-30 20:28:31 -04:00
{
2017-06-17 21:03:27 -04:00
Console . WriteLine ( "{ERR_COMMANDNOTFOUND}" ) ;
2017-03-12 09:29:17 -07:00
}
2017-06-17 21:03:27 -04:00
CommandProcessed ? . Invoke ( command , JsonConvert . SerializeObject ( arguments ) ) ;
2017-04-30 20:28:31 -04:00
}
catch ( Exception ex )
{
2017-06-17 21:03:27 -04:00
Console . WriteLine ( "{ERR_SYNTAXERROR}" ) ;
2017-03-12 09:29:17 -07:00
PrefixEnabled = true ;
}
}
2017-05-20 15:58:04 -04:00
/// <summary>
/// Transforms a <see cref="Dictionary{String, String}"/> of arguments to a <see cref="Dictionary{String, Object}"/>.
/// </summary>
/// <param name="argss">The original argument dictionary to convert.</param>
/// <returns>The converted dictionary.</returns>
2017-04-30 20:28:31 -04:00
public static string GetSentArgs ( Dictionary < string , string > argss )
{
2017-03-12 09:29:17 -07:00
Dictionary < string , object > args = new Dictionary < string , object > ( ) ;
2017-04-30 20:28:31 -04:00
foreach ( KeyValuePair < string , string > arg in argss )
{
2017-03-12 09:29:17 -07:00
args [ arg . Key ] = arg . Value ;
}
return JsonConvert . SerializeObject ( args ) ;
}
2017-05-24 16:41:49 -04:00
public class TerminalCommand
{
2017-07-29 11:01:32 -04:00
public virtual bool MatchShell ( )
{
if ( ShellMatch ! = "metacmd" )
{
return ( ShellMatch = = _shellOverrideString ) ;
}
return true ;
}
public string ShellMatch { get ; set ; }
2017-05-24 16:41:49 -04:00
public override int GetHashCode ( )
{
int hash = 0 ;
foreach ( char c in ToString ( ) )
{
hash + = ( int ) c ;
}
return hash ;
}
public Command CommandInfo { get ; set ; }
public List < string > RequiredArguments { get ; set ; }
public string Dependencies { get ; set ; }
public MethodInfo CommandHandler ;
public Type CommandType ;
public override string ToString ( )
{
StringBuilder sb = new StringBuilder ( ) ;
sb . Append ( this . CommandInfo . name ) ;
if ( this . RequiredArguments . Count > 0 )
{
2017-06-16 20:36:43 -04:00
sb . Append ( " " ) ;
2017-05-24 16:41:49 -04:00
foreach ( var arg in RequiredArguments )
{
2017-06-16 20:36:43 -04:00
sb . Append ( "--" + arg ) ;
sb . Append ( " " ) ;
2017-05-24 16:41:49 -04:00
if ( RequiredArguments . IndexOf ( arg ) < RequiredArguments . Count - 1 )
sb . Append ( ',' ) ;
}
sb . Append ( "}" ) ;
}
sb . Append ( "|" ) ;
sb . Append ( CommandHandler . Name + "()" ) ;
return sb . ToString ( ) ;
}
public bool RequiresElevation { get ; set ; }
2017-06-16 21:10:13 -04:00
public virtual void Invoke ( Dictionary < string , object > args )
2017-05-24 16:41:49 -04:00
{
List < string > errors = new List < string > ( ) ;
2017-07-29 11:01:32 -04:00
if ( ShellMatch ! = "metacmd" )
{
if ( ShellMatch ! = TerminalBackend . _shellOverrideString )
{
errors . Add ( "Command not found." ) ;
}
}
2017-05-24 16:41:49 -04:00
if ( errors . Count > 0 )
{
foreach ( var error in errors )
{
2017-07-29 11:01:32 -04:00
Console . WriteLine ( error ) ;
2017-05-24 16:41:49 -04:00
}
return ;
}
try
{
CommandHandler . Invoke ( null , new [ ] { args } ) ;
2017-07-31 22:48:17 -04:00
2017-05-24 16:41:49 -04:00
}
2017-08-02 20:15:46 -04:00
catch ( System . Reflection . TargetParameterCountException )
2017-05-24 16:41:49 -04:00
{
CommandHandler . Invoke ( null , null ) ;
}
2017-08-02 20:15:46 -04:00
catch ( Exception ex )
{
Console . WriteLine ( ex . Message ) ;
}
2017-07-31 22:48:17 -04:00
CommandFinished ? . Invoke ( Localization . Parse ( this . CommandInfo . name ) , args ) ;
2017-05-24 16:41:49 -04:00
}
}
2017-07-29 11:01:32 -04:00
[MetaCommand]
[Command("exit")]
public static void Exit ( )
{
if ( _shellOverrideString ! = "" )
_shellOverrideString = "" ;
else
{
Console . WriteLine ( "error: cannot exit system shell" ) ;
}
}
2017-06-16 21:10:13 -04:00
public class WinOpenCommand : TerminalCommand
{
public Type ShiftOSWindow { get ; set ; }
2017-07-29 11:01:32 -04:00
public override bool MatchShell ( )
{
return ( _shellOverrideString = = "" ) ;
}
2017-06-16 21:10:13 -04:00
public override void Invoke ( Dictionary < string , object > args )
{
AppearanceManager . SetupWindow ( ( IShiftOSWindow ) Activator . CreateInstance ( ShiftOSWindow , null ) ) ;
}
}
2017-07-31 22:48:17 -04:00
public static event Action < string , Dictionary < string , object > > CommandFinished ;
2017-05-24 16:41:49 -04:00
public class MemoryTextWriter : System . IO . TextWriter
{
public override Encoding Encoding
{
get
{
return Encoding . Unicode ;
}
}
private StringBuilder sb = null ;
public MemoryTextWriter ( )
{
sb = new StringBuilder ( ) ;
}
public override string ToString ( )
{
return sb . ToString ( ) ;
}
public override void Write ( char value )
{
sb . Append ( value ) ;
}
public override void WriteLine ( )
{
sb . AppendLine ( ) ;
}
public override void Write ( string value )
{
sb . Append ( value ) ;
}
public override void Close ( )
{
sb . Clear ( ) ;
sb = null ;
base . Close ( ) ;
}
public override void WriteLine ( string value )
{
sb . AppendLine ( value ) ;
}
}
public static List < TerminalCommand > Commands { get ; private set ; }
public static void PopulateTerminalCommands ( )
{
Commands = new List < TerminalCommand > ( ) ;
2017-06-16 20:36:43 -04:00
foreach ( var type in ReflectMan . Types )
2017-05-24 16:41:49 -04:00
{
2017-06-16 21:10:13 -04:00
if ( type . GetInterfaces ( ) . Contains ( typeof ( IShiftOSWindow ) ) )
{
var winopenattrib = type . GetCustomAttributes ( false ) . FirstOrDefault ( x = > x is WinOpenAttribute ) as WinOpenAttribute ;
if ( winopenattrib ! = null )
{
var winc = new WinOpenCommand ( ) ;
winc . CommandType = type ;
var rupg = type . GetCustomAttributes ( ) . FirstOrDefault ( x = > x is RequiresUpgradeAttribute ) as RequiresUpgradeAttribute ;
if ( rupg ! = null )
winc . Dependencies = rupg . Upgrade ;
winc . CommandInfo = new Engine . Command ( winopenattrib . ID , "" , "Opens the \"" + winopenattrib . ID + " program." ) ;
winc . RequiredArguments = new List < string > ( ) ;
winc . RequiresElevation = false ;
winc . ShiftOSWindow = type ;
var ambiguity = Commands . FirstOrDefault ( x = > x . CommandInfo . name = = winc . CommandInfo . name ) ;
if ( ambiguity ! = null )
throw new Exception ( "Ambiguity error. The program " + winc . CommandInfo . name + " collides with another program or terminal command with the same name. Please either change the already-existing program/command, or change this one's WinOpenAttribute value to compensate." ) ;
Commands . Add ( winc ) ;
}
}
2017-06-16 20:36:43 -04:00
foreach ( var mth in type . GetMethods ( BindingFlags . Public | BindingFlags . Static ) )
2017-05-24 16:41:49 -04:00
{
2017-06-16 20:36:43 -04:00
var cmd = mth . GetCustomAttributes ( false ) . FirstOrDefault ( x = > x is Command ) ;
if ( cmd ! = null )
2017-05-24 16:41:49 -04:00
{
2017-06-16 20:36:43 -04:00
var tc = new TerminalCommand ( ) ;
tc . RequiresElevation = ! ( type . GetCustomAttributes ( false ) . FirstOrDefault ( x = > x is KernelModeAttribute ) = = null ) ;
2017-06-11 17:34:38 +10:00
2017-07-29 11:01:32 -04:00
var shellConstraint = mth . GetCustomAttributes ( false ) . FirstOrDefault ( x = > x is ShellConstraintAttribute ) as ShellConstraintAttribute ;
tc . ShellMatch = ( shellConstraint = = null ) ? "" : shellConstraint . Shell ;
if ( mth . GetCustomAttributes ( false ) . FirstOrDefault ( x = > x is MetaCommandAttribute ) ! = null )
{
tc . ShellMatch = "metacmd" ;
}
2017-06-11 17:34:38 +10:00
2017-06-16 20:36:43 -04:00
tc . CommandInfo = cmd as Command ;
tc . RequiresElevation = tc . RequiresElevation | | ! ( mth . GetCustomAttributes ( false ) . FirstOrDefault ( x = > x is KernelModeAttribute ) = = null ) ;
tc . RequiredArguments = new List < string > ( ) ;
foreach ( var arg in mth . GetCustomAttributes ( false ) . Where ( x = > x is RequiresArgument ) )
{
var rarg = arg as RequiresArgument ;
tc . RequiredArguments . Add ( rarg . argument ) ;
2017-05-24 16:41:49 -04:00
}
2017-06-16 20:36:43 -04:00
var rupg = mth . GetCustomAttributes ( false ) . FirstOrDefault ( x = > x is RequiresUpgradeAttribute ) as RequiresUpgradeAttribute ;
if ( rupg ! = null )
tc . Dependencies = rupg . Upgrade ;
else
tc . Dependencies = "" ;
tc . CommandType = type ;
tc . CommandHandler = mth ;
var ambiguity = Commands . FirstOrDefault ( x = > x . CommandInfo . name = = tc . CommandInfo . name ) ;
if ( ambiguity ! = null )
throw new Exception ( "Command ambiguity error. You can't have two commands with the same name: " + $"{tc} == {ambiguity}" ) ;
if ( ! Commands . Contains ( tc ) )
Commands . Add ( tc ) ;
2017-05-24 16:41:49 -04:00
}
}
2017-06-16 20:36:43 -04:00
2017-05-24 16:41:49 -04:00
}
Console . WriteLine ( "[termdb] " + Commands . Count + " commands found." ) ;
}
2017-05-20 15:58:04 -04:00
/// <summary>
/// Invokes a ShiftOS terminal command.
/// </summary>
/// <param name="text">The full command text in regular ShiftOS syntax</param>
/// <param name="isRemote">Whether the command should be sent through Remote Terminal Session (RTS).</param>
2017-04-30 20:28:31 -04:00
public static void InvokeCommand ( string text , bool isRemote = false )
{
2017-05-28 07:21:16 -04:00
if ( string . IsNullOrWhiteSpace ( text ) )
return ;
2017-07-31 22:48:17 -04:00
var args = GetArgs ( ref text ) ;
var oargs = JsonConvert . DeserializeObject < Dictionary < string , object > > ( GetSentArgs ( args ) ) ;
2017-04-30 20:28:31 -04:00
try
{
2017-01-08 09:57:10 -05:00
2017-01-21 21:27:25 -05:00
bool commandWasClient = RunClient ( text , args , isRemote ) ;
2017-04-30 20:28:31 -04:00
if ( ! commandWasClient )
{
2017-06-02 08:15:51 -04:00
Console . WriteLine ( "Error: Command not found." ) ;
2017-01-08 09:57:10 -05:00
}
2017-03-12 09:29:17 -07:00
CommandProcessed ? . Invoke ( text , GetSentArgs ( args ) ) ;
2017-04-30 20:28:31 -04:00
}
catch ( Exception ex )
{
2017-01-08 09:57:10 -05:00
Console . WriteLine ( $"Command parse error: {ex.Message}" ) ;
PrefixEnabled = true ;
}
}
2017-05-20 15:58:04 -04:00
/// <summary>
/// Gets or sets whether the user prefix is printed after a command completes.
/// </summary>
2017-01-08 09:57:10 -05:00
public static bool PrefixEnabled { get ; set ; }
2017-05-20 15:58:04 -04:00
/// <summary>
/// Gets or sets whether the user is in a story plot, and thus, the terminal input should be disabled.
/// </summary>
2017-01-08 09:57:10 -05:00
public static bool InStory { get ; set ; }
2017-05-20 15:58:04 -04:00
/// <summary>
/// Another latest command string.
/// </summary>
2017-01-08 09:57:10 -05:00
public static string latestCommmand = "" ;
2017-05-20 15:58:04 -04:00
/// <summary>
/// Occurs when the engine requests a Terminal to be open.
/// </summary>
2017-01-08 09:57:10 -05:00
public static event EmptyEventHandler TerminalRequested ;
2017-05-20 15:58:04 -04:00
/// <summary>
/// Opens a Terminal.
/// </summary>
2017-04-30 20:28:31 -04:00
internal static void OpenTerminal ( )
{
2017-01-08 09:57:10 -05:00
TerminalRequested ? . Invoke ( ) ;
}
2017-05-20 15:58:04 -04:00
/// <summary>
/// Determines if the specified command method can be ran in RTS
/// </summary>
/// <param name="mth">The method to scan</param>
/// <param name="isRemote">Is the user in an RTS session?</param>
/// <returns>Whether the command can be run.</returns>
2017-04-30 20:28:31 -04:00
public static bool CanRunRemotely ( MethodInfo mth , bool isRemote )
{
2017-01-21 21:27:25 -05:00
if ( ! isRemote )
return true ;
2017-04-30 20:28:31 -04:00
foreach ( var attr in mth . GetCustomAttributes ( false ) )
{
2017-01-21 21:27:25 -05:00
if ( attr is RemoteLockAttribute )
return false ;
}
return true ;
}
2017-05-20 15:58:04 -04:00
/// <summary>
/// Runs a command on the client.
/// </summary>
/// <param name="text">The command text.</param>
/// <param name="argss">The command arguments.</param>
/// <param name="isRemote">Whether the command should be ran through RTS.</param>
/// <returns>Whether the command ran successfully.</returns>
2017-04-30 20:28:31 -04:00
public static bool RunClient ( string text , Dictionary < string , string > argss , bool isRemote = false )
{
2017-03-12 09:45:11 -07:00
Dictionary < string , object > args = new Dictionary < string , object > ( ) ;
2017-04-30 20:28:31 -04:00
foreach ( KeyValuePair < string , string > arg in argss )
{
2017-03-12 09:45:11 -07:00
args [ arg . Key ] = arg . Value ;
}
return RunClient ( text , args , isRemote ) ;
}
2017-05-20 15:58:04 -04:00
/// <summary>
/// Runs a command on the client.
/// </summary>
/// <param name="text">The command text.</param>
/// <param name="args">The command arguments.</param>
/// <param name="isRemote">Whether the command should be run in RTS.</param>
/// <returns>Whether the command ran successfully.</returns>
2017-04-30 20:28:31 -04:00
public static bool RunClient ( string text , Dictionary < string , object > args , bool isRemote = false )
{
2017-01-08 09:57:10 -05:00
latestCommmand = text ;
2017-03-12 09:45:11 -07:00
//Console.WriteLine(text + " " + "{" + string.Join(",", args.Select(kv => kv.Key + "=" + kv.Value).ToArray()) + "}" + " " + isRemote);
2017-05-24 16:41:49 -04:00
2017-06-18 20:31:19 -04:00
var cmd = Commands . FirstOrDefault ( x = > Localization . Parse ( x . CommandInfo . name ) = = text ) ;
2017-05-24 16:41:49 -04:00
if ( cmd = = null )
return false ;
if ( ! Shiftorium . UpgradeInstalled ( cmd . Dependencies ) )
return false ;
bool res = false ;
foreach ( var arg in cmd . RequiredArguments )
{
if ( ! args . ContainsKey ( arg ) )
2017-04-30 20:28:31 -04:00
{
2017-05-24 16:41:49 -04:00
res = true ;
Console . WriteLine ( "You are missing an argument with the key \"" + arg + "\"." ) ;
2017-04-30 20:28:31 -04:00
}
2017-01-08 09:57:10 -05:00
}
2017-05-24 16:41:49 -04:00
if ( res = = true )
return true ;
try
{
cmd . Invoke ( args ) ;
}
catch ( Exception ex )
{
Console . WriteLine ( "Command error: " + ex . Message ) ;
}
return true ;
2017-01-08 09:57:10 -05:00
}
2017-05-12 19:17:47 -04:00
2017-07-29 11:01:32 -04:00
#if DEBUG
[Command("setshell", hide = true)]
[RequiresArgument("id")]
public static void Debug_SetShellOverrideCMD ( Dictionary < string , object > args )
{
SetShellOverride ( args [ "id" ] . ToString ( ) ) ;
}
#endif
2017-05-20 15:58:04 -04:00
/// <summary>
/// Prints the user prompt to the terminal.
/// </summary>
2017-03-09 16:46:34 -05:00
public static void PrintPrompt ( )
{
2017-07-29 11:01:32 -04:00
if ( PrefixEnabled )
2017-03-11 10:28:16 -05:00
{
2017-07-29 11:01:32 -04:00
Console . Write ( ShellOverride ) ;
2017-05-28 07:21:16 -04:00
ConsoleEx . Flush ( ) ;
2017-03-11 10:28:16 -05:00
}
2017-03-09 16:46:34 -05:00
}
2017-05-20 15:58:04 -04:00
/// <summary>
/// Gets whether the terminal backend is forwarding console write requests through RTS to a remote client.
/// </summary>
2017-02-06 15:21:53 -05:00
public static bool IsForwardingConsoleWrites { get ; internal set ; }
2017-05-20 15:58:04 -04:00
/// <summary>
/// Gets the RTS forward GUID.
/// </summary>
2017-02-06 16:00:01 -05:00
public static string ForwardGUID { get ; internal set ; }
2017-03-12 09:29:17 -07:00
2017-05-20 15:58:04 -04:00
/// <summary>
/// Occurs when the user inputs text in a Terminal.
/// </summary>
2017-04-30 20:28:31 -04:00
public static event TextSentEventHandler TextSent ;
2017-05-20 15:58:04 -04:00
/// <summary>
/// Fakes the user inputting text to a Terminal.
/// </summary>
/// <param name="text">The text to input.</param>
2017-04-30 20:28:31 -04:00
public static void SendText ( string text )
{
TextSent ? . Invoke ( text ) ;
}
2017-01-08 09:57:10 -05:00
}
2017-07-29 11:01:32 -04:00
/// <summary>
/// Marks this command so that it can be run in ANY shell.
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class MetaCommandAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class ShellConstraintAttribute : Attribute
{
/// <summary>
/// Instructs the terminal command interpreter to disallow running of this command unless the user shell override matches up with the value provided here.
/// </summary>
/// <param name="shell">The required shell string. Null or whitespace to match with the default ShiftOS shell.</param>
public ShellConstraintAttribute ( string shell )
{
Shell = shell ;
}
/// <summary>
/// Gets the required shell string for the command.
/// </summary>
public string Shell { get ; private set ; }
}
2017-01-08 09:57:10 -05:00
}