ACC SHELL

Path : /usr/share/YaST2/include/autoinstall/
File Upload :
Current File : //usr/share/YaST2/include/autoinstall/autopart.ycp

/*


$Id: autopart.ycp 60940 2010-02-22 14:17:12Z ug $
*/
{
    textdomain "autoinst";

    import "FileSystems";
    import "Partitions";
    import "Arch";


    map get_gap_info( map disk, map pd, boolean add_exist_linux );
    map add_cylinder_info( map conf, map gap );
    map get_perfect_list( list ps, map g );
    map process_partition_data( string dev, map solution );
    map find_matching_disk( list<string> disks, map target, map conf );
    map try_resize_windows( map disk );
    map remove_possible_partitions( map disk, map conf );
    map distribute_space( integer rest, list weights, list added, list ps );
    void add_part_recursive( list ps, map g );
    map normalize_gaps( list ps, map g );
    integer do_weighting( list ps, map g );

    symbol cur_mode = `free;
    integer cur_weight = -10000;
    map cur_gap = $[];


    list GetNoneLinuxPartitions(string device) {
        list ret = [];
        foreach ( string dev, map disk, Storage::GetTargetMap(), ``{
                if( Storage::IsRealDisk( disk )  && dev == device)
                {
                list<map> l = (list<map>) filter( map p, disk["partitions"]:[],
                    ``(!p["delete"]:false &&
                        !p["format"]:false &&
                        !Partitions::IsLinuxPartition(p["fsid"]:0)) );

                l = filter(map p, l, ``(!contains( [`xfs, `ext2, `ext3, `ext4, `jfs, `reiser],
                            p["used_fs"]:`unknown)));
                l = filter(map p, l,
                    ``(!FileSystems::IsSystemMp( p["mount"]:"", false )));
                if( size(l)>0 )
                {
                    list<integer> ln =  maplist( map p, l, ``(p["nr"]:0));
                    ret = union( ret, ln );
                }
                }
                });
        y2milestone( "GetNoneLinuxPartitions ret=%1", ret );
        return( ret );
    }


    list<integer> GetAllPartitions(string device) {
        list<integer> ret = [];
        foreach ( string dev, map disk, Storage::GetTargetMap(), ``{
                if( Storage::IsRealDisk( disk )  && dev == device)
                {
                list<integer> l = maplist( map p, disk["partitions"]:[],
                    ``(p["nr"]:0));

                ret = (list<integer>)union( ret, l );
                }
                });
        y2milestone( "All Partitions ret=%1", ret );
        return( ret );
    }

    /**
     * Read partition data from XML control file
     * @return map flexible propsal map
     */
    define list<map> preprocess_partition_config(list<map> xmlflex) {
        
        y2debug("xml input: %1", xmlflex );
        map<string,any> tm = Storage::GetTargetMap();
        list<map>partitioning = maplist(map d, xmlflex, ``{
            
            foreach( string key, [ "keep_partition_id", "keep_partition_num"], ``{
                list num = [];
                list<string> nlist = splitstring(d[key]:"", ",");
                foreach( string n, nlist, ``{ num = union( num, [tointeger(n)] );});
                d[key] = num;
            });

            list fsys = [];
            list<string> nlist = splitstring( d["keep_partition_fsys"]:"" , "," );
            foreach( string n, nlist,
                     ``{
                symbol fs = FileSystems::FsToSymbol(n);
                if( fs != `none )
                {
                    fsys = union( fsys, [ fs ] );
                }
            });
            d["keep_partition_fsys"] = fsys;


            list<map> user_partitions = d["partitions"]:[];
            if (size(user_partitions) == 0)
            {
                y2milestone("no partitions specified, creating default scheme");
                map root = $[];
                root["mount"] = "/";
                root["size"] = "max";
                map swap = $[];
                swap["mount"] = "swap";
                swap["size"] = "auto";
                user_partitions=add(user_partitions, swap);
                user_partitions=add(user_partitions, root);
                
            }
            
            list partitions = [];
            foreach(map partition, user_partitions,
                    ``{
                if (haskey(partition, "maxsize"))
                {
                    partition["max"] = AutoinstStorage::humanStringToByte(partition["maxsize"]:"", true );
                } 
                    
                if (partition["size"]:"" != "")
                {
                    string s = partition["size"]:"";
                    if( tolower(s) == "auto" )
                    {
                        partition["size"] = -1;
                    }
                    else if( tolower(s) == "max" )
                    {
                        partition["size"] = 0;
                    }
                    else
                    {
                        partition["size"] = AutoinstStorage::humanStringToByte( s, true );
                    }
                }
                    
                if( partition["size"]:0 == -1 && partition["mount"]:"" == "swap" )
                {
                    partition["size"] = 1024*1024*Partitions::SwapSizeMb(0);
                }
            
                if( partition["mount"]:"" == Partitions::BootMount() )
                {
                    if( partition["size"]:0 == -1 )
                        partition["size"] = Partitions::MinimalNeededBootsize();

                    if( partition["filesystem"]:`none == `none )
                        partition["filesystem"] = Partitions::DefaultBootFs();

                    if( partition["filesystem_id"]:0 == 0 )
                        partition["filesystem_id"] = Partitions::FsidBoot();

                    //partition["max_cyl"] = Partitions::BootCyl();
                }
                
                if( partition["size"]:0 == -1 )
                    partition["size"] = 0;

                if (partition["used_by_type"]:`UB_NONE==`UB_LVM)
                        partition["filesystem_id"] =  Partitions::fsid_lvm;
                else if (partition["used_by_type"]:`UB_NONE==`UB_MD)
                    partition["filesystem_id"] =  Partitions::fsid_raid;

                if( haskey( partition, "filesystem_id" ) )
                    partition["fsid"] = partition["filesystem_id"]:Partitions::fsid_native;
                
                y2debug("partition: %1", partition);
                partitions = add( partitions, partition );
            });
        
            if (d["type"]:`CT_UNKNONW!=`CT_LVM ) {
                d["partitions"] = partitions;
            }
            return (d);
        });
        
        y2milestone( "conf: %1", partitioning );
        return( partitioning );
    }

    define map try_add_boot( map conf, map disk ) {
        boolean root = size(filter( map e, conf["partitions"]:[], 
                                    ``(e["mount"]:""=="/"))) > 0;
        map tc = (map) eval(conf);
        if( !planHasBoot && root &&
            (disk["cyl_count"]:0 > Partitions::BootCyl() ||
             Arch::ia64 () || Arch::ppc () || Arch::sparc ()) )
        {
            map pb = $[];
            if( ! Arch::ppc () ) {
                pb["mount"] = Partitions::BootMount();
                pb["fsys"] = Partitions::DefaultBootFs();
            }
            pb["size"] = Partitions::MinimalNeededBootsize();
            pb["filesystem"] = Partitions::DefaultBootFs();
            pb["fsid"] = Partitions::FsidBoot(); // FIXME: might be useless
            pb["filesystem_id"] = Partitions::FsidBoot();
            pb["id"] = Partitions::FsidBoot(); // FIXME: might be useless
            pb["auto_added"] = true;
            pb["type"] = `primary; // FIXME: might be useless
            pb["partition_type"] = "primary";
            pb["nr"] = 1;
            //pb["max_cyl"] = Partitions::BootCyl();
            //tc["partitions"] = add( tc["partitions"]:[], pb );
            tc["partitions"] = merge( [ pb ], tc["partitions"]:[] );
            y2milestone( "boot added automagically pb %1", pb );
        }
        return( tc );
    }


    /**
     * Find matching disk
     * find_matching_disk( ["/dev/sda"], Storage::GetTargetMap(), AutoTargetMap["/dev/sda"] )
     */
    define map find_matching_disk( list<string> disks, map target, map conf )
        ``{
        map<string,map> solutions = $[];

        cur_weight = -100000;
        cur_gap = $[];
        foreach( string k, disks,
                 ``{
            map e = target[k]:$[];
            map pd = conf;
            y2milestone( "processing disk %1", k );
            y2milestone( "parts %1", conf["partitions"]:[] );
            map tc = try_add_boot( conf, e );
            cur_mode = `free;
            if( !tc["prefer_remove"]:false )
            {
                map gap = get_gap_info( e, pd , false );
                tc = add_cylinder_info( tc, gap );
                map l = get_perfect_list( tc["partitions"]:[], gap );
                if( size(l)>0 )
                {
                    solutions[k] = eval(l);
                    solutions[k,"disk"] = eval(e);
                }
                cur_mode = `reuse;
                map egap = get_gap_info( e, pd, true );
                if( size(egap["gap"]:[]) > size(gap["gap"]:[]) )
                {
                    tc = add_cylinder_info( tc, egap );
                    l = get_perfect_list( tc["partitions"]:[], egap );
                    if( size(l)>0 && 
                        (!haskey(solutions,k) || 
                         (haskey( l, "weight" ) && 
                          l["weigth"]:0 > solutions[k,"weigth"]:0 )))
                    {
                        y2milestone( "solution reuse existing" );
                        solutions[k] = eval(l);
                        solutions[k,"disk"] = eval(e);
                    }
                }
                cur_mode = `resize;
                map rw = try_resize_windows( e );
                if( size( filter( map p, rw["partitions"]:[],
                                  ``(haskey(p, "winfo")) ))>0 )
                {
                    egap = get_gap_info( rw, pd, true );
                    tc = add_cylinder_info( tc, egap );
                    l = get_perfect_list( tc["partitions"]:[], egap );
                    if( size(l)>0 && 
                        (!haskey(solutions,k) ||
                         (haskey( l, "weight" ) && 
                          l["weigth"]:0 > solutions[k,"weigth"]:0 )))
                    {
                        y2milestone( "solution resizing windows" );
                        solutions[k] = eval(l);
                        solutions[k,"disk"] = eval(rw);
                    }
                }
            }
            else
            {
                cur_mode = `free;
                map rp = remove_possible_partitions( e, tc );
                map gap = get_gap_info( rp, pd, false );
                tc = add_cylinder_info( tc, gap );
                map l = get_perfect_list( tc["partitions"]:[], gap );
                if( size(l)>0 )
                {
                    solutions[k] = eval(l);
                    solutions[k,"disk"] = eval(rp);
                }
            }
        });
        map ret = $[];
        if( size(solutions)>0 )
        {
            foreach( string k, map e, solutions,
                     ``{
                y2milestone( "disk %1 weight %2", k, e["weight"]:0 );
            });
            list<string> disks = maplist( string k, map e, solutions, ``(k) );
            disks = sort( string a, string b, disks, 
                          ``(solutions[a,"weight"]:0>solutions[b,"weight"]:0));
            y2milestone( "sorted disks %1", disks );
            ret = solutions[disks[0]:""]:$[];
            ret["device"] = disks[0]:"";
        }
        return( ret );
    }


    
    /**
     * Process partition data
     */
    define map process_partition_data( string dev, map solution )
        ``{
        map disk = solution["disk"]:$[];
        list<map> partitions = [];
        string value = "";
        map mapvalue = $[];
        boolean remove_boot = false;
        if( size( filter( map e, solution["partitions"]:[], 
                          ``(e["mount"]:""==Partitions::BootMount() && 
                             e["auto_added"]:false)))>0 )
        {
            foreach( map e, solution["solution","gap"]:[],
                     ``{
                foreach( list a, e["added"]:[],
                         ``{
                    integer pindex = a[0]:0;
                    if( solution["partitions",pindex,"mount"]:"" == "/" &&
                        disk["cyl_count"]:0 > Partitions::BootCyl() &&
                        e["end"]:0 <= Partitions::BootCyl() )
                    {
                        remove_boot = true;
                    }
                });
            });
        }
        integer index = 0;
        if( remove_boot )
        {
            foreach( map e, solution["solution","gap"]:[],
                     ``{
                list nlist = [];
                foreach( list a, e["added"]:[],
                         ``{
                    integer pindex = a[0]:0;
                    if( solution["partitions",pindex,"mount"]:"" == 
                        Partitions::BootMount() )
                    {
                        integer rest = a[2]:0;
                        y2milestone( "remove unneeded %3 %1 cyl %2", 
                                     e["added"]:[], rest, Partitions::BootMount() );
                        list<list> nlist = filter( list l, e["added"]:[], ``(l[0]:0!=pindex));
                        if( size(nlist)>0 && !e["exists"]:false )
                        {
                            list weight = maplist( list l, nlist, ``(l[2]:0) );
                            map r = $[];
                            r = distribute_space( rest, weight, nlist, 
                                                  solution["partitions"]:[] );
                            nlist = eval(r["added"]:[]);
                            solution["solution","gap",index,"cylinders"] = 
                                e["cylinders"]:0 - r["diff"]:0;
                        }
                        solution["solution","gap",index,"added"] = eval(nlist);
                        y2milestone( "remove unneeded %2 %1", e["added"]:[],
                                     Partitions::BootMount() );
                    }
                    pindex = pindex+1;
                });
                index = index + 1;
            });
        }
        index = 0;
        foreach( map e, solution["solution","gap"]:[],
                 ``{
            if( !e["exists"]:false && e["cylinders"]:0>0 )
            {
                integer increase = 0;
                list<integer> weight = 
                    maplist( list l, e["added"]:[], 
                             ``(solution["partitions",l[0]:0,"grow"]:false ? 1 : 0) );
                if( find( integer l, weight, ``(l>0) ) != nil ) 
                {
                    map r = $[];
                    r = distribute_space( e["cylinders"]:0, weight, e["added"]:[], 
                                          solution["partitions"]:[] );
                    solution["solution","gap",index,"added"] = eval(r["added"]:[]);
                    solution["solution","gap",index,"cylinders"] = 
                        e["cylinders"]:0 - r["diff"]:0;
                    y2milestone( "increase increasable p %1 cyl %2", 
                                 solution["solution","gap",index,"added"]:[],
                                 solution["solution","gap",index,"cylinders"]:0 );
                }
            }
            index = index + 1;
        });
        foreach( map e, solution["solution","gap"]:[],
                 ``{
            if( e["exists"]:false )
            {
                integer index = 0;
                integer pindex = e["added",0,0]:0;
                string mount = solution["partitions",pindex,"mount"]:"";
//                integer fsid = Partitions::fsid_native;
                integer fsid = disk["partitions",pindex,"fsid"]:Partitions::fsid_native;
                if( disk["partitions",pindex,"type"]:`primary != `primary )
                    fsid = disk["partitions",pindex+1,"fsid"]:Partitions::fsid_native;
                if( mount == "swap" )
                {
                    fsid = Partitions::fsid_swap;
                }
                if( solution["partitions",pindex,"fsid"]:0 != 0 )
                {
                    fsid = solution["partitions",pindex,"fsid"]:0;
                }
                foreach( map p, disk["partitions"]:[],
                         ``{
                    if( !p["delete"]:false && p["nr"]:0 == e["added",0,1]:0 )
                    {
                        p["format"] = solution["partitions",pindex,"format"]:true;
                        if( solution["partitions",pindex,"resize"]:false == true ) {
                            p["resize"] = true;
                            p["region"] = solution["partitions",pindex,"region"]:[];
                        }
                        p["mount"] = mount;
                        if (e["reuse"]:false)
                            p["used_fs"] = solution["partitions",pindex,"filesystem"]:p["detected_fs"]:Partitions::DefaultFs();
                        else
                            p["used_fs"] = solution["partitions",pindex,"filesystem"]:Partitions::DefaultFs();
                        
                        value = solution["partitions",pindex,"fstopt"]:"";
                        if( size(value)>0 )
                        {
                            p["fstopt"] = value;
                        }
                        else
                        {
                            p["fstopt"] = FileSystems::DefaultFstabOptions( p );
                        }
                        mapvalue = solution["partitions",pindex,"fs_options"]:$[];
                        if( size(mapvalue)>0 )
                        {
                            p["fs_options"] = mapvalue;
                        }
                        value = solution["partitions",pindex,"label"]:"";
                        symbol mb = solution["partitions",pindex,"mountby"]:`no_mb;
                        if( mb != `no_mb ) {
                                p["mountby"] = mb;
                        }
                        if( size(value)>0 )
                        {
                            p["label"] = value;
                        }
                
                        
                        if (solution["partitions",pindex,"loop_fs"]:false
                            || solution["partitions",pindex,"crypt_fs"]:false)
                        {
                            //p["loop_fs"]  =	solution["partitions",pindex,"crypt_fs"]:false;
                            p["enc_type"] = solution["partitions",pindex,"enc_type"]:`twofish;
                            Storage::SetCryptPwd( p["device"]:"",
                                                  solution["partitions",pindex,"crypt_key"]:"" );
                            //p["crypt"] =	solution["partitions",pindex,"crypt"]:"twofish256";
                        }
                        
                        if( p["fsid"]:0 != fsid )
                        {
                            p["change_fsid"] = true;
                            p["ori_fsid"] = p["fsid"]:0;
                            p["fsid"] = fsid;
                        }
                        if (solution["partitions",pindex,"lvm_group"]:"" != "")
                        {
                            p["used_fs"]	=  `unknown;
                            p["fsid"]		=  Partitions::fsid_lvm;
                            p["format"]		=  false;
                            p["lvm_group"]	=  solution["partitions",pindex,"lvm_group"]:"";
                            p["mount"]		=  "";
                            p["fstype"]		=  "Linux LVM";
                        } 
                        else if (solution["partitions",pindex,"raid_name"]:"" != "")
                        {			   
                            p["used_fs"]	= `unknown;
                            p["fsid"]	= Partitions::fsid_raid;
                            p["format"]	= false;
                            p["raid_name"]	= solution["partitions",pindex,"raid_name"]:"";
                            p["raid_type"]   = solution["partitions",pindex,"raid_type"]:"raid";
                            p["mount"]	= "";
                            p["fstype"]	= "Linux RAID";
                        }
                        
                        disk["partitions",index] = p;
                        y2milestone( "reuse auto partition %1", p );
                    }
                    index = index + 1;
                });
            }
            else
            {
                list region = [ e["start"]:0, e["end"]:0-e["start"]:0+1 ];
                map part = $[];
        
                if( e["extended"]:false && e["created"]:0 > 0 && size(e["created_primary"]:[]) == 0)
                {
                    part["create"] = true;
                    part["nr"] = e["created"]:0;
                    part["device"] = Storage::GetDeviceName( dev, part["nr"]:-1 );
                    part["region"] = eval(region);
                    part["type"] = `extended;
                    part["fsid"] = Partitions::fsid_extended_win;
                    part["fstype"] = Partitions::FsIdToString( part["fsid"]:0 );
                    y2milestone( "extended auto partition %1", part );
                    partitions = add( partitions, eval(part));
                }
                foreach( list a, e["added"]:[],
                         ``{
                    part = $[];
                    integer pindex = a[0]:0;
                    string mount = solution["partitions",pindex,"mount"]:"";
                    integer fsid = Partitions::fsid_native;
                    part["format"] =  solution["partitions",pindex,"format"]:true;
                    if( mount == "swap" )
                    {
                        fsid = Partitions::fsid_swap;
                    }
                    if( solution["partitions",pindex,"filesystem_id"]:0 != 0 )
                    {
                        fsid = solution["partitions",pindex,"filesystem_id"]:0;
                        if( !haskey( solution["partitions",pindex]:$[], "filesystem" ))
                        {
                            part["format"] = false;
                        }
                        y2milestone( "partition id %1 format %2 part %3", fsid, 
                                     part["format"]:false, 
                                     solution["partitions",pindex]:$[] );
                    }
                    part["create"] = true;
                    part["nr"] = a[1]:0;
                    part["device"] = Storage::GetDeviceName( dev, part["nr"]:0 );
                    region[1] = a[2]:0;
                    part["region"] = eval(region);
                    region[0] = region[0]:0 + region[1]:0;
                    part["type"] = `primary;
                    if( e["extended"]:false )
                    {
                        part["type"] = `logical;
                    }
                    
                    if(contains(e["created_primary"]:[],  a[1]:0 ))
                    {
                        part["type"] = `primary;
                    }
                         
                    part["mount"] = mount;
                    symbol mb = solution["partitions",pindex,"mountby"]:`no_mb;
                    if( mb != `no_mb ) {
                            part["mountby"] = mb;
                    }
                    part["used_fs"] = 
                        solution["partitions",pindex,"filesystem"]:((mount == "swap")?(`swap):(Partitions::DefaultFs()));
                    value = solution["partitions",pindex,"fstopt"]:"";
                    if( size(value)>0 )
                    {
                        part["fstopt"] = value;
                    }
                    else
                    {
                        part["fstopt"] = FileSystems::DefaultFstabOptions( part );
                    }
                    
                    mapvalue = solution["partitions",pindex,"fs_options"]:$[];
                    if( size(mapvalue)>0 )
                    {
                        part["fs_options"] = mapvalue;
                    }
                    
                    if (solution["partitions",pindex,"loop_fs"]:false
                        || solution["partitions",pindex,"crypt_fs"]:false)
                    {
                        //part["loop_fs"]  =	solution["partitions",pindex,"crypt_fs"]:false;
            part["enc_type"] = solution["partitions",pindex,"enc_type"]:`twofish;
                        Storage::SetCryptPwd( part["device"]:"",
                                              solution["partitions",pindex,"crypt_key"]:"" );
                        //part["crypt"] =	solution["partitions",pindex,"crypt"]:"twofish256";
                    }
                    
                    value = solution["partitions",pindex,"label"]:"";
                    if( size(value)>0 )
                    {
                        part["label"] = value;
                    }
                    part["fsid"] = fsid;
                    part["fstype"] = Partitions::FsIdToString( fsid );
                    if (solution["partitions",pindex,"lvm_group"]:"" != "")
                    {
                        part["used_fs"]	=  `unknown;
                        part["fsid"]		=  Partitions::fsid_lvm;
                        part["format"]		=  false;
                        part["lvm_group"]	=  solution["partitions",pindex,"lvm_group"]:"";
                        part["mount"]		=  "";
                        part["fstype"]		=  "Linux LVM";
                    }
                    else if (solution["partitions",pindex,"raid_name"]:"" != "")
                    {			   
                        part["used_fs"]	= `unknown;
                        part["fsid"]	= Partitions::fsid_raid;
                        part["format"]	= false;
                        part["raid_name"]	= solution["partitions",pindex,"raid_name"]:"";
                        part["raid_type"]   = solution["partitions",pindex,"raid_type"]:"raid";
                        part["mount"]	= "";
                        part["fstype"]	= "Linux RAID";
                    }
                    
                    y2milestone( "auto partition %1", part );
                    partitions = add( partitions, eval(part));

                    if (a[1]:0 + 1 == e["created"]:0 &&  e["extended"]:false )
                    {
                        part = $[];
                        list ext_region = [ region[0]:0, e["end"]:0-region[0]:0+1 ];
                        part["create"] = true;
                        part["nr"] = e["created"]:0;
                        part["device"] = Storage::GetDeviceName( dev, part["nr"]:-1 );
                        part["region"] = ext_region;
                        part["type"] = `extended;
                        part["fsid"] = Partitions::fsid_extended_win;
                        part["fstype"] = Partitions::FsIdToString( part["fsid"]:0 );
                        y2milestone( "extended auto partition %1", part );
                        partitions = add( partitions, eval(part));					
                    }
                });
                partitions = sort( map a, map b, partitions, ``(a["nr"]:0<b["nr"]:0));
            }
        });
        disk["partitions"] = union( disk["partitions"]:[], partitions );
        y2milestone( "disk %1", disk );
        return( disk );
    }

    define map find_matching_partition_size(map gap, integer nr)
        {
            list mg = filter(map g, gap["gap"]:[], ``(g["nr"]:-1 == nr && g["reuse"]:false));
            y2milestone("usepart partition: %1", mg[0]:$[]);
            return mg[0]:$[];
        }

    /**
     * Add Cylinder Information
     */
    define map add_cylinder_info( map conf, map gap )
        ``{
        integer big_cyl = 4 * 1024 * 1024 * 1024; 
        integer cyl_size = gap["cyl_size"]:1;
        /*
          // FIXME: Why is sorting needed here?
        conf["partitions"] = 
            sort( map a, map b, conf["partitions"]:[],
                  ``({
                      if( a["max_cyl"]:big_cyl != b["max_cyl"]:big_cyl )
                          return( a["max_cyl"]:big_cyl < b["max_cyl"]:big_cyl );
                      else
                          return( a["size"]:0 > b["size"]:0 );
                  }));
        */
        y2milestone( "parts %1", conf["partitions"]:[] );
        integer sum = 0;
        conf["partitions"] = maplist( map p, conf["partitions"]:[],
                                      ``{
                                          sum = sum + p["pct"]:0;
                                          p["cylinders"] = (p["size"]:0+cyl_size-1)/cyl_size;
                                          
                                          // p["cylinders"] = (p["size"]:0)/cyl_size;

                                          map mg = find_matching_partition_size(gap,p["usepart"]:0);
                                          if (mg != $[])
                                          {
                                              /* need next two lines because of #284102 */
                                              /* FIXME: check problems with resizing */
                                              p["cylinders"] = mg["cylinders"]:0;
                                              p["size"] = mg["size"]:0;
                                              if (p["usepart"]:0< gap["max_primary"]:0)
                                              {
                                                  p["partition_type"] = "primary";						  
                                              }
                                          }
                                          
                                          if( p["cylinders"]:0 == 0 )
                                          {
                                              p["cylinders"] = 1;
                                          }
                                          return( p );
                                      });
        y2milestone( "sum %1", sum );
        y2milestone( "parts %1", conf["partitions"]:[] );
        if( sum>100 )
        {
            integer rest = sum - 100;
            conf["partitions"] = maplist( map p, conf["partitions"]:[],
                                          ``{
                                              if( haskey( p, "pct" ) )
                                              {
                                                  integer pct = p["pct"]:0;
                                                  integer diff = ((rest * pct) + sum/2) / sum;
                                                  sum = sum - pct;
                                                  rest = rest - diff;
                                                  p["pct"] = pct - diff;
                                              }
                                              return( p );
                                          });
        }	
        conf["partitions"] = maplist( map p, conf["partitions"]:[],
                                      ``{
                                          if( haskey( p, "pct" ) )
                                          {
                                              integer cyl = gap["sum"]:0 / 100 * p["pct"]:0;
                                              cyl = (cyl+cyl_size/2) / cyl_size;
                                              if( cyl == 0 )
                                              {
                                                  cyl = 1;
                                              }
                                              p["want_cyl"] = cyl;
                                          }
                                          if( p["max"]:0 > 0 )
                                          {
                                              integer cyl = (p["max"]:0+cyl_size-1) / cyl_size;
                                              p["size_max_cyl"] = cyl;
                                              if( p["want_cyl"]:0 > cyl )
                                              {
                                                  p["want_cyl"] = cyl;
                                              }
                                          }					  
                                          return( p );
                                      });
        y2milestone( "parts %1", conf["partitions"]:[] );
        return( conf );
    }

    
    /**
     * Compute perfect partition list
     * @param ps Partition List from control file
     * @param g Calculated Gaps
     * @return map Best partition list using the gaps
     */
    define map get_perfect_list( list ps, map g ) {
        y2milestone( "requested partitions  %1", ps );
        y2milestone( "calculated gaps %1", g );

        ps = maplist( map partition, (list<map>)ps, ``{
            if( partition["resize"]:false ) {
                // this is a cylinder correction for resized partitions
                // bnc#580842
                partition["cylinders"] = partition["region",1]:0;
                y2milestone("cylinder correction to %1", partition["cylinders"]:0);
            }
            return partition;
        });

        foreach( map rp, (list<map>)ps, ``{
            if( rp["resize"]:false ) {
                integer new_cyl_size = 0;
                integer cyl_size_change = 0;
                integer old_end = 0;
                integer new_end = 0;
                g["gap"] = maplist( map gap, g["gap"]:[], ``{
                    y2milestone("working on gap %1", gap);
                    if( new_cyl_size != 0 ) {
                        gap["cylinders"] = gap["cylinders"]:0 + cyl_size_change;
                        gap["start"] = gap["start"]:0 - cyl_size_change;
                        gap["size"] = gap["size"]:0 + cyl_size_change * g["cyl_size"]:0;
                        y2milestone("changing gap because of a resize of a previous partition gap is now: cyl=%1 start=%2 size=%3", gap["cylinders"]:0, gap["start"]:0, gap["size"]:0 );
                        new_cyl_size = 0;
                    } else if( gap["nr"]:-1 == rp["partition_nr"]:-2 ) {
                        new_cyl_size = rp["cylinders"]:0;
                        cyl_size_change = gap["cylinders"]:0 - new_cyl_size;
                        old_end = gap["end"]:0;
                        y2milestone("partition resize cyl_size_change=%1",cyl_size_change);

                        gap["cylinders"] = new_cyl_size;
                        //gap["size"] = gap["size"]:0 + cyl_size_change * g["cyl_size"]:0;
                        gap["size"] = new_cyl_size * g["cyl_size"]:0;
                        gap["end"] = gap["start"]:0 + new_cyl_size - 1;
                        new_end = gap["end"]:0;
                        y2milestone("changing gap to %1",gap);
                    }
                    return gap;
                });
                if( new_cyl_size != 0 ) {
                    map new_gap = $[];
                    new_gap["cylinders"] = cyl_size_change;
                    new_gap["size"] = cyl_size_change * g["cyl_size"]:0;
                    new_gap["start"] = new_end + 1;
                    new_gap["end"] = old_end;
                    new_gap["size"] = new_gap["end"]:0 - new_gap["start"]:0 + 1;
                    g["gap"] = add( g["gap"]:[], new_gap );
                    y2milestone("added new gap after shrinking %1", new_gap);
                }
            }
        });

        /**
          * If gaps are available
          * AND (
          * extended partitions are possible and there are
          * primaries left and number of requested partitions(+1) is less than all available
          * primaries and logical slots
          * OR
          * extended is not possible and number of requested partitions is less than all
          * available primaries and logical slots )
          */
        if( size(g["gap"]:[])>0 &&
            ((g["extended_possible"]:false && 
//            size(g["free_pnr"]:[])>0 &&             // reusing all 4 primaries will fail with this
              size(ps)+1 <= size(g["ext_pnr"]:[])+size(g["free_pnr"]:[])) ||
             (!g["extended_possible"]:false &&
              size(ps) <= size(g["ext_pnr"]:[])+size(g["free_pnr"]:[]))) )
        {
            map lg = (map) eval(g);

            // prepare local gap var
            lg["gap"] = maplist( map e, lg["gap"]:[], 
                                 ``{ 
                                     e["orig_cyl"] = e["cylinders"]:0;
                                     e["added"] = [];
                                     return( e );
                                 });
            lg["procpart"] = 0;
            
            list lp = (list) eval(ps);
            
            if( g["extended_possible"]:false &&
                size(ps)+1>=size(g["free_pnr"]:[]) &&
                size(filter(map up, (list<map>)ps, ``(
                            up["partition_type"]:"none" == "primary" || 
                            contains(lg["free_pnr"]:[], up["partition_nr"]:0) )))  == 0 
                )
            {
                y2milestone( "creating extended" );
                integer index = 0;
                foreach( map e, lg["gap"]:[], 
                         ``{
                    if( !e["exists"]:false )
                    {
                        map gap = (map) eval(lg);
                        gap["gap",index,"created"] = gap["free_pnr",0]:1;
                        gap["free_pnr"] = remove( gap["free_pnr"]:[1], 0 );
                        gap["gap",index,"extended"] = true;
                        add_part_recursive( ps, gap );
                    }
                    index = index+1;
                });
            }
            else
            {
                y2milestone( "not creating extended now" );		
                add_part_recursive( ps, lg );
            }
        }
        map ret = $[];
        if( size(cur_gap)>0 )
        {
            ret["weight"] = cur_weight;
            ret["solution"] = eval(cur_gap);
            ret["partitions"] = eval(ps);
        }
        y2milestone( "ret weight %1", ret["weight"]:-1000000 );
        y2milestone( "ret solution %1", ret["solution","gap"]:[] );
        return( ret );
    }


    
    /**
     * Recursive Adding of partitions
     * @param ps Partition list from control file
     * @param g Calculated gaps
     * @return void
     */
    define void add_part_recursive( list ps, map g )
        ``{
        y2milestone( "partition index %1", g["procpart"]:0 );
        y2milestone( "partitions %1", ps );
        y2milestone( "gap %1", g );

    // creation_needed indicates the case, that we do not
    // create a single partition but are reusing some
    boolean creation_needed = false;
    foreach( map p, (list<map>)ps, ``{
        if( p["create"]:true == true ) {
            creation_needed = true;
        }
    });
    y2milestone("creation is needed? %1",creation_needed);


        map lg = (map) eval(g);
        integer gindex = 0;
        integer pindex = lg["procpart"]:0;
        map part = ps[pindex]:$[];
        lg["procpart"] = pindex + 1;
        y2milestone( "working on partition %1", part );
        foreach( map e, lg["gap"]:[], 
                 ``{
            y2milestone( "start: gap section  %1", e );
            
            if( part["max_cyl"]:0 <= e["end"]:0 &&	
                part["cylinders"]:0 <= e["cylinders"]:0 &&
                (!e["extended"]:false && (size(lg["free_pnr"]:[])>0 || ! creation_needed ) ||
                 e["extended"]:false && size(lg["ext_pnr"]:[])>0)
                )
            {
                map llg = (map) eval(lg);

                list addl = [ pindex ];
                
                // number of needed primaries by user
                integer sp = size(filter(map up, (list<map>)ps,
                                         ``(up["partition_type"]:"none"  == "primary" ||
                                             contains(llg["free_pnr"]:[], up["partition_nr"]:0))));

                y2milestone("number of primaries requested: %1", sp );
                integer cp = 0;
                foreach(map gg, llg["gap"]:[], ``{
                    cp = cp + size(gg["created_primary"]:[]);
                });

                boolean ex = false;
                y2milestone("number of created primaries: %1", cp );
        
                // Extended
                if( llg["extended_possible"]:false &&
                    !e["extended"]:false &&
                    size(ps)+1>=size(llg["free_pnr"]:[]) && // maybe not needed
                    cp == sp  &&   sp  > 0    )
                {
                    y2milestone( "creating extended" );
                                      
                    map gap = (map) eval(lg);
                    llg["gap",gindex,"created"] = llg["free_pnr",0]:1;
                    llg["free_pnr"] = remove( llg["free_pnr"]:[1], 0 );
                    llg["gap",gindex,"extended"] = true;
                    // reset
                    llg["procpart"] = pindex ;
                    ex = true;
                
                }
                // Logical
                else if( e["extended"]:false ) // ||
                    /*
                         ( ps[pindex,"partition_type"]:"none" != "primary" &&
                           ps[pindex,"partition_type"]:"none" != "extended")
                         )
                         */
                {
                    if (!e["reuse"]:false)
                    {	
                        addl = add( addl, llg["ext_pnr",0]:5 );
                        llg["ext_pnr"] = remove( llg["ext_pnr"]:[0], 0 );
                    } else {
                        addl = add( addl, e["nr"]:0 );
                    }
                }
                // Primary
                else
                {
                    if (e["exists"]:false)
                    {
                        addl = add( addl, e["nr"]:0 );
                        llg["gap",gindex,"created_primary"] = add(llg["gap",gindex,"created_primary"]:[],
                                                                  e["nr"]:0 );
                    } else {
                        addl = add( addl, llg["free_pnr",0]:1 );
                        llg["gap",gindex,"created_primary"] = add(llg["gap",gindex,"created_primary"]:[],
                                                                  llg["free_pnr",0]:1 );
                        llg["free_pnr"] = remove( llg["free_pnr"]:[0], 0 );
                    }

                }
                
                if (!ex)
                {
                    llg["gap",gindex,"added"] = 
                        add( llg["gap",gindex,"added"]:[], addl );
                }

                if (!ex) {
                    if( e["exists"]:false )
                    {
                        llg["gap",gindex,"cylinders"] = 0;
                    }
                    else
                    {
                        llg["gap",gindex,"cylinders"] = 
                            llg["gap",gindex,"cylinders"]:0 - part["cylinders"]:0;
                    }
                }
                
                if( pindex+1 < size(ps) || ex )
                {
                    add_part_recursive( ps, llg );
                }
                else
                {
                    map ng = normalize_gaps(ps, llg);
                    integer val = do_weighting( ps, ng );
                    y2milestone( "val %1 cur_weight %2 size %3", val, cur_weight, size(cur_gap));
                    if( val > cur_weight || size(cur_gap)==0 )
                    {
                        cur_weight = val;
                        cur_gap = (map)eval(ng);
                    }
                }
            }
            gindex = gindex+1;
        });
        
    };



    /**
     * Normalize Gaps
     */
    define map normalize_gaps( list ps, map g )
        ``{
        y2milestone( "normalize_gaps: gap %1", g );
        integer gindex = 0;
        integer pindex = 0;
        foreach( map e, g["gap"]:[],
                 ``{
            y2milestone( "gap section %1", e );
            if( e["exists"]:false )
            {
                if( size(e["added"]:[])>0 && size(e["added",0]:[])==2 )
                {
                    e["added",0] = add( e["added",0]:[], e["orig_cyl"]:1 );
                }
            }
            else
            {
                integer rest = e["cylinders"]:0;
                integer needed = 0;
                integer tidx = 0;
                foreach( list p, e["added"]:[],
                         ``{
                    tidx = p[0]:0;
                    if( ps[tidx,"want_cyl"]:0 > ps[tidx,"cylinders"]:0 )
                    {
                        needed = needed + ps[tidx,"want_cyl"]:0 -
                            ps[tidx,"cylinders"]:0;
                    }
                });
                y2milestone( "needed %1 rest %2", needed, rest );
                if( needed > rest )
                {
                    list tr = [];
                    list weight = 
                        maplist( list l, e["added"]:[], 
                                 ``({
                                     integer idx = l[0]:0;
                                     integer d = ps[idx,"want_cyl"]:0 - 
                                         ps[idx,"cylinders"]:0;
                                     if( d>0 )
                                     {
                                         l = add( l, ps[idx,"cylinders"]:0 );
                                     }
                                     tr = add( tr, l );
                                     return( d>0 ? d : 0 );
                                 }));
                    y2milestone( "tr %1", tr );
                    map r = $[];
                    r = distribute_space( rest, weight, tr, ps );
                    g["gap",gindex,"added"] = eval(r["added"]:[]);
                    g["gap",gindex,"cylinders"] = e["cylinders"]:0 - r["diff"]:0;
                    y2milestone( "partly satisfy %1 cyl %2", g["gap",gindex,"added"]:[],
                                 g["gap",gindex,"cylinders"]:0 );
                }
                else
                {
                    g["gap",gindex,"cylinders"] = e["cylinders"]:0 - needed;
                }

                pindex = 0;
                foreach( list p, g["gap",gindex,"added"]:[], 
                         ``{
                    if( size(p)<3 )
                    {
                        tidx = p[0]:0;
                        if( ps[tidx,"want_cyl"]:0 > ps[tidx,"cylinders"]:0 )
                        {
                            p = add( p, ps[tidx,"want_cyl"]:0 );
                        }
                        else
                        {
                            p = add( p, ps[tidx,"cylinders"]:0 );
                        }
                        g["gap",gindex,"added",pindex] = p;
                        y2milestone( "satisfy p %1 cyl %2", p, e["cylinders"]:0 );
                    }
                    pindex = pindex+1;
                });
                y2milestone( "added %1", g["gap",gindex,"added"]:[] );
            }
            gindex = gindex + 1;
        });
        gindex = 0;
        foreach( map e, g["gap"]:[],
                 ``{
            if( !e["exists"]:false && e["cylinders"]:0>0 )
            {
                list<integer> weight = maplist( list l, e["added"]:[], 
                                                ``(ps[l[0]:0,"size"]:0==0 ? 1 : 0) );
                if( find( integer l, weight, ``(l>0) ) != nil ) 
                {
                    map r = $[];
                    r = distribute_space( e["cylinders"]:0, weight, e["added"]:[], 
                                          ps );
                    g["gap",gindex,"added"] = eval(r["added"]:[]);
                    g["gap",gindex,"cylinders"] = e["cylinders"]:0 - r["diff"]:0;
                    y2milestone( "increase max p %1 cyl %2", g["gap",gindex,"added"]:[], 
                                 g["gap",gindex,"cylinders"]:0 );
                }
            }
            gindex = gindex + 1;
        });
        gindex = 0;
        /* makes trouble on small harddisks
        foreach( map e, g["gap"]:[],
                 ``{
            if( !e["exists"]:false && e["cylinders"]:0>0 && 
                e["cylinders"]:0 < g["disk_cyl"]:0/20 )
            {
                list weight = maplist( list l, e["added"]:[], ``(l[2]:0) );
                map r = $[];
                r = distribute_space( e["cylinders"]:0, weight, e["added"]:[], ps );
                g["gap",gindex,"added"] = eval(r["added"]:[]);
                g["gap",gindex,"cylinders"] = e["cylinders"]:0 - r["diff"]:0;
                y2milestone( "close small gap p %1 cyl %2", g["gap",gindex,"added"]:[], 
                             g["gap",gindex,"cylinders"]:0 );
            }
            gindex = gindex + 1;
        });
        */
        y2milestone( "gap %1", g );
        return( g );
    };


    /**
     * Distribute Spaces
     */
    define map distribute_space( integer rest, list weights, list added, list ps )
        ``{
        integer diff_sum = 0;
        integer sum = 0;
        integer index = 0;
        integer pindex = 0;
        y2milestone( "rest %1 weights %2 added %3", rest, weights, added );
        foreach( list p, (list<list>)added,
                 ``{
            pindex = p[0]:0;
            if( ps[pindex,"size_max_cyl"]:0==0 || ps[pindex,"grow"]:false ||
                ps[pindex,"size_max_cyl"]:0 > p[2]:0 )
            {
                sum = sum + weights[index]:0;
            }
            index = index+1;
        });
        index = 0;
        y2milestone( "sum %1 rest %2 added %3", sum, rest, added );
        foreach( list p, (list<list>)added,
                 ``{
            pindex = p[0]:0;
            if( size(p)==3 && sum>0 &&
                (ps[pindex,"size_max_cyl"]:0==0 || ps[pindex,"grow"]:false ||
                 ps[pindex,"size_max_cyl"]:0 > p[2]:0) )
            {
                integer diff = ((rest*weights[index]:0) + sum/2) / sum;
                if( ps[pindex,"size_max_cyl"]:0>0 && 
                    !ps[pindex,"grow"]:false &&
                    diff > ps[pindex,"size_max_cyl"]:0-p[2]:0 )
                {
                    diff = ps[pindex,"size_max_cyl"]:0-p[2]:0;
                }
                sum = sum - weights[index]:0;
                rest = rest - diff;
                added[index,2] = added[index,2]:0 + diff;
                diff_sum = diff_sum + diff;
                y2milestone( "sum %1 rest %2 diff %3 added %4", sum, rest, diff, 
                             added[index]:[] );
            }
            index = index+1;
        });
        map ret = $[ "added":added, "diff" : diff_sum ];
        y2milestone( "ret (distribute_space) %1", ret );
        return( ret );
    }


    /**
     * Calculate plan weights
     */
    define integer do_weighting( list ps, map g )
        ``{
        y2milestone( "gap %1", g["gap"]:[] );
        integer ret = 0;
        integer index = 0;
        if( cur_mode == `free )
        {
            ret = 0;
        }
        if( cur_mode == `reuse )
        {
            ret = ret - 100;
        }
        else if( cur_mode == `resize )
        {
            ret = ret - 1000;
        }
        else if( cur_mode == `desparate )
        {
            ret = ret - 1000000;
        }
        y2milestone( "weight after mode ret %1", ret );
        foreach( map e, g["gap"]:[],
                 ``{
            y2milestone( "added %1", e["added"]:[] );
            if( !e["exists"]:false && e["cylinders"]:0 > 0 )
            {
                ret = ret - 5;
                if( e["cylinders"]:0 < g["disk_cyl"]:0/20 )
                {
                    ret = ret - 10;
                }
                y2milestone("weight (cyl) %1", ret );
            }
            y2milestone( "weight after gaps %1", ret );
            foreach( list p, e["added"]:[], 
                     ``{
                index = p[0]:0;
                if( e["exists"]:false && ps[index,"mount"]:""=="swap" && 
                    e["swap"]:false )
                {
                    ret = ret + 100;
                    y2milestone( "weight after swap reuse %1", ret );
                }
                if( ps[index,"want_cyl"]:0>0 )
                {
                    integer diff = ps[index,"want_cyl"]:0 - p[2]:0;
                    integer normdiff = diff * 100 / p[2]:0;
                    if( diff < 0 )
                    {
                        normdiff = -normdiff;
                    }
                    else if( diff > 0 )
                    {
                        normdiff = normdiff / 10;
                    }
                    ret = ret - normdiff;
                    ret = ret + ps[index,"want_cyl"]:0*g["cyl_size"]:1 / 
                        (100 * 1024 * 1024);
                    y2milestone( "after pct parts %1", ret );
                }
                if( ps[index,"size"]:0==0 )
                {
                    ret = ret + p[2]:0 * g["cyl_size"]:1 / (50 * 1024 * 1024);
                    y2milestone( "after maximizes parts %1", ret );
                }
                if( ps[index,"size_max_cyl"]:0 > 0 && 
                    ps[index,"size_max_cyl"]:0 < p[2]:0 )
                {
                    integer diff = p[2]:0 - ps[index,"size_max_cyl"]:0;
                    integer normdiff = diff * 100 / ps[index,"size_max_cyl"]:0;
                    ret = ret - normdiff;
                    y2milestone( "after maximal size %1", ret );
                }
            });
            /*
            if( e["cylinders"]:0 > 0 )
            {
                y2milestone("ret (before rounding): %1", ret);
                ret = ret - (e["cylinders"]:0 * g["cyl_size"]:1) / (1024*1024*1024);
                y2milestone("weight (after rounding): %1", ret);
            }
            */
        });
        y2milestone( "weight:  %1", ret );
        return( ret );
    };

    /**
     * Remove partitions
     * @param disk disk data
     * @param pm partitioning as in the control file.
     * @return map the new partition map with removed partitions
     */
    define map remove_possible_partitions( map disk , map pm)
    {
        boolean remove_special_partitions = pm["remove_special_partitions"]:false;
        list keep_partition_num = pm["keep_partition_num"]:[];
        list keep_partition_id = pm["keep_partition_id"]:[];
        list keep_partition_fsys = pm["keep_partition_fsys"]:[];

        // Special partitions
        list nodelpart = [ 0x12, 0xde, 257 ];

        // Handle <usepart> which is analog to create=false and partition_nr>0
        foreach(map p, pm["partitions"]:[], ``{
            if (p["usepart"]:0 != 0 )
                keep_partition_num=add(keep_partition_num, p["usepart"]:0 );
        });

        map ret = (map)disk;
        ret["partitions"] = maplist( map p, ret["partitions"]:[],
                ``{
                integer fsid = p["fsid"]:0;
                if( (remove_special_partitions ||
                        !contains( nodelpart, fsid ) ) &&
                    p["type"]:`primary != `extended &&
                    !contains( keep_partition_num, p["nr"]:0 ) &&
                    !contains( keep_partition_id, fsid ) &&
                    !contains( keep_partition_fsys, p["used_fs"]:`none ))
                {
                    p["delete"] = true;
                    if (haskey(p, "raid_name")) {
                        p["old_raid_name"] = p["raid_name"]:"";
                        p=remove(p, "raid_name");
                    }
                }
                return( p );
                });
        integer max_prim = Partitions::MaxPrimary(disk["label"]:"msdos");

        // delete extended if no logical remain
        if( size( filter( map p, ret["partitions"]:[],
                        ``(p["type"]:`primary == `extended)))>0 &&
                size( filter( map p, ret["partitions"]:[],
                        ``(p["nr"]:0>max_prim && !p["delete"]:false) ))==0 )
        {
            ret["partitions"] = maplist( map p, ret["partitions"]:[],
                    ``{
                    if( p["type"]:`primary == `extended )
                    {
                    p["delete"] = true;
                    }
                    return( p );
                    });
        }
        y2milestone("after removal: %1", ret);
        return( ret );
    }


    
/*
    define map remove_possible_partitions( map disk, map conf )
        ``{
        list nodelpart = [ 0x12, 0xde, 257 ];
        map ret = (map)eval(disk);
        ret["partitions"] = maplist( map p, ret["partitions"]:[],
                                     ``{
                                         integer fsid = p["fsid"]:0;
                                         if( (conf["remove_special_partitions"]:false ||
                                              !contains( nodelpart, fsid ) ) &&
                                             p["type"]:`primary != `extended &&
                                             !contains( conf["keep_partition_num"]:[], p["nr"]:0 ) &&
                                             !contains( conf["keep_partition_id"]:[], fsid ) &&
                                             !contains( conf["keep_partition_fsys"]:[], p["used_fs"]:`none ))
                                         {
                                             p["delete"] = true;
                                         }
                                         return( p );
                                     });
        integer max_prim = Partitions::MaxPrimary(disk["label"]:"msdos");
        if( size( filter( map p, ret["partitions"]:[], 
                          ``(p["type"]:`primary == `extended)))>0 &&
            size( filter( map p, ret["partitions"]:[], 
                          ``(p["nr"]:0>max_prim && !p["delete"]:false) ))==0 )
        {
            ret["partitions"] = maplist( map p, ret["partitions"]:[],
                                         ``{
                                             if( p["type"]:`primary == `extended )
                                             {
                                                 p["delete"] = true;
                                             }
                                             return( p );
                                         });
        }
        return( ret );
    };
*/


    /**
     * Try resizing windows partition
     *
     */
    define map try_resize_windows( map disk )
        ``{
        integer cyl_size = disk["cyl_size"]:1;
        map win = $[];
        map ret = (map)eval(disk);

        ret["partitions"] = maplist( map p, ret["partitions"]:[], ``{
            integer fsid = p["fsid"]:0;
            if( Partitions::IsDosPartition( fsid ) ) 
            {
                integer psize = (p["region",0]:0 + p["region",1]:1 - 1) * cyl_size;
                win = Storage::GetFreeSpace( p["device"]:"", `fat32, false );
                y2milestone( "win=%1", win );
                if( win != nil && psize > 300*1024*1024 )
                {
                    p["winfo"] = win;
                    p["region",1] = (win["new_size"]:0 + cyl_size - 1) / cyl_size;
                    y2milestone( "win part %1", p );
                }
            }
            return( p );
        });
        return( ret );
    };


    /**
     * Collect gap information
     *
     */
    define list<map> get_gaps( integer start, integer end,
                               map pd,
                               list<map> part, 
                               boolean add_exist_linux )
        ``{
        y2milestone("partitions: %1", pd["partitions"]:[] );
        list<integer> usepart_p =  maplist(map pa, pd["partitions"]:[],
                                                        ``{
                                                            return pa["usepart"]:0;
                                                        });
        list<integer> reuse = filter(integer i, usepart_p, ``(i!=0));
        
        y2milestone("reuse: %1", reuse);
        
        y2milestone( "start %1 end %2 add_exist %3", start, end, add_exist_linux );
        list<map> ret = [];
        map entry = $[];
        foreach( map p, part, 
                 ``{	    	    
            integer s = p["region",0]:0;
            integer e = s + p["region",1]:1 - 1;
            entry = $[];
            y2milestone("Getting gap from part: %1", p );
            y2milestone("start %1 s %2 e %3", start, s, e );
            if( start < s )
            {
                entry["start"] = start;
                entry["end"] = s-1;
                ret = add( ret, eval(entry) );
            }
            if( add_exist_linux && 
                (p["fsid"]:0==Partitions::fsid_native || 
                 p["fsid"]:0==Partitions::fsid_swap) )
            {
                entry["swap"] = p["fsid"]:0==Partitions::fsid_swap;
                entry["start"] = s;
                entry["end"] = e;
                entry["exists"] = true;
                entry["nr"] = p["nr"]:0;
                ret = add( ret, entry );		
            }
            
            if (contains(reuse,  p["nr"]:0))
            {
                // This partition is to be used as specified in the control file
                entry["swap"] = p["fsid"]:0==Partitions::fsid_swap;
                entry["start"] = s;
                entry["end"] = e;
                entry["exists"] = true;
                entry["reuse"] = true;
                entry["nr"] = p["nr"]:0;
                ret = add( ret, entry );	
            }
            
            start = e+1;
        });
        if( start < end )
        {
            entry = $[];
            entry["start"] = start;
            entry["end"] = end;
            ret = add( ret, entry );
        }
        y2milestone( "ret %1", ret );
        return( ret );
    }

    
    /**
     * Collect gap information
     * get_gap_info( Storage::GetTargetMap()["/dev/sda"], AutoTargetMap["/dev/sda"], false )
     */
    define map get_gap_info( map disk, map pd, boolean add_exist_linux )
        ``{
        map ret = $[];
        list<map> gap = [];
        list<map> plist = filter( map p, disk["partitions"]:[], ``(!p["delete"]:false) );
        plist = sort( map a, map b, plist, ``(a["region",0]:0<b["region",0]:0) );
        list exist_pnr = sort( maplist( map e, plist, ``(e["nr"]:0) ));
        integer max_prim = Partitions::MaxPrimary(disk["label"]:"msdos");
        boolean has_ext = Partitions::HasExtended( disk["label"]:"msdos" );

        // check if we support extended partitions
        if( has_ext )
        {
            // see if disk has an extended already
            map ext = filter( map p, plist,
                              ``(p["type"]:`primary == `extended))[0]:$[];
            ret["extended_possible"] = size(ext)==0;
            if( size(ext)>0 )
            {
                gap = get_gaps( ext["region",0]:0, 
                                ext["region",0]:0 + ext["region",1]:1-1,
                                pd,
                                filter( map p, plist, ``(p["nr"]:0>max_prim)),
                                add_exist_linux );
                gap = maplist( map e, gap, 
                               ``{
                                   e["extended"]=true;
                                   return e;
                               });
                plist = filter( map p, plist, ``(p["nr"]:0<=max_prim));
            }
        }
        else
        {
            ret["extended_possible"] = false;
        }

        gap = (list<map>)union( gap, 
                                get_gaps( 0, disk["cyl_count"]:1-1, pd, plist, add_exist_linux ));
        integer av_size = 0;
        gap = maplist( map e, gap,
                       ``{
                           e["cylinders"] = e["end"]:0 - e["start"]:0 + 1;
                           e["size"] = e["cylinders"]:0 * disk["cyl_size"]:1;
                           av_size = av_size + e["size"]:0;
                           return( e );
                       });
        gap = maplist( map e, gap,
                       ``{
                           e["sizepct"] = (e["size"]:0 * 201 / 2) / av_size;
                           if( e["sizepct"]:0 == 0 )
                           {
                               e["sizepct"] = 1;
                           }
                           return( e );
                       });
        ret["cyl_size"] = disk["cyl_size"]:1;
        ret["disk_cyl"] = disk["cyl_count"]:1;
        ret["max_primary"] = disk["max_primary"]:0;
        ret["sum"] = av_size;
        integer max_pnr = max_prim;
        integer pnr = 1;
        list free_pnr = [];
        y2milestone( "exist_pnr %1", exist_pnr );
        while( pnr<=max_pnr )
        {
            if( !contains( exist_pnr, pnr ) )
            {
                free_pnr = add( free_pnr, pnr );
            }
            pnr = pnr + 1;
        }
        ret["free_pnr"] = free_pnr;
        list<integer> ext_pnr = [ 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,
                                  25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,
                                  45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63];
        integer max_logical = disk["max_logical"]:15;
        if( max_logical<63 )
        {
            ext_pnr = filter( integer i, ext_pnr, ``(i<=max_logical));
        }
        if( !ret["extended_possible"]:false )
        {
            if( !has_ext )
            {
                ext_pnr = [];
            }
            else
            {
                integer maxext = exist_pnr[size(exist_pnr)-1]:4;
                pnr = 5;
                while( pnr<=maxext )
                {
                    ext_pnr = remove( ext_pnr, 0 );
                    pnr = pnr+1;
                }
            }
        }
        ret["ext_pnr"] = ext_pnr;
        
        ret["gap"] = sort(map a, map b, gap, ``(a["start"]:0<b["start"]:0));
        y2milestone( "ret %1", ret );
        return( ret );
    }

}


ACC SHELL 2018