/*

CLASS {File_path|_C_sharp}, NATIVE, UNTESTED, STOPPING :

IMPORT: {String_functions}, {System_functions}, {Hardware} ;
IMPORT, ANYWAY: {Date_time} ;
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

/*
NATIVE: "using System.IO;" .
NATIVE: "using System.Runtime.InteropServices;" . 
NATIVE: "using System.Globalization;" .
*/
using System.IO;
using AL4;

namespace AL4
{
	
        
   public class File_path
   {
        /*
        
        FUN Delete_file|_or_directory(STR File|_name_and_path) :
			BOOL is_dir|ectory = File.Dir_exists
			CASE !is_dir && File.[0] != "\" ?
				 STR path|_only = File.Extract_path  
	             CASE (path != "" && !path.Directory_exists) 
	             	  || !File.Exists 
	             	  ? 
	                  ==> ;
			;
            CASE TEST && "test" !IN File.Lower ?
                 STOP "In test mode, only TEST files can be deleted" ;
            CASE is_dir ? File.delete_dir
            ELSE File.delete ; .

        FUN delete|_file(STR Path|_file), NATIVE :
           @"try {"
            "   File.Delete(X_Path);"
            "} catch(Exception ed) {}" 
            "if (X_Path.Length < 260) return;"
            "Methods_C_File_path.DeleteFileW(@" #QU"\\?\"#QU " + X_Path);" .
        
        FUN delete_dir|ectory(STR Path|_directory), NATIVE :
           @"try {"
            "   Directory.Delete(X_Path, true);"
            "} catch {"
            "}" .
            
        FUN Can_delete_dir_not_empty ==> BOOL :
            = TRUE .

        FUN Recycle_file|_or_directory
        	|_or_several_ones_semicolon_separated(
        	STR File|_name_and_path)
            :
            CASE TEST && "test" !IN File.Lower ?
                 STOP "In test mode, only TEST files can be deleted" ;
            recycle(File.Replace_all(";", #0) #0 #0) .
            
        FUNCTION Remove_file|_or_directory
        |_or_several_onesemicolon_separated(
        STR File|_name_and_path) 
        	:
            CASE TEST && "test" !IN File.Lower ?
                 STOP "In test mode, only TEST files can be deleted" ;
            rem_files(File.Replace_all(";", #0) #0 #0) .
            
        FUNCTION Exists|_file(STR Path|_of_file) ==> BOOL :
            CASE Path.Trim IN ["" , #QU#QU] ? ==> ;
            STR p|ath_clean = Path.Replace_all(Path_sep Path_sep, Path_sep)
            CASE p.[0]!="\" ? 
                 RESULT = do_Exists(p.Extract_path, p.Extract_file_name) 
            ELSE {file_detail} fd|etails = File_detail(Path)
            	 RESULT = fd.Size > 0 || fd.Modified > 1 ; .
            
   NATIVE: @
   ""
  ''[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]''
   "internal static extern int GetFileAttributesW(string lpFileName);"
  ''[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]''
   "internal static extern bool DeleteFileW(string lpFileName);"
   .
		            
        FUN do_Exists(STR Path|_only, STR Filename|_only) ==> BOOL, NATIVE :
        	STR pf|_path_pluf_filename = Path Filename
            //FOR i IN [0 TO pf.Len-1] :
            //    CASE pf.[i].Ord >= 256 ? //BOOL has_unicode|_symbols = TRUE ;
            //         RESULT = TRUE ==> ;
            //;
        	NATIVE @
        	"try {"
            "    if (File.Exists(X_pf)) return true;"
            "	 if (File.Exists(System_functions.Short_path(X_pf)))"
            " 		 return true;"     
            "	 if (X_Path != " #QU#QU " && X_Path[0] != '\\'"
            " 		 && X_pf.Length > 260) {"
            "	 		int a = Methods_C_File_path.GetFileAttributesW("
            "					@" #QU"\\?\"#QU " + "
            "						X_Path + X_Filename);"
            "	 		if(a != -1) return true;"     
            "	 }"
        	"}"
        	"catch { }" .
            
   */
   public static bool Exists(string Path)
   {
       if (File.Exists(Path)) return true;
       try
       {
           string[] ff = Directory.GetFiles(System.IO.Path.GetDirectoryName(Path));
           Path = Path.ToLower();
           for (int i = ff.Length - 1; i >= 0; i--)
           {
               string s = ff[i].ToLower();
               if (s == Path) return true;
           }
       }
       catch { }
       return false;
   }
   
