Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
ProviderHelpProvider.cs433 linesDownload Raw Back to help
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections;5using System.Collections.Generic;6using System.Collections.ObjectModel;7using System.Globalization;8using System.IO;9using System.Reflection;10using System.Xml;11 12namespace System.Management.Automation13{14    /// <summary>15    /// Class ProviderHelpProvider implement the help provider for commands.16    /// </summary>17    /// <remarks>18    /// Provider Help information are stored in 'help.xml' files. Location of these files19    /// can be found from CommandDiscovery.20    /// </remarks>21    internal class ProviderHelpProvider : HelpProviderWithCache22    {23        /// <summary>24        /// Constructor for HelpProvider.25        /// </summary>26        internal ProviderHelpProvider(HelpSystem helpSystem) : base(helpSystem)27        {28            _sessionState = helpSystem.ExecutionContext.SessionState;29        }30 31        private readonly SessionState _sessionState;32 33        #region Common Properties34 35        /// <summary>36        /// Name of this help provider.37        /// </summary>38        /// <value>Name of this help provider.</value>39        internal override string Name40        {41            get42            {43                return "Provider Help Provider";44            }45        }46 47        /// <summary>48        /// Help category of this provider.49        /// </summary>50        /// <value>Help category of this provider</value>51        internal override HelpCategory HelpCategory52        {53            get54            {55                return HelpCategory.Provider;56            }57        }58 59        #endregion60 61        #region Help Provider Interface62 63        /// <summary>64        /// Do exact match help based on the target.65        /// </summary>66        /// <param name="helpRequest">Help request object.</param>67        internal override IEnumerable<HelpInfo> ExactMatchHelp(HelpRequest helpRequest)68        {69            Collection<ProviderInfo> matchingProviders = null;70 71            try72            {73                matchingProviders = _sessionState.Provider.Get(helpRequest.Target);74            }75            catch (ProviderNotFoundException e)76            {77                // We distinguish two cases here,78                //      a. If the "Provider" is the only category to search for in this case,79                //         an error will be written.80                //      b. Otherwise, no errors will be written since in end user's mind,81                //         he may mean to search for provider help.82                if (this.HelpSystem.LastHelpCategory == HelpCategory.Provider)83                {84                    ErrorRecord errorRecord = new ErrorRecord(e, "ProviderLoadError", ErrorCategory.ResourceUnavailable, null);85                    errorRecord.ErrorDetails = new ErrorDetails(typeof(ProviderHelpProvider).Assembly, "HelpErrors", "ProviderLoadError", helpRequest.Target, e.Message);86                    this.HelpSystem.LastErrors.Add(errorRecord);87                }88            }89 90            if (matchingProviders != null)91            {92                foreach (ProviderInfo providerInfo in matchingProviders)93                {94                    try95                    {96                        LoadHelpFile(providerInfo);97                    }98                    catch (IOException ioException)99                    {100                        ReportHelpFileError(ioException, helpRequest.Target, providerInfo.HelpFile);101                    }102                    catch (System.Security.SecurityException securityException)103                    {104                        ReportHelpFileError(securityException, helpRequest.Target, providerInfo.HelpFile);105                    }106                    catch (XmlException xmlException)107                    {108                        ReportHelpFileError(xmlException, helpRequest.Target, providerInfo.HelpFile);109                    }110 111                    HelpInfo helpInfo = GetCache(providerInfo.PSSnapInName + "\\" + providerInfo.Name);112 113                    if (helpInfo != null)114                    {115                        yield return helpInfo;116                    }117                }118            }119        }120 121        private static string GetProviderAssemblyPath(ProviderInfo providerInfo)122        {123            if (providerInfo == null)124                return null;125 126            if (providerInfo.ImplementingType == null)127                return null;128 129            return Path.GetDirectoryName(providerInfo.ImplementingType.Assembly.Location);130        }131 132        /// <summary>133        /// This is a hashtable to track which help files are loaded already.134        ///135        /// This will avoid one help file getting loaded again and again.136        /// (Which should not happen unless some provider is pointing137        /// to a help file that actually doesn't contain the help for it).138        /// </summary>139        private readonly Hashtable _helpFiles = new Hashtable();140 141        /// <summary>142        /// Load help file provided.143        /// </summary>144        /// <remarks>145        /// This will load providerHelpInfo from help file into help cache.146        /// </remarks>147        /// <param name="providerInfo">ProviderInfo for which to locate help.</param>148        private void LoadHelpFile(ProviderInfo providerInfo)149        {150            if (providerInfo == null)151            {152                throw PSTraceSource.NewArgumentNullException(nameof(providerInfo));153            }154 155            string helpFile = providerInfo.HelpFile;156 157            if (string.IsNullOrEmpty(helpFile) || _helpFiles.Contains(helpFile))158            {159                return;160            }161 162            string helpFileToLoad = helpFile;163 164            // Get the mshsnapinfo object for this cmdlet.165            PSSnapInInfo mshSnapInInfo = providerInfo.PSSnapIn;166 167            // Search fallback168            // 1. If PSSnapInInfo exists, then always look in the application base169            //    of the mshsnapin170            // Otherwise,171            //    Look in the default search path and cmdlet assembly path172            Collection<string> searchPaths = new Collection<string>();173            if (mshSnapInInfo != null)174            {175                Diagnostics.Assert(!string.IsNullOrEmpty(mshSnapInInfo.ApplicationBase),176                    "Application Base is null or empty.");177                // not minishell case..178                // we have to search only in the application base for a mshsnapin...179                // if you create an absolute path for helpfile, then MUIFileSearcher180                // will look only in that path.181                helpFileToLoad = Path.Combine(mshSnapInInfo.ApplicationBase, helpFile);182            }183            else if ((providerInfo.Module != null) && (!string.IsNullOrEmpty(providerInfo.Module.Path)))184            {185                helpFileToLoad = Path.Combine(providerInfo.Module.ModuleBase, helpFile);186            }187            else188            {189                searchPaths.Add(GetDefaultShellSearchPath());190                searchPaths.Add(GetProviderAssemblyPath(providerInfo));191            }192 193            string location = MUIFileSearcher.LocateFile(helpFileToLoad, searchPaths);194            if (string.IsNullOrEmpty(location))195                throw new FileNotFoundException(helpFile);196 197            XmlDocument doc = InternalDeserializer.LoadUnsafeXmlDocument(198                new FileInfo(location),199                false, /* ignore whitespace, comments, etc. */200                null); /* default maxCharactersInDocument */201 202            // Add this file into _helpFiles hashtable to prevent it to be loaded again.203            _helpFiles[helpFile] = 0;204 205            XmlNode helpItemsNode = null;206 207            if (doc.HasChildNodes)208            {209                for (int i = 0; i < doc.ChildNodes.Count; i++)210                {211                    XmlNode node = doc.ChildNodes[i];212                    if (node.NodeType == XmlNodeType.Element && string.Equals(node.Name, "helpItems", StringComparison.OrdinalIgnoreCase))213                    {214                        helpItemsNode = node;215                        break;216                    }217                }218            }219 220            if (helpItemsNode == null)221                return;222 223            using (this.HelpSystem.Trace(location))224            {225                if (helpItemsNode.HasChildNodes)226                {227                    for (int i = 0; i < helpItemsNode.ChildNodes.Count; i++)228                    {229                        XmlNode node = helpItemsNode.ChildNodes[i];230                        if (node.NodeType == XmlNodeType.Element && string.Equals(node.Name, "providerHelp", StringComparison.OrdinalIgnoreCase))231                        {232                            HelpInfo helpInfo = ProviderHelpInfo.Load(node);233 234                            if (helpInfo != null)235                            {236                                this.HelpSystem.TraceErrors(helpInfo.Errors);237                                // Add snapin qualified type name for this command..238                                // this will enable customizations of the help object.239                                helpInfo.FullHelp.TypeNames.Insert(240                                    index: 0,241                                    string.Create(242                                        CultureInfo.InvariantCulture,243                                        $"ProviderHelpInfo#{providerInfo.PSSnapInName}#{helpInfo.Name}"));244 245                                if (!string.IsNullOrEmpty(providerInfo.PSSnapInName))246                                {247                                    helpInfo.FullHelp.Properties.Add(new PSNoteProperty("PSSnapIn", providerInfo.PSSnapIn));248                                    helpInfo.FullHelp.TypeNames.Insert(249                                        index: 1,250                                        string.Create(251                                            CultureInfo.InvariantCulture,252                                            $"ProviderHelpInfo#{providerInfo.PSSnapInName}"));253                                }254 255                                AddCache(providerInfo.PSSnapInName + "\\" + helpInfo.Name, helpInfo);256                            }257                        }258                    }259                }260            }261        }262 263        /// <summary>264        /// Search for provider help based on a search target.265        /// </summary>266        /// <param name="helpRequest">Help request object.</param>267        /// <param name="searchOnlyContent">268        /// If true, searches for pattern in the help content. Individual269        /// provider can decide which content to search in.270        ///271        /// If false, searches for pattern in the command names.272        /// </param>273        /// <returns></returns>274        internal override IEnumerable<HelpInfo> SearchHelp(HelpRequest helpRequest, bool searchOnlyContent)275        {276            int countOfHelpInfoObjectsFound = 0;277            string target = helpRequest.Target;278            string pattern = target;279            // this will be used only when searchOnlyContent == true280            WildcardPattern wildCardPattern = null;281 282            bool decoratedSearch = !WildcardPattern.ContainsWildcardCharacters(target);283 284            if (!searchOnlyContent)285            {286                if (decoratedSearch)287                {288                    pattern += "*";289                }290            }291            else292            {293                string searchTarget = helpRequest.Target;294                if (decoratedSearch)295                {296                    searchTarget = "*" + helpRequest.Target + "*";297                }298 299                wildCardPattern = WildcardPattern.Get(searchTarget, WildcardOptions.Compiled | WildcardOptions.IgnoreCase);300                // search in all providers301                pattern = "*";302            }303 304            PSSnapinQualifiedName snapinQualifiedNameForPattern =305                PSSnapinQualifiedName.GetInstance(pattern);306 307            if (snapinQualifiedNameForPattern == null)308            {309                yield break;310            }311 312            foreach (ProviderInfo providerInfo in _sessionState.Provider.GetAll())313            {314                if (providerInfo.IsMatch(pattern))315                {316                    try317                    {318                        LoadHelpFile(providerInfo);319                    }320                    catch (IOException ioException)321                    {322                        if (!decoratedSearch)323                        {324                            ReportHelpFileError(ioException, providerInfo.Name, providerInfo.HelpFile);325                        }326                    }327                    catch (System.Security.SecurityException securityException)328                    {329                        if (!decoratedSearch)330                        {331                            ReportHelpFileError(securityException, providerInfo.Name, providerInfo.HelpFile);332                        }333                    }334                    catch (XmlException xmlException)335                    {336                        if (!decoratedSearch)337                        {338                            ReportHelpFileError(xmlException, providerInfo.Name, providerInfo.HelpFile);339                        }340                    }341 342                    HelpInfo helpInfo = GetCache(providerInfo.PSSnapInName + "\\" + providerInfo.Name);343 344                    if (helpInfo != null)345                    {346                        if (searchOnlyContent)347                        {348                            // ignore help objects that do not have pattern in its help349                            // content.350                            if (!helpInfo.MatchPatternInContent(wildCardPattern))351                            {352                                continue;353                            }354                        }355 356                        countOfHelpInfoObjectsFound++;357                        yield return helpInfo;358 359                        if (countOfHelpInfoObjectsFound >= helpRequest.MaxResults && helpRequest.MaxResults > 0)360                            yield break;361                    }362                }363            }364        }365 366        internal override IEnumerable<HelpInfo> ProcessForwardedHelp(HelpInfo helpInfo, HelpRequest helpRequest)367        {368            ProviderCommandHelpInfo providerCommandHelpInfo = new ProviderCommandHelpInfo(369                helpInfo, helpRequest.ProviderContext);370            yield return providerCommandHelpInfo;371        }372#if V2373        /// <summary>374        /// Process a helpInfo forwarded from other providers (normally commandHelpProvider)375        /// </summary>376        /// <remarks>377        /// For command help info, this will378        ///     1. check whether provider-specific commandlet help exists.379        ///     2. merge found provider-specific help with commandlet help provided.380        /// </remarks>381        /// <param name="helpInfo">HelpInfo forwarded in.</param>382        /// <param name="helpRequest">Help request object.</param>383        /// <returns>The help info object after processing.</returns>384        override internal HelpInfo ProcessForwardedHelp(HelpInfo helpInfo, HelpRequest helpRequest)385        {386            if (helpInfo == null)387                return null;388 389            if (helpInfo.HelpCategory != HelpCategory.Command)390            {391                return helpInfo;392            }393 394            string providerName = helpRequest.Provider;395            if (string.IsNullOrEmpty(providerName))396            {397                providerName = this._sessionState.Path.CurrentLocation.Provider.Name;398            }399 400            HelpRequest providerHelpRequest = helpRequest.Clone();401            providerHelpRequest.Target = providerName;402 403            ProviderHelpInfo providerHelpInfo = (ProviderHelpInfo)this.ExactMatchHelp(providerHelpRequest);404 405            if (providerHelpInfo == null)406                return null;407 408            CommandHelpInfo commandHelpInfo = (CommandHelpInfo)helpInfo;409 410            CommandHelpInfo result = commandHelpInfo.MergeProviderSpecificHelp(providerHelpInfo.GetCmdletHelp(commandHelpInfo.Name), providerHelpInfo.GetDynamicParameterHelp(helpRequest.DynamicParameters));411 412            // Reset ForwardHelpCategory for the helpinfo to be returned so that it will not be forwarded back again.413            result.ForwardHelpCategory = HelpCategory.None;414 415            return result;416        }417#endif418 419        /// <summary>420        /// This will reset the help cache. Normally this corresponds to a421        /// help culture change.422        /// </summary>423        internal override void Reset()424        {425            base.Reset();426 427            _helpFiles.Clear();428        }429 430        #endregion431    }432}433