using System; using System.IO; using System.Data; using System.Text.RegularExpressions; using HtmlHelp; using HtmlHelp.ChmDecoding; using TechBot.Library; namespace TechBot.Commands.MSDN { [Command("api", Help = "!api ")] public class ApiCommand : Command { private bool IsVerbose = false; private HtmlHelpSystem chm; public ApiCommand() { LoadCHM(); } [CommandParameter("api", "The API name")] public string API { get { return Parameters; } set { Parameters = value; } } private void WriteIfVerbose(string message) { if (IsVerbose) Say(message); } private void LoadCHM() { string CHMFilename = Path.Combine(Settings.Default.ChmPath, Settings.Default.MainChm); chm = new HtmlHelpSystem(); chm.OpenFile(CHMFilename, null); Console.WriteLine(String.Format("Loaded main CHM: {0}", Path.GetFileName(CHMFilename))); foreach (string filename in Directory.GetFiles(Settings.Default.ChmPath)) { if (!Path.GetExtension(filename).ToLower().Equals(".chm")) continue; if (Path.GetFileName(filename).ToLower().Equals(Settings.Default.MainChm)) continue; Console.WriteLine(String.Format("Loading CHM: {0}", Path.GetFileName(filename))); try { chm.MergeFile(filename); } catch (Exception ex) { Console.WriteLine(String.Format("Could not load CHM: {0}. Exception {1}", Path.GetFileName(filename), ex)); } } Console.WriteLine(String.Format("Loaded {0} CHMs", chm.FileList.Length)); } public override void ExecuteCommand() { if (Name.Trim().Equals(String.Empty)) { Say("Please give me a keyword."); } else { Search(Name); } } private bool SearchIndex( string keyword) { if (chm.HasIndex) { IndexItem item = chm.Index.SearchIndex(keyword, IndexType.KeywordLinks); if (item != null && item.Topics.Count > 0) { WriteIfVerbose(String.Format("Keyword {0} found in index", item.KeyWord)); IndexTopic indexTopic = item.Topics[0] as IndexTopic; return DisplayResult( keyword, indexTopic); } else { WriteIfVerbose(String.Format("Keyword {0} not found in index", keyword)); return false; } } else return false; } private void SearchFullText(string keyword) { string sort = "Rating ASC"; WriteIfVerbose(String.Format("Searching fulltext database for {0}", keyword)); bool partialMatches = false; bool titlesOnly = true; int maxResults = 100; DataTable results = chm.PerformSearch(keyword, maxResults, partialMatches, titlesOnly); WriteIfVerbose(String.Format("results.Rows.Count = {0}", results != null ? results.Rows.Count.ToString() : "(none)")); if (results != null && results.Rows.Count > 0) { results.DefaultView.Sort = sort; if (!DisplayResult(keyword, results)) { Say("No result"); } } else { Say("No result"); } } private void Search(string keyword) { if (!SearchIndex(keyword)) SearchFullText(keyword); } private bool DisplayResult(string keyword, IndexTopic indexTopic) { keyword = keyword.Trim().ToLower(); string url = indexTopic.URL; WriteIfVerbose(String.Format("URL from index search {0}", url)); string prototype = ExtractPrototype(url); if (prototype == null || prototype.Trim().Equals(String.Empty)) return false; string formattedPrototype = FormatPrototype(prototype); Say(formattedPrototype); return true; } private bool DisplayResult(string keyword, DataTable results) { keyword = keyword.Trim().ToLower(); for (int i = 0; i < results.DefaultView.Count; i++) { DataRowView row = results.DefaultView[i]; string title = row["Title"].ToString(); WriteIfVerbose(String.Format("Examining {0}", title)); if (title.Trim().ToLower().Equals(keyword)) { string location = row["Location"].ToString(); string rating = row["Rating"].ToString(); string url = row["Url"].ToString(); string prototype = ExtractPrototype(url); if (prototype == null || prototype.Trim().Equals(String.Empty)) continue; string formattedPrototype = FormatPrototype(prototype); Say(formattedPrototype); return true; } } return false; } private void DisplayNoResult(MessageContext context, string keyword) { TechBot.ServiceOutput.WriteLine(context, String.Format("I don't know about keyword {0}", keyword)); } private string ReplaceComments(string s) { return Regex.Replace(s, "//(.+)\r\n", ""); } private string ReplaceLineEndings(string s) { return Regex.Replace(s, "(\r\n)+", " "); } private string ReplaceSpaces(string s) { return Regex.Replace(s, @" +", " "); } private string ReplaceSpacesBeforeLeftParenthesis(string s) { return Regex.Replace(s, @"\( ", @"("); } private string ReplaceSpacesBeforeRightParenthesis(string s) { return Regex.Replace(s, @" \)", @")"); } private string ReplaceSemicolon(string s) { return Regex.Replace(s, @";", @""); } private string FormatPrototype(string prototype) { string s = ReplaceComments(prototype); s = ReplaceLineEndings(s); s = ReplaceSpaces(s); s = ReplaceSpacesBeforeLeftParenthesis(s); s = ReplaceSpacesBeforeRightParenthesis(s); s = ReplaceSemicolon(s); return s; } private string ExtractPrototype(string url) { string page = GetPage(url); Match match = Regex.Match(page, "
(.+)
", RegexOptions.Multiline | RegexOptions.Singleline); if (match.Groups.Count > 1) { string prototype = match.Groups[1].ToString(); return StripHtml(StripAfterSlashPre(prototype)); } return ""; } private string StripAfterSlashPre(string html) { int index = html.IndexOf(""); if (index != -1) { return html.Substring(0, index); } else return html; } private string StripHtml(string html) { return Regex.Replace(html, @"<(.|\n)*?>", String.Empty); } private string GetPage(string url) { string CHMFileName = ""; string topicName = ""; string anchor = ""; CHMStream.CHMStream baseStream; if (!chm.BaseStream.GetCHMParts(url, ref CHMFileName, ref topicName, ref anchor)) { baseStream = chm.BaseStream; CHMFileName = baseStream.CHMFileName; topicName = url; anchor = ""; } else { baseStream = GetBaseStreamFromCHMFileName(CHMFileName); } if ((topicName == "") || (CHMFileName == "") || (baseStream == null)) { return ""; } return baseStream.ExtractTextFile(topicName); } private CHMStream.CHMStream GetBaseStreamFromCHMFileName(string CHMFileName) { foreach (CHMFile file in chm.FileList) { WriteIfVerbose(String.Format("Compare: {0} <> {1}", file.ChmFilePath, CHMFileName)); if (file.ChmFilePath.ToLower().Equals(CHMFileName.ToLower())) { return file.BaseStream; } } WriteIfVerbose(String.Format("Could not find loaded CHM file in list: {0}", CHMFileName)); return null; } } }