diff --git a/.idea/.idea.DiscImageChef/.idea/contentModel.xml b/.idea/.idea.DiscImageChef/.idea/contentModel.xml index 283708d5c..3f6f648b6 100644 --- a/.idea/.idea.DiscImageChef/.idea/contentModel.xml +++ b/.idea/.idea.DiscImageChef/.idea/contentModel.xml @@ -145,6 +145,7 @@ + diff --git a/DiscImageChef.Core/DiscImageChef.Core.csproj b/DiscImageChef.Core/DiscImageChef.Core.csproj index 48a106884..269fd5da7 100644 --- a/DiscImageChef.Core/DiscImageChef.Core.csproj +++ b/DiscImageChef.Core/DiscImageChef.Core.csproj @@ -37,6 +37,7 @@ + diff --git a/DiscImageChef.Core/Options.cs b/DiscImageChef.Core/Options.cs new file mode 100644 index 000000000..e47a552a8 --- /dev/null +++ b/DiscImageChef.Core/Options.cs @@ -0,0 +1,170 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : Partitions.cs +// Author(s) : Natalia Portillo +// +// Component : Core algorithms. +// +// --[ Description ] ---------------------------------------------------------- +// +// Logic to handle name=value option pairs. +// +// --[ License ] -------------------------------------------------------------- +// +// 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 3 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, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2011-2018 Natalia Portillo +// ****************************************************************************/ + +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +namespace DiscImageChef.Core +{ + public static class Options + { + public static Dictionary Parse(string options) + { + Dictionary parsed = new Dictionary(); + bool escaped = false; + bool quoted = false; + bool inValue = false; + string name = null; + string value = null; + StringBuilder sb = new StringBuilder(); + + if(options == null) return parsed; + + for(int index = 0; index < options.Length; index++) + { + char c = options[index]; + + switch(c) + { + case '\\' when !escaped: + escaped = true; + break; + case '"' when !escaped: + quoted = !quoted; + break; + case '=' when quoted: + sb.Append(c); + break; + case '=': + name = sb.ToString().ToLower(CultureInfo.CurrentCulture); + sb = new StringBuilder(); + inValue = true; + break; + case ',' when quoted: + sb.Append(c); + break; + case ',' when inValue: + value = sb.ToString(); + sb = new StringBuilder(); + inValue = false; + + if(string.IsNullOrEmpty(name) || string.IsNullOrEmpty(value)) continue; + + if(parsed.ContainsKey(name)) parsed.Remove(name); + + parsed.Add(name, value); + break; + default: + if(escaped) + switch(c) + { + case 'a': + sb.Append('\a'); + escaped = false; + break; + case 'b': + sb.Append('\b'); + escaped = false; + break; + case 'f': + sb.Append('\f'); + escaped = false; + break; + case 'n': + sb.Append('\n'); + escaped = false; + break; + case 'r': + sb.Append('\r'); + escaped = false; + break; + case 't': + sb.Append('\t'); + escaped = false; + break; + case 'v': + sb.Append('\v'); + escaped = false; + break; + case '\\': + sb.Append('\\'); + escaped = false; + break; + case '\'': + sb.Append('\''); + escaped = false; + break; + case '"': + sb.Append('"'); + escaped = false; + break; + case '0': + sb.Append('\0'); + escaped = false; + break; + case 'u': + string unicode = options.Substring(index + 1, 4); + sb.Append((char)int.Parse(unicode, NumberStyles.HexNumber)); + escaped = false; + index += 4; + break; + case 'U': + string longUnicode = options.Substring(index + 1, 8); + sb.Append((char)int.Parse(longUnicode, NumberStyles.HexNumber)); + escaped = false; + index += 8; + break; + default: + sb.Append(c); + escaped = false; + break; + } + else sb.Append(c); + break; + } + } + + if(!inValue) return parsed; + + value = sb.ToString(); + + if(string.IsNullOrEmpty(name) || string.IsNullOrEmpty(value)) return parsed; + + if(parsed.ContainsKey(name)) parsed.Remove(name); + + parsed.Add(name, value); + + return parsed; + } + } +} \ No newline at end of file diff --git a/DiscImageChef/Commands/ConvertImage.cs b/DiscImageChef/Commands/ConvertImage.cs index 57640352f..5bda8b58f 100644 --- a/DiscImageChef/Commands/ConvertImage.cs +++ b/DiscImageChef/Commands/ConvertImage.cs @@ -67,6 +67,12 @@ namespace DiscImageChef.Commands DicConsole.DebugWriteLine("Analyze command", "--drive-model={0}", options.DriveModel); DicConsole.DebugWriteLine("Analyze command", "--drive-serial={0}", options.DriveSerialNumber); DicConsole.DebugWriteLine("Analyze command", "--drive-revision={0}", options.DriveFirmwareRevision); + DicConsole.DebugWriteLine("Analyze command", "--options={0}", options.Options); + + Dictionary parsedOptions = Options.Parse(options.Options); + DicConsole.DebugWriteLine("Analyze command", "Parsed options:"); + foreach(KeyValuePair parsedOption in parsedOptions) + DicConsole.DebugWriteLine("Analyze command", "{0} = {1}", parsedOption.Key, parsedOption.Value); if(options.Count == 0) { @@ -193,7 +199,7 @@ namespace DiscImageChef.Commands return; } - if(!outputFormat.Create(options.OutputFile, inputFormat.Info.MediaType, new Dictionary(), + if(!outputFormat.Create(options.OutputFile, inputFormat.Info.MediaType, parsedOptions, inputFormat.Info.Sectors, inputFormat.Info.SectorSize)) { DicConsole.ErrorWriteLine("Error {0} creating output image.", outputFormat.ErrorMessage); diff --git a/DiscImageChef/Commands/ExtractFiles.cs b/DiscImageChef/Commands/ExtractFiles.cs index 8889a311d..55d90723c 100644 --- a/DiscImageChef/Commands/ExtractFiles.cs +++ b/DiscImageChef/Commands/ExtractFiles.cs @@ -56,9 +56,13 @@ namespace DiscImageChef.Commands FiltersList filtersList = new FiltersList(); IFilter inputFilter = filtersList.GetFilter(options.InputFile); - Dictionary optionsDict = - new Dictionary {{"debug", options.Debug.ToString()}}; + Dictionary parsedOptions = Options.Parse(options.Options); + DicConsole.DebugWriteLine("Extract-Files command", "Parsed options:"); + foreach(KeyValuePair parsedOption in parsedOptions) + DicConsole.DebugWriteLine("Extract-Files command", "{0} = {1}", parsedOption.Key, parsedOption.Value); + parsedOptions.Add("debug", options.Debug.ToString()); + if(inputFilter == null) { DicConsole.ErrorWriteLine("Cannot open specified file."); @@ -165,7 +169,7 @@ namespace DiscImageChef.Commands .GetConstructor(Type.EmptyTypes) ?.Invoke(new object[] { }); - error = fs.Mount(imageFormat, partitions[i], encoding, optionsDict); + error = fs.Mount(imageFormat, partitions[i], encoding, parsedOptions); if(error == Errno.NoError) { List rootDir = new List(); @@ -321,7 +325,7 @@ namespace DiscImageChef.Commands IReadOnlyFilesystem fs = (IReadOnlyFilesystem)plugin .GetType().GetConstructor(Type.EmptyTypes) ?.Invoke(new object[] { }); - error = fs.Mount(imageFormat, partitions[i], encoding, optionsDict); + error = fs.Mount(imageFormat, partitions[i], encoding, parsedOptions); if(error == Errno.NoError) { List rootDir = new List(); @@ -482,7 +486,7 @@ namespace DiscImageChef.Commands IReadOnlyFilesystem fs = (IReadOnlyFilesystem)plugin .GetType().GetConstructor(Type.EmptyTypes) ?.Invoke(new object[] { }); - error = fs.Mount(imageFormat, wholePart, encoding, optionsDict); + error = fs.Mount(imageFormat, wholePart, encoding, parsedOptions); if(error == Errno.NoError) { List rootDir = new List(); @@ -627,7 +631,7 @@ namespace DiscImageChef.Commands DicConsole.WriteLine($"Identified by {plugin.Name}."); IReadOnlyFilesystem fs = (IReadOnlyFilesystem)plugin.GetType().GetConstructor(Type.EmptyTypes)?.Invoke(new object[] { }); - error = fs.Mount(imageFormat, wholePart, encoding, optionsDict); + error = fs.Mount(imageFormat, wholePart, encoding, parsedOptions); if(error == Errno.NoError) { List rootDir = new List(); diff --git a/DiscImageChef/Commands/Ls.cs b/DiscImageChef/Commands/Ls.cs index e3215898b..1326d5c42 100644 --- a/DiscImageChef/Commands/Ls.cs +++ b/DiscImageChef/Commands/Ls.cs @@ -52,8 +52,12 @@ namespace DiscImageChef.Commands FiltersList filtersList = new FiltersList(); IFilter inputFilter = filtersList.GetFilter(options.InputFile); - Dictionary optionsDict = - new Dictionary {{"debug", options.Debug.ToString()}}; + + Dictionary parsedOptions = Options.Parse(options.Options); + DicConsole.DebugWriteLine("Ls command", "Parsed options:"); + foreach(KeyValuePair parsedOption in parsedOptions) + DicConsole.DebugWriteLine("Ls command", "{0} = {1}", parsedOption.Key, parsedOption.Value); + parsedOptions.Add("debug", options.Debug.ToString()); if(inputFilter == null) { @@ -154,7 +158,7 @@ namespace DiscImageChef.Commands if(fs == null) continue; - error = fs.Mount(imageFormat, partitions[i], encoding, optionsDict); + error = fs.Mount(imageFormat, partitions[i], encoding, parsedOptions); if(error == Errno.NoError) { List rootDir = new List(); @@ -184,7 +188,7 @@ namespace DiscImageChef.Commands ?.Invoke(new object[] { }); if(fs == null) continue; - error = fs.Mount(imageFormat, partitions[i], encoding, optionsDict); + error = fs.Mount(imageFormat, partitions[i], encoding, parsedOptions); if(error == Errno.NoError) { List rootDir = new List(); @@ -224,7 +228,7 @@ namespace DiscImageChef.Commands ?.Invoke(new object[] { }); if(fs == null) continue; - error = fs.Mount(imageFormat, wholePart, encoding, optionsDict); + error = fs.Mount(imageFormat, wholePart, encoding, parsedOptions); if(error == Errno.NoError) { List rootDir = new List(); @@ -251,7 +255,7 @@ namespace DiscImageChef.Commands .GetType().GetConstructor(Type.EmptyTypes)?.Invoke(new object[] { }); if(fs != null) { - error = fs.Mount(imageFormat, wholePart, encoding, optionsDict); + error = fs.Mount(imageFormat, wholePart, encoding, parsedOptions); if(error == Errno.NoError) { List rootDir = new List(); diff --git a/DiscImageChef/Options.cs b/DiscImageChef/Options.cs index 9830c20a2..8487b0799 100644 --- a/DiscImageChef/Options.cs +++ b/DiscImageChef/Options.cs @@ -326,6 +326,10 @@ namespace DiscImageChef [Option('e', "encoding", Default = null, HelpText = "Name of character encoding to use.")] public string EncodingName { get; set; } + + [Option('O', "options", Default = null, + HelpText = "Comma separated name=value pairs of options to pass to filesystem plugin")] + public string Options { get; set; } } [Verb("extract-files", HelpText = "Extracts all files in disc image.")] @@ -343,6 +347,10 @@ namespace DiscImageChef [Option('e', "encoding", Default = null, HelpText = "Name of character encoding to use.")] public string EncodingName { get; set; } + + [Option('O', "options", Default = null, + HelpText = "Comma separated name=value pairs of options to pass to filesystem plugin")] + public string Options { get; set; } } [Verb("list-devices", HelpText = "Lists all connected devices.")] @@ -412,5 +420,9 @@ namespace DiscImageChef HelpText = "Firmware revision of the drive used to read the media represented by the image")] public string DriveFirmwareRevision { get; set; } + + [Option('O', "options", Default = null, + HelpText = "Comma separated name=value pairs of options to pass to output image plugin")] + public string Options { get; set; } } } \ No newline at end of file