Select additional pattern

From openSUSE

This modules lets you select an additional pattern. Using this, you can define different modes (client, server, standalone in our example) where each mode is actually a pattern. Then, the additional pattern is stored into the Packages::additional_pattern variable. In order to work, some hacking needs to be done into the modules/Packages.ycp. A modified version for SLED10SP1 is copied under it.

inst_additional_pattern.ycp

/**
 * File:	clients/inst_additional_pattern.ycp
 * Package:	Installation
 * Summary:	Desktop Selection
 * Authors:	Jordi Massaguer i Pla <jordi.massaguer [at] opentrends [dot] net>	
 *
 * Copyright (c) 2007 Departament d'Educació de la Generalitat de Catalunya
 * $Id: inst_installation_mode.ycp,v 1.1.1.1 2007/01/30 11:40:02 jmassaguer Exp $
 *
 */

{

textdomain "packager";

import "Directory";
import "GetInstArgs";
import "Packages";
import "Label";
import "Popup";
import "ProductFeatures";
import "Stage";
import "Wizard";

symbol additional_pattern = nil;

map<symbol,string> option2pattern = $[
    `autonom : "autonom",
    `client_aula : "client_aula",
    `servidor_aula : "servidor_aula",
];
map<string,symbol> pattern2option = $[
    "Novell-software" : `autonom,
    "client_aula" : `client_aula,
    "servidor_aula" : `servidor_aula,
];


additional_pattern = pattern2option[ Packages::additional_pattern ]:`autonom;


map display = UI::GetDisplayInfo();
integer space = display["TextMode"]:true ? 1 : 3;

// all the arguments
map argmap = GetInstArgs::argmap();


string autonom_blurb =
// explanation text for autonom   
_("Select this option if you are installing a stand alone computer.");

string client_aula_blurb =
// explanation text for client_aula
_("Select this option if you are installing a client.");

string servidor_aula_blurb =
// explanation text for servidor_aula
_("Select this option if you are installing a server.");


// help text 1/3
string help = _("<p>Choose your installation mode</p>")
+
// help text 2/3
_("<p>As a client, a server or a standalone machine.</p>")
+
// help text 3/3
_("<p>Choose your option.</p>");

term autonom = `VBox (
	    `Left (`RadioButton (`id (`autonom), `opt(`notify, `boldFont),
		// radio button
		_("S&tandalone"),
		additional_pattern == `autonom)),
	    `Left (`HBox (
		`HSpacing (3),
		`Top (`Label (autonom_blurb)),
		`HSpacing (1),
		`Right (`Top (`Image (
		    Directory::themedir + "/current/icons/48x48/apps/autonom.png",
			"")))
	    ))
	);

term client_aula = `VBox (
	    `Left (`RadioButton (`id (`client_aula), `opt (`notify, `boldFont),
		// radio button
		_("&Client"),
		additional_pattern == `client_aula)),
	    `HBox (
		`HSpacing (3),
		`Top (`Label (client_aula_blurb)),
		`HSpacing (1),
		`Right (`Top (`Image (
		    Directory::themedir + "/current/icons/48x48/apps/client.png",
			"")))
	    )
	);

