/**
 * Module: 		Partitions.ycp
 *
 * Authors:		Thomas Fehr (fehr@suse.de)
 *
 * Purpose: 		Provides information about partitions
 *
 * $Id: Partitions.ycp 22695 2005-03-21 10:59:13Z fehr $
 */
{
    module "Partitions";

    textdomain "storage";

    import "Arch";
    import "Mode";
    import "Stage";
    import "AsciiFile";

    // The filesystem ids for the partitions
    global integer fsid_empty    = 0;
    global integer fsid_native   = 131;
    global integer fsid_swap     = 130;
    global integer fsid_lvm      = 142;
    global integer fsid_raid     = 253;
    global integer fsid_hibernation = 160;
    global integer fsid_extended = 5;
    global integer fsid_extended_win = 15;
    global integer fsid_fat16    = 6;
    global integer fsid_fat32    = 12;
    global integer fsid_prep_chrp_boot = 0x41;
    global integer fsid_mac_hfs = 0x102;
    global integer fsid_gpt_boot = 0x103;
    global integer fsid_gpt_service = 0x104;
    global integer fsid_freebsd = 0xa5;
    global integer fsid_openbsd = 0xa6;
    global integer fsid_beos = 0xeb;
    global integer fsid_solaris = 0xbf;
    global integer fsid_root = fsid_native;
           integer fsid_boot = 0;
           integer boot_cyl = 0;
           string boot_mount_point = "";

    global boolean no_fsid_menu = Arch::s390 ();

    global string  raid_name = "MD Raid";
    global string  loop_name = "Loop Device";

    // filesystems for /win
                         /* FAT32, Win95-Fat32, Win95LBA, Win95-Fat16  */
    global list fsid_wintypes = [ 6, 11, 12, 14 ]; 

    // filesystems for /dos
    global list fsid_dostypes = [ 1, 4 ];          /* FAT12, FAT16  */

    // filesystems for /windows
    global list fsid_ntfstypes = [ 7, 23 ];       /* NTFS  */

    // filesystems mounted read-only
    global list fsid_readonly = [ 7, 23 ];

    // filesystems skipped on sparc and axp
    global list fsid_skipped = [ 0, 5 ];

    // partition ids not to delete when suggesting to use whole disk
    global list do_not_delete = [ 0x12, 0xde, fsid_mac_hfs, fsid_gpt_service ];

    // partition ids not to display as windows when fat32 is on it
    global list no_windows = [ 0x12, 0x82, 0xde, fsid_gpt_boot, fsid_gpt_service ];

    global integer minimal_needed_disksize = 400 * 1024 * 1024;  // for installation of a minimal system
           integer minimal_needed_bootsize = 0; 

    global symbol default_fs      = `reiser;
           symbol default_boot_fs = `unknown;

    global list<string> not_shown_partitions = 
	[
	"Apple_partition_map",
	"Apple_Patches",
	"Apple_Driver",
	"Apple_Loader",
	"Apple_Boot",
	"Apple_Bootstrap",
	"Apple_FWDriver",
	"Apple_ProDOS" 
	];


global define void Partitions()
    ``{
        return ;
    }

global define symbol DefaultBootFs()
    ``{
    if( default_boot_fs == `unknown )
	{
	default_boot_fs = `ext2;
	if( Arch::ia64 () )
	    {
	    default_boot_fs = `fat32;
	    }
	else if( Arch::board_mac () )
	    {
	    default_boot_fs = `hfs;
	    }
	}
    return( default_boot_fs );
    }

global define string BootMount()
    ``{
    if( boot_mount_point == "" )
	{
	boot_mount_point = "/boot";
	if( Arch::ia64 () )
	    {
	    boot_mount_point = "/boot/efi";
	    }
	}
    return( boot_mount_point );
    }

global define integer MinimalNeededBootsize()
    ``{
    if( minimal_needed_bootsize == 0 )
	{
	minimal_needed_bootsize = 64 * 1024 * 1024;
	if( Arch::ia64 () )
	    {
	    minimal_needed_bootsize = 200*1024*1024;
	    }
	else if( Arch::board_chrp () )
	    {
	    minimal_needed_bootsize = 16*1024*1024;
	    }
	else if( Arch::board_prep () )
	    {
	    minimal_needed_bootsize = 5*1024*1024;
	    }
	else if( Arch::board_iseries () )
	    {
	    minimal_needed_bootsize = 8*1024*1024;
	    }
	else if( Arch::board_mac () )
	    {
	    minimal_needed_bootsize = 32*1024*1024;
	    }
	}
    return( minimal_needed_bootsize );
    }

global define integer BootCyl()
    ``{
    if( boot_cyl == 0 )
	{
	boot_cyl = 1024;
	if( !Arch::i386 () )
	    {
	    // Assume on non-i386 archs machine can boot from every cylinder
	    boot_cyl = 4*1024*1024*1024;
	    }
	else
	    {
	    list internal_bios = (list<map>) SCR::Read (.probe.bios);
	    boolean lba = internal_bios[0, "lba_support"]:false;
	    y2milestone( "lba_support %1", lba );
	    if( lba )
		{
		boot_cyl = 4*1024*1024*1024;
		}
	    }
	}
    return( boot_cyl );
    }

global define boolean PrepBoot()
    ``{
    boolean ret = Arch::ppc () && 
		  (Arch::board_chrp () || Arch::board_prep () || Arch::board_iseries ());
    y2milestone( "ret:%1", ret );
    return( ret );
    };

global define integer FsidBoot()
    ``{
    if( fsid_boot == 0 )
	{
	fsid_boot = fsid_native;
	if( Arch::ia64 () )
	    {
	    fsid_boot = fsid_gpt_boot;
	    }
	else if( PrepBoot() )
	    {
	    fsid_boot = fsid_prep_chrp_boot;
	    }
	else if( Arch::board_mac () )
	    {
	    fsid_boot = fsid_mac_hfs;
	    }
	}
    return( fsid_boot );
    }

global define boolean NeedBoot()
    ``{
    boolean ret = false;
    if( Arch::ia64 ()||Arch::ppc ()||Arch::sparc ()||Arch::alpha () )
	{
	ret = true;
	}
    y2milestone( "ret %1", ret );
    return( ret );
    }

global define boolean IsDosPartition( integer fsid )
    ``{
    return (contains( fsid_dostypes, fsid) || contains( fsid_wintypes, fsid));
    }

global define boolean IsDosWinNtPartition( integer fsid )
    ``{
    return( IsDosPartition( fsid ) || contains( fsid_ntfstypes, fsid ));
    }

global define integer SwapSizeMb( integer slot_size )
    ``{
    integer swap_size = 0;

    // a good approach swap == mem x 2
    integer mem = 256;
    if( !Mode::test () )
	{
        map mem_info_map = (map) SCR::Read(.proc.meminfo);
	y2milestone( "mem_info_map %1", mem_info_map );
        mem = mem_info_map["memtotal"]:0;
	mem = mem / 1024;
	}
    if( mem == 0 || mem == nil ) mem = 64;

    if( slot_size==0 )
	{
	if( mem <= 128 )
	    {
	    swap_size = mem * 2;
	    }
	else
	    {
	    swap_size = mem + 128;
	    }
	}
    else
	{
	if( mem * 9 < slot_size )
	    {
	    swap_size = mem * 2; 
	    }
	else if( mem * 5 < slot_size )
	    {
	    swap_size = mem; 
	    }
	else if( mem * 3 < slot_size )
	    {
	    swap_size = mem / 2; 
	    }
	else if( mem * 2 < slot_size )
	    {
	    swap_size = mem / 3; 
	    }
	else 
	    {
	    swap_size = mem / 4;
	    }
	}

    if( swap_size > 1024 )
	{
	swap_size = 1024;
	}
    if( swap_size < 0 ) 
	{
	swap_size = 0;
	}


    // look for a min size
    // 1G    -> 128MB
    // 2G    -> 256MB
    // 10G   -> 512MB
    // 40G   -> 1GB

    if( slot_size > (40 * 1024) &&  (swap_size + mem) < 1024 )
	{
	swap_size = 1024 - mem;
	}
    else if( slot_size > (10 * 1024)  &&  (swap_size + mem) < 512 )
	{
	swap_size = 512 - mem;
	}
    else if( slot_size > (2 * 1024)  &&  (swap_size + mem) < 256 )
	{
	swap_size = 256 - mem;
	}
    else if( slot_size > (1 * 1024)  &&  (swap_size + mem) < 128 )
	{
	swap_size = 128 - mem;
	}

    if( swap_size == 0 )
	{
	swap_size = -1;
	}
    y2milestone( "slot_size %1 swap_size %2", slot_size, swap_size );

    return( swap_size );
    };

global define boolean UseParted()
    ``{
    boolean ret = true;
/*
    if( Arch::i386 () || Arch::x86_64 () )
	ret = false;
*/
    y2milestone( "UseParted ret:%1", ret );
    return( ret );
    }

global define boolean IsResizable( integer fsid )
    ``{
    boolean ret = false;
    ret = IsDosWinNtPartition(fsid) || fsid == fsid_swap || fsid == fsid_native;
    y2milestone( "IsResizable fsid:%1 ret:%2", fsid, ret );
    return( ret );
    }

global define boolean IsLinuxPartition( integer fsid )
    ``{
    return ( fsid==fsid_native || fsid==fsid_swap || fsid==fsid_lvm ||
	     fsid==fsid_raid || fsid==fsid_gpt_boot );
    };

global define list<map> mountedPartitionsOnDisk( string dev_str, list<map> mounts_read )
    ``{

    list<map> mounts = filter(map mount, mounts_read,
                         ``( issubstring(mount["spec"]:"", dev_str )));

    // reset
    if( mounts == nil ) mounts = [];

    return mounts;
    }

global define string MountedOn( string partition_str, list<map> mounts_read )
    ``{

    list mounts = filter(map mount, mounts_read,
                          ``( mount["spec"]:""==partition_str) );

    if( size( mounts) > 0 )
        return mounts[0, "file"]:"";
    return "";
    };

global define map GetLoopOn( string device )
    ``{
    map ret = $[];
    string cmd = sformat( "/sbin/losetup %1", device );
    map bash_call = (map) SCR::Execute (.target.bash_output, cmd, $[] );
    if( bash_call["exit"]:1 == 0)
	{
	string text = bash_call["stdout"]:"";
	if( find( text, ")" )>0 )
	    {
	    text = substring( text, 0, find( text, ")" ) );
	    if( find( text, "(" )>0 )
		{
		text = substring( text, find( text, "(" )+1 );
		ret["file"] = text;
		map stat = (map) SCR::Read( .target.stat, text );
		ret["blockdev"] = stat["isblock"]:false;
		}
	    }
	}
    y2milestone( "dev %1 ret %2", device, ret );
    return( ret );
    }

define string TanslateMapperName( string device )
    ``{
    string ret = device;
    string regex = "[^-]-[^-]";
    if( find( device, "/dev/mapper/" )==0 )
	{
	list<integer> pos = regexppos( device, regex );
	y2milestone( "pos=%1", pos );
	if( size(pos)>0 )
	    {
	    ret = "/dev/" + substring(device,12,pos[0]:0-11) + "/" +
	          substring(device,pos[0]:0+2);
	    }
	y2milestone( "TanslateMapperName %1 -> %2", device, ret );
	}
    return( ret );
    }

/**
 *	Return a list with all mounted partition
 *  @return list<map>
 */
global define list<map> CurMounted()
    ``{
    SCR::UnmountAgent (.proc.mounts);
    SCR::UnmountAgent (.proc.swaps);
    SCR::UnmountAgent (.etc.mtab);
    list<map> mounts = (list<map>) SCR::Read(.proc.mounts);
    list<map> swaps  = (list<map>) SCR::Read(.proc.swaps );
    list<map> mtab   = (list<map>) SCR::Read(.etc.mtab );

    foreach(map swap, swaps,
	``{
	map swap_entry = $[
			   "file" : "swap",
			   "spec" :  swap["file"]:""
	    ];

	mounts = add( mounts, swap_entry );

	});

    map mtab_root = (map) find(map mount, mtab, ``( mount["file"]:"" == "/" ));
    map root_map  = (map) find(map mount, mounts, 
                               ``( mount["spec"]:"" == "/dev/root" ));
    if( root_map == nil )
	{
	root_map = (map) find(map mount, mounts, 
                              ``( mount["spec"]:"" != "rootfs"&&
			          mount["file"]:"" == "/"));
	}
    y2milestone( "mtab_root %1 root_map %2", mtab_root, root_map );
//    root_map = add (root_map, "spec", mtab_root["spec"]:"");
    if( root_map["spec"]:"" == "/dev/root" )
	root_map["spec"] = mtab_root["spec"]:"";
    if( (find( root_map["spec"]:"", "LABEL=" )==0 ||
         find( root_map["spec"]:"", "UUID=" )==0) && !Stage::initial () )
	{
	map bo = (map) SCR::Execute (.target.bash_output, "fsck -N /", $[] );
	y2milestone( "CurMounted bo:%1", bo );
	string dev = "";
	if( bo["exit"]:1==0 )
	    {
	    list tmp = filter( string k, splitstring( bo["stdout"]:"", " \n" ),
	                       ``(size(k)>0) );
	    if( size(tmp)>0 )
		dev = tmp[size(tmp)-1]:"";
	    y2milestone( "CurMounted LABEL/UUID dev:%1", dev );
	    }
	if( size(dev)>0 )
	    {
	    root_map["spec"] = dev;
	    }
	}
    y2milestone( "root_map %1", root_map );
//    this version makes some problems with interpreter, above lookup/add is OK
    mounts = filter(map mount, mounts, ``( mount["file"]:"" != "/"));
    mounts = add( mounts, root_map );
    list<map> ret = [];
    foreach(map p, mounts, ``{
	if( find( p["spec"]:"", "/dev/loop" )>=0 )
	    {
	    map r = GetLoopOn( p["spec"]:"" );
	    if( r["blockdev"]:false )
		{
		p["loop_on"] = r["file"]:"";
		}
	    }
	ret = add( ret, p );
	});
    ret = maplist( map p, ret, 
	``{
	p["spec"] = TanslateMapperName( p["spec"]:"" );
	return( p );
	});


    y2milestone( "CurMounted all mounts %1", ret);
    return ret;
    };


global define map GetFstab( string pathname )
    ``{
    map file = $[];
    AsciiFile::SetComment( file, "^[ \t]*#" );
    AsciiFile::SetDelimiter( file, " \t" );
    AsciiFile::SetListWidth( file, [ 20, 20, 10, 21, 1, 1 ] );
    AsciiFile::ReadFile( file, pathname );
    return( file );
    };

global define map GetCrypto( string pathname )
    ``{
    map file = $[];
    AsciiFile::SetComment( file, "^[ \t]*#" );
    AsciiFile::SetDelimiter( file, " \t" );
    AsciiFile::SetListWidth( file, [ 11, 15, 20, 10, 10, 1 ] );
    AsciiFile::ReadFile( file, pathname );
    return( file );
    };

global define string ToHexString( integer num ) 
    ``{
    string to_hex = "0123456789ABCDEF";
    string ret = "";
    if( num>256 )
	{
	ret = sformat("0x%1%2%3", substring( to_hex, num/256, 1 ),
	              substring( to_hex, (num%256)/16, 1 ),
                      substring( to_hex, num%16, 1 ) );
	}
    else
	{
	ret = sformat("0x%1%2", substring( to_hex, num/16, 1 ),
                      substring( to_hex, num%16, 1 ) );
	}
    return( ret );
    }

global define string FsIdToString( integer fs_id ) 
    ``{
    if ( fs_id == 0xa7) return( "NeXTSTEP");
    if ( fs_id == 0xb7) return( "BSDI fs");
    if ( fs_id == 0xb8) return( "BSDI swap");
    if ( fs_id == 0xc1) return( "DRDOS/sec");
    if ( fs_id == 0xc4) return( "DRDOS/sec");
    if ( fs_id == 0xc6) return( "DRDOS/sec");
    if ( fs_id == 0xc7) return( "Syrinx");
    if ( fs_id == 0xda) return( "Non-Fs data");
    if ( fs_id == 0xdb) return( "CP/M / CTOS");
    if ( fs_id == 0xde) return( "Dell Utility");
    if ( fs_id == 0xe1) return( "DOS access");
    if ( fs_id == 0xe3) return( "DOS R/O");
    if ( fs_id == 0xe4) return( "SpeedStor");
    if ( fs_id == 0xeb) return( "BeOS fs");
    if ( fs_id == 0xee) return( "EFI GPT");
    if ( fs_id == 0xef) return( "EFI (FAT-12/16)");
    if ( fs_id == 0xf1) return( "SpeedStor");
    if ( fs_id == 0xf4) return( "SpeedStor");
    if ( fs_id == 0xf2) return( "DOS secondary ");
    if ( fs_id == 0xfd) return( "Linux RAID");
    if ( fs_id == 0xfe) return( "LANstep");
    if ( fs_id == 0xff) return( "BBT");
    if ( fs_id == 0x0)  return( "empty");
    if ( fs_id == 0x1)  return( "FAT12");
    if ( fs_id == 0x2)  return( "XENIX root");
    if ( fs_id == 0x3)  return( "XENIX usr");
    if ( fs_id == 0x4)  return( "FAT16 <32M");
    if ( fs_id == 0x5)  return( "Extended");
    if ( fs_id == 0x6)  return( "FAT16");
    if ( fs_id == 0x7)  return( "HPFS/NTFS");
    if ( fs_id == 0x8)  return( "AIX");
    if ( fs_id == 0x9)  return( "AIX boot");
    if ( fs_id == 0xa)  return( "OS/2 boot manager");
    if ( fs_id == 0xb)  return( "Win95 FAT32");
    if ( fs_id == 0xc)  return( "Win95 FAT32 LBA");
    if ( fs_id == 0xe)  return( "Win95 FAT16");
    if ( fs_id == 0xf)  return( "Extended");
    if ( fs_id == 0x10) return( "OPUS");
    if ( fs_id == 0x11) return( "Hidden FAT12");
    if ( fs_id == 0x12) return( "Vendor diag");
    if ( fs_id == 0x14) return( "Hidden FAT16");
    if ( fs_id == 0x16) return( "Hidden FAT16");
    if ( fs_id == 0x17) return( "Hidden HPFS/NTFS");
    if ( fs_id == 0x18) return( "AST Windows");
    if ( fs_id == 0x1b) return( "Hidden Win95");
    if ( fs_id == 0x1c) return( "Hidden Win95");
    if ( fs_id == 0x1e) return( "Hidden Win95");
    if ( fs_id == 0x24) return( "NEC DOS");
    if ( fs_id == 0x39) return( "Plan 9");
    if ( fs_id == 0x3c) return( "PartitionMagic");
    if ( fs_id == 0x40) return( "Venix 80286");
    if ( fs_id == 0x41) return( "PPC PReP Boot");
    if ( fs_id == 0x42) return( "SFS");
    if ( fs_id == 0x4d) return( "QNX4.x");
    if ( fs_id == 0x4e) return( "QNX4.x 2nd par");
    if ( fs_id == 0x4f) return( "QNX4.x 3rd par");
    if ( fs_id == 0x50) return( "OnTrack DM");
    if ( fs_id == 0x51) return( "OnTrack DM6");
    if ( fs_id == 0x52) return( "CP/M");
    if ( fs_id == 0x53) return( "OnTrack DM6");
    if ( fs_id == 0x54) return( "OnTrack DM6");
    if ( fs_id == 0x55) return( "EZ-Drive");
    if ( fs_id == 0x56) return( "Golden Bow");
    if ( fs_id == 0x5c) return( "Priam Edisk");
    if ( fs_id == 0x61) return( "SpeedStor");
    if ( fs_id == 0x63) return( "GNU HURD");
    if ( fs_id == 0x64) return( "Novell Netware");
    if ( fs_id == 0x65) return( "Novell Netware");
    if ( fs_id == 0x70) return( "DiskSecure");
    if ( fs_id == 0x75) return( "PC/IX");
    if ( fs_id == 0x80) return( "Old Minix");
    if ( fs_id == 0x81) return( "Minix");
    if ( fs_id == 0x82) return( "Linux swap");
    if ( fs_id == 0x83) return( "Linux native");
    if ( fs_id == 0x84) return( "OS/2 hidden");
    if ( fs_id == 0x85) return( "Linux extended");
    if ( fs_id == 0x86) return( "NTFS volume");
    if ( fs_id == 0x87) return( "NTFS volume");
    if ( fs_id == 0x8e) return( "Linux LVM");
    if ( fs_id == 0x93) return( "Amoeba");
    if ( fs_id == 0x94) return( "Amoeba BBT");
    if ( fs_id == 0x9f) return( "BSD/OS");
    if ( fs_id == 0xa0) return( "Hibernation");
    if ( fs_id == 0xa5) return( "FreeBSD");
    if ( fs_id == 0xa6) return( "OpenBSD");
    if ( fs_id == 0x102) return( "Apple_HFS");
    if ( fs_id == 0x103) return( "EFI boot");
    if ( fs_id == 0x104) return( "Service");
    return( "unknown" );
    };

global define string DefaultPartLabel() 
    ``{
    string ret ="msdos";
    if( Arch::ia64 () )
	{
	ret = "gpt";
	}
    else if( Arch::board_mac () )
	{
	ret = "mac";
	}
    else if( Arch::sparc () )
	{
	ret = "sun";
	}
    y2milestone( "ret %1", ret );
    return( ret );
    }

map capabilities = $[ "msdos" : $[ "extended" : true,
                                   "max_primary" : 4 ],
		      "gpt"   : $[ "max_primary" : 16 ],
		      "mac"   : $[ "max_primary" : 16 ],
		      "sun"   : $[ "max_primary" : 8 ],
		      "bsd"   : $[ "max_primary" : 8 ],
		      "dasd"  : $[ "max_primary" : 3 ]
		    ];

global define integer MaxPrimary( string label )
    ``{
    integer ret = capabilities[label,"max_primary"]:4;
    y2milestone( "label:%1 ret:%2", label, ret );
    return( ret );
    }

global define integer MaxLogical( string device )
    ``{
    integer ret = 15;
    if( find( device, "/dev/hd" )==0 )
	{
	ret = 63;
	}
    else if( find( device, "/dev/rd/c" )==0 )
	{
	ret = 7;
	}
    y2milestone( "MaxLogical:%1 ret:%2", device, ret );
    return( ret );
    }

global define boolean HasExtended( string label )
    ``{
    boolean ret = capabilities[label,"extended"]:false;
    y2milestone( "label:%1 ret:%2", label, ret );
    return( ret );
    }

global define string RdonlyText( string disk, boolean expert_partitioner )
    ``{
    string text = "";
    if( expert_partitioner )
	{
	text = sformat( "Operation not permitted on disk %1 !\n", disk );
	}
    // popup text %1 is replaced by disk name e.g. /dev/hda
    text = text + sformat( _("
The partitioning on your disk %1 is not readable by
the partitioning tool \"parted\" that YaST2 uses to change the
partition table.

You may use the partitions on disk %1 as they are.
You may format them and assign mount points to them, but you
cannot add, edit, resize, or remove partitions from that
disk with YaST2.
"), disk );
    if( expert_partitioner )
	{
	// popup text 
	text = text + _("

You may initialize the disk partition table to a sane state in the Expert
Partitioner by selecting \"Expert\"->\"Delete Partition Table 
and Disk Label\", but this will destroy all data on all partitions of this 
disk.
");
	}
    else
	{
	text = text + _("

Safely ignore this message if you do not intend to use 
this disk during installation.
");
	}
    return( text );
    }

    /*---------------------------------------------------------------------
     * returns for a string a string with a minimum of 9 characters:
     * "1"      -> "       1"
     * "22"     -> "      22"
     * "145"    -> "     145"
     * "2134"   -> "    2134"
     *----------------------------------------------------------------------
     */
define string sto8string( string str )
    ``{
    integer nb = size( str );
    if ( nb > 8  )   return( sformat( "%1", str ));
    if ( nb > 7  )   return( sformat( " %1", str ));
    if ( nb > 6  )   return( sformat( "  %1", str ));
    if ( nb > 5  )   return( sformat( "   %1", str ));
    if ( nb > 4  )   return( sformat( "    %1", str ));
    if ( nb > 3  )   return( sformat( "     %1", str ));
    if ( nb > 2  )   return( sformat( "      %1", str ));
    if ( nb > 1  )   return( sformat( "       %1", str ));
    return( sformat( "         %1", str ));
    };

/*---------------------------------------------------------------------
 * Convert <number-of-bytes> to XXX.X MB or XXX.X GB or XXX.X TB
 *
 * see also ByteToHumanStringWithZero !
 * Return value: string  "unknown" if input == 0
 *----------------------------------------------------------------------
 */
global define string ByteToHumanString ( integer number )
    ``{
    // column description, if disk space is not known
    if( number == 0 ) return( _("unknown") );

    if( number < 1073741824 )
	{
	// < 1 GB
	integer MB    = number / 1048576;
	integer hunKB = (number - (MB * 1048576)) / 104858;
	return( sto8string(sformat( "%1.%2 MB", MB,hunKB )));
	}
    else if ( number < 1099511627776 )
	{
	// < 1 TB
	integer GB    = number / 1073741824;
	integer hunMB = (number - (GB * 1073741824)) / 107374183;
	return( sto8string(sformat( "%1.%2 GB", GB,hunMB )));
	}
    else
        {
        // >= 1 TB
        integer TB    = number / 1099511627776;
        integer hunGB = (number - (TB * 1099511627776)) / 109951162778;
	return( sto8string(sformat( "%1.%2 TB", TB,hunGB )));
        }
    };


}
