MegaBites-AI/Windows-powershell
0372
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.Diagnostics;8using System.IO;9using System.Xml;10 11using Dbg = System.Management.Automation.Diagnostics;12 13namespace System.Management.Automation14{15 internal class DscResourceHelpProvider : HelpProviderWithCache16 {17 /// <summary>18 /// Constructor for DscResourceHelpProvider.19 /// </summary>20 internal DscResourceHelpProvider(HelpSystem helpSystem)21 : base(helpSystem)22 {23 _context = helpSystem.ExecutionContext;24 }25 26 /// <summary>27 /// Execution context of the HelpSystem.28 /// </summary>29 private readonly ExecutionContext _context;30 31 /// <summary>32 /// This is a hashtable to track which help files are loaded already.33 ///34 /// This will avoid one help file getting loaded again and again.35 /// </summary>36 private readonly Hashtable _helpFiles = new Hashtable();37 38 [TraceSource("DscResourceHelpProvider", "DscResourceHelpProvider")]39 private static readonly PSTraceSource s_tracer = PSTraceSource.GetTracer("DscResourceHelpProvider", "DscResourceHelpProvider");40 41 #region common properties42 43 /// <summary>44 /// Name of the Help Provider.45 /// </summary>46 internal override string Name47 {48 get { return "Dsc Resource Help Provider"; }49 }50 51 /// <summary>52 /// Supported Help Categories.53 /// </summary>54 internal override HelpCategory HelpCategory55 {56 get { return Automation.HelpCategory.DscResource; }57 }58 59 #endregion60 61 /// <summary>62 /// Override SearchHelp to find a dsc resource help matching a pattern.63 /// </summary>64 /// <param name="helpRequest">Help request.</param>65 /// <param name="searchOnlyContent">Not used.</param>66 /// <returns></returns>67 internal override IEnumerable<HelpInfo> SearchHelp(HelpRequest helpRequest, bool searchOnlyContent)68 {69 Debug.Assert(helpRequest != null, "helpRequest cannot be null.");70 71 string target = helpRequest.Target;72 Collection<string> patternList = new Collection<string>();73 74 bool decoratedSearch = !WildcardPattern.ContainsWildcardCharacters(helpRequest.Target);75 76 if (decoratedSearch)77 {78 patternList.Add("*" + target + "*");79 }80 else81 patternList.Add(target);82 83 foreach (string pattern in patternList)84 {85 DscResourceSearcher searcher = new DscResourceSearcher(pattern, _context);86 87 foreach (var helpInfo in GetHelpInfo(searcher))88 {89 if (helpInfo != null)90 yield return helpInfo;91 }92 }93 }94 95 /// <summary>96 /// Override ExactMatchHelp to find the matching DscResource matching help request.97 /// </summary>98 /// <param name="helpRequest">Help Request for the search.</param>99 /// <returns>Enumerable of HelpInfo objects.</returns>100 internal override IEnumerable<HelpInfo> ExactMatchHelp(HelpRequest helpRequest)101 {102 Debug.Assert(helpRequest != null, "helpRequest cannot be null.");103 104 if ((helpRequest.HelpCategory & Automation.HelpCategory.DscResource) == 0)105 {106 yield return null;107 }108 109 string target = helpRequest.Target;110 111 DscResourceSearcher searcher = new DscResourceSearcher(target, _context);112 113 foreach (var helpInfo in GetHelpInfo(searcher))114 {115 if (helpInfo != null)116 {117 yield return helpInfo;118 }119 }120 }121 122 /// <summary>123 /// Get the help in for the DscResource Info. ///124 /// </summary>125 /// <param name="searcher">Searcher for DscResources.</param>126 /// <returns>Next HelpInfo object.</returns>127 private IEnumerable<HelpInfo> GetHelpInfo(DscResourceSearcher searcher)128 {129 while (searcher.MoveNext())130 {131 DscResourceInfo current = ((IEnumerator<DscResourceInfo>)searcher).Current;132 133 string moduleName = null;134 string moduleDir = current.ParentPath;135 136 // for binary modules, current.Module is empty.137 // in such cases use the leaf folder of ParentPath as filename.138 if (current.Module != null)139 {140 moduleName = current.Module.Name;141 }142 else if (!string.IsNullOrEmpty(moduleDir))143 {144 string[] splitPath = moduleDir.Split('\\');145 moduleName = splitPath[splitPath.Length - 1];146 }147 148 if (!string.IsNullOrEmpty(moduleName) && !string.IsNullOrEmpty(moduleDir))149 {150 string helpFileToFind = moduleName + "-Help.xml";151 152 string helpFileName = null;153 154 Collection<string> searchPaths = new Collection<string>();155 searchPaths.Add(moduleDir);156 157 HelpInfo helpInfo = GetHelpInfoFromHelpFile(current, helpFileToFind, searchPaths, true, out helpFileName);158 159 if (helpInfo != null)160 {161 yield return helpInfo;162 }163 }164 }165 }166 167 /// <summary>168 /// Check whether a HelpItems node indicates that the help content is169 /// authored using maml schema.170 ///171 /// This covers two cases:172 /// a. If the help file has an extension .maml.173 /// b. If HelpItems node (which should be the top node of any command help file)174 /// has an attribute "schema" with value "maml", its content is in maml175 /// schema.176 /// </summary>177 /// <param name="helpFile">File name.</param>178 /// <param name="helpItemsNode">Nodes to check.</param>179 /// <returns></returns>180 internal static bool IsMamlHelp(string helpFile, XmlNode helpItemsNode)181 {182 Debug.Assert(!string.IsNullOrEmpty(helpFile), "helpFile cannot be null.");183 184 if (helpFile.EndsWith(".maml", StringComparison.OrdinalIgnoreCase))185 return true;186 187 if (helpItemsNode.Attributes == null)188 return false;189 190 foreach (XmlNode attribute in helpItemsNode.Attributes)191 {192 if (attribute.Name.Equals("schema", StringComparison.OrdinalIgnoreCase)193 && attribute.Value.Equals("maml", StringComparison.OrdinalIgnoreCase))194 {195 return true;196 }197 }198 199 return false;200 }201 202 #region private methods203 204 private HelpInfo GetHelpInfoFromHelpFile(DscResourceInfo resourceInfo, string helpFileToFind, Collection<string> searchPaths, bool reportErrors, out string helpFile)205 {206 Dbg.Assert(resourceInfo != null, "Caller should verify that resourceInfo != null");207 Dbg.Assert(helpFileToFind != null, "Caller should verify that helpFileToFind != null");208 209 helpFile = MUIFileSearcher.LocateFile(helpFileToFind, searchPaths);210 211 if (!File.Exists(helpFile))212 return null;213 214 if (!string.IsNullOrEmpty(helpFile))215 {216 // Load the help file only once. Then use it from the cache.217 if (!_helpFiles.Contains(helpFile))218 {219 LoadHelpFile(helpFile, helpFile, resourceInfo.Name, reportErrors);220 }221 222 return GetFromResourceHelpCache(helpFile, Automation.HelpCategory.DscResource);223 }224 225 return null;226 }227 228 /// <summary>229 /// Gets the HelpInfo object corresponding to the command.230 /// </summary>231 /// <param name="helpFileIdentifier">Help file identifier (either name of PSSnapIn or simply full path to help file).</param>232 /// <param name="helpCategory">Help Category for search.</param>233 /// <returns>HelpInfo object.</returns>234 private HelpInfo GetFromResourceHelpCache(string helpFileIdentifier, HelpCategory helpCategory)235 {236 Debug.Assert(!string.IsNullOrEmpty(helpFileIdentifier), "helpFileIdentifier should not be null or empty.");237 238 HelpInfo result = GetCache(helpFileIdentifier);239 240 if (result != null)241 {242 MamlCommandHelpInfo original = (MamlCommandHelpInfo)result;243 result = original.Copy(helpCategory);244 }245 246 return result;247 }248 249 private void LoadHelpFile(string helpFile, string helpFileIdentifier, string commandName, bool reportErrors)250 {251 Exception e = null;252 try253 {254 LoadHelpFile(helpFile, helpFileIdentifier);255 }256 catch (IOException ioException)257 {258 e = ioException;259 }260 catch (System.Security.SecurityException securityException)261 {262 e = securityException;263 }264 catch (XmlException xmlException)265 {266 e = xmlException;267 }268 catch (NotSupportedException notSupportedException)269 {270 e = notSupportedException;271 }272 catch (UnauthorizedAccessException unauthorizedAccessException)273 {274 e = unauthorizedAccessException;275 }276 catch (InvalidOperationException invalidOperationException)277 {278 e = invalidOperationException;279 }280 281 if (e != null)282 s_tracer.WriteLine("Error occurred in DscResourceHelpProvider {0}", e.Message);283 284 if (reportErrors && (e != null))285 {286 ReportHelpFileError(e, commandName, helpFile);287 }288 }289 290 /// <summary>291 /// Load help file for HelpInfo objects. The HelpInfo objects will be292 /// put into help cache.293 /// </summary>294 /// <remarks>295 /// 1. Needs to pay special attention about error handling in this function.296 /// Common errors include: file not found and invalid xml. None of these error297 /// should cause help search to stop.298 /// 2. a helpfile cache is used to avoid same file got loaded again and again.299 /// </remarks>300 private void LoadHelpFile(string helpFile, string helpFileIdentifier)301 {302 Dbg.Assert(!string.IsNullOrEmpty(helpFile), "HelpFile cannot be null or empty.");303 Dbg.Assert(!string.IsNullOrEmpty(helpFileIdentifier), "helpFileIdentifier cannot be null or empty.");304 305 XmlDocument doc = InternalDeserializer.LoadUnsafeXmlDocument(306 new FileInfo(helpFile),307 false, /* ignore whitespace, comments, etc. */308 null); /* default maxCharactersInDocument */309 310 // Add this file into _helpFiles hashtable to prevent it to be loaded again.311 _helpFiles[helpFile] = 0;312 313 XmlNode helpItemsNode = null;314 315 if (doc.HasChildNodes)316 {317 for (int i = 0; i < doc.ChildNodes.Count; i++)318 {319 XmlNode node = doc.ChildNodes[i];320 if (node.NodeType == XmlNodeType.Element && string.Equals(node.LocalName, "helpItems", StringComparison.OrdinalIgnoreCase))321 {322 helpItemsNode = node;323 break;324 }325 }326 }327 328 if (helpItemsNode == null)329 {330 s_tracer.WriteLine("Unable to find 'helpItems' element in file {0}", helpFile);331 return;332 }333 334 bool isMaml = IsMamlHelp(helpFile, helpItemsNode);335 336 using (this.HelpSystem.Trace(helpFile))337 {338 if (helpItemsNode.HasChildNodes)339 {340 for (int i = 0; i < helpItemsNode.ChildNodes.Count; i++)341 {342 XmlNode node = helpItemsNode.ChildNodes[i];343 344 string nodeLocalName = node.LocalName;345 346 bool isDscResource = (string.Equals(nodeLocalName, "dscResource", StringComparison.OrdinalIgnoreCase));347 348 if (node.NodeType == XmlNodeType.Element && isDscResource)349 {350 MamlCommandHelpInfo helpInfo = null;351 352 if (isMaml)353 {354 if (isDscResource)355 helpInfo = MamlCommandHelpInfo.Load(node, HelpCategory.DscResource);356 }357 358 if (helpInfo != null)359 {360 this.HelpSystem.TraceErrors(helpInfo.Errors);361 AddCache(helpFileIdentifier, helpInfo);362 }363 }364 }365 }366 }367 }368 369 #endregion370 }371}372 