term servidor_aula = `VBox (
	    `Left (`RadioButton (`id (`servidor_aula), `opt (`notify, `boldFont),
		// radio button
		_("&Server"),
		additional_pattern == `servidor_aula)),
	    `HBox (
		`HSpacing (3),
		`Top (`Label (servidor_aula_blurb)),
		`HSpacing (1),
		`Right (`Top (`Image (
		    Directory::themedir + "/current/icons/48x48/apps/servidor.png",
			"")))
	    )
	);


term contents = `RadioButtonGroup (`id (`installation_mode), `HBox (
    `HWeight(1, `Empty()),	// Distribute excess space 1:2 (left:right)
    `VBox(
	`VStretch (),
	// label (in bold font)
//	`Left (`Label (`opt (`boldFont), _("Graphical Desktop Environment"))),
//	`VSpacing (1.4),
	`VWeight (10, autonom),
	`VSpacing (0.4),
	`VWeight (10, client_aula),
	`VSpacing (0.4),
	`VWeight (10, servidor_aula),
	`VSpacing (0.4),
	`VStretch ()
//	`VWeight (5, `Empty())
    ),
    `HWeight (2, `Empty ())
));

// dialog caption
Wizard::SetContents (_("Select an installation mode."), contents, help,
    GetInstArgs::enable_back(), GetInstArgs::enable_next());
Wizard::SetTitleIcon ("yast-installation-method");
Wizard::SetFocusToNextButton();

symbol ret = nil;

repeat {
    map event = UI::WaitForEvent();
    ret = (symbol)event["ID"]:nil;

    if (event["WidgetClass"]:nil == `RadioButton)
    {
		Wizard::EnableNextButton();
	}
    else if (ret == `next)
    {
		additional_pattern = (symbol)UI::QueryWidget (`id (`installation_mode), `CurrentButton);
		if (additional_pattern == nil)
		{
	    	// popup message
		    Popup::Message (_("Select the installation mode, please."));
		    ret = nil;
		}
    }
    if (ret == `abort)
    {
		if (Popup::ConfirmAbort (Stage::initial () ? `painless : `incomplete))
	    	return `abort;
		continue;
    }

} until (ret == `back || ret == `next);

Wizard::EnableNextButton ();

if (ret == `accept)
    ret = `next;

if (ret == `next)
{
	y2milestone ("Adding aditional pattern: %1", additional_pattern);
	Packages::additional_pattern = option2pattern[additional_pattern]:"autonom";
	Packages::base_selection_changed = true;

}

return ret;



/* EOF */
}

modules/Packages.ycp

/**
 * File:	Packages.ycp
 * Package:	Package selections
 * Authors:	Anas Nashif <nashif@suse.de>
 *
 * Modifications: Jordi Massaguer Pla <jordi.massaguer [at] opentrends [dot] net>: accept aditional patterns
 * $Id: Packages.ycp 37646 2007-04-23 07:37:35Z lslezak $
 */

{

module "Packages";

textdomain "packager";

import "AddOnProduct";
import "Arch";
import "Directory";
import "InstURL";
import "Kernel";
import "Mode";
import "Stage";
import "Linuxrc";
import "Language";
import "ProductFeatures";
import "ProductControl";
import "Report";
import "SlideShow";
import "SpaceCalculation";
import "String";
import "Popup";
import "Label";
import "Wizard";
import "DirInstall";
import "PackageCallbacksInit";
import "Product";
import "DefaultDesktop";
import "SourceDialogs";

/**
 * Force full proposal routine next run
 */
boolean full_repropose = false;

/**
 * Installation source has been initialized?
 */
boolean init_called = false;

/**
 * Installation source initialization is WIP
 */
boolean init_in_progress = false;

/**
 * Error which occurred during installation source initialization
 */
string init_error = nil;

// cache for the proposed summary
map cached_proposal = nil;
// the selection used for the cached proposal
list<string> cached_proposal_packages = [];
list<map> cached_proposal_patterns = [];
list<map> cached_proposal_products = [];
list<map> cached_proposal_patches = [];
list<map> cached_proposal_selections = [];
list<string> cached_proposal_languages = [];

    global boolean install_sources = false;	// Installing source packages ?
    global integer timestamp = 0;		// last time of getting the target map

    global string metadir = "/yast-install";
    global boolean metadir_used = false;	// true if meta data and inst-sys is in ramdisk

    global list<integer> theSources = [];		// id codes of sources in priority order
    global list<string> theSourceDirectories = [];	// product directories on sources
    global map<integer,integer> theSourceOrder = $[];	// installation order

    string servicepack_metadata = "/servicepack.tar.gz";

    // to remember if warning should occurre if switching base selection
    global boolean base_selection_modified = false;

    global boolean base_selection_changed = false;

    //opentrends hack: to add additional pattern
    global string additional_pattern = nil;
    //

    // Local variables


    string choosen_base_selection = "";

    // count of errors during packages solver
    global integer solve_errors = 0;

    /**
     * Packages to be selected when proposing the list
     */
    list<string> additional_packages = [];

    boolean system_packages_selected = false;

// DefaultDesktop::PrefferedWindowManager
//    global string window_manager = nil;

    global boolean using_patterns = false;

    global string add_on_products_list = nil;


// summary functions

/**
 * List selected resolvables of specified kind
 * @param what symbol specifying the kind of resolvables to select
 * @param format string format string to print summaries in
 * @return a list of selected resolvables
 */
global list<string> ListSelected (symbol what, string format) {
    if (format == "" || format == nil)
	format = "%1";
    list<map<string,any> > selected = Pkg::ResolvableProperties ("", what, "");

    // ignore hidden patterns
    if (what == `pattern)
    {
	selected = filter (map<string,any> r, selected, {
	    return r["user_visible"]:nil == true;
	});
    }

    selected = filter (map<string,any> r, selected, {
	return r["status"]:nil == `selected;
    });
    list<string> ret = maplist (map<string,any> r, selected, {
	string disp = r["summary"]:r["name"]:"";
	return sformat (format, disp);
    });
    return ret;
}

/**
 * Count the total size of packages to be installed
 * @return string formatted size of packages to be installed
 */
global string CountSizeToBeInstalled () {
    integer sz = 0;
    if (! Mode::installation () && !DirInstall::installing_into_dir )
    {
	list<string> packages = Pkg::GetPackages (`selected, true);
	foreach (string p, packages, {
	    sz = sz + Pkg::PkgSize (p);
	});
	// convert into kB
	sz = sz / 1024;
    }
    else // in case of fresh installation, disk is initially empty
    {
	map<string,list> du = Pkg::TargetGetDU ();
	foreach (string mp, list usages, du, {
	    sz = sz + usages[2]:0 - usages[1]:0;
	});
    }
    y2milestone ("Total size of packages to install (kB): %1", sz);
    return String::FormatSizeWithPrecision (sz*1024, 1, true);
}

/**
 * Return information about suboptimal distribution if relevant
 * @return string the information string or empty string
 */
global string InfoAboutSubOptimalDistribution () {
    // warn about suboptimal distribution
    // this depends on the kernel
    string dp = (string) SCR::Read(.content.DISTPRODUCT);
    if (dp==nil)
	dp = "";

    if (ProductFeatures::GetBooleanFeature ("software",
	"inform_about_suboptimal_distribution") &&
	Arch::i386 () && issubstring(dp, "DVD"))
    {
	string tmp = (string) SCR::Read(.proc.cpuinfo.value."0"."flags");
	list flags = (size (tmp) > 0) ? splitstring (tmp, " ") : [];

	// this depends on the cpu (lm = long mode)
	if (contains (flags, "lm"))
	{
	    // warning text
	    return _("Your computer is a 64-bit x86-64 system. However, you are trying to install a 32-bit distribution.");
	}
    }
    return "";
}

/**
 * Return the summary output lines
 * @param flags a list of flags, allowed are `product, `pattern, `selection,
 *  `size, `desktop
 * @return a list of the output lines
 */
global list<string> SummaryOutput (list<symbol> flags) {
    list<string> output = [ InfoAboutSubOptimalDistribution () ];
    if (contains (flags, `product))
	output = (list<string>)merge (output, ListSelected (`product, ""));
    if (contains (flags, `desktop))
	output = (list<string>)add (output, DefaultDesktop::Description ());
    if (contains (flags, `pattern))
	output = (list<string>)
	    merge (output, ListSelected (`pattern, "+  %1"));
    if (contains (flags, `selection))
	output = (list<string>)
	    merge (output, ListSelected (`selection, "+  %1"));
    if (contains (flags, `size))
	output = (list<string>)add (output,
	    // part of summary, %1 is size of packages (in MB or GB)
	    sformat (_("Size of Packages to Install: %1"),
		CountSizeToBeInstalled ()));

    output = filter (string o, output, {
	return o != "" && o != nil;
    });

    return output;
}

/**
 * Check if selected software fits on the partitions
 * @param init boolean true if partition sizes have changed
 * @return boolean true if selected software fits, false otherwise
 */
global boolean CheckDiskSize (boolean init) {
    if (init)
    {
	y2milestone ("Resetting space calculation");
	SpaceCalculation::GetPartitionInfo();
    }
    return SpaceCalculation::CheckDiskSize();
}

/**
 * Print the installatino proposal summary
 * @param flags a list of symbols, see above
 * @param boolean use_cache if true, use previous proposal if possible
 * @returnu a map proposal summary
 */
global map Summary (list<symbol> flags, boolean use_cache) {
    if (init_error != nil)
    {
	return $[
	    "warning" : init_error,
	    "warning_level" : `blocker,
	];
    }
    map ret = $[];

    if (! CheckDiskSize (! use_cache))
    {
	ret = $[
	    "warning" : ProductFeatures::GetFeature ("software","selection_type") == `fixed
		// summary warning
		? _("Not enough disk space.")
		// summary warning
		: _("Not enough disk space. Remove some packages in the single selection."),
	    "warning_level" : Mode::update() ? `warning : `blocker,
	];
    }
    else
    {
	// check available free space (less than 5% and less than 1GB)
	list<map> free_space = SpaceCalculation::CheckDiskFreeSpace(5, 1024*1024);

	if (size(free_space) > 0)
	{
	    string warning = "";

	    foreach(map df, free_space,
		{
		    string partition = df["dir"]:"";
		    integer free_pct = df["free_percent"]:0;
		    integer free_kB = df["free_size"]:0;

		    string w = sformat(_("Only %1 (%2%%) free space available on partition %3.<BR>"), String::FormatSize(free_kB*1024), free_pct, partition);

		    warning = warning + w;
		}
	    );

	    if (warning != "")
	    {
		ret["warning"] = warning;
		ret["warning_level"] = `warning;
	    }
	}
    }

    ret["raw_proposal"] = SummaryOutput (flags);
    return ret;
}






// proposal control functions

global void ForceFullRepropose () {
    full_repropose = true;
}

global boolean SelectProduct ();

/**
 * Reset package selection, but keep objects of specified type
 * @param keep a list of symbols specifying type of objects to be kept
 */
global void Reset (list<symbol> keep) {
    list<map<string,any> > restore = [];
    foreach (symbol type, keep, {
	list<map<string,any> > selected = Pkg::ResolvableProperties ("", type, "");
	foreach (map<string,any> s, selected, {
	    restore = add (restore, $[
		"type" : type,
		"name" : s["name"]:""
	    ]);
	});
    });
    Pkg::PkgReset();
    foreach (map<string,any> res, restore, {
	Pkg::ResolvableInstall (res["name"]:"", (symbol)(res["type"]:nil));
    });

    system_packages_selected = false;
}

/**
 * Initialize add-on products provided by the installation source
 */
global void InitializeAddOnProducts() {
    if (Packages::add_on_products_list != nil)
    {
	y2milestone ("Found list of add-on products to preselect: %1", Packages::add_on_products_list);
	Packages::SelectProduct ();
	PackageCallbacksInit::SetMediaCallbacks();
	AddOnProduct::AddPreselectedAddOnProducts (Packages::add_on_products_list);
	Packages::add_on_products_list = nil; // do not select them any more
    }
}


    /*-----------------------------------------------------------------------
     * LOCALE FUNCTIONS
     *-----------------------------------------------------------------------*/

    /**
     * Add a package to list to be selected before proposal
     * Can be called only before the installation proposal, later doesn't
     * have any effect
     * @param package string package to be selected
     */
    global void addAdditionalPackage(string package)
    {
	additional_packages = add (additional_packages, package);
    }

    /**
     * Compute architecture packages
     * @return list(string)
     */
    define list<string> architecturePackages ()
    {
	list<string> packages = [];

	// remove unneeded / add needed packages for ppc
	if (Arch::ppc ())
	{
	    if (Arch::board_mac ())
	    {
		packages = add (packages, "mouseemu");
	    }

	    if (Arch::board_mac_new ()
		|| Arch::board_mac_old ())
	    {
		string pmac_board = "";
		list<map> pmac_compatible = (list<map>) SCR::Read(.probe.cpu);
		foreach (map pmac_compatible_tmp, pmac_compatible, {
		    pmac_board = pmac_compatible_tmp["system"]:"";
		});

		// install pbbuttonsd on PowerBooks and iMacs
		if (issubstring (pmac_board, "PowerBook")
		    || issubstring (pmac_board, "PowerMac2,1")
		    || issubstring (pmac_board, "PowerMac2,2")
		    || issubstring (pmac_board, "PowerMac4,1")
		    || issubstring (pmac_board, "iMac,1"))
		{
		    packages = add (packages, "pbbuttonsd");
		    packages = add (packages, "powerprefs");
		}
	    }

	    if (Arch::ppc64 () && (Arch::board_chrp () || Arch::board_iseries ()))
	    {
		packages = add (packages, "iprutils");
	    }
	}

	if (Arch::ia64 ())
	{
	    // install fpswa if the firmware has an older version
	    if (SCR::Execute(.target.bash, "/sbin/fpswa_check_version") != 0)
	    {
		packages = add (packages, "fpswa");
	    }
	}

	if (Arch::is_xenU())
	{
	    // xen-tools-domU are required for registration of a Xen VM (#249157)
	    packages = add (packages, "xen-tools-domU");
	}

	// add numactl on x86_64 with SMP
	if (Arch::has_smp () && Arch::x86_64 ())
	{
	    packages = add (packages, "numactl");
	    packages = add (packages, "irqbalance");
	}

	return packages;
    }


    /**
     * graphicPackages ()
     * Compute graphic (x11) packages
     * @return list(string)	list of rpm packages needed
     */
    define list<string> graphicPackages ()
    {
	list<string> packages = [];

	// don't setup graphics if running via serial console
	if (!Linuxrc::serial_console ())
	{
	    packages = [ "xorg-x11", "xorg-x11-server", "xorg-x11-server-glx",
		"libusb", "sax2", "sax2-gui", "sax2-ident", "sax2-tools",
		"sax2-libsax", "sax2-libsax-perl"];
	}

	y2milestone ("X11 Packages to install: %1", packages);
	return packages;
    }


    /**
     * Compute special packages
     * @return list(string)
     */
    define list<string> modePackages ()
    {
	list<string> packages = [];

	if (Linuxrc::vnc ())
	{
	    packages = add (packages, "tightvnc");
	    packages = add (packages, "yast2-qt");
	    packages = add (packages, "xorg-x11");
	    packages = add (packages, "fvwm2");
	    packages = add (packages, "sax2-tools");
	}

	if (Linuxrc::display_ip ())
	{
	    packages = add (packages, "yast2-qt");
	    packages = add (packages, "xorg-x11");
	    packages = add (packages, "fvwm2");
	    packages = add (packages, "sax2-tools");
	}

	if (Linuxrc::braille ())
	{
	    packages = add (packages, "sbl");
	}
	y2milestone ("Installation mode packages: %1", packages);
	return packages;
    }


    /**
     * Compute special java packages
     * @return list(string)
     */
    define list<string> javaPackages ()
    {
	if (!Arch::alpha ())
	    return [];

	list<string> packages = [];

	list cpus = (list) SCR::Read (.probe.cpu);
	string model = cpus[0, "model"]:"EV4";
	string cputype = substring (model, 2, 1);

	if ((cputype == "6") || (cputype == "7") || (cputype == "8"))
	{
	    packages = ["cpml_ev6"];
	}
	else
	{
	    packages = ["cpml_ev5"];
	}
	return packages;
    }


    /**
     * Compute language dependant packages
     * @return list(string)
     */
    define list<string> languagePackages ()
    {
	list<string> packages = [];
	list<string> locales = Pkg::GetAdditionalLocales();
	locales = prepend (locales, Pkg::GetLocale());
	string transpac = "yast2-trans-";

	foreach (string loc, locales, {
	    if (Pkg::IsAvailable (transpac + loc))
		packages = add (packages, transpac + loc);
	    else if (Pkg::IsAvailable (transpac + substring (loc, 0, 2)))
		packages = add (packages, transpac + substring (loc, 0, 2));
	    else
		y2warning ("No locale package found for %1", loc);
	});
	y2milestone ("Language packages: %1", packages);
	return packages;
    }


    /**
     * Compute board (vendor) dependant packages
     * @return list(string)
     */
    define list<string> boardPackages ()
    {
	list<string> packages = [];

	list <map <string, any> > probe = (list <map <string, any> >)SCR::Read (.probe.system);
	packages = (list<string>)probe[0,"requires"]:[];
	y2milestone ("Board/Vendor specific packages: %1", packages);

	return packages;
    }


    /**
     * Compute packages required to access the installation source
     * @return list(string) list of the required packages
     */
    list<string> sourceAccessPackages()
    {
	list<string> ret = [];

	string instmode = Linuxrc::InstallInf("InstMode");
	y2milestone("Installation mode: %1", instmode);

	if (instmode == "smb" || instmode == "cifs")
	{
	    // /sbin/mount.cifs is required to mount a SMB/CIFS share
	    ret = ["cifs-mount"];
	}

	y2milestone("Packages for access to the installation source: %1", ret);

	return ret;
    }

    /*
     * Additional kernel packages from control file
     * @return list<string> Additional Kernel packages
     */
    define list<string> ComputeAdditionalKernelPackages ()
    {
	string final_kernel = Kernel::GetFinalKernel ();
        integer pos = findfirstof(final_kernel, "-");
        string extension = substring(final_kernel, pos, size(final_kernel));
        list<string> akp = [];
        if (extension!="")
        {
	    list<string> kernel_packages = (list<string>)
		ProductFeatures::GetFeature ("software", "kernel_packages");
            if (size(kernel_packages) > 0 && kernel_packages != nil)
            {
                akp = maplist(string p , kernel_packages, {
                        return (p + "-" + extension);
                        });
            }
        }
        return akp;
    }

    /*-----------------------------------------------------------------------
     * GLOBAL FUNCTIONS
     *-----------------------------------------------------------------------*/


global list<string> ComputeSystemPatternList () {
    list<string> pattern_list = [];
    // also add the 'laptop' selection if PCMCIA detected
    if (Arch::is_laptop () || Arch::has_pcmcia ())
    {
	foreach (string pat_name, ["laptop", "Laptop"], {
	    list<map<string, any> > pat_list = Pkg::ResolvableProperties (pat_name, `pattern, "");
	    if (size (pat_list) > 0)
		pattern_list = add (pattern_list, pat_name);
	});
    }
    y2milestone ("System patterns: %1", pattern_list);
    return pattern_list;
}


    /**
     * Build and return list of packages which depends on the
     * the current target system and the preselected packages
     * (architecture, X11....)
     * @return list<string> packages
     */
    global define list<string> ComputeSystemPackageList ()
    {
	import "Storage";
	list<string> install_list =  architecturePackages ();

	install_list = (list<string>) union (install_list, modePackages ());
	install_list = (list<string>) union (install_list,
	                                     Storage::AddPackageList());
	install_list = (list<string>) union (install_list,
					     additional_packages);

	// Kernel is added in autoinstPackages () if autoinst is enabled
	if (!Mode::update () || !Mode::autoinst ())
	{
            list <string> kernel_pkgs = Kernel::ComputePackages ();
            list <string> kernel_pkgs_additional = ComputeAdditionalKernelPackages();
	    install_list = (list <string>) union (install_list, kernel_pkgs);
            if (size(kernel_pkgs_additional) > 0 && kernel_pkgs_additional != nil)
            {
	        install_list = (list <string>) union (install_list, kernel_pkgs_additional);
            }
        }

	if (Pkg::IsSelected("xorg-x11") && Linuxrc::vnc ())
	{
	    install_list = (list<string>) union (install_list, graphicPackages ());
	}
	else
	{
	    y2milestone ("Not selecting graphic packages");
	}

	if (Pkg::IsSelected("java"))
	{
	    install_list = (list<string>) union (install_list, javaPackages ());
	}
	else
	{
	    y2milestone ("Not selecting java packages");
	}

	install_list = (list<string>) union (install_list, languagePackages ());

	install_list = (list<string>) union (install_list, boardPackages ());

	// add packages required to access the installation source in the 2nd stage and at run-time
	install_list = (list<string>) union (install_list, sourceAccessPackages());

	// and the most flexible enhancement for other products
        // NOTE: not really flexible, because it requires the client
        // in the instsys, instead use <kernel-packages> in the control file.
	if (ProductFeatures::GetFeature ("software", "packages_transmogrify") != "")
	{
            list<string> tmp_list = (list<string>)
		WFM::CallFunction (ProductFeatures::GetStringFeature ("software", "packages_transmogrify"),
				   [ install_list ]);

            // Make sure we did not get a nil from calling the client, i.e.
            // if the client does not exist at all..
            if (tmp_list != nil)
            {
                install_list = tmp_list;
            }
	}

	list<string> packages = (list<string>)
	    ProductFeatures::GetFeature ("software", "packages");
        if (size(packages) > 0 && packages != nil )
        {
            y2milestone("Adding packages from control file: %1", packages);
            install_list = (list<string>) union (install_list, packages);
        }

	install_list = toset (install_list);
	y2milestone ("auto-adding packages: %1", install_list);
	return install_list;
    }

/**
 * Check whether content file in the specified source is the same
 * as the one in the ramdisk
 * @param source integer the source ID to check
 * @return boolean true if content files match
 */
global boolean CheckContentFile (integer source) {
    y2milestone ("Checking content file");
    string instmode = Linuxrc::InstallInf("InstMode");
    if (! (instmode == nil || instmode == "cd" || instmode == "dvd"))
    {
	y2milestone ("Installing via network, not checking the content file");
	return true;
    }
    string media_content = Pkg::SourceProvideFile (source, 1, "/content");
    string media = (string)SCR::Read (.target.string, media_content);
    string ramdisk = (string)SCR::Read (.target.string, "/content");
    boolean ret = (media == ramdisk);
    y2milestone ("Content files are the same: %1", ret);
    return ret;
}

/**
 * Import GPG keys found in the inst-sys
 */
void ImportGPGKeys () {
    map out = (map) SCR::Execute (.target.bash_output, "/bin/ls -d /*.gpg");
    foreach (string file, splitstring (out["stdout"]:"", "\n"), {
	if (file != "")
	    Pkg::ImportGPGKey (file, true);
    });
}

string UpdateSourceURL (string url) {
    string ret = "";
    while (ret == "")
    {
	if (Popup::YesNo (_("Failed to initialize the catalog. Try again?")))
	{
	    ret = SourceDialogs::EditPopup (url);
	}
	else
	{
	    // error in proposal, %1 is URL
	    init_error = sformat (_("No catalog found at '%1'."),
		InstURL::HidePassword (url));
	    return "";
	}
    }
    return ret;
}

list<string> LocaleVersions (string lang) {
    list<string> ret = [ lang ];
    list<string> components = splitstring (lang, ".");
    if (components[0]:"" != lang && components[0]:"" != "")
    {
	lang = components[0]:"";
	ret = add (ret, lang);
    }
    components = splitstring (lang, "_");
    if (components[0]:"" != lang && components[0]:"" != "")
    {
	lang = components[0]:"";
	ret = add (ret, lang);
    }
    return ret;
}

string ContentFileProductLabel () {
    string language = Language::language;
    list<string> locales = LocaleVersions (Language::language);
    string ret = "";
    foreach (string loc, locales, {
	if (ret == "")
	{
	    string val = (string)SCR::Read (add (.content, "LABEL." + loc));
	    if (val != "" && val != nil)
	    {
		ret = val;
		return ret;
	    }
	}
    });
    return (string)SCR::Read (.content.LABEL);
}

void SlideShowSetUp (integer source) {
    // setup slidedir
    map productmap = Pkg::SourceProductData (source);
    string datadir = productmap["datadir"]:"suse";
    string slidedir_find = "/" + datadir + "/setup/slide";
    string dir = Pkg::SourceProvideOptionalFile (source, 1,
	slidedir_find + "/directory.yast");
    string slidedir = nil;
    if (dir != nil)
	slidedir = Pkg::SourceProvideDir (source, 1, slidedir_find);

    string our_slidedir = (string)WFM::Read (.local.tmpdir, "");
    if (slidedir == nil)
     {
	y2milestone("No slide directory '%1' found on source '%2'.",
	    slidedir_find, source);
    }
    else
    {
	// copy all files to our own copy
	WFM::Execute (.local.bash,
	    sformat ("cp -r %1/* %2/", slidedir, our_slidedir)) ;
    }
    y2milestone ("Setting up the slide directory local copy: %1",
	our_slidedir);
    SlideShow::SetSlideDir (our_slidedir);
}

integer IntegrateServicePack (boolean show_popup, string base_url) {
    /* Check for Service Pack */
    boolean servicepack_available = false;
    if ((integer)WFM::Read(.local.size, servicepack_metadata) > 0)
    {
	y2milestone("Service Pack data available");
	boolean popup_open = false;
	if (show_popup)
	{
	    UI::OpenDialog(`opt(`decorated ),
		// popup - information label
		`Label(_("Integrating booted media...")));
	    popup_open = true;
	}
	string spdir = metadir + "/Service-Pack/CD1";
	WFM::Execute (.local.mkdir, spdir);
	y2milestone ("Filling %1", spdir);
	WFM::Execute(.local.bash, "tar -zxvf " +
	    servicepack_metadata + " -C " + spdir);
	string sp_url = "dir:" + spdir;
	integer sp_source = Pkg::SourceCreate (sp_url, "");
	// close the popup in order to be able to ask about the license
	if (popup_open)
	{
	    popup_open = false;
	    UI::CloseDialog ();
	}
	if (sp_source == -1)
	{
	    Report::Error (_("Failed to integrate service pack source."));
	    return nil;
	}
	if (! AddOnProduct::AcceptedLicenseAndInfoFile(sp_source))
	{
	    y2milestone ("service pack license rejected");
	    Pkg::SourceDelete (sp_source);
	    return nil;
	}
	if ((integer)WFM::Read (.local.size, spdir + "/installation.xml") > 0)
	{
	    AddOnProduct::WFIntegrate (spdir + "/installation.xml");
	}
	if ((integer)WFM::Read (.local.size, spdir + "/y2update.tgz") > 0)
	{
	    AddOnProduct::UpdateInstSys (spdir + "/y2update.tgz");
	}
	theSources = add (theSources, sp_source);
	y2internal ("Service pack source: %1, changing to URL: %2",
	     sp_source, base_url);
	Pkg::SourceChangeUrl (sp_source, base_url);
    }
}

/**
 * Initialize the installation sources
 * @param show_popup boolean true to display information about initialization
 */
global void Initialize(boolean show_popup) {
    if (init_called || init_in_progress)
    {
	y2warning ("Packages::Initialize() already called");
	return;
    }
    init_in_progress = true;
    boolean popup_open = false;
    if (show_popup)
    {
	UI::OpenDialog(`opt(`decorated ),
	    // popup - information label
	    `Label(_("Initializing catalogs...")));
	popup_open = true;
    }

    PackageCallbacksInit::InitPackageCallbacks ();

    // Initialize package manager
    init_error = nil;
    y2milestone ("Packages::Initialize()");
    // usual mountpoint for the source medium
    string base_url = "";

    if (Mode::test ())
    {
	// Fake values for testing purposes
	base_url = "dir:///dist/next-i386";
    }
    else
    {
	base_url = InstURL::installInf2Url ("");
    }

    // hide password from URL if present
    string log_url = InstURL::HidePassword(base_url);
    y2milestone ("Initialize Package Manager: %1", log_url);

    // Set languages for packagemanager. Always set the UI language. Set
    // language for additional packages only in Stage::initial ().
    Pkg::SetLocale (Language::language);

    boolean again = true;
    theSources = Pkg::SourceStartCache (true); // dummy in 1st stage
    while (again)
    {
	if (Stage::initial ())
	{
	    integer initial_source = nil;
	    ImportGPGKeys ();
	    while (initial_source == nil)
	    {
		initial_source = Pkg::SourceCreateBase (base_url, "");
		if (initial_source == -1 || initial_source == nil)
		{
		    y2error ("No source on '%1'", log_url);
		    base_url = UpdateSourceURL (base_url);
		    if (base_url != "")
		    {
			initial_source = nil;
		    }
		    else
		    {
			if (popup_open)
			    UI::CloseDialog ();
			init_in_progress = false;
			return;
		    }
		}
		if (! CheckContentFile (initial_source))
		{
		    string label = ContentFileProductLabel ();
		    // bug #159754, release the mounted CD
		    Pkg::SourceReleaseAll();
		    Pkg::SourceDelete (initial_source);
		    initial_source = nil;
		    if (! Popup::ContinueCancel (
			// message popup, %1 is product name
			sformat (_("Insert %1 CD 1"), label)))
		    {
			init_error = sformat (_("%1 CD 1 not found"), label);
			if (popup_open)
			    UI::CloseDialog ();
			init_in_progress = false;
			return;
		    }
		}
	    }

	    SlideShowSetUp (initial_source);

	    // Set the product before setting up add-on products
	    // In the autoyast mode it could be that the proposal
	    // screen will not be displayed. So the product will
	    // not be set. Bug 178831
	    SelectProduct ();

	    if (popup_open)
	    {
		popup_open = false;
		UI::CloseDialog ();
	    }
	    theSources = [ initial_source ];
	    integer sp_source = IntegrateServicePack (show_popup, base_url);
	    if (sp_source != nil)
		theSources = add (theSources, sp_source);

	    if (ProductFeatures::GetFeature ("software", "selection_type") == `fixed)
	    {
                Pkg::SetSelection (ProductFeatures::GetStringFeature ("software", "base_selection"));
	    }

	    string tmp_add_on_products = Pkg::SourceProvideOptionalFile (initial_source, 1, "/add_on_products");
	    if (tmp_add_on_products != nil)
	    {
		add_on_products_list = ((string)SCR::Read (.target.tmpdir)) + "/add_on_products";
		WFM::Execute (.local.bash, sformat ("cp %1 %2", tmp_add_on_products, add_on_products_list));
	    }
	    else
	    {
		add_on_products_list = nil;
	    }

	}
	else // cont or normal mode
	{
	    if (theSources == nil || size (theSources) <= 0)
	    {
		y2error ("Pkg::SourceStartCache failed");
		theSources = [];
	    }
	    else if ( Stage::cont ()    // rewrite URL if cd/dvd since ide-scsi might have changed it
		     && ((substring (base_url, 0, 2) == "cd")
			|| (substring (base_url, 0, 3) == "dvd")))
	    {
		foreach (integer source, theSources, {
		    map data = Pkg::SourceGeneralData (source);		// get source data
		    string url = data["url"]:"";
		    if ((substring (url, 0, 2) == "cd")			// source comes from cd/dvd
			|| (substring (url, 0, 3) == "dvd"))
		    {
                        string new_url = InstURL::RewriteCDUrl(url);
			y2milestone ("rewrite url: '%1'->'%2'", url, InstURL::HidePassword(new_url));
			Pkg::SourceChangeUrl (source, new_url);
		    }
		});
	    }
	}

	y2milestone ("theSources %1", theSources);
	y2milestone ("theSourceDirectories %1", theSourceDirectories);
	if (size (theSources) >= 0)
	{
	    init_called = true;
	    again = false;
	}
	else
	{
	    import "PackageCallbacks";

	    // an error message
	    string errortext = sformat (_("Error while initializing package descriptions.
Check the log file %1 for more details."), Directory::logdir + "/y2log") +
		"\n" + Pkg::LastError();

	    // FIXME somewhere get correct current_label and wanted_label
	    string result = PackageCallbacks::MediaChange (errortext, base_url, "",
							   0, "", 1, "", false);
	}
    }
    if (popup_open)
	UI::CloseDialog ();
    init_in_progress = false;
}

global void Init(boolean unused) {
    Initialize (true);
}

/**
 * Select the base product on the media for installation
 * @return boolean true on success
 */
global boolean SelectProduct () {
    Packages::Initialize (true);
    list<map<string,any> > products = Pkg::ResolvableProperties ("", `product, "");
    products = filter (map<string,any> p, products, {
	return p["category"]:"" == "base";
    });
    if (size (products) == 0)
    {
	y2milestone ("No base product found on media");
	return true;
    }
    list<map<string,any> >selected_products = filter (map<string,any> p, products, {
	return p["status"]:nil == `selected;
    });
    // no product selected -> select them all
    boolean ret = true;
    if (size (selected_products) == 0)
    {
	y2milestone ("No product selected so far...");
	foreach (map<string,any> p, products, {
	    y2milestone ("Selecting product %1", p["name"]:"");
	    ret = Pkg::ResolvableInstall (p["name"]:"", `product) && ret;
	});
    }
    return ret;
}

/**
 * Select system patterns
 * @param reselect boolean true to select only those which are alrady selected
 */
void SelectSystemPatterns (boolean reselect) {
    list<string> system_patterns = ComputeSystemPatternList ();
    // autoinstallation has patterns specified in the profile
    if (! Mode::autoinst ())
    {
	system_patterns = (list<string>)
	    merge (system_patterns, DefaultDesktop::PatternsToSelect ());
	system_patterns = (list<string>)
	    toset (merge (system_patterns, Product::patterns));
   	//opentrends hack in order to add an additional_pattern
	if (additional_pattern != nil )
	{
		system_patterns = add(system_patterns, additional_pattern);
		y2milestone("adding additional_pattern %1", additional_pattern);
	}
	//end opentrends hack
	    
    }
    if (! reselect)
    {
	list<string> to_deselect = DefaultDesktop::PatternsToDeselect ();
	y2milestone ("Deselecting system patterns %1", to_deselect);
	foreach (string p, to_deselect, {
	    Pkg::ResolvableRemove (p, `pattern);
	});
	y2milestone ("Selecting system patterns %1", system_patterns);
	foreach (string p, system_patterns, {
	    Pkg::ResolvableInstall (p, `pattern);
	});
    }
    else
    {
	y2milestone ("Re-selecting system patterns %1", system_patterns);
	list<string>pats = filter (string p, system_patterns, {
	    list<map<string,any> > descrs = Pkg::ResolvableProperties (p, `pattern, "");
	    descrs = filter (map<string,any> descr, descrs, {
		return descr["status"]:nil == `selected;
	    });
	    return size (descrs) > 0;
	});
	y2milestone ("Selected patterns to be reselected: %1", pats);
	foreach (string p, pats, {
	    Pkg::ResolvableRemove (p, `pattern);
	    Pkg::ResolvableInstall (p, `pattern);
	});
    }
}

/**
 * Select system packages
 * @param reselect boolean true to select only those which are alrady selected
 */
void SelectSystemPackages (boolean reselect) {
    list<string> system_packages = ComputeSystemPackageList();
    if (! reselect)
    {
	y2milestone ("Selecting system packages %1", system_packages);
    }
    else
    {
	y2milestone ("Re-selecting new versions of system packages %1", system_packages);
	// first deselect the package (and filter selected ones)
	system_packages = filter (string p, system_packages, {
	    if (Pkg::IsProvided (p) || Pkg::IsSelected (p))
	    {
		Pkg::PkgDelete (p);
		return true;
	    }
	    return false;
	});
	y2milestone ("System packages to be reselected: %1", system_packages);
    }
    map <string, any> res = Pkg::DoProvide (system_packages);
    if (size (res) > 0)
    {
	foreach (string s, any a, res, {
	    y2warning ("Pkg::DoProvide failed for %1: %2", s, a);
	});
    }
}

/**
 * Select appropriate XEN kernel if the XEN pattern is selected
 */
void SelectXenKernel () {
    // check Xen pattern status
    list<map> xen_patterns  = Pkg::ResolvableProperties("xen_server", `pattern, "");

    // is the Xen pattern selected?
    boolean xen_selected = false;

    foreach(map pattern, xen_patterns,
    {
	y2milestone ("Pattern '%1' status '%2'", pattern["name"]:nil, pattern["status"]:`unknown);
	if (pattern["status"]:`unknown == `selected)
	{
	    xen_selected = true;
	}
    });

    y2milestone ("XEN Selected: %1", xen_selected);
    if (xen_selected)
    {
	if (contains (Kernel::GetPackages (), "kernel-bigsmp"))
	{
	    y2milestone("Selected Xen kernel: kernel-xenpae");
	    // install PAE Xen
	    Pkg::PkgNeutral("kernel-xen");
	    Pkg::PkgInstall("kernel-xenpae");
	}
	else
	{
	    // install standard Xen
	    Pkg::PkgNeutral("kernel-xenpae");
	    Pkg::PkgInstall("kernel-xen");
	}
    }
}

/**
 * Make a proposal for package selection
 * @param force reset
 * @param re-initialize
 * @return map for the API proposal
 */
global map Proposal (boolean force_reset, boolean reinit, boolean simple) {

    // if the cache is valid and reset or reinitialization is not required
    // then the cached proposal can be used
    if (cached_proposal != nil && force_reset == false && reinit == false && base_selection_changed == false)
    {
	// selected packages
	list<string> selected_packages = Pkg::GetPackages(`selected, false);

	// selected patterns
	list<map> selected_patterns = filter(map p, Pkg::ResolvableProperties("", `pattern, ""), {return p["status"]:`unknown == `selected;});

	// selected products
	list<map> selected_products = filter(map p, Pkg::ResolvableProperties("", `product, ""), {return p["status"]:`unknown == `selected;});

	// selected patches
	list<map> selected_patches = filter(map p, Pkg::ResolvableProperties("", `patch, ""), {return p["status"]:`unknown == `selected;});

	// selected selections
	list<map> selected_selections = filter(map s, Pkg::ResolvableProperties("", `selection, ""), {return s["status"]:`unknown == `selected;});

	// selected languages
	list<string> selected_languages = (list<string>)union([Pkg::GetLocale()], Pkg::GetAdditionalLocales());


	// if the package selection has not been changed the cache is up to date
	if (selected_packages == cached_proposal_packages && selected_patterns == cached_proposal_patterns
	    && selected_products == cached_proposal_products && selected_patches == cached_proposal_patches
	    && selected_selections == cached_proposal_selections && selected_languages == cached_proposal_languages)
	{
	    y2milestone("using cached software proposal");
	    return cached_proposal;
	}
	else
	{
	    y2milestone("invalid cache: the software selection has been chaged");
	}
    }
    else
    {
	y2milestone("the cached proposal is empty or reset is required");
    }

    UI::OpenDialog(`opt(`decorated ),
	// popup label
	`Label (_("Evaluating package selection...")));

    y2milestone ("Packages::Proposal: force_reset %1, reinit %2, lang '%3', base_selection_changed %4",
	force_reset, reinit, Language::language,base_selection_changed);

    if ( force_reset || base_selection_changed)
    {
	Kernel::ProbeKernel();
	Packages::Reset ([`product]);
	reinit = true;
    }

    boolean initial_run = reinit || ! init_called;
    Initialize (true);

    if (init_error != nil)
    {
	UI::CloseDialog();
	return Summary ([], false);
    }

    if (initial_run)
    {
	// autoyast can configure AdditionalLocales
	// we don't want to overwrite this
	if( ! Mode::autoinst ())
	{
	    Pkg::SetAdditionalLocales ([Language::language]);
	}
    }

    SelectProduct ();

    if (ProductFeatures::GetFeature ("software", "selection_type") == `auto)
    {
	y2milestone ("Doing pattern-based software selection");

	SelectSystemPackages (system_packages_selected && ! initial_run);
	SelectSystemPatterns (system_packages_selected && ! initial_run);
	system_packages_selected = true;
	Pkg::PkgFreshen();
    }
    else if (ProductFeatures::GetFeature ("software","selection_type") == `fixed)
    {
	y2milestone ("Selection type: fixed");
    }
    else
    {
	y2error ("unknown value %1 for ProductFeatures::GetFeature (software, selection_type)",
	     (symbol)ProductFeatures::GetFeature ("software", "selection_type"));
    }

    SelectXenKernel ();

    if (! Pkg::PkgSolve (false))
    {
	solve_errors = Pkg::PkgSolveErrors ();
    }

    map ret = Summary (
	[ `product, `pattern, `selection, `size, `desktop ],
	false);
    // TODO simple proposal

    // cache the proposal
    cached_proposal = ret;

    // remember the status
    cached_proposal_packages = Pkg::GetPackages(`selected, false);
    cached_proposal_patterns = filter(map p, Pkg::ResolvableProperties("", `pattern, ""), {return p["status"]:`unknown == `selected;});
    cached_proposal_products = filter(map p, Pkg::ResolvableProperties("", `product, ""), {return p["status"]:`unknown == `selected;});
    cached_proposal_patches = filter(map p, Pkg::ResolvableProperties("", `patch, ""), {return p["status"]:`unknown == `selected;});
    cached_proposal_selections = filter(map s, Pkg::ResolvableProperties("", `selection, ""), {return s["status"]:`unknown == `selected;});
    cached_proposal_languages = (list<string>)union([Pkg::GetLocale()], Pkg::GetAdditionalLocales());

    UI::CloseDialog();

    y2milestone ("Software proposal: %1", ret);

    return ret;
}

/**
 * Initialize the catalogs with popup feedback
 * Use Packages::Initialize (true) instead
 */
global void InitializeCatalogs() {
    Packages::Initialize (true);
}

global boolean InitFailed () {
    boolean ret = init_error != nil;
    y2milestone ("Package manager initialization failed: %1", ret);
    return ret;
}

/* EOF */
}