Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Various improvements to ZTUpdater. Got rid of Html2Markdown, ported own poor man’s HtmlToText to C#, which appears to work better for this forum. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | develop |
Files: | files | file ages | folders |
SHA1: |
56d8711cc98c7851b7dc908d469e985d |
User & Date: | tinus 2018-03-12 02:28:55.367 |
Context
2018-03-21
| ||
19:53 | Added options for archive and backup directories. check-in: 16163e0aca user: tinus tags: develop | |
2018-03-12
| ||
02:28 | Various improvements to ZTUpdater. Got rid of Html2Markdown, ported own poor man’s HtmlToText to C#, which appears to work better for this forum. check-in: 56d8711cc9 user: tinus tags: develop | |
00:30 | Added C# project for automatic updater for ZTreeWin. check-in: 355de8cbb4 user: tinus tags: develop | |
Changes
Changes to ZTUpdater/Program.cs.
1 2 3 4 5 6 7 8 | using System; using System.Diagnostics; using System.Globalization; namespace ZTUpdater { class Program { | > | 1 2 3 4 5 6 7 8 9 | using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; namespace ZTUpdater { class Program { |
︙ | ︙ | |||
16 17 18 19 20 21 22 | if (arg == "/?" || arg.StartsWith("/h", true, CultureInfo.CurrentCulture)) { ShowHelp(); return 0; } } | | | > > | < < < > > > > > > | > > > | | > | > > > > > > > > > > > > > > > > > | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | if (arg == "/?" || arg.StartsWith("/h", true, CultureInfo.CurrentCulture)) { ShowHelp(); return 0; } } var Updater = new ZTWUpdater() { OnLog = WriteLog }; if (Updater.ZTreeHome == null) { // ask for folder (using dialog?) var Dialog = new System.Windows.Forms.FolderBrowserDialog { SelectedPath = @"C:\ZTree", ShowNewFolderButton = false, Description = "Please select the folder where ZTreeWin is installed" }; if (Dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) Updater.ZTreeHome = Dialog.SelectedPath; } var UpdateTask = Updater.Update(); UpdateTask.Wait(); return UpdateTask.Result ? 0 : 1; } catch (Exception ex) { var BackupColor = Console.ForegroundColor; Console.ForegroundColor = ConsoleColor.Magenta; Console.Error.WriteLine(ex.Message); Console.ForegroundColor = BackupColor; return 255; } } private static void ShowHelp() { throw new NotImplementedException(); } private static Dictionary<TraceLevel, ConsoleColor> _levelColors = new Dictionary<TraceLevel, ConsoleColor>(); private static void WriteLog(string message, TraceLevel level) { if (_levelColors.Count == 0) { _levelColors.Add(TraceLevel.Verbose, ConsoleColor.DarkGray); _levelColors.Add(TraceLevel.Warning, ConsoleColor.Yellow); _levelColors.Add(TraceLevel.Error, ConsoleColor.Red); } var Output = level >= TraceLevel.Warning ? Console.Error : Console.Out; var BackupColor = Console.ForegroundColor; if (_levelColors.TryGetValue(level, out var Color)) Console.ForegroundColor = Color; Output.WriteLine(message); Console.ForegroundColor = BackupColor; } } } |
Changes to ZTUpdater/ZTUpdater.cs.
︙ | ︙ | |||
11 12 13 14 15 16 17 | namespace ZTUpdater { public class ZTWUpdater { public Action<string, TraceLevel> OnLog = (msg, level) => Debug.WriteLine(msg, level.ToString()); | < | | | | > > > > > | > > | > > > > | > | < | | | > | | | > > | | | < < | | | | < < < < < < < < < < < < < | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | namespace ZTUpdater { public class ZTWUpdater { public Action<string, TraceLevel> OnLog = (msg, level) => Debug.WriteLine(msg, level.ToString()); public string Executable { get; set; } = Environment.Is64BitOperatingSystem ? "ZTW64.exe" : "ZTW.exe"; public string CurrentVersion { get; private set; } public string LatestVersion { get; private set; } public string WhatsNew { get; private set; } private string _ZTreeHome; public ZTWUpdater() : this(GetZTreeHome()) { } public ZTWUpdater(string ztreeHome) { ZTreeHome = ztreeHome; } public string ZTreeHome { get { return _ZTreeHome; } set { _ZTreeHome = value; if (_ZTreeHome != null) { Executable = Path.Combine(ZTreeHome, Executable); // determine version of local executable CurrentVersion = FileVersionInfo.GetVersionInfo(Executable).FileVersion; } } } protected void Log(string message, TraceLevel level = TraceLevel.Verbose) { OnLog(message, level); } public static string GetZTreeHome() { // from command-line arguments bool Found = false; foreach (var Arg in Environment.GetCommandLineArgs()) { if (Found) return Arg; else if (Arg == "/home") Found = true; } // from the environment string ZTHome = Environment.GetEnvironmentVariable("#ZTHome"); if (Directory.Exists(ZTHome)) return ZTHome; // from registry (both Win32 and Win64) var InstallDir = Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\ZTreeWin", "Install_Dir", null); if (InstallDir is string && Directory.Exists(ZTHome = (string)InstallDir)) return ZTHome; InstallDir = Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\ZTreeWin", "Install_Dir", null); if (InstallDir is string && Directory.Exists(ZTHome = (string)InstallDir)) return ZTHome; return null; } private Regex _rexZetaAvailable = new Regex(@"forum_entry.php\?id=(\d+)""[^\<]*?v(\d+\.\S+) Now Available", RegexOptions.Compiled | RegexOptions.IgnoreCase); private Regex _rexWhatsNew = new Regex(@"<div class=""posting"">(.*?)</div><p class=""signature"">", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline); private Regex _rexLink = new Regex(@"""(https?://www.z(edtek|tree).com\/[^""]*\.(zip|exe))""", RegexOptions.Compiled | RegexOptions.IgnoreCase); |
︙ | ︙ | |||
117 118 119 120 121 122 123 | // extract the "What’s New" Match = _rexWhatsNew.Match(Html); if (Match.Success) { WhatsNew = Match.Groups[1].Value; try { | | | | | 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | // extract the "What’s New" Match = _rexWhatsNew.Match(Html); if (Match.Success) { WhatsNew = Match.Groups[1].Value; try { WhatsNew = Utils.HtmlToText(WhatsNew); } catch { // ignore errors, just use the HTML instead } Log($"What’s New in v{LatestVersion}:" + Environment.NewLine + WhatsNew, TraceLevel.Info); } // extract the download link Match = _rexLink.Match(Html); if (!Match.Success) { Log($"No download link found for version {LatestVersion}!", TraceLevel.Warning); return null; } // return the link Log("Download link found: " + Match.Groups[1].Value); return new Uri(Match.Groups[1].Value); } } public bool IsVersionNewer(string newVersion, string oldVersion) { var New = new Version(newVersion); var Old = new Version(oldVersion); return newVersion.CompareTo(oldVersion) > 0; } public async Task<bool> Update() { Log($"ZTreeWin version {CurrentVersion} located in \"{ZTreeHome}\".", TraceLevel.Info); var DownloadUri = await GetUriLatestVersion(); |
︙ | ︙ | |||
171 172 173 174 175 176 177 | int Counter = 0; while (File.Exists(ZipFile)) { Counter++; ZipFile = Base + $" ({Counter})" + Extension; } | | | 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | int Counter = 0; while (File.Exists(ZipFile)) { Counter++; ZipFile = Base + $" ({Counter})" + Extension; } Log($@"Downloading update file to ""{OrgZipFile}""..."); using (var Client = new HttpClient()) { var ZipStream = await Client.GetStreamAsync(DownloadUri); using (var FileStream = new FileStream(ZipFile, FileMode.CreateNew, FileAccess.Write, FileShare.Read)) { await ZipStream.CopyToAsync(FileStream); } |
︙ | ︙ | |||
211 212 213 214 215 216 217 | foreach(var Entry in Zip.Entries) { var File = new FileInfo(Path.Combine(ZTreeHome, Entry.FullName.Replace('/', '\\'))); if (File.Exists) { if (Identical(Entry, File)) { | | | | | 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 | foreach(var Entry in Zip.Entries) { var File = new FileInfo(Path.Combine(ZTreeHome, Entry.FullName.Replace('/', '\\'))); if (File.Exists) { if (Identical(Entry, File)) { Log($"\t{Entry.FullName}\tidentical to existing file; skipping."); continue; } // rename as backup for current version var Extension = File.Extension; var BackupName = File.FullName.Substring(0, File.FullName.Length - Extension.Length) + "-v" + CurrentVersion + Extension; Log($"\t{Entry.FullName}\tbacking up existing file."); File.MoveTo(BackupName); } Log($"\t{Entry.FullName}\textracting..."); Entry.ExtractToFile(File.FullName); } } private bool Identical(ZipArchiveEntry Entry, FileInfo File) { if (Entry.Length != File.Length) |
︙ | ︙ | |||
257 258 259 260 261 262 263 | AtEndOfB = b.Read(BufferB, 0, BufferSize) == 0; if (!BufferA.SequenceEqual(BufferB)) return false; } return (AtEndOfA == AtEndOfB); } } | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 | AtEndOfB = b.Read(BufferB, 0, BufferSize) == 0; if (!BufferA.SequenceEqual(BufferB)) return false; } return (AtEndOfA == AtEndOfB); } } static class Utils { public static string HtmlToText(string Html) { // Normalize whitespace var Text = Regex.Replace(Html, @"\s+", " "); // Convert some simple formatting tags Text = Regex.Replace(Text, @"</?(b|strong)\b[^>]*>", "**", RegexOptions.IgnoreCase); Text = Regex.Replace(Text, @"</?(i|em)\b[^>]*>", "/", RegexOptions.IgnoreCase); Text = Regex.Replace(Text, @"</?(u)\b[^>]*>", "_", RegexOptions.IgnoreCase); Text = Regex.Replace(Text, @"</?(br|p)\b[^>]*>", "\r\n", RegexOptions.IgnoreCase); Text = Regex.Replace(Text, @"</?(hr)\b[^>]*>", "\r\n--------------------\r\n", RegexOptions.IgnoreCase); Text = Regex.Replace(Text, @"(\r\n|\r|\n)<li\b[^>]*>", "\r\n- ", RegexOptions.IgnoreCase); // get rid of all other HTML tags Text = Regex.Replace(Text, @"<[^>]*>", ""); // decode all HTML entities var rexEntity = new Regex(@"&(#x?[0-9a-f]{1,4}|\w{2,8});", RegexOptions.Compiled | RegexOptions.IgnoreCase); Match Match; int StartAt = 0; while ((Match = rexEntity.Match(Text, StartAt)).Success) { var Entity = Match.Groups[1].Value; var Replacement = "?"; if (Entity.StartsWith("#")) { int CharCode = 0; if (Entity.Substring(1, 1) == "x") // hexadecimal char code CharCode = int.Parse(Entity.Substring(2), System.Globalization.NumberStyles.HexNumber); else // decimal char code CharCode = int.Parse(Entity.Substring(1)); Replacement = Char.ConvertFromUtf32(CharCode); } else { switch (Entity) { case "amp": Replacement = "&"; break; case "lt": Replacement = "<"; break; case "gt": Replacement = ">"; break; case "quot": Replacement = "\""; break; case "apos": Replacement = "'"; break; default: // if we don't recognize this entity, just pick its first character Replacement = Entity.Substring(0, 1); break; } } Text = Text.Substring(0, Match.Index) + Replacement + Text.Substring(Match.Index + Match.Length); StartAt = Match.Index + Replacement.Length; } return Text; } } } |
Changes to ZTUpdater/ZTUpdater.csproj.
︙ | ︙ | |||
28 29 30 31 32 33 34 | <Optimize>true</Optimize> <OutputPath>bin\Release\</OutputPath> <DefineConstants>TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> </PropertyGroup> <ItemGroup> | < < < < < < | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | <Optimize>true</Optimize> <OutputPath>bin\Release\</OutputPath> <DefineConstants>TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> </PropertyGroup> <ItemGroup> <Reference Include="Syroot.Windows.IO.KnownFolders, Version=1.0.2.0, Culture=neutral, processorArchitecture=MSIL"> <HintPath>packages\Syroot.Windows.IO.KnownFolders.1.0.2\lib\net40\Syroot.Windows.IO.KnownFolders.dll</HintPath> </Reference> <Reference Include="System" /> <Reference Include="System.Core" /> <Reference Include="System.IO.Compression" /> <Reference Include="System.IO.Compression.FileSystem" /> |
︙ | ︙ |
Changes to ZTUpdater/packages.config.
1 2 | <?xml version="1.0" encoding="utf-8"?> <packages> | < < | 1 2 3 4 | <?xml version="1.0" encoding="utf-8"?> <packages> <package id="Syroot.Windows.IO.KnownFolders" version="1.0.2" targetFramework="net461" /> </packages> |