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
};
}
}
}