/**
 * File:	modules/ProductControl.ycp
 * Package:	installation
 * Summary:	Product Control routines
 * Authors:	Anas Nashif <nashif@suse.de>
 *
 * $Id: ProductControl.ycp 22482 2005-03-12 03:46:54Z nashif $
 */
{
textdomain "base";
module "ProductControl";

import "XML";
import "ProductFeatures";
import "Mode";
import "Arch";
import "Stage";
import "Directory";
import "Label";
import "Wizard";
import "Report";
import "Hooks";
import "Popup";


// The complete parsed control file
global map productControl = $[];

// all workflows
global list<map> workflows = [];

// all proposals
global list<map> proposals = [];

// Flexible partitioning map. XML represenation of the the part.info file
global map partitioning = $[];

// Software configuration
global map software = $[];

// Global configuration
global map<string, any> globals = $[];


// Location of a custom control file
global string custom_control_file = "";

// Control file in service packs
global string y2update_control_file = "/y2update/control.xml";

// The custom control file location, usually copied from
// the root of the CD to the installation directory by linuxrc
global string default_control_file = "/control.xml";

// The file above get saved into the installed system for later
// processing
global string saved_control_file = Directory::etcdir + "/control.xml";

// The packaged file which contains all default worklfows
global string packaged_control_file = "/usr/share/YaST2/control/control.xml";

// The control file we are using for this session.
global string current_control_file = "";


// Current Wizard Step
global string CurrentWizardStep = "";


// Last recently used stage_mode for RetranslateWizardSteps
global list<map> last_stage_mode = [];


// List of module to disable in the current run
global list<string> DisabledModules = [];


// Log files for hooks
global list<string> logfiles = [];


string _client_prefix = "inst_";

list stack = [];


string first_id = "";


/**
 * Set Client Prefix 
 */
global define void setClientPrefix(string prefix)  {
    _client_prefix = prefix;
    return;
}

/**
 * Check if a module is disabled
 * @param map module map
 * @return boolean
 */
global define boolean checkDisabled (map mod ) 
{
    if (contains(DisabledModules, mod["name"]:""))
    {
        return true;
    }
    return false;
}

/**
  * Read XML Control File
  * @param string control file
  * @return boolean
  */
global define boolean ReadControlFile( string controlfile) 
{
    productControl = XML::XMLToYCPFile(controlfile);

    if (productControl == nil)
        return false;
    
    workflows = productControl["workflows"]:[];
    proposals = productControl["proposals"]:[];
 
    // Software
    software = productControl["software"]:$[];
    ProductFeatures::SetSection("software", software);

    // Network
    ProductFeatures::SetSection("network", productControl["network"]:$[]);

    // Globals
    globals = productControl["globals"]:$[];
    ProductFeatures::SetSection("globals", globals);

    // Storage
    partitioning = productControl["partitioning"]:$[];
    ProductFeatures::SetSection("partitioning", partitioning);

    if (haskey(productControl, "partitioning"))
    {
        if (size( productControl["partitioning", "partitions"]:[] ) > 0)
        {
            
            ProductFeatures::flexible_partitioning = true;
            
            ProductFeatures::FlexiblePartitioning = partitioning;
        }
        if (productControl["partitioning", "evms_config"]:false)
        {
            ProductFeatures::evms_config = true;
        }
    }
    if (haskey(productControl, "software"))
    {

        ProductFeatures::base_selection_choice = software["selection_type"]:`auto;

        if (size(software["base_selection"]:"") > 0 )
        {
            ProductFeatures::fixed_base_selection = software["base_selection"]:"Minimal";
        }
        // Default is true
        ProductFeatures::delete_old_packages = software["delete_old_packages"]:true;

        ProductFeatures::onlyUpdateInstalled =
            software["only_update_installed"]:false;

        // What is the software proposal based on?
        // Possible values: selection|desktop
        //
        ProductFeatures::software_proposal =
            software["software_proposal"]:"selection";

        ProductFeatures::packages_transmogrify =
            software["packages_transmogrify"]:"";
 
        /*
         *  Kernel dependent packages, i.e. if xxx is given, xxx-smp will be installed
         *  in case of an SMP system
         */
        ProductFeatures::kernel_packages = software["kernel_packages"]:[];

        /*
         *  Additional packages which are not in any selection
         */
        ProductFeatures::packages = software["packages"]:[];

        // additional addon selections
        ProductFeatures::addon_selections =
            software["addon_selections"]:[];

        
        ProductFeatures::inform_about_suboptimal_distribution =
         software["inform_about_suboptimal_distribution"]:false;
    }


    if (haskey(productControl, "globals"))
    {

        ProductFeatures::enable_autologin = globals["enable_autologin"]:true;
        
        ProductFeatures::language = globals["language"]:"";
        
        ProductFeatures::skip_language_dialog = globals["skip_language_dialog"]:false;
        
        ProductFeatures::keyboard = globals["keyboard"]:"";
        
        ProductFeatures::runlevel = globals["runlevel"]:"";
        
        ProductFeatures::timezone = globals["timezone"]:"";
        ProductFeatures::incomplete_translation_treshold = globals["incomplete_translation_treshold"]:"95";
        
        ProductFeatures::ui_mode = globals["ui_mode"]:"expert";
        
	ProductFeatures::fam_local_only =
	    globals["fam_local_only"]:"never";
        
	ProductFeatures::enable_firewall =
	    globals["enable_firewall"]:true;

	ProductFeatures::firewall_enable_ssh =
	    globals["firewall_enable_ssh"]:false;

        // Additional kernel parameters, i.e. elevator, desktop
        ProductFeatures::additional_kernel_parameters =
            globals["additional_kernel_parameters"]:"";
    }

    if (haskey(productControl, "network"))
    {
	map network = productControl["network"]:$[];
	ProductFeatures::force_static_ip = network["force_static_ip"]:false;
    }

    return true;
}



boolean Check(string allowed, string current)
{
    // create allowed list
    list<string> allowedlist = filter(string s,
            splitstring(deletechars(allowed, " "), ","), ``(s!=""));
    y2debug("allowedlist: %1", allowedlist );
    y2debug("current: %1", current );
    if (size(allowedlist) == 0 )
    {
        return true;
    }
    else if (contains(allowedlist, current ))
    {
        return true;
    }
    else
    {
        return false;
    }
}



/**
 * Check if valid architecture
 * @param map module data
 * @param map default data
 * @return boolean true if arch match
 */
global define boolean checkArch(map mod , map def)
{
    y2milestone("mod: %1", mod);
    string archs = mod["archs"]:"";
    if (archs == "")
    {
        archs=def["archs"]:"all";
    }

    if (archs == "all") {
        return true;
    }

    y2milestone("short arch desc: %1", Arch::arch_short () );
    y2milestone("supported archs: %1", archs );
    if (issubstring(archs,Arch::arch_short ()))
    {
        return true;
    }

    return false;
};


string getClientName(string name ) {

    if (Mode::test()) 
    {
        return "inst_test_workflow";
    }
    // All client start with "inst_".
    string client = "";
    if ( custom_control_file != "")
    {
        return name;
    }
    else
    {
        if (issubstring(name, _client_prefix)) {
            return name;	    
	} else {
            client = _client_prefix + name;
	    return client;
	}
	
    }
}

/**
 * Return term to be used to run module with CallFunction
 * @param map module data
 * @param map default data
 * @return term module data with params
 */
global define term getClientTerm (map step, map def, any former_result)
{    
    string client =  getClientName(step["name"]:"dummy");
    term result = toterm(client);
    map<string,any> arguments = $[];
    
    foreach(string button, ["enable_back", "enable_next"], ``{
	
            string default_setting = def[button]:"yes";            
	    arguments[button] = step[button]:default_setting == "yes";           
            });

    if (haskey(step,"proposal"))
    {
	arguments["proposal"] = step["proposal"]:"";
    }
    map<string, any> other_args = step["arguments"]:$[];
    
    if (size(other_args) > 0 )
        arguments = (map<string, any>)union(arguments, other_args );

    if (is(former_result,symbol) && former_result == `back)
        arguments["going_back"] = true;

    if (Mode::test())
    {
        arguments["step_name"] = step["name"]:"";
        arguments["step_id"] = step["id"]:"";
    }
    result = add(result, arguments);
    return result;

}


/**
 * Get workflow defaults
 * @param string stage
 * @param string mode
 * @return map defaults
 */
global define map getModeDefaults(string stage, string mode)
{
    list<map> tmp = filter(map wf, workflows, ``(
                Check(wf["stage"]:"", stage) && Check(wf["mode"]:"", mode )
    ));
    map workflow = tmp[0]:$[];
    return workflow["defaults"]:$[];
}

/**
 * Prepare Workflow Scripts
 * @param m Workflow module map
 * @return void
 */
void PrepareScripts(map m)
{
    string tmp_dir = (string)WFM::Read(.local.tmpdir, []);
    if (haskey(m,"prescript"))
    {
        string interpreter = m["prescript", "interpreter"]:"shell";
        string source = m["prescript", "source"]:"";
        string type = (interpreter == "shell") ? "sh" : "pl";
        string f = sformat("%1/%2_pre.%3", tmp_dir, m["name"]:"", type);
        WFM::Write(.local.string,f , source);
        logfiles = add(logfiles, sformat("%1.log", sformat("%1_pre.%2",
                        m["name"]:"", type)));
    }
    if (haskey(m,"postscript"))
    {
        string interpreter = m["postscript", "interpreter"]:"shell";
        string source = m["postscript", "source"]:"";
        string type = (interpreter == "shell") ? "sh" : "pl";
        string f = sformat("%1/%2_post.%3", tmp_dir, m["name"]:"", type);
        WFM::Write(.local.string, f, source);
        logfiles = add(logfiles, sformat("%1.log", sformat("%1_post.%2",
                         m["name"]:"", type)));
    }
    return;
}



/**
 * Get list of required files for the workflow.
 * @return list<string> Required files list.
 */
global list<string> RequiredFiles (string stage, string mode) 
{
    // Files needed during installation.
    list<string> needed_client_files = [];

    list<map> tmp = filter(map wf, workflows, ``(
                Check(wf["stage"]:"", stage) && Check(wf["mode"]:"", mode )
    ));
    map workflow = tmp[0]:$[];

    list<map> modules = workflow["modules"]:[];
    integer id = 1;
    modules = filter(map m, modules, ``(m["enabled"]:true));

    foreach(map m, modules,
    {
	string client = "";
	if (Stage::firstboot ())
	{
	    client = m["name"]:"dummy";
	}
	else
	{
	    if (issubstring(m["name"]:"dummy", "inst_"))
	    {
		client = m["name"]:"dummy";
	    }
	    else
	    {
		client = "inst_" + m["name"]:"dummy";
	    }
	}
	client = Directory::clientdir + "/" + client + ".ycp";
	needed_client_files = add(needed_client_files, client);
    });
    
    needed_client_files=toset(needed_client_files);
    return needed_client_files;
}



/**
 * Get Workflow
 * @param stage Stage
 * @param mode Mdoe
 * @return map Workflow map
 */
global define map getCompleteWorkflow(string stage, string mode)
{
    list<map> tmp = filter(map wf, workflows, ``(
                Check(wf["stage"]:"", stage) && Check(wf["mode"]:"", mode )
    ));
    map workflow = tmp[0]:$[];
    return workflow;
}


/**
 * Get modules of current Workflow
 * @param string stage
 * @param string mode
 * @param boolean all enabled and disabled or enabled only
 * @return list<map> modules
 */
global define list<map> getModules(string stage, string mode, symbol which)
{
    y2debug("workflows: %1", workflows );
    list<map> tmp = filter(map wf, workflows, ``(
                Check(wf["stage"]:"", stage) && Check(wf["mode"]:"", mode )
    ));
    map workflow = tmp[0]:$[];

    y2debug("Workflow: %1", workflow );
    list<map> modules = workflow["modules"]:[];
    y2debug("modules: %1", modules );

    integer id = 1;
    if (which == `enabled)
    {
        modules = filter(map m, modules, ``(m["enabled"]:true));
    }

    modules = maplist(map m, modules, ``{
            m["id"] = sformat("%1_%2", stage, id);
            PrepareScripts(m);
            id = id + 1;
            return (m);
            });

    y2debug("Log files: %1", logfiles);
    return modules;
}

/**
 * Get Workflow Label
 * @param string stage
 * @param string mode
 * @return string
 */
global define string getWorkflowLabel(string stage, string mode)
{
    list<map> tmp = filter(map wf, workflows, ``(
                Check(wf["stage"]:"", stage) && Check(wf["mode"]:"", mode )
    ));
    map workflow = tmp[0]:$[];
    return workflow["label"]:"";
}



/**
 * Add Wizard Steps
 * @param list<map> stagemode A List of maps containing info about complete
 *                  installation workflow.
 * @return void
 */
global define void AddWizardSteps(list<map> stagemode)
{
    last_stage_mode = stagemode;

    // UI::WizardCommand() can safely be called even if the respective UI
    // doesn't support the wizard widget, but for optimization it makes sense
    // to do expensive operations only when it is available.

    // if ( ! UI::HasSpecialWidget(`Wizard ) )
    // return;

    string wizard_textdomain = (string) productControl["textdomain"]:"control";
    y2debug( "Using textdomain '%1' for wizard steps", wizard_textdomain );

    string first_id = "";
    // UI::WizardCommand(`SetVerboseCommands( true ) );
    foreach (map sm , stagemode, ``{
        y2debug( "Adding wizard steps for %1", sm );
        string slabel = getWorkflowLabel(sm["stage"]:"", sm["mode"]:"");
        if ( slabel != "" )
        {
            UI::WizardCommand(`AddStepHeading( dgettext( wizard_textdomain, slabel ) ) );
        }

        string last_label = "";
        foreach(map m, getModules(sm["stage"]:"", sm["mode"]:"", `enabled),
                ``{
            y2debug("Adding wizard step: %1", m );
            if (m["label"]:"" != "")
	    {
                if (first_id=="")
                {
                    first_id = m["id"]:"";
                }
                UI::WizardCommand(`AddStep( dgettext(
                                                     wizard_textdomain,
                                                     m["label"]:"" ),
                                            m["id"]:"" )
                                  );
                last_label = m["label"]:"";
            } else {
                if (first_id=="")
                {
                    first_id = m["id"]:"";
                }
		if (last_label != "")
		{
	            UI::WizardCommand(`AddStep( dgettext(
                                                     wizard_textdomain,
                                                     last_label ),
                                            m["id"]:"" )
                                  );
		}
            }
        });
    });

    UI::WizardCommand(`SetCurrentStep( first_id ) );
    return;
}




/**
 * Update Steps
 */
global define void UpdateWizardSteps(list<map> stagemode)
{
    last_stage_mode = stagemode;

    UI::WizardCommand(`DeleteSteps());
    AddWizardSteps( stagemode );
    UI::WizardCommand(`UpdateSteps());
    UI::WizardCommand(`SetCurrentStep( CurrentWizardStep ) );
    return;
}


