Skip to content

Commit

Permalink
Initial commit of working utility.
Browse files Browse the repository at this point in the history
  • Loading branch information
sullivanmj committed Sep 21, 2021
1 parent 4300015 commit b49b860
Show file tree
Hide file tree
Showing 9 changed files with 612 additions and 1 deletion.
25 changes: 25 additions & 0 deletions BSABR/BSABR.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31624.102
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BSABR", "BSABR\BSABR.csproj", "{7B474A17-7931-4385-A6C3-0045B17FD9EC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7B474A17-7931-4385-A6C3-0045B17FD9EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7B474A17-7931-4385-A6C3-0045B17FD9EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7B474A17-7931-4385-A6C3-0045B17FD9EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7B474A17-7931-4385-A6C3-0045B17FD9EC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E5B22D7B-0383-41CE-B986-DDC48207B64B}
EndGlobalSection
EndGlobal
6 changes: 6 additions & 0 deletions BSABR/BSABR/App.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
</configuration>
57 changes: 57 additions & 0 deletions BSABR/BSABR/ArgParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright © 2021 Matt Sullivan

using System.Linq;

namespace Bsabr
{
/// <summary>
/// Extracts string arguments for B-SABR into a more easy to use object.
/// </summary>
class ArgParser
{
private readonly string[] _args;

/// <summary>
/// Initializes a new instance of the <see cref="ArgParser"/> class.
/// </summary>
/// <param name="args">The arguments to extract.</param>
private ArgParser(string[] args)
{
_args = args;
}

/// <summary>
/// Gets a new instance of the <see cref="ArgParser"/> class.
/// </summary>
/// <param name="args">The arguments that the ArgParser should process.</param>
/// <returns>An <see cref="ArgParser"/> for the specified <paramref name="args"/>.</returns>
public static ArgParser InitializeNew(string[] args)
{
return new ArgParser(args);
}

/// <summary>
/// Gets the options that this <see cref="ArgParser"/>'s arguments specified.
/// </summary>
/// <returns>The options that were represented by the arguments.</returns>
internal Options GetOptions()
{
// help will have been requested if the first of the arguments match a normal help string
var helpRequested = _args.FirstOrDefault() == "/?" || _args.FirstOrDefault() == "/help";

// studio file name will be the first argument (as long as that wasn't the help arg)
var studioFileName = helpRequested ? null : _args.FirstOrDefault();

// whether or not the caller is requesting build output to be saved to a file
var outFileSpecified = _args.Any(a => a == "/out");

// output filename will be the first argument after the "/out" filename
var outFileName = outFileSpecified ? _args.SkipWhile(a => a != "/out").Skip(1).FirstOrDefault() : null;

// studio arguments will be all of the arguments after the first argument
var studioArgs = helpRequested ? null : string.Join(" ", _args.Skip(1));

return new Options(studioFileName, outFileName, outFileSpecified, studioArgs, helpRequested);
}
}
}
155 changes: 155 additions & 0 deletions BSABR/BSABR/ArgValidator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
// Copyright © 2021 Matt Sullivan

using System;
using System.IO;
using System.Text;

