// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
namespace WixToolset
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using WixToolset.Data;
using WixToolset.Extensibility;
///
/// Common utilities for Wix command-line processing.
///
public static class CommandLineHelper
{
///
/// Get a set of files that possibly have a search pattern in the path (such as '*').
///
/// Search path to find files in.
/// Type of file; typically "Source".
/// An array of files matching the search path.
///
/// This method is written in this verbose way because it needs to support ".." in the path.
/// It needs the directory path isolated from the file name in order to use Directory.GetFiles
/// or DirectoryInfo.GetFiles. The only way to get this directory path is manually since
/// Path.GetDirectoryName does not support ".." in the path.
///
/// Throws WixFileNotFoundException if no file matching the pattern can be found.
public static string[] GetFiles(string searchPath, string fileType)
{
if (null == searchPath)
{
throw new ArgumentNullException("searchPath");
}
// convert alternate directory separators to the standard one
string filePath = searchPath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
int lastSeparator = filePath.LastIndexOf(Path.DirectorySeparatorChar);
string[] files = null;
try
{
if (0 > lastSeparator)
{
files = Directory.GetFiles(".", filePath);
}
else // found directory separator
{
files = Directory.GetFiles(filePath.Substring(0, lastSeparator + 1), filePath.Substring(lastSeparator + 1));
}
}
catch (DirectoryNotFoundException)
{
// don't let this function throw the DirectoryNotFoundException. (this exception
// occurs for non-existant directories and invalid characters in the searchPattern)
}
catch (ArgumentException)
{
// don't let this function throw the ArgumentException. (this exception
// occurs in certain situations such as when passing a malformed UNC path)
}
catch (IOException)
{
throw new WixFileNotFoundException(searchPath, fileType);
}
// file could not be found or path is invalid in some way
if (null == files || 0 == files.Length)
{
throw new WixFileNotFoundException(searchPath, fileType);
}
return files;
}
///
/// Validates that a valid string parameter (without "/" or "-"), and returns a bool indicating its validity
///
/// The list of strings to check.
/// The index (in args) of the commandline parameter to be validated.
/// True if a valid string parameter exists there, false if not.
public static bool IsValidArg(string[] args, int index)
{
if (args.Length <= index || String.IsNullOrEmpty(args[index]) || '/' == args[index][0] || '-' == args[index][0])
{
return false;
}
else
{
return true;
}
}
///
/// Validates that a commandline parameter is a valid file or directory name, and throws appropriate warnings/errors if not
///
/// The path to test.
/// The string if it is valid, null if it is invalid.
public static string VerifyPath(string path)
{
return VerifyPath(path, false);
}
///
/// Validates that a commandline parameter is a valid file or directory name, and throws appropriate warnings/errors if not
///
/// The path to test.
/// Indicates if a colon-delimited prefix is allowed.
/// The full path if it is valid, null if it is invalid.
public static string VerifyPath(string path, bool allowPrefix)
{
string fullPath;
if (0 <= path.IndexOf('\"'))
{
Messaging.Instance.OnMessage(WixErrors.PathCannotContainQuote(path));
return null;
}
try
{
string prefix = null;
if (allowPrefix)
{
int prefixLength = path.IndexOf('=') + 1;
if (0 != prefixLength)
{
prefix = path.Substring(0, prefixLength);
path = path.Substring(prefixLength);
}
}
if (String.IsNullOrEmpty(prefix))
{
fullPath = Path.GetFullPath(path);
}
else
{
fullPath = String.Concat(prefix, Path.GetFullPath(path));
}
}
catch (Exception e)
{
Messaging.Instance.OnMessage(WixErrors.InvalidCommandLineFileName(path, e.Message));
return null;
}
return fullPath;
}
///
/// Validates that a string is a valid bind path, and throws appropriate warnings/errors if not
///
/// The commandline switch we're parsing (for error display purposes).
/// The list of strings to check.
/// The index (in args) of the commandline parameter to be parsed.
/// The bind path if it is valid, null if it is invalid.
public static BindPath GetBindPath(string commandlineSwitch, string[] args, int index)
{
commandlineSwitch = String.Concat("-", commandlineSwitch);
if (!IsValidArg(args, index))
{
Messaging.Instance.OnMessage(WixErrors.DirectoryPathRequired(commandlineSwitch));
return null;
}
BindPath bindPath = BindPath.Parse(args[index]);
if (File.Exists(bindPath.Path))
{
Messaging.Instance.OnMessage(WixErrors.ExpectedDirectoryGotFile(commandlineSwitch, bindPath.Path));
return null;
}
bindPath.Path = VerifyPath(bindPath.Path, true);
return String.IsNullOrEmpty(bindPath.Path) ? null : bindPath;
}
///
/// Validates that a commandline parameter is a valid file or directory name, and throws appropriate warnings/errors if not
///
/// The commandline switch we're parsing (for error display purposes).
/// The messagehandler to report warnings/errors to.
/// The list of strings to check.
/// The index (in args) of the commandline parameter to be parsed.
/// The string if it is valid, null if it is invalid.
public static string GetFileOrDirectory(string commandlineSwitch, string[] args, int index)
{
commandlineSwitch = String.Concat("-", commandlineSwitch);
if (!IsValidArg(args, index))
{
Messaging.Instance.OnMessage(WixErrors.FileOrDirectoryPathRequired(commandlineSwitch));
return null;
}
return VerifyPath(args[index]);
}
///
/// Validates that a string is a valid directory name, and throws appropriate warnings/errors if not
///
/// The commandline switch we're parsing (for error display purposes).
/// The list of strings to check.
/// The index (in args) of the commandline parameter to be parsed.
/// Indicates if a colon-delimited prefix is allowed.
/// The string if it is valid, null if it is invalid.
public static string GetDirectory(string commandlineSwitch, string[] args, int index, bool allowPrefix = false)
{
commandlineSwitch = String.Concat("-", commandlineSwitch);
if (!IsValidArg(args, index))
{
Messaging.Instance.OnMessage(WixErrors.DirectoryPathRequired(commandlineSwitch));
return null;
}
if (File.Exists(args[index]))
{
Messaging.Instance.OnMessage(WixErrors.ExpectedDirectoryGotFile(commandlineSwitch, args[index]));
return null;
}
return VerifyPath(args[index], allowPrefix);
}
///
/// Validates that a string is a valid filename, and throws appropriate warnings/errors if not
///
/// The commandline switch we're parsing (for error display purposes).
/// The list of strings to check.
/// The index (in args) of the commandline parameter to be parsed.
/// The string if it is valid, null if it is invalid.
public static string GetFile(string commandlineSwitch, string[] args, int index)
{
commandlineSwitch = String.Concat("-", commandlineSwitch);
if (!IsValidArg(args, index))
{
Messaging.Instance.OnMessage(WixErrors.FilePathRequired(commandlineSwitch));
return null;
}
if (Directory.Exists(args[index]))
{
Messaging.Instance.OnMessage(WixErrors.ExpectedFileGotDirectory(commandlineSwitch, args[index]));
return null;
}
return VerifyPath(args[index]);
}
}
}