Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
BaseCommandHelpInfo.cs490 linesDownload Raw Back to help
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections.Generic;5using System.Globalization;6using System.Linq;7using System.Management.Automation.Runspaces;8using System.Text;9 10using Dbg = System.Management.Automation.Diagnostics;11 12namespace System.Management.Automation13{14    /// <summary>15    /// Class BaseCommandHelpInfo provides common functionality for16    /// extracting information from FullHelp property.17    /// </summary>18    internal abstract class BaseCommandHelpInfo : HelpInfo19    {20        internal BaseCommandHelpInfo(HelpCategory helpCategory)21            : base()22        {23            HelpCategory = helpCategory;24        }25 26        #region Basic Help Properties27 28        internal PSObject Details29        {30            get31            {32                if (this.FullHelp == null)33                    return null;34 35                if (this.FullHelp.Properties["Details"] == null ||36                    this.FullHelp.Properties["Details"].Value == null)37                {38                    return null;39                }40 41                return PSObject.AsPSObject(this.FullHelp.Properties["Details"].Value);42            }43        }44 45        /// <summary>46        /// Name of command.47        /// </summary>48        /// <value>Name of command</value>49        internal override string Name50        {51            get52            {53                PSObject commandDetails = this.Details;54                if (commandDetails == null)55                {56                    return string.Empty;57                }58 59                if (commandDetails.Properties["Name"] == null ||60                    commandDetails.Properties["Name"].Value == null)61                {62                    return string.Empty;63                }64 65                string name = commandDetails.Properties["Name"].Value.ToString();66                if (name == null)67                    return string.Empty;68 69                return name.Trim();70            }71        }72 73        /// <summary>74        /// Synopsis for this command help.75        /// </summary>76        /// <value>Synopsis for this command help</value>77        internal override string Synopsis78        {79            get80            {81                PSObject commandDetails = this.Details;82                if (commandDetails == null)83                {84                    return string.Empty;85                }86 87                if (commandDetails.Properties["Description"] == null ||88                    commandDetails.Properties["Description"].Value == null)89                {90                    return string.Empty;91                }92 93                object[] synopsisItems = (object[])LanguagePrimitives.ConvertTo(94                    commandDetails.Properties["Description"].Value,95                    typeof(object[]),96                    CultureInfo.InvariantCulture);97                if (synopsisItems == null || synopsisItems.Length == 0)98                {99                    return string.Empty;100                }101 102                PSObject firstSynopsisItem = synopsisItems[0] == null ? null : PSObject.AsPSObject(synopsisItems[0]);103                if (firstSynopsisItem == null ||104                    firstSynopsisItem.Properties["Text"] == null ||105                    firstSynopsisItem.Properties["Text"].Value == null)106                {107                    return string.Empty;108                }109 110                string synopsis = firstSynopsisItem.Properties["Text"].Value.ToString();111                if (synopsis == null)112                {113                    return string.Empty;114                }115 116                return synopsis.Trim();117            }118        }119 120        /// <summary>121        /// Help category for this command help, which is constantly HelpCategory.Command.122        /// </summary>123        /// <value>Help category for this command help</value>124        internal override HelpCategory HelpCategory { get; }125 126        /// <summary>127        /// Returns the Uri used by get-help cmdlet to show help128        /// online. Returns only the first uri found under129        /// RelatedLinks.130        /// </summary>131        /// <returns>132        /// Null if no Uri is specified by the helpinfo or a133        /// valid Uri.134        /// </returns>135        /// <exception cref="InvalidOperationException">136        /// Specified Uri is not valid.137        /// </exception>138        internal override Uri GetUriForOnlineHelp()139        {140            Uri result = null;141            UriFormatException uriFormatException = null;142 143            try144            {145                result = GetUriFromCommandPSObject(this.FullHelp);146                if (result != null)147                {148                    return result;149                }150            }151            catch (UriFormatException urie)152            {153                uriFormatException = urie;154            }155            // else get uri from CommandInfo HelpUri attribute156            result = this.LookupUriFromCommandInfo();157            if (result != null)158            {159                return result;160            }161            else if (uriFormatException != null)162            {163                throw uriFormatException;164            }165 166            return base.GetUriForOnlineHelp();167        }168 169        internal Uri LookupUriFromCommandInfo()170        {171            CommandTypes cmdTypesToLookFor = CommandTypes.Cmdlet;172            switch (this.HelpCategory)173            {174                case Automation.HelpCategory.Cmdlet:175                    cmdTypesToLookFor = CommandTypes.Cmdlet;176                    break;177 178                case Automation.HelpCategory.Function:179                    cmdTypesToLookFor = CommandTypes.Function;180                    break;181 182                case Automation.HelpCategory.ScriptCommand:183                    cmdTypesToLookFor = CommandTypes.Script;184                    break;185 186                case Automation.HelpCategory.ExternalScript:187                    cmdTypesToLookFor = CommandTypes.ExternalScript;188                    break;189 190                case Automation.HelpCategory.Filter:191                    cmdTypesToLookFor = CommandTypes.Filter;192                    break;193 194                case Automation.HelpCategory.Configuration:195                    cmdTypesToLookFor = CommandTypes.Configuration;196                    break;197 198                default:199                    return null;200            }201 202            string commandName = this.Name;203            string moduleName = string.Empty;204            if (this.FullHelp.Properties["ModuleName"] != null)205            {206                PSNoteProperty moduleNameNP = this.FullHelp.Properties["ModuleName"] as PSNoteProperty;207                if (moduleNameNP != null)208                {209                    LanguagePrimitives.TryConvertTo<string>(moduleNameNP.Value, CultureInfo.InvariantCulture,210                                                            out moduleName);211                }212            }213 214            string commandToSearch = commandName;215            if (!string.IsNullOrEmpty(moduleName))216            {217                commandToSearch = string.Create(CultureInfo.InvariantCulture, $"{moduleName}\\{commandName}");218            }219 220            ExecutionContext context = LocalPipeline.GetExecutionContextFromTLS();221            if (context == null)222            {223                return null;224            }225 226            try227            {228                CommandInfo cmdInfo = null;229 230                if (cmdTypesToLookFor == CommandTypes.Cmdlet)231                {232                    cmdInfo = context.SessionState.InvokeCommand.GetCmdlet(commandToSearch);233                }234                else235                {236                    cmdInfo = context.SessionState.InvokeCommand.GetCommands(commandToSearch, cmdTypesToLookFor, false).FirstOrDefault();237                }238 239                if ((cmdInfo == null) || (cmdInfo.CommandMetadata == null))240                {241                    return null;242                }243 244                string uriString = cmdInfo.CommandMetadata.HelpUri;245                if (!string.IsNullOrEmpty(uriString))246                {247                    if (!System.Uri.IsWellFormedUriString(uriString, UriKind.RelativeOrAbsolute))248                    {249                        // WinBlue: 545315 Online help links are broken with localized help250                        // Example: https://go.microsoft.com/fwlink/?LinkID=113324 (moglicherwei se auf Englisch)251                        // Split the string based on <s> (space). We decided to go with this approach as252                        // UX localization authors use spaces. Correctly extracting only the wellformed URI253                        // is out-of-scope for this fix.254                        string[] tempUriSplitArray = uriString.Split(' ');255                        uriString = tempUriSplitArray[0];256                    }257 258                    try259                    {260                        return new System.Uri(uriString);261                        // return only the first Uri (ignore other uris)262                    }263                    catch (UriFormatException)264                    {265                        throw PSTraceSource.NewInvalidOperationException(HelpErrors.InvalidURI,266                                                                         cmdInfo.CommandMetadata.HelpUri);267                    }268                }269            }270            catch (CommandNotFoundException)271            {272            }273 274            return null;275        }276 277        internal static Uri GetUriFromCommandPSObject(PSObject commandFullHelp)278        {279            // this object knows Maml format...280            // So retrieve Uri information as per the format..281            if ((commandFullHelp == null) ||282                (commandFullHelp.Properties["relatedLinks"] == null) ||283                (commandFullHelp.Properties["relatedLinks"].Value == null))284            {285                // return the default..286                return null;287            }288 289            PSObject relatedLinks = PSObject.AsPSObject(commandFullHelp.Properties["relatedLinks"].Value);290            if (relatedLinks.Properties["navigationLink"] == null)291            {292                return null;293            }294 295            object[] navigationLinks = (object[])LanguagePrimitives.ConvertTo(296                relatedLinks.Properties["navigationLink"].Value,297                typeof(object[]),298                CultureInfo.InvariantCulture);299            foreach (object navigationLinkAsObject in navigationLinks)300            {301                if (navigationLinkAsObject == null)302                {303                    continue;304                }305 306                PSObject navigationLink = PSObject.AsPSObject(navigationLinkAsObject);307                PSNoteProperty uriNP = navigationLink.Properties["uri"] as PSNoteProperty;308                if (uriNP != null)309                {310                    string uriString = string.Empty;311                    LanguagePrimitives.TryConvertTo<string>(uriNP.Value, CultureInfo.InvariantCulture, out uriString);312                    if (!string.IsNullOrEmpty(uriString))313                    {314                        if (!System.Uri.IsWellFormedUriString(uriString, UriKind.RelativeOrAbsolute))315                        {316                            // WinBlue: 545315 Online help links are broken with localized help317                            // Example: https://go.microsoft.com/fwlink/?LinkID=113324 (moglicherwei se auf Englisch)318                            // Split the string based on <s> (space). We decided to go with this approach as319                            // UX localization authors use spaces. Correctly extracting only the wellformed URI320                            // is out-of-scope for this fix.321                            string[] tempUriSplitArray = uriString.Split(' ');322                            uriString = tempUriSplitArray[0];323                        }324 325                        try326                        {327                            return new System.Uri(uriString);328                            // return only the first Uri (ignore other uris)329                        }330                        catch (UriFormatException)331                        {332                            throw PSTraceSource.NewInvalidOperationException(HelpErrors.InvalidURI, uriString);333                        }334                    }335                }336            }337 338            return null;339        }340 341        /// <summary>342        /// Returns true if help content in help info matches the343        /// pattern contained in <paramref name="pattern"/>.344        /// The underlying code will usually run pattern.IsMatch() on345        /// content it wants to search.346        /// Cmdlet help info looks for pattern in Synopsis and347        /// DetailedDescription.348        /// </summary>349        /// <param name="pattern"></param>350        /// <returns></returns>351        internal override bool MatchPatternInContent(WildcardPattern pattern)352        {353            Dbg.Assert(pattern != null, "pattern cannot be null");354 355            string synopsis = Synopsis;356            string detailedDescription = DetailedDescription;357 358            synopsis ??= string.Empty;359 360            detailedDescription ??= string.Empty;361 362            return pattern.IsMatch(synopsis) || pattern.IsMatch(detailedDescription);363        }364 365        /// <summary>366        /// Returns help information for a parameter(s) identified by pattern.367        /// </summary>368        /// <param name="pattern">Pattern to search for parameters.</param>369        /// <returns>A collection of parameters that match pattern.</returns>370        internal override PSObject[] GetParameter(string pattern)371        {372            // this object knows Maml format...373            // So retrieve parameter information as per the format..374            if ((this.FullHelp == null) ||375                (this.FullHelp.Properties["parameters"] == null) ||376                (this.FullHelp.Properties["parameters"].Value == null))377            {378                // return the default..379                return base.GetParameter(pattern);380            }381 382            PSObject prmts = PSObject.AsPSObject(this.FullHelp.Properties["parameters"].Value);383 384            if (prmts.Properties["parameter"] == null)385            {386                return base.GetParameter(pattern);387            }388 389            // The Maml format simplifies array fields containing only one object390            // by transforming them into the objects themselves. To ensure the consistency391            // of the help command result we change it back into an array.392            var param = prmts.Properties["parameter"].Value;393            PSObject[] paramAsPSObjArray = new PSObject[1];394 395            if (param is PSObject paramPSObj)396            {397                paramAsPSObjArray[0] = paramPSObj;398            }399 400            PSObject[] prmtArray = (PSObject[])LanguagePrimitives.ConvertTo(401                paramAsPSObjArray[0] != null ? paramAsPSObjArray : param,402                typeof(PSObject[]),403                CultureInfo.InvariantCulture);404 405            if (string.IsNullOrEmpty(pattern))406            {407                return prmtArray;408            }409 410            List<PSObject> returnList = new List<PSObject>();411            WildcardPattern matcher = WildcardPattern.Get(pattern, WildcardOptions.IgnoreCase);412            foreach (PSObject prmtr in prmtArray)413            {414                if ((prmtr.Properties["name"] == null) || (prmtr.Properties["name"].Value == null))415                {416                    continue;417                }418 419                string prmName = prmtr.Properties["name"].Value.ToString();420                if (matcher.IsMatch(prmName))421                {422                    returnList.Add(prmtr);423                }424            }425 426            return returnList.ToArray();427        }428 429        #endregion430 431        #region Cmdlet Help specific Properties432 433        /// <summary>434        /// Detailed Description string of this cmdlet help info.435        /// </summary>436        internal string DetailedDescription437        {438            get439            {440                if (this.FullHelp == null)441                    return string.Empty;442 443                if (this.FullHelp.Properties["Description"] == null ||444                    this.FullHelp.Properties["Description"].Value == null)445                {446                    return string.Empty;447                }448 449                object[] descriptionItems = (object[])LanguagePrimitives.ConvertTo(450                    this.FullHelp.Properties["Description"].Value,451                    typeof(object[]),452                    CultureInfo.InvariantCulture);453                if (descriptionItems == null || descriptionItems.Length == 0)454                {455                    return string.Empty;456                }457 458                // I think every cmdlet description should at least have 400 characters...459                // so starting with this assumption..I did an average of all the cmdlet460                // help content available at the time of writing this code and came up461                // with this number.462                StringBuilder result = new StringBuilder(400);463                foreach (object descriptionItem in descriptionItems)464                {465                    if (descriptionItem == null)466                    {467                        continue;468                    }469 470                    PSObject descriptionObject = PSObject.AsPSObject(descriptionItem);471                    if ((descriptionObject == null) ||472                        (descriptionObject.Properties["Text"] == null) ||473                        (descriptionObject.Properties["Text"].Value == null))474                    {475                        continue;476                    }477 478                    string text = descriptionObject.Properties["Text"].Value.ToString();479                    result.Append(text);480                    result.Append(Environment.NewLine);481                }482 483                return result.ToString().Trim();484            }485        }486 487        #endregion488    }489}490