Browse Source

- Replace DecompilerTextView.CleanUpName with direct uses of WholeProjectDecompiler.CleanUpFileName

- Add file extension handling to WholeProjectDecompiler.CleanUpName
- Add WholeProjectDecompiler.SanitizeFileName, which respects file extensions
pull/2567/head
Siegfried Pammer 4 years ago
parent
commit
849161e362
  1. 60
      ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs
  2. 5
      ILSpy/Commands/ExtractPackageEntryContextMenuEntry.cs
  3. 3
      ILSpy/Commands/GeneratePdbContextMenuEntry.cs
  4. 4
      ILSpy/Commands/SelectPdbContextMenuEntry.cs
  5. 10
      ILSpy/TextView/DecompilerTextView.cs
  6. 4
      ILSpy/TreeNodes/AssemblyTreeNode.cs
  7. 4
      ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs
  8. 5
      ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs
  9. 3
      ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs

60
ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs

@ -269,7 +269,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
{ {
foreach (var (name, value) in resourcesFile) foreach (var (name, value) in resourcesFile)
{ {
string fileName = CleanUpFileName(name) string fileName = SanitizeFileName(name)
.Replace('/', Path.DirectorySeparatorChar); .Replace('/', Path.DirectorySeparatorChar);
string dirName = Path.GetDirectoryName(fileName); string dirName = Path.GetDirectoryName(fileName);
if (!string.IsNullOrEmpty(dirName) && directories.Add(dirName)) if (!string.IsNullOrEmpty(dirName) && directories.Add(dirName))
@ -571,14 +571,25 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
/// </summary> /// </summary>
public static string CleanUpFileName(string text) public static string CleanUpFileName(string text)
{ {
return CleanUpFileName(text, separateAtDots: false); return CleanUpName(text, separateAtDots: false, treatAsFileName: false);
}
/// <summary>
/// Removes invalid characters from file names and reduces their length,
/// but keeps file extensions intact.
/// </summary>
public static string SanitizeFileName(string fileName)
{
return CleanUpName(fileName, separateAtDots: false, treatAsFileName: true);
} }
/// <summary> /// <summary>
/// Cleans up a node name for use as a file system name. If <paramref name="separateAtDots"/> is active, /// Cleans up a node name for use as a file system name. If <paramref name="separateAtDots"/> is active,
/// dots are seen as segment separators. Each segment is limited to 255 characters. /// dots are seen as segment separators. Each segment is limited to maxSegmentLength characters.
/// (see <see cref="GetLongPathSupport"/>) If <paramref name="treatAsFileName"/> is active,
/// we check for file a extension and try to preserve it, if it's valid.
/// </summary> /// </summary>
static string CleanUpFileName(string text, bool separateAtDots) static string CleanUpName(string text, bool separateAtDots, bool treatAsFileName)
{ {
int pos = text.IndexOf(':'); int pos = text.IndexOf(':');
if (pos > 0) if (pos > 0)
@ -587,23 +598,49 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
if (pos > 0) if (pos > 0)
text = text.Substring(0, pos); text = text.Substring(0, pos);
text = text.Trim(); text = text.Trim();
// Whitelist allowed characters, replace everything else: string extension = null;
StringBuilder b = new StringBuilder(text.Length);
int currentSegmentLength = 0; int currentSegmentLength = 0;
var (supportsLongPaths, maxPathLength, maxSegmentLength) = longPathSupport.Value; var (supportsLongPaths, maxPathLength, maxSegmentLength) = longPathSupport.Value;
if (treatAsFileName)
{
// Check if input is a file name, i.e., has a valid extension
// If yes, preserve extension and append it at the end.
// But only, if the extension length does not exceed maxSegmentLength,
// if that's the case we just give up and treat the extension no different
// from the file name.
int lastDot = text.LastIndexOf('.');
if (lastDot >= 0 && text.Length - lastDot < maxSegmentLength)
{
string originalText = text;
extension = text.Substring(lastDot);
text = text.Remove(lastDot);
foreach (var c in extension)
{
if (!(char.IsLetterOrDigit(c) || c == '-' || c == '_' || c == '.'))
{
// extension contains an invalid character, therefore cannot be a valid extension.
extension = null;
text = originalText;
break;
}
}
}
}
// Whitelist allowed characters, replace everything else:
StringBuilder b = new StringBuilder(text.Length + (extension?.Length ?? 0));
foreach (var c in text) foreach (var c in text)
{ {
currentSegmentLength++; currentSegmentLength++;
if (char.IsLetterOrDigit(c) || c == '-' || c == '_') if (char.IsLetterOrDigit(c) || c == '-' || c == '_')
{ {
// if the current segment exceeds 255 characters, // if the current segment exceeds maxSegmentLength characters,
// skip until the end of the segment. // skip until the end of the segment.
if (currentSegmentLength <= maxSegmentLength) if (currentSegmentLength <= maxSegmentLength)
b.Append(c); b.Append(c);
} }
else if (c == '.' && b.Length > 0 && b[b.Length - 1] != '.') else if (c == '.' && b.Length > 0 && b[b.Length - 1] != '.')
{ {
// if the current segment exceeds 255 characters, // if the current segment exceeds maxSegmentLength characters,
// skip until the end of the segment. // skip until the end of the segment.
if (separateAtDots || currentSegmentLength <= maxSegmentLength) if (separateAtDots || currentSegmentLength <= maxSegmentLength)
b.Append('.'); // allow dot, but never two in a row b.Append('.'); // allow dot, but never two in a row
@ -614,7 +651,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
} }
else else
{ {
// if the current segment exceeds 255 characters, // if the current segment exceeds maxSegmentLength characters,
// skip until the end of the segment. // skip until the end of the segment.
if (currentSegmentLength <= maxSegmentLength) if (currentSegmentLength <= maxSegmentLength)
b.Append('-'); b.Append('-');
@ -625,6 +662,8 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
if (b.Length == 0) if (b.Length == 0)
b.Append('-'); b.Append('-');
string name = b.ToString(); string name = b.ToString();
if (extension != null)
name += extension;
if (IsReservedFileSystemName(name)) if (IsReservedFileSystemName(name))
return name + "_"; return name + "_";
else if (name == ".") else if (name == ".")
@ -638,7 +677,8 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
/// </summary> /// </summary>
public static string CleanUpDirectoryName(string text) public static string CleanUpDirectoryName(string text)
{ {
return CleanUpFileName(text, separateAtDots: true).Replace('.', Path.DirectorySeparatorChar); return CleanUpName(text, separateAtDots: true, treatAsFileName: false)
.Replace('.', Path.DirectorySeparatorChar);
} }
static bool IsReservedFileSystemName(string name) static bool IsReservedFileSystemName(string name)

5
ILSpy/Commands/ExtractPackageEntryContextMenuEntry.cs

@ -23,6 +23,7 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.TreeNodes;
@ -46,7 +47,7 @@ namespace ICSharpCode.ILSpy
return; return;
var assembly = selectedNodes[0].PackageEntry; var assembly = selectedNodes[0].PackageEntry;
SaveFileDialog dlg = new SaveFileDialog(); SaveFileDialog dlg = new SaveFileDialog();
dlg.FileName = Path.GetFileName(DecompilerTextView.CleanUpName(assembly.Name)); dlg.FileName = Path.GetFileName(WholeProjectDecompiler.SanitizeFileName(assembly.Name));
dlg.Filter = ".NET assemblies|*.dll;*.exe;*.winmd" + Resources.AllFiles; dlg.Filter = ".NET assemblies|*.dll;*.exe;*.winmd" + Resources.AllFiles;
dlg.InitialDirectory = Path.GetDirectoryName(bundleNode.LoadedAssembly.FileName); dlg.InitialDirectory = Path.GetDirectoryName(bundleNode.LoadedAssembly.FileName);
if (dlg.ShowDialog() != true) if (dlg.ShowDialog() != true)
@ -70,7 +71,7 @@ namespace ICSharpCode.ILSpy
{ {
foreach (var node in selectedNodes) foreach (var node in selectedNodes)
{ {
var fileName = Path.GetFileName(DecompilerTextView.CleanUpName(node.PackageEntry.Name)); var fileName = Path.GetFileName(WholeProjectDecompiler.SanitizeFileName(node.PackageEntry.Name));
SaveEntry(output, node.PackageEntry, Path.Combine(outputFolderOrFileName, fileName)); SaveEntry(output, node.PackageEntry, Path.Combine(outputFolderOrFileName, fileName));
} }
} }

3
ILSpy/Commands/GeneratePdbContextMenuEntry.cs

@ -25,6 +25,7 @@ using System.Windows;
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.DebugInfo;
using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.TextView;
@ -63,7 +64,7 @@ namespace ICSharpCode.ILSpy
return; return;
} }
SaveFileDialog dlg = new SaveFileDialog(); SaveFileDialog dlg = new SaveFileDialog();
dlg.FileName = DecompilerTextView.CleanUpName(assembly.ShortName) + ".pdb"; dlg.FileName = WholeProjectDecompiler.CleanUpFileName(assembly.ShortName) + ".pdb";
dlg.Filter = Resources.PortablePDBPdbAllFiles; dlg.Filter = Resources.PortablePDBPdbAllFiles;
dlg.InitialDirectory = Path.GetDirectoryName(assembly.FileName); dlg.InitialDirectory = Path.GetDirectoryName(assembly.FileName);
if (dlg.ShowDialog() != true) if (dlg.ShowDialog() != true)

4
ILSpy/Commands/SelectPdbContextMenuEntry.cs

@ -19,8 +19,8 @@
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.TreeNodes;
using Microsoft.Win32; using Microsoft.Win32;
@ -35,7 +35,7 @@ namespace ICSharpCode.ILSpy
if (assembly == null) if (assembly == null)
return; return;
OpenFileDialog dlg = new OpenFileDialog(); OpenFileDialog dlg = new OpenFileDialog();
dlg.FileName = DecompilerTextView.CleanUpName(assembly.ShortName) + ".pdb"; dlg.FileName = WholeProjectDecompiler.CleanUpFileName(assembly.ShortName) + ".pdb";
dlg.Filter = Resources.PortablePDBPdbAllFiles; dlg.Filter = Resources.PortablePDBPdbAllFiles;
dlg.InitialDirectory = Path.GetDirectoryName(assembly.FileName); dlg.InitialDirectory = Path.GetDirectoryName(assembly.FileName);
if (dlg.ShowDialog() != true) if (dlg.ShowDialog() != true)

10
ILSpy/TextView/DecompilerTextView.cs

@ -1034,7 +1034,7 @@ namespace ICSharpCode.ILSpy.TextView
SaveFileDialog dlg = new SaveFileDialog(); SaveFileDialog dlg = new SaveFileDialog();
dlg.DefaultExt = language.FileExtension; dlg.DefaultExt = language.FileExtension;
dlg.Filter = language.Name + "|*" + language.FileExtension + Properties.Resources.AllFiles; dlg.Filter = language.Name + "|*" + language.FileExtension + Properties.Resources.AllFiles;
dlg.FileName = CleanUpName(treeNodes.First().ToString()) + language.FileExtension; dlg.FileName = WholeProjectDecompiler.CleanUpFileName(treeNodes.First().ToString()) + language.FileExtension;
if (dlg.ShowDialog() == true) if (dlg.ShowDialog() == true)
{ {
SaveToDisk(new DecompilationContext(language, treeNodes.ToArray(), options), dlg.FileName); SaveToDisk(new DecompilationContext(language, treeNodes.ToArray(), options), dlg.FileName);
@ -1149,14 +1149,6 @@ namespace ICSharpCode.ILSpy.TextView
thread.Start(); thread.Start();
return tcs.Task; return tcs.Task;
} }
/// <summary>
/// Cleans up a node name for use as a file name.
/// </summary>
internal static string CleanUpName(string text)
{
return WholeProjectDecompiler.CleanUpFileName(text);
}
#endregion #endregion
internal ReferenceSegment? GetReferenceSegmentAtMousePosition() internal ReferenceSegment? GetReferenceSegmentAtMousePosition()

4
ILSpy/TreeNodes/AssemblyTreeNode.cs

@ -26,10 +26,10 @@ using System.Windows.Controls;
using System.Windows.Documents; using System.Windows.Documents;
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.ViewModels; using ICSharpCode.ILSpy.ViewModels;
using ICSharpCode.TreeView; using ICSharpCode.TreeView;
@ -384,7 +384,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
if (string.IsNullOrEmpty(language.ProjectFileExtension)) if (string.IsNullOrEmpty(language.ProjectFileExtension))
return false; return false;
SaveFileDialog dlg = new SaveFileDialog(); SaveFileDialog dlg = new SaveFileDialog();
dlg.FileName = DecompilerTextView.CleanUpName(LoadedAssembly.ShortName) + language.ProjectFileExtension; dlg.FileName = WholeProjectDecompiler.CleanUpFileName(LoadedAssembly.ShortName) + language.ProjectFileExtension;
dlg.Filter = language.Name + " project|*" + language.ProjectFileExtension + "|" + language.Name + " single file|*" + language.FileExtension + "|All files|*.*"; dlg.Filter = language.Name + " project|*" + language.ProjectFileExtension + "|" + language.Name + " single file|*" + language.FileExtension + "|All files|*.*";
if (dlg.ShowDialog() == true) if (dlg.ShowDialog() == true)
{ {

4
ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs

@ -20,8 +20,8 @@ using System;
using System.IO; using System.IO;
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.ILSpy.TextView;
using Microsoft.Win32; using Microsoft.Win32;
@ -80,7 +80,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
public override bool Save(ViewModels.TabPageModel tabPage) public override bool Save(ViewModels.TabPageModel tabPage)
{ {
SaveFileDialog dlg = new SaveFileDialog(); SaveFileDialog dlg = new SaveFileDialog();
dlg.FileName = Path.GetFileName(DecompilerTextView.CleanUpName(key)); dlg.FileName = Path.GetFileName(WholeProjectDecompiler.SanitizeFileName(key));
if (dlg.ShowDialog() == true) if (dlg.ShowDialog() == true)
{ {
using var data = OpenStream(); using var data = OpenStream();

5
ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs

@ -24,6 +24,7 @@ using System.Text;
using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.AvalonEdit.Utils;
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.TextView;
@ -91,7 +92,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
if (type == FileType.Xml) if (type == FileType.Xml)
ext = ".xml"; ext = ".xml";
else else
ext = Path.GetExtension(DecompilerTextView.CleanUpName(Resource.Name)); ext = Path.GetExtension(WholeProjectDecompiler.SanitizeFileName(Resource.Name));
tabPage.ShowTextView(textView => textView.ShowNode(output, this, HighlightingManager.Instance.GetDefinitionByExtension(ext))); tabPage.ShowTextView(textView => textView.ShowNode(output, this, HighlightingManager.Instance.GetDefinitionByExtension(ext)));
tabPage.SupportsLanguageSwitching = false; tabPage.SupportsLanguageSwitching = false;
return true; return true;
@ -106,7 +107,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
if (s == null) if (s == null)
return false; return false;
SaveFileDialog dlg = new SaveFileDialog(); SaveFileDialog dlg = new SaveFileDialog();
dlg.FileName = DecompilerTextView.CleanUpName(Resource.Name); dlg.FileName = Path.GetFileName(WholeProjectDecompiler.SanitizeFileName(Resource.Name));
if (dlg.ShowDialog() == true) if (dlg.ShowDialog() == true)
{ {
s.Position = 0; s.Position = 0;

3
ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs

@ -24,6 +24,7 @@ using System.IO;
using System.Linq; using System.Linq;
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Util; using ICSharpCode.Decompiler.Util;
using ICSharpCode.ILSpy.Controls; using ICSharpCode.ILSpy.Controls;
@ -129,7 +130,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
if (s == null) if (s == null)
return false; return false;
SaveFileDialog dlg = new SaveFileDialog(); SaveFileDialog dlg = new SaveFileDialog();
dlg.FileName = DecompilerTextView.CleanUpName(Resource.Name); dlg.FileName = Path.GetFileName(WholeProjectDecompiler.SanitizeFileName(Resource.Name));
dlg.Filter = Resources.ResourcesFileFilter; dlg.Filter = Resources.ResourcesFileFilter;
if (dlg.ShowDialog() == true) if (dlg.ShowDialog() == true)
{ {

Loading…
Cancel
Save