   /*
            
    NATIVE: @
"[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto, Pack=1)]"
"public struct SHFILEOPSTRUCT"
"{"
"         public IntPtr hwnd;"
"         [MarshalAs(UnmanagedType.U4)] public int wFunc;"
"         public string pFrom;"
"         public string pTo;"
"         public short fFlags;"
"         [MarshalAs(UnmanagedType.Bool)] public bool fAnyOperationsAborted;"
"         public IntPtr hNameMappings;"
"         public string lpszProgressTitle;"
"}"
""
''[DllImport("shell32.dll", CharSet=CharSet.Auto)]''
"public static extern int SHFileOperation(ref SHFILEOPSTRUCT FileOp);"
		.
            
        FUN recycle|_file(STR Path|_file), NATIVE : @
"int FO_DELETE = 3;"
"int FOF_NO_UI = 1556;"       
"short FOF_ALLOWUNDO = 0x40;"
"short FOF_NOCONFIRMATION = 0x10;"
           @"try {"
			"var shf = new Methods_C_File_path.SHFILEOPSTRUCT();"
			"shf.wFunc = FO_DELETE;"
			"shf.fFlags = (short)(FOF_ALLOWUNDO|FOF_NOCONFIRMATION|FOF_NO_UI);"
			"shf.pFrom = X_Path;"
			"Methods_C_File_path.SHFileOperation(ref shf);"
            "} catch {"
            "}" .
            
        FUN rem_files(STR Path|_file), NATIVE : @
"int FO_DELETE = 3;"
"int FOF_NO_UI = 1556;"     
"short FOF_ALLOWUNDO = 0x40;"
"short FOF_NOCONFIRMATION = 0x10;"
           @"try {"
			"var shf = new Methods_C_File_path.SHFILEOPSTRUCT();"
			"shf.wFunc = FO_DELETE;"
			"shf.fFlags = (short)(FOF_NOCONFIRMATION|FOF_NO_UI);"
			"shf.pFrom = X_Path;"
			"Methods_C_File_path.SHFileOperation(ref shf);"
            "} catch {"
            "}" .
            
        FUN Rename|_file|_or_directory(
        STR Path|_file, STR New|_name)
        	:
			BOOL file|_present = Path.Exists
			BOOL dir|ectory = Path.Dir_exists  
            CASE !file && !dir ?
                 //DEBUG: << "file_present=" file.S " dir_present=" dir.S #NL ;
                 ==> ;
            CASE TEST && ("test" !IN Path.Lower || "test" !IN New.Lower)?
                 STOP "In TEST mode only TEST files can be renamed "
                      "and only to TEST places"
            ;
            CASE file ? 
                 rename_file(Path, New)
            ELSE rename_dir(Path, New) ; .

        FUN rename_file(STR Path|_file, STR New|_name), NATIVE :
        	BOOL only_reg|istry_of_letters_changing = Path.Lower == New.Lower
        	NATIVE @
        	"try {"
            "    if (!File.Exists(X_Path)) {"
                  //''AL4_system.WriteLine("source !exists: " + X_Path);''
            "		return;"
            "	 }"
            "    if (!X_only_reg && File.Exists(X_New)) File.Delete(X_New);"
            "    if (File.Exists(X_Path)) File.Move(X_Path, X_New);"
            "} catch(Exception e) {"
              //''AL4_system.WriteLine("exception: " + e.Message +''
              //''" [" + X_Path + "]-->[" + X_New + "]" );''
            "}" .
        
        
        FUN rename_dir(STR Path|_file, STR New|_name), NATIVE :
           @"try {"
            "    if (!Directory.Exists(X_Path)) return;"
            "    if (Directory.Exists(X_New)) return;"
            "    if (Directory.Exists(X_Path)) Directory.Move(X_Path, X_New);"
            "} catch {"
            "}" .
        
        
        FUN Move_file(STR Path|_file, STR New|_path) :
            Rename(Path, New) .

        FUN Move_file_direct|ly_no_checks(
        STR Path|_file, STR New|_path), NATIVE :
           @"try {"
            "    File.Move(X_Path, X_New);"
            "} catch {"
            "}" .

        FUN Copy_file(STR S|ource_file_path, STR D|estination) :
            CASE !S.Exists ? ==> ;
            CASE !D.Extract_path.Directory_exists ? ==> ;
            Stop_if_test(S)
            Stop_if_test(D)
            do_copy_file(S, D) .

        FUN do_copy_file(STR S|ource_file_path, STR D|estination_path),
                NATIVE :
           @"    try {"
            "        File.Delete(X_D);"
            "        File.Copy(X_S, X_D);"
            "    } catch { }" .
            
        FUNCTION Directory_exists||Dir_exists(
        STR Path|_of_dir) ==> BOOL, NATIVE :
	        CASE Path.[1]==":" && Path.Trailing_slash.Len==3
	             && Get_drive_type(Path.[0])=='REMOTE' 
	             ? RESULT = TRUE ==> ;
	        NATIVE @
            "    RESULT = Directory.Exists(X_Path);" .
        
        
        FUNCTION Create_dir|ectory(
        STR Path|_to_new_dir), NATIVE : @
           ""
         ''if (X_Path == "") return;''
          "try {"
          "    Directory.CreateDirectory(X_Path);"
          "}"
          "catch { }"
           .
           
        FUN Catalog|ue_of_directory( STR Dir|ectory_path,
                STR Cat|alogue_scanned[]), NATIVE :
			Cat[].Clear
   			STR dir|ectory = Dir.Trailing_slash
   			CASE !dir.Dir_exists ? ==> ;
   			NATIVE @
   			"    try"
		    "	 {"
		    "    	string[] dirs = Directory.GetDirectories(X_dir);"
            "    	string[] files = Directory.GetFiles(X_dir, "#QU"*.*"#QU","
            "        	SearchOption.TopDirectoryOnly);"
            "    	foreach (string d in dirs)"
            "        	X_Cat.Add(Path.GetFileName(d) + "#QU"\\"#QU");"
            "    	foreach (string f in files)"
            "        	X_Cat.Add(Path.GetFileName(f));"
			"	 }"
			"	 catch (UnauthorizedAccessException UE)"
			"	 {"
			"	 }"     
			"	 catch (Exception E)"
			"	 {"
			"			AL4_system.Any_exception(E);"
			"	 }" .
			
        FUN Get_cat|alog|ue|_of_path(
        STR Cat|alogue_scanned[], STR Path|_of_directory)
        :
		Catalog(Path, Cat[]) .
            
        //ENUM {file_attr|ibutes} :
        //    'READONLY   |_FILE',
        //    'HIDDEN     |_FILE_DIRECTORY',
        //    'SYSTEM     |_FILE_DIRECTORY',
        //    'DIRECTORY  |_NOT_A_FILE',
        //    'ARCHIVE    |_FILE',
        //    'NORMAL     |_FILE',
        //    'TEMPORARY  |_FILE',
        //    'COMPRESSED |_FILE',
        //    'OFFLINE    |_FILE',
        //    'RESERVE_1',
        //    'RESERVE_2' .
            
        CONST INT {file_attr|ibutes} : 
	         READ_ONLY		= 1 
	         HIDDEN|_FILE	= 2
	         SYSTEM|_FILE	= 4
	         DIRECTORY		= 16
	         ARCHIVE|_ATTR	= 32
	         NORMAL |_ATTR	= 128
	         TEMPORARY		= 256
	         COMPRESSED		= 0x800
	         OFFLINE|_ATTR	= 0x1000
	         RESERVE_1		= 8
	         RESERVE_2		= 64
	         .
        STRUCTURE {file_detail|ed_info} :
            //BOOL Attr|ib|utes[{file_attr}]
            INT Attrib|utes
            REAL Size|_in_bytes
            REAL Creation|_date_time
            REAL Modified|_date_time
            REAL Accessed|_date_time .
            
        FUN Catalog_detailed( STR Dir|ectory_path,
            STR Cat|alogue_scanned[], {file_detail} Det|ails_for_files[])
            :
			Cat[].Clear
			Det[].Clear
			Catalog(Dir, Cat[])
			STR path|_trailed_slash = Dir.Trailing_slash
			--------------------- 'getting details on each file found'
			FOR s IN Cat[] :
				Det[] << File_detail(path s.Remove_trailing(Path_separator))
			; .

				
        FUNCTION File_detail|ed_info(
        	STR Path|_to_file ) ==> {file_detail}
        	:
			//RESULT = {file_detail}(File_name = Path.Extract_file_name)
            CASE Path == "" ? ==> ;
            INT attr|ibutes[]
            REAL times|_of_file[]
            RESULT.Size = native_File_details(Path, attr[], times[])
            -------------------------------- 'times'
            RESULT.Creation = times[0]
            RESULT.Modified = times[1]
            RESULT.Accessed = times[2]
            -------------------------------- 'attribs'
            RESULT.Attrib = attr[0] 
            .
            //do_File_detail(RESULT, Path) .
        FUN native_File_details(STR Path|_of_file,
        	INT attr|ibutes_put_in_0[], REAL times|_of_file[]) ==> REAL, NATIVE:
        	CASE "*" IN Path || "?" IN Path ? ==> ;
		NATIVE : @
        "FileInfo fi = null;"
        "try {"
        "	fi = new FileInfo(X_Path);"
        "} catch { }"
        "if (fi != null) {"
        "    try {"
        "		 FileAttributes fa = fi.Attributes;"
        "		 if ((int)fa != -1)"
        "		 {"
        "		     int a = 0;"
        "            if ((fa & FileAttributes.ReadOnly) != 0) a += 1;"
        "            if ((fa & FileAttributes.Hidden) != 0) a += 2;"
        "            if ((fa & FileAttributes.System) != 0) a += 4;"
        "            if ((fa & FileAttributes.Directory) != 0) a += 16;"
        "            if ((fa & FileAttributes.Archive) != 0) a += 32;"
        "            if ((fa & FileAttributes.Normal) != 0) a += 128;"
        "            if ((fa & FileAttributes.Temporary) != 0) a += 256;"
        "            if ((fa & FileAttributes.Compressed)!= 0) a += 0x800;"
        "            if ((fa & FileAttributes.Offline) != 0) a += 0x1000;"
        "		     X_attr.Add(a);"
        "        	 if ((fa & FileAttributes.Directory) == 0)"
        "            	RESULT = fi.Length;"
        "		 }"
        "    } catch { }"
        "	 try {"
        "    X_times.Add(fi.CreationTime.ToOADate() * 24.0 * 3600000);"
        "    X_times.Add(fi.LastWriteTime.ToOADate() * 24.0 * 3600000);"
        "    X_times.Add(fi.LastAccessTime.ToOADate()* 24.0 * 3600000);"
        "	 } catch { }"
        "}" .

            
        */
        public static void Catalog(string dir, List<string> cat)
        {
            cat.Clear();
            string[] dirs = Directory.GetDirectories(dir);
            string[] files = Directory.GetFiles(dir, "*.*", SearchOption.TopDirectoryOnly);
            foreach (string d in dirs)
                    cat.Add(Path.GetFileName(d) + "\\");
            foreach (string f in files)
                    cat.Add(Path.GetFileName(f));
        }

