//
//
//
//
// $Revision$
//
using System;
using System.IO;
using System.Text;
namespace ICSharpCode.WixBinding
{
///
/// Utility class that converts from long filenames to DOS 8.3 filenames based on the
/// rough algorithm outlined here: http://support.microsoft.com/kb/142982/EN-US/
/// apart from using an underscore instead of a '~'.
///
public sealed class ShortFileName
{
const int MaximumFileNameWithoutExtensionLength = 8;
///
/// The filename length to use when appending "_1" to it.
///
const int FileNameWithoutExtensionTruncatedLength = 6;
///
/// Maximum short filename extension length including dot.
///
const int MaximumFileNameExtensionLength = 4;
///
/// Maximum short filename length including extension.
///
public const int MaximumFileNameLength = MaximumFileNameWithoutExtensionLength + MaximumFileNameExtensionLength;
///
/// Called by Convert to check whether the specified filename exists. If it
/// does then the FileNameExists delegate is called repeatedly until it
/// finds a filename that does not exist.
///
public delegate bool NameExists(string name);
ShortFileName()
{
}
///
/// Converts a long filename to a short 8.3 filename.
///
/// The long filename.
/// The converted 8.3 filename. If the filename includes a
/// starting path then this is stripped from the returned value.
public static string Convert(string fileName, NameExists getFileNameExists)
{
if (fileName == null) {
return String.Empty;
}
// Remove invalid characters.
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName);
string extension = Path.GetExtension(fileName);
StringBuilder shortFileName = new StringBuilder();
int length = 0;
foreach (char ch in fileNameWithoutExtension) {
if (IsValidShortFileNameChar(ch)) {
shortFileName.Append(ch);
++length;
}
}
// Get truncated extension.
if (extension.Length > MaximumFileNameExtensionLength) {
extension = extension.Substring(0, MaximumFileNameExtensionLength);
}
// Truncate filename without extension if it is too long.
if (length > MaximumFileNameWithoutExtensionLength) {
shortFileName.Remove(FileNameWithoutExtensionTruncatedLength, length - FileNameWithoutExtensionTruncatedLength);
return GetTruncatedFileName(shortFileName.ToString(), extension, getFileNameExists);
}
// Add extension.
shortFileName.Append(extension);
return shortFileName.ToString().ToUpperInvariant();
}
///
/// Converts a long filename to a short 8.3 filename.
///
/// The long filename
/// The converted 8.3 filename. If the filename includes a
/// starting path then this is stripped from the returned value.
public static string Convert(string fileName)
{
return Convert(fileName, FileNameNeverExists);
}
///
/// Gets the unique name string by adding a digit until a unique name is
/// produced. The name string itself will be truncated as the number
/// increases in size to make sure the total length of the string is
/// always equal to start.Length + 1.
///
/// The string to be used at the start of the
/// Method called to check that the
/// name does not already exist.
public static string GetUniqueName(string start, NameExists getNameExists)
{
return GetUniqueName(start, String.Empty, String.Empty, getNameExists);
}
///
/// Gets the unique name string by adding a digit until a unique name is
/// produced. The name string itself will be truncated as the number
/// increases in size to make sure the total length of the string is
/// always equal to start.Length + numberPrefix.Length + 1 + end.Length.
///
/// The string to be used at the start of the
/// The string to be prefixed to the number.
/// The string to be added after the number.
/// Method called to check that the
/// name does not already exist.
public static string GetUniqueName(string start, string numberPrefix, string end, NameExists getNameExists)
{
int count = 0;
string truncatedName;
int divisor = 10;
do {
++count;
int removeCharCount = count / divisor;
if (removeCharCount > 0) {
start = start.Substring(0, start.Length - 1);
divisor *= divisor;
}
truncatedName = String.Concat(start, numberPrefix, count.ToString(), end);
} while (getNameExists(truncatedName));
return truncatedName;
}
///
/// Determines whether the specified filename is a long name
/// and should be converted into a short name.
///
public static bool IsLongFileName(string fileName)
{
if (fileName == null) {
return false;
}
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName);
string extension = Path.GetExtension(fileName);
return fileNameWithoutExtension.Length > MaximumFileNameWithoutExtensionLength ||
extension.Length > MaximumFileNameExtensionLength;
}
///
/// Truncates the filename start and adds "_N" where N produces a filename that
/// does not exist.
///
static string GetTruncatedFileName(string fileNameStart, string extension, NameExists getFileNameExists)
{
return GetUniqueName(fileNameStart.ToUpperInvariant(), "_", extension.ToUpperInvariant(), getFileNameExists);
}
///
/// Default delegate that always returns false to the Convert method.
///
static bool FileNameNeverExists(string fileName)
{
return false;
}
///
/// Returns whether the character is considered a valid character
/// for the starting part of the filename (i.e. not including the extension).
///
///
/// We include the period character here to avoid ICE03 invalid filename
/// errors.
///
/// http://msdn.microsoft.com/library/en-us/msi/setup/ice03.asp
///
static bool IsValidShortFileNameChar(char ch)
{
switch (ch) {
case ' ':
case '[':
case ']':
case ';':
case '=':
case ',':
case '.':
return false;
}
return true;
}
}
}