/**
 * Retranslate Wizard Steps
 */
global define void RetranslateWizardSteps()
{
    if ( size( last_stage_mode ) > 0 )
    {
	y2debug( "Retranslating wizard steps" );
	UpdateWizardSteps( last_stage_mode );
    }
}


define list<map> getMatchingProposal(
        string stage,
        string mode,
        string proptype)
{
    y2milestone("Stage: %1 Mode: %2", Stage::stage(), Mode::mode());
    
    // First we search for proposals for current stage if there are
    // any. 
    list<map> props = filter(map p, proposals, ``(Check(p["stage"]:"", stage)));
    y2debug("1. proposals: %1", props);

    // Then we check for mode: installation or update
    props = filter(map p, props, ``(
                    Check(p["mode"]:"", mode)));

    y2debug("2. proposals: %1", props);

    // Now we check for architecture
    y2debug("Architecture: %1, Proposals: %2", Arch::architecture (), props );
    
    list<map> arch_proposals = filter(map p, props, ``(
                p["name"]:"" == proptype &&
                issubstring(p["archs"]:"dummy", Arch::arch_short () )));

    y2debug("3. arch proposals: %1", arch_proposals );
    
    props = filter(map p, props, ``(
				    p["archs"]:"" == "all" || !haskey(p, "archs")
				    )	
		   );


    y2debug("4. proposals: %1", arch_proposals );
    // If architecture specific proposals are available, we continue with those
    // and check for proposal type, else we continue with pre arch proposal
    // list
    if (size(arch_proposals) > 0 )
    {
        props = filter(map p, arch_proposals, ``(
                     p["name"]:"" == proptype ));

    }
    else
    {
        props = filter(map p, props, ``(
                     p["name"]:"" == proptype ));
    }

    if (size(props)> 1 )
    {
        y2error("Something Wrong happened, more than one proposal after filter:
                %1", props);
    }

    // old style proposal
    y2milestone ("Proposal modules: %1",props[0, "proposal_modules"]:nil );
    return props;
}



/**
 * Get modules of current Workflow
 * @param string stage
 * @param string mode
 * @return list<string> modules
 */
global define list < list > getProposals(string stage, string mode, string proptype) {

    list<map> props = getMatchingProposal(stage, mode, proptype);
    
    list< list > final_proposals = maplist(any p, props[0, "proposal_modules"]:[], ``{
	    
	    string proposal_name = "";
	    integer order_value = 50;
	    if ( is (p, string) )
	    {
		proposal_name = (string)p;
	    }
	    else
	    {
		map<string,string> pm = (map<string,string>)p;
		proposal_name = pm["name"]:"";
		string proposal_order = pm["presentation_order"]:"50";
		
		order_value = tointeger (proposal_order);
		if (order_value == nil)
		{
		    y2error ("Unable to use '%1' as proposal order, using %2 instead"
			, proposal_order, 50);
		    order_value = 50;
		}
	    }

	    // All proposal file names end with _proposal
            if (!issubstring(proposal_name, "_proposal"))
                return( [proposal_name + "_proposal", order_value ] );
            else
                return( [proposal_name, order_value] );

    });

  
    y2debug("final proposals: %1", final_proposals );
    return final_proposals;
}


/**
 * Get Proposal list that can not be changed by the user.
 * @return list<string> list of locked proposals
 */
global list<string> getLockedProposals (string stage,string mode,string proptype) {

    list<map> props = getMatchingProposal(stage, mode, proptype);
    list<string> locked_proposals = maplist(string p, props[0, "locked_modules"]:[], ``{
	if (!issubstring(p, "_proposal"))
	return(p + "_proposal");
	else
	return(p);

    });
    return locked_proposals;
}

/**
 * Return text domain
 */
global string getProposalTextDomain() {
    string current_proposal_textdomain = (string)
	productControl["textdomain"]:"control";

    y2debug( "Using textdomain '%1' for proposals", current_proposal_textdomain);
    return current_proposal_textdomain;
}


/**
 * Return proposal Label
 */
global map getProposalProperties( string stage, string mode, string proptype
)
{
    list<map> proposal = getMatchingProposal(stage, mode, proptype);   
    return proposal[0]:$[];    

}




/**
 * Initialize Product Control
 * @return boolean True on success
 */
global define boolean Init()
{

    boolean ret = false;
    current_control_file = "";
    list<string> order =
        [
	 y2update_control_file,  // /y2update/control.xml
	 default_control_file,  // /control.xml
	 saved_control_file, // /etc/YaST2/control.xml
	 packaged_control_file // /usr/share/YaST2/control/control.xml
	 ];

    if ( custom_control_file != "")
    {
        order=prepend(order, custom_control_file);
    }

    y2milestone("Candidates: %1", order );
    foreach(string cf, order,
    {
	if ((integer)SCR::Read( .target.size, cf )>0
            && current_control_file == "")
	{
	    current_control_file = cf;
	}
    });

    if (current_control_file == "")
    {
        y2error("Control file not found");
        return false;
    }

    y2milestone("Reading control file: %1", current_control_file );
    ReadControlFile( current_control_file );
    return (current_control_file != "");
}

/**
 * Re-translate static part of wizard dialog and other predefined messages
 * after language change
 */
void retranslateWizardDialog()
    {
	y2milestone( "Retranslating messages 1" );

	// Make sure the labels for default function keys are retranslated, too.
	// Using Label::DefaultFunctionKeyMap() from Label module.
	UI::SetFunctionKeys( Label::DefaultFunctionKeyMap() );

	// Activate language changes on static part of wizard dialog

	ProductControl::RetranslateWizardSteps();
	Wizard::RetranslateButtons();
	Wizard::SetFocusToNextButton();
        return;
    }



void addToStack(string name) {
    stack=add(stack, name);
    return;
}

global boolean wasRun(string name)
{
    return contains(stack, name);
}

/**
 * Run Workflow
 * 
 */
global symbol Run()
{
    any  former_result = `next;
    integer m = 0;			// current module

    Wizard::SetFocusToNextButton();

    y2debug("Starting Workflow with  \"%1\" \"%2\"",  Stage::stage (), Mode::mode ());
    
    list<map> modules = 
	getModules( Stage::stage (), Mode::mode (), `enabled);

    map defaults = getModeDefaults(Stage::stage (), Mode::mode ());

    y2debug("modules: %1", modules);

    if (size(modules) == 0 )
    {
	y2error("No workflow found: %1", modules );
	// error report
	Report::Error(_("No workflow defined for this installation mode."));
	return `abort;
    }


    while ((m >= 0) && (m < size(modules)))
    {
	map step = modules[m]:$[];
	string  step_name = step["name"]:"";
	boolean run_in_update_mode = step["update"]:true; // default is true
	boolean retranslate = step["retranslate"]:false;
        string step_id = step["id"]:"";

        if (m == 0)
        {
            step["enable_back"] = "no";
        }

	boolean   do_continue           = false;

	if (!checkArch(step, defaults))
	{
	    do_continue = true;
	}

	if (checkDisabled(step))
	{
	    do_continue = true;
	}

	if (!run_in_update_mode && Mode::update ())
	{
	    do_continue = true;
	}

	if ( do_continue )
	{
	    if      (former_result == `next) m = m + 1;
	    else                             m = m - 1;
	}
	if ( do_continue ) continue;


	term argterm = getClientTerm( step, defaults, former_result);
	y2debug("Running module: %1 (%2)", argterm, m);
		
	symbol module_name = symbolof( argterm );

	y2milestone( "Calling %1", argterm );

        if (!wasRun(step_name)) {
            Hooks::Checkpoint (sformat("%1", module_name), true);
            Hooks::Run (step_name, true);
        }

	list args = [];
	integer i = 0;
	while (i < size(argterm)) 
	{
	    any def = nil;
	    args[i] = argterm[i]:def;
	    i = i + 1;
	}

	UI::WizardCommand(`SetCurrentStep( step_id ) );
	CurrentWizardStep = step_id;

	
        // Register what step we are going to run
        if (!Stage::initial())
        {
            if (!SCR::Write (.target.string, "/var/lib/YaST2/step", step_id))
                y2error("Error writing step identifier");
        }

	symbol result = (symbol) WFM::CallFunction (getClientName(step_name), args);


        // Remove file if step was run and returned (without a crash);
	if (m < size(modules) - 1 && !Stage::initial())
        {
            if (!(boolean)SCR::Execute(.target.remove, "/var/lib/YaST2/step"))
                y2error("Error removing step identifier");
        }

	// Dont call hook scripts after installation is done. (#36831)
	if (m < size(modules) - 1 && !wasRun(step_name))
	    Hooks::Run (step_name, false );
	else
	    y2milestone("Not running hooks at the end of the installation");

	// This should be safe (#36831)
	Hooks::Checkpoint ( step_name, false);          // exit hook

	addToStack(step_name);

	if ( retranslate ) 
	{
            y2milestone("retranslate");
	    retranslateWizardDialog();
	    retranslate = false;
	}

	// If the module return nil, something basic went wrong.
	// We show a stub dialog instead.
	if (result == nil)
	{
	    /**
	     * If workflow module is marked as optional, skip if it returns nil,
	     * For example, if it is not installed.
	     */
	    if (step["optional"]:false)
	    {
		y2milestone("Skipping optional %1", symbolof(argterm) );
		m = m + 1;
		continue;         
	    }

	    any r = nil;
	    r = Popup::ModuleError(sformat("The module %1 does not work.", symbolof(argterm)));

	    if      (r == `next)   m = m + 1;
	    else if (r == `back)   m = m - 1;
	    else if (r != `again)
	    {
		UI::CloseDialog();
		return nil;
	    }
	    continue;
	}

	if      (result == `next)
	{
	    m = m + 1;
	}
	else if (result == `back)
	{
	    m = m - 1;
	}
	else if (result == `cancel)
	{
	    break;
	}
	else if (result == `abort)
	{
	    break;
	}
	else if (result == `finish)
	{
	    break;
	}
	else if (result == `again)
	{
	    continue; // Show same dialog again
	}
	else if (result == `auto)
	{
	    if (former_result != nil)
	    {
		if      (former_result == `next) m = m + 1;
		else if (former_result == `back) m = m - 1;
	    }
	    continue;
	}
	former_result = result;
    }
    if (former_result == `abort)
    {
	return `abort;
    }
    
    if (m <= -1)	return `back;
    else		return `next;
}


/**
 * ProductControl Constructor
 * @return void
 */
global define void ProductControl()
{
    if (!Init())
    {
	y2error("control file missing");
    }
    return;
}

// EOF
}