namespace Bsabr
{
/// <summary>
/// Validates options for the process instance.
/// </summary>
class ArgValidator
{
private readonly Options _options;
private readonly string _errorMessage;

/// <summary>
/// Initializes a new instance of the <see cref="ArgValidator"/> class.
/// </summary>
/// <param name="options">The <see cref="Options"/> to validate.</param>
public ArgValidator(Options options)
{
_options = options;

if (!_options.HelpRequested)
{
_errorMessage = GetErrorMessage();
}
}

/// <summary>
/// Summary of what is wrong with the options.
/// </summary>
public string ErrorMessage => _errorMessage;

/// <summary>
/// Indicates whether or not the options for this <see cref="ArgValidator"/> are valid.
/// </summary>
public bool OptionsAreValid => _errorMessage == null;

/// <summary>
/// Determines whether an <see cref="Options"/> is suitable for the program to execute.
/// </summary>
/// <param name="options">The configuration to validate.</param>
/// <returns>An error message, if an error occurred. <c>null</c> otherwise.</returns>
private string GetErrorMessage()
{
var errorBuilder = new StringBuilder();

var studioExecutableErrorMessage = GetStudioExecutableErrorString();

if (studioExecutableErrorMessage != null)
{
errorBuilder.AppendLine($"\t- {studioExecutableErrorMessage}");
}

var outFileNameErrorMessage = GetOutFileNameErrorString();

if (outFileNameErrorMessage != null)
{
errorBuilder.AppendLine($"\t- {outFileNameErrorMessage}");
}

if (errorBuilder.Length > 0)
{
return $"The following error(s) occurred: \n{errorBuilder}";
}

return null;
}

/// <summary>
/// Gets an error string based on the specified output filename.
/// </summary>
/// <returns>An error message if the argument is invalid, <c>null</c> otherwise.</returns>
private string GetOutFileNameErrorString()
{
if (_options.OutFileName == null)
{
if (_options.OutFileSpecified)
{
return "The \"/out\" argument should be followed by a valid file path.";
}

return null;
}

try
{
var attributes = File.GetAttributes(_options.OutFileName);
}
catch (ArgumentException)
{
return "Invalid output file path. Path is empty, contains only white spaces, or contains invalid characters.";
}
catch (PathTooLongException)
{
return "Output file path is too long. File paths should be less than 260 characters.";
}
catch (NotSupportedException)
{
return "Output file path is in an invalid format.";
}
catch (FileNotFoundException)
{
// Not a problem in this scenario as we will be creating the file
}
catch (DirectoryNotFoundException)
{
return "Output file path represents a directory and is invalid, such as being on an unmapped drive, or the directory cannot be found.";
}
catch (IOException)
{
return "Output file path is to a file that is in use by another process.";
}
catch (UnauthorizedAccessException)
{
return "You do not have permission to access the file specified as the output file path argument.";
}

return null;
}

/// <summary>
/// Gets an error string based on the specified path to AtmelStudio.exe.
/// </summary>
/// <returns>An error message if the argument is invalid, <c>null</c> otherwise.</returns>
private string GetStudioExecutableErrorString()
{
bool fileExists = File.Exists(_options.StudioExecutable);

if (!fileExists)
{
// see if it exists on the path somewhere
foreach (var evt in Enum.GetValues(typeof(EnvironmentVariableTarget)))
{
var paths = Environment.GetEnvironmentVariable("PATH", (EnvironmentVariableTarget)evt).Split(';');

foreach (var path in paths)
{
if (File.Exists(_options.StudioExecutable))
{
fileExists = true;
break;
}
}
}

return "The specified Atmel Studio/Microchip Studio executable does not exist. Make sure you have entered the path correctly and that you have permission to access the executable.";
}

return null;
}
}
}
86 changes: 86 additions & 0 deletions BSABR/BSABR/BSABR.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{7B474A17-7931-4385-A6C3-0045B17FD9EC}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>Bsabr</RootNamespace>
<AssemblyName>bsabr</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Management" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ArgParser.cs" />
<Compile Include="ArgValidator.cs" />
<Compile Include="Options.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.7.2">
<Visible>False</Visible>
<ProductName>Microsoft .NET Framework 4.7.2 %28x86 and x64%29</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>false</Install>
</BootstrapperPackage>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
51 changes: 51 additions & 0 deletions BSABR/BSABR/Options.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright © 2021 Matt Sullivan

namespace Bsabr
{
/// <summary>
/// Represents the command line options selected when the process was started.
/// </summary>
class Options
{
/// <summary>
/// Initializes a new instance of the <see cref="Options"/> class.
/// </summary>
/// <param name="studioExecutable">Path to the Atmel Studio/Microchip Studio executable.</param>
/// <param name="outFileName">The name of the output file requested in the process arguments. <c>null</c> if no /out argument was supplied.</param>
/// <param name="studioArgs">The arguments that should be passed to Atmel Studio/Microchip Studio.</param>
/// <param name="helpRequested">Indicates whether or not a help argument was passed in at process start.</param>
public Options(string studioExecutable, string outFileName, bool outFileSpecified, string studioArgs, bool helpRequested)
{
StudioExecutable = studioExecutable;
OutFileName = outFileName;
OutFileSpecified = outFileSpecified;
StudioArgs = studioArgs;
HelpRequested = helpRequested;
}

/// <summary>
/// The name of the output file requested in the process arguments. Is <c>null</c> when no /out argument was supplied.
/// </summary>
public string OutFileName { get; }

/// <summary>
/// Whether or not the user specified the "/out" argument for AtmelStudio.exe.
/// </summary>
public bool OutFileSpecified { get; }

/// <summary>
/// Path to the Atmel Studio/Microchip Studio executable.
/// </summary>
public string StudioExecutable { get; }

/// <summary>
/// The arguments that should be passed to Atmel Studio/Microchip Studio.
/// </summary>
public string StudioArgs { get; }

/// <summary>
/// Indicates whether or not a help argument was passed in at process start.
/// </summary>
public bool HelpRequested { get; }
}
}
Loading

0 comments on commit b49b860

Please sign in to comment.