using System; using System.Globalization; using System.Text.RegularExpressions; namespace NcProgramManager.Cnc.Mazak { /// /// Parsing puro (senza I/O) delle righe LIST/ListDirectoryDetails FTP dei /// controlli Mazak Smooth/Matrix. Supporta formato Unix (drwx...) e DOS/IIS /// (MM-DD-YY ... <DIR>), piu' fallback NLST (solo nome). /// internal static class FtpListParser { /// Assicura path con slash iniziale e finale per uso come directory. public static string NormalizeDir(string path) { string dir = string.IsNullOrEmpty(path) ? "/" : path; if (!dir.StartsWith("/")) dir = "/" + dir; if (!dir.EndsWith("/")) dir += "/"; return dir; } // DOS/IIS: "07-18-26 10:00AM NAME" // "07-18-26 10:00AM 1234 FILE.EIA" private static readonly Regex _dosLine = new Regex( @"^\d{2}-\d{2}-\d{2,4}\s+\d{1,2}:\d{2}\s*(AM|PM)?\s+(?|\d+)\s+(?.+)$", RegexOptions.IgnoreCase); // Whitespace chars used to detect multi-token (non-NLST) lines in fallback. private static readonly char[] _whitespace = { ' ', '\t', '\v', '\f' }; // Unix: "drwxr-xr-x 1 owner group 4096 Jul 18 10:00 NAME" private static readonly Regex _unixLine = new Regex( @"^(?[dl\-][rwxsStT\-]{9})\s+\d+\s+\S+\s+\S+\s+(?\d+)\s+\S+\s+\S+\s+\S+\s+(?.+)$"); /// Parsa una riga LIST. Ritorna null se non riconosciuta o '.'/'..'. public static FtpEntry Parse(string line, string dir) { if (string.IsNullOrWhiteSpace(line)) return null; string name = null; bool isDir = false; long size = -1; var dos = _dosLine.Match(line); if (dos.Success) { name = dos.Groups["name"].Value.Trim(); if (string.Equals(dos.Groups["dir"].Value, "", StringComparison.OrdinalIgnoreCase)) isDir = true; else long.TryParse(dos.Groups["dir"].Value, out size); } else { var unix = _unixLine.Match(line); if (unix.Success) { name = unix.Groups["name"].Value.Trim(); isDir = unix.Groups["perm"].Value[0] == 'd'; if (!isDir) long.TryParse(unix.Groups["size"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out size); } else { // Fallback: NLST-style (bare name only). Only a single token // (no internal whitespace) is a real name-only listing; a line // with internal whitespace that matched neither regex is an // unrecognized header/garbage -> skip (avoid whole-line-as-name). string trimmed = line.Trim(); if (trimmed.IndexOfAny(_whitespace) >= 0) return null; name = trimmed; } } if (string.IsNullOrEmpty(name) || name == "." || name == "..") return null; return new FtpEntry { Name = name, FullPath = dir + name, IsDirectory = isDir, Size = size, Raw = line }; } } }