<commit_before>﻿using System;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
using SIL.Reporting;

namespace Bloom.Utils
{
	internal class PathTooLongException : System.IO.PathTooLongException
	{
		public string Path;
		public string AdditionalInfo;
		public PathTooLongException(string path, string additionaInfo = "")
		{
			Path = path;
			AdditionalInfo = additionaInfo;
		}
		public override string Message => $"{base.Message} Path was '{Path}. Additional Info:{AdditionalInfo}";
	}

	internal static class LongPathAware
	{
		const int kmaxPath = 255; // not 256 becuase that includes the C++ null terminator

		[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
		static extern uint GetLongPathName(
			[MarshalAs(UnmanagedType.LPTStr)] string lpszShortPath,
			[MarshalAs(UnmanagedType.LPTStr)] StringBuilder lpszLongPath,
			[MarshalAs(UnmanagedType.U4)] int cchBuffer);


		/// <summary>
		/// Converts a short path (8.3) to a long path. We can be given these kinds of paths by
		/// Windows File Explorer when the user double-clicks on a .bloomCollection that has
		/// a really long path.
		/// </summary>
		/// <param name="path">A path that may or may not be an 8.3-style path.</param>
		/// <returns>The long path.  Null or empty if the input is null or empty.</returns>
		internal static string GetLongPath(string path)
		{
			if (String.IsNullOrEmpty(path))
			{
				return path;
			}
			if (!GetIsPossiblyShortenedPath(path))
			{
				return path;
			}

			StringBuilder builder = new StringBuilder(2000);  // randomly chosen max
			var result = Convert.ToInt32(GetLongPathName(path, builder, builder.Capacity));
			if (result == 0)
			{
				throw new FileNotFoundException("The file was not found", path);
			}
			if (result >= builder.Capacity)
			{
				throw new ApplicationException("GetLongPath() exceeded capacity.");
			}

			return builder.ToString(0, result);
		}

		/// <summary>
		/// This doesn't do any fancy regex, so it could give false positives. Which is fine, becuase it's just a time saver.
		/// </summary>
		/// <param name="path"></param>
		/// <returns>True if it is worth asking the OS to expand the path in case it is an 8.3-style one.</returns>
		internal static bool GetIsPossiblyShortenedPath(string path)
		{
			if (path == null) return false;
			return path.Contains("~");<commit_msg>Make Bloom.Utils.LongPathAware methods safe for Linux (BL-11721)<commit_after>﻿using System;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
using SIL.Reporting;

namespace Bloom.Utils
{
	internal class PathTooLongException : System.IO.PathTooLongException
	{
		public string Path;
		public string AdditionalInfo;
		public PathTooLongException(string path, string additionaInfo = "")
		{
			Path = path;
			AdditionalInfo = additionaInfo;
		}
		public override string Message => $"{base.Message} Path was '{Path}. Additional Info:{AdditionalInfo}";
	}

	internal static class LongPathAware
	{
		const int kmaxPath = 255; // not 256 becuase that includes the C++ null terminator

		[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
		static extern uint GetLongPathName(
			[MarshalAs(UnmanagedType.LPTStr)] string lpszShortPath,
			[MarshalAs(UnmanagedType.LPTStr)] StringBuilder lpszLongPath,
			[MarshalAs(UnmanagedType.U4)] int cchBuffer);


		/// <summary>
		/// Converts a short path (8.3) to a long path. We can be given these kinds of paths by
		/// Windows File Explorer when the user double-clicks on a .bloomCollection that has
		/// a really long path.
		/// </summary>
		/// <param name="path">A path that may or may not be an 8.3-style path.</param>
		/// <returns>The long path.  Null or empty if the input is null or empty.</returns>
		internal static string GetLongPath(string path)
		{
			if (String.IsNullOrEmpty(path))
			{
				return path;
			}
			if (SIL.PlatformUtilities.Platform.IsLinux)
			{
				return path;
			}
			if (!GetIsPossiblyShortenedPath(path))
			{
				return path;
			}

			StringBuilder builder = new StringBuilder(2000);  // randomly chosen max
			var result = Convert.ToInt32(GetLongPathName(path, builder, builder.Capacity));
			if (result == 0)
			{
				throw new FileNotFoundException("The file was not found", path);
			}
			if (result >= builder.Capacity)
			{
				throw new ApplicationException("GetLongPath() exceeded capacity.");
			}

			return builder.ToString(0, result);
		}

		/// <summary>
		/// This doesn't do any fancy regex, so it could give false positives. Which is fine, becuase it's just a time saver.
		/// </summary>
		/// <param name="path"></param>
		/// <returns>True if it is worth asking the OS to expand the path in case it is an 8.3-style one.</returns>
		internal static bool GetIsPossiblyShortenedPath(string path)
		{
			if (path == null) return false;
			if (SIL.PlatformUtilities.Platform.IsLinux) return false;
			return path.Contains("~");
{'fraction_non_alphanumeric': 0.11140315406389001, 'fraction_numerical': 0.010311362717347351, 'mean_word_length': 3.606145251396648, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6628276', 'n_tokens_mistral': 1711, 'n_tokens_neox': 1477, 'n_words': 493}