        /*
        
        FUNCTION Change_ext|ension(STR Path|_of_file,
            STR New_ex|tension) ==> STR
            :
            ----------------------------------------- 'Find a dot from the end'
            FOR i IN [Path.Len-1 DOWNTO 0] :
                CASE Path.[i] ?
                    ["."]: RESULT = Path.[0 TO i-1] New_ex ==>
                    ["\", "/"]: BREAK i ;
            ;
            ----------------------------------------- 'Not found ? Just append'
            RESULT = Path New_ex .
            
        FUN Ext|ension|_of_file(STR Path|_of_file) ==> STR :
        	STR no_ext|ension = Path.Change_ext("")
        	RESULT = Path.[no_ext.Len TO *] .
        */
        public static string Change_Ext(string Path, string New_ext)
        {
            bool break_i = false;
            for (int i = Path.Length - 1; i >= 0 && !break_i; i--)
            {
                switch (Path[i])
                {
                    case '.': return Path.Substring(0, i - 1) + New_ext;
                    case '\\':
                    case '/': break_i = true; break;
                }
            }
            return Path + New_ext;
        }

        /*
        
        FUNCTION Extract_path( STR Path|_of_file_full) ==> STR :
            FOR i IN [Path.Len-1 DOWNTO 0] :
                STR ch|aracter_at_i = Path.[i]
                CASE ch == "\" || ch == "|" || ch == ":" ? 
                     RESULT = Path.[0 TO i-1] Path_sep ==> ;
            ; .

        FUNCTION Extract_file_name( STR Path|_file) ==> STR :
            FOR i IN [Path.Len-1 DOWNTO 0] :
                CASE Path.[i] IN ":\/" ? RESULT = Path.[i+1 TO *] ==> ;
            ;
            RESULT = Path .
            
		FUN Get_file_size(STR Path|_file) ==> REAL, NATIVE : @
		@	"var f = new FileInfo(X_Path);"
			"RESULT = (double)f.Length;" .

            

		FUNCTION Get_file_time_age_seconds(
		STR Path|_file) ==> REAL, NATIVE, 
		DEPRECATED('use only for compatibility with the primary AL4 compiler'):
		@	"var f = new FileInfo(X_Path);"
			"RESULT = DateTime.Now.Subtract(f.LastWriteTime).TotalSeconds;" .
			
		FUNCTION Get_file_modified_time_formatted(
		STR Path|_file) ==> STR, NATIVE,
		DEPRECATED('use only for compatibility with the primary AL4 compiler'):
		CASE Path == "" || (!Path.Exists && !Path.Dir_exists) ? ==> ;
		NATIVE @	
		"var f = new FileInfo(X_Path);"
		"RESULT = f.LastWriteTime.ToString("#QU"yyyyMMdd HHmmss"#QU");" .

		FUN Set_file_time_age(
		STR Path|_file, {Date_time}.{date_time} Age|_2_seconds_recision),
		DEPRECATED('Platform dependant')
			:
			CASE ? [Path.Exists] ? do_Set_file_time_age(Path, Age.DT) 
				   [Path.Dir_exists] ? do_Set_dir_time_age(Path, Age.DT) ; .
	 	FUN do_Set_file_time_age(
	 	STR Path|_file, REAL Age|_2_seconds_recision), NATIVE : @
			"try {"
			"	File.SetLastWriteTime(X_Path, DateTime.FromOADate("
			"		X_Age / 24.0 / 3600000));"
			"}"
			"catch {"
			"}"	.
	 	FUN do_Set_dir_time_age(
	 	STR Path|_file, REAL Age|_2_seconds_recision), NATIVE : @
			"try {"
			"	Directory.SetLastWriteTime(X_Path, DateTime.FromOADate("
			"		X_Age / 24.0 / 3600000));"
			"}"
			"catch {"
			"}"	.
			
		FUN Set_file_creation_time(
		STR Path|_file, {Date_time}.{date_time} T|ime_creation_to_set),
		DEPRECATED('Platform dependant')
			:
			CASE ? [Path.Exists] ? do_Set_file_creation_time(Path, T.DT) 
				   [Path.Dir_exists] ?
				       //DEBUG: "set creation time " T.S #NL ;
				       do_Set_dir_creation_time(Path, T.DT) ; .
	 	FUN do_Set_file_creation_time(
	 	STR Path|_file, REAL T|ime_to_set), NATIVE : @
			"try {"
			"	File.SetCreationTime(X_Path, DateTime.FromOADate("
			"		X_T / 24.0 / 3600000));"
			"}"
			"catch {"
			"}"	.
	 	FUN do_Set_dir_creation_time(
	 	STR Path|_file, REAL T|ime_to_set), NATIVE : @
			"try {"
			"	Directory.SetCreationTime(X_Path, DateTime.FromOADate("
			"		X_T / 24.0 / 3600000));"
			"}"
			"catch {"
			"}"	.
			
		FUN Set_file_last_access_time(
		STR Path|_file, {Date_time}.{date_time} T|ime_creation_to_set),
		DEPRECATED('Platform dependant')
			:
			CASE ? [Path.Exists] ? do_Set_file_access_time(Path, T.DT) 
				   [Path.Dir_exists] ?
				       //DEBUG: "set access time " T.S #NL ;
				       do_Set_dir_access_time(Path, T.DT) ; .
	 	FUN do_Set_file_access_time(
	 	STR Path|_file, REAL T|ime_to_set), NATIVE : @
			"try {"
			"	File.SetLastAccessTime(X_Path, DateTime.FromOADate("
			"		X_T / 24.0 / 3600000));"
			"}"
			"catch {"
			"}"	.
	 	FUN do_Set_dir_access_time(
	 	STR Path|_file, REAL T|ime_to_set), NATIVE : @
			"try {"
			"	Directory.SetLastAccessTime(X_Path, DateTime.FromOADate("
			"		X_T / 24.0 / 3600000));"
			"}"
			"catch {"
			"}"	.
			
		FUN Set_file_attr|ibutes(
		STR Path|_file, STR Attr|ibutes_Hidden_Readonly_System),
		DEPRECATED('Platform dependent')
			:
			CASE Path.[*]==Path_sep ?
			     CASE !Path.Dir_exists ? ==> ;
			ELSE
			     CASE !Path.Exists ? ==> ;
			;
			BOOL hidden|_file = "H" IN Attr
			BOOL readonly = "R" IN Attr
			BOOL system|_file = "S" IN Attr
			do_Set_file_attr(Path, hidden, readonly, system) .
		FUN do_Set_file_attr(STR Path|_file, BOOL Hidden|_file, BOOL Readonly,
			BOOL System|_file), NATIVE : @
		"	FileAttributes fa = FileAttributes.Normal;"
		"	if (X_Hidden) fa = fa | FileAttributes.Hidden;"
		"	if (X_Readonly) fa = fa | FileAttributes.ReadOnly;"
		"	if (X_System) fa = fa | FileAttributes.System;"
		"	File.SetAttributes(X_Path, fa);" .
			
		

FUN Format_size(REAL Size|_in_bytes) ==> STR :
	CASE ?
	[Size < 1024] ? RESULT = Size.Int.S " Bytes"
	[Size < 1024 * 1024 * 0.9] ? RESULT = (Size/1024).S 
		----------------------------- 'rest 1 decimal digit'
		RESULT = RESULT.Replace(",", ".")
		CASE "." IN RESULT ? RESULT = RESULT.[0 TO RESULT.Find(".")+1] ;
		RESULT = RESULT " KBytes"
	[Size < 1024.0 * 1024 * 1024 * 0.9] ? RESULT = (Size/1024/1024).S
		----------------------------- 'rest 2 decimal digit', REUSED
		RESULT = RESULT.Replace(",", ".")
		CASE "." IN RESULT ? RESULT = RESULT.[0 TO RESULT.Find(".")+2] ;
		----------------------------- 'end'
		RESULT = RESULT " MBytes"
	[Size < 1024.0 * 1024 * 1024 * 1024 * 0.75] ?
		RESULT = (Size/1024/1024/1024).S
		LIKE ........................ 'rest 2 decimal digit'
		RESULT = RESULT " GBytes"
	[Size < 1024.0 * 1024 * 1024 * 1024 * 1024 * 0.75] ?
		RESULT = (Size/1024/1024/1024/1024).S
		LIKE ........................ 'rest 2 decimal digit'
		RESULT = RESULT " TBytes"
	[Size < 1024.0 * 1024 * 1024 * 1024 * 1024 * 1024 * 0.75] ?
		RESULT = (Size/1024/1024/1024/1024/1024).S
		LIKE ........................ 'rest 2 decimal digit'
		RESULT = RESULT " PBytes"
	ELSE
		RESULT = (Size/1024/1024/1024/1024/1024/1024).S
		LIKE ........................ 'rest 2 decimal digit'
		RESULT = RESULT " HBytes" ; .
		
TEST of_Format_size :
	ASSERT Format_size(999) == "999 Bytes", Format_size(999)
	ASSERT Format_size(1999) == "1.9 KBytes", Format_size(1999)
	ASSERT Format_size(2550) == "2.4 KBytes", Format_size(2550)
	ASSERT Format_size(3000_000.0) == "2.86 MBytes", Format_size(3000_000.0)
	----------------------------------- 'even bigger'
	ASSERT Format_size(4_000_000_000.0) == "3.72 GBytes",
		   Format_size(4_000_000_000.0)
		   
	ASSERT Format_size(5_000_000_000_000.0) == "4.54 TBytes",
		   Format_size(5_000_000_000_000.0)
		   
	ASSERT Format_size(6_000_000_000_000_000.0) == "5.32 PBytes",
		   Format_size(6_000_000_000_000_000.0)
		   
	ASSERT Format_size(7_000_000_000_000_000_000.0) == "6.07 HBytes",
		   Format_size(7_000_000_000_000_000_000.0) .
		   
FUN File_to_string(STR Path|_of_file) ==> STR, NATIVE :
	CASE !Path.Exists ? ==> ;
	NATIVE @
	"try {"
	"	FileStream fs = new FileStream(X_Path, FileMode.Open, FileAccess.Read,"
	"		FileShare.ReadWrite | FileShare.Delete);" 
	"	StreamReader f = new StreamReader(fs);"
	"	try {"
	"		//if (f.Size > 1000000) return;"
	"		RESULT = f.ReadToEnd();"
	"	}"
	"	finally {"
	"		f.Close();" 
	"	}" 
	"}"
	"catch(Exception) { }" .
		   
FUN Oem_file_to_string(STR Path|_of_file) ==> STR, NATIVE :
	CASE !Path.Exists ? ==> ;
	NATIVE @
	"try {"
	"	StreamReader f = new StreamReader(X_Path, Encoding.GetEncoding("
	" 		CultureInfo.CurrentCulture.TextInfo.OEMCodePage));"
	"	try {"
	"		//if (f.Size > 1000000) return;"
	"		RESULT = f.ReadToEnd();"
	"	}"
	"	finally {"
	"		f.Close();" 
	"	}" 
	"}"
	"catch(Exception) { }" .
		   
FUN String_to_file(
STR S|tring_to_save, STR Path|_of_file), NATIVE :
	CASE Path == "" ? ==> ;
	Stop_if_test(Path)
	NATIVE @
	"try {"
	"	StreamWriter f = new StreamWriter(X_Path);"
	"	try {"
	"		f.Write(X_S);"
	"	}"
	"	finally {"
	"		f.Close();" 
	"	}" 
	"}"
	"catch (Exception) { }" .

            
        */
            
    }
}
/*
END
*/

