%BBINSTALL  Maintenance utility for BBTools
%   BBINSTALL is a graphical utility for maintaining the toolbox. Its
%   main functions are to install and uninstall BBTools. If Java is
%   enabled, and there is a working Internet connection, it can also
%   upgrade to the most recent version from the toolbox web site.
%
%   Syntax:
%     BBINSTALL
%     BBINSTALL -install
%     BBINSTALL -upgrade
%     BBINSTALL -checkupgrade
%     BBINSTALL -download
%     BBINSTALL -remove
%
%   If BBINSTALL is started without any arguments it will present a menu.
%   This is skipped if the option given at the commandline is feasible.
%   The first letter can be used as a short-hand, i.e. '-i' instead
%   of '-install'.
%
%   If the toolbox is broken, the easiest way to repair it is to remove it
%   and reinstall it from an installation package.
%
%   To evaluate BBTools from an installation package, without installing
%   it, you may use BBSETPATH and BBDOC. However, this will not integrate
%   the toolbox into the Matlab environment.
%
%   See also BBDOC, BBSETPATH.

% Copyright (C) 2004-2006, Esben Høgh-Rasmussen.
function bbinstall(varargin)
  % Matlab gives subfunctions higher precendence than the
  % top-function. Since we do a lot of file-shuffling and
  % directory stuff, we pass control directly to the
  % dispatcher.
  InitInstall(0,varargin{:});

% The files/directories/paths used for installation is
% hardwired to create a selfcontained installer.
% It is nice to keep these near the top.
%
% NOTE: the documentation is installed separately.
% Its location is dictated by Matlab standards, so
% these are likely to be rather stable.
function [Files,Dirs,Paths]=GetInstallFiles()
  Files={'COPYING', 'Contents.m', 'GPL', 'bbdoc.m', ...
         'bbinstall.m', 'stamp.dat'};
  Paths={'demos', 'examples', 'iter', 'matop', ...
         'matrix', 'wrapper', 'other'};
  Dirs= [Paths, {'@bbparcel', '@blackboxbase', '@blackbox'}];


%-------------------------------------------------------
%
% DISPATCH
%
%-------------------------------------------------------

function InitInstall(varargin)
  % Check the Matlab version.
  t=sscanf(version,'%i.%i.%i');                         % Get major, minor, and revision of Matlab
  if (t(1)<6) | ((t(1)==6) & t(2)<5)
    fprintf('Sorry, BBTools requires Matlab v6.5 or later.\n');
    return
  end

  % We may be called as a callback from a button.
  % In that case we ignore the (object,event) pair.
  if (nargin>=2) && numel(varargin{1})==1 && ishandle(varargin{1})
    if strcmp(get(varargin{1},'type'),'uicontrol')
      varargin=varargin(3:end);
    end
  end

  % The first argument determines if the installer runs from
  % the commandline. Otherwise we allow for a relaunch, i.e.
  % starting the installer without examining the system.
  cmdline=false;
  relaunch=false;
  ask=true;
  if ~isempty(varargin)
    if isequal(varargin{1},0)                           % Commandline?
      cmdline=true;
    elseif isequal(varargin{1},1)                       % Relaunch?
      relaunch=true;
    elseif isequal(varargin{1},2)                       % Quick-step?
      ask=false;
    end
    varargin=varargin(2:end);
  end

  % OLD INSTALLER HACK:
  %   "If there are no input-arguments, we may use the
  %   global variable BBDEFARG. This enables us to use
  %   RUN, so we avoid messing up the path."
  %
  % This is obsolete. We could support it, except it is
  % downloaded as an UU-encoded ASCII file, and BBTools
  % has outgrown this technique.
  if nargin==0
    s=who('global','BBDEFARG');
    if ~isempty(s)
      msgbox({'The installer appears to run as part of an upgrade', ...
              'using a method (ASCII-transfer) that could not be', ...
              'sustained by the current size of BBTools.', ...
              '', ...
              'Please use this installer to upgrade instead.', ...
              'We apologize for the inconvenience.'}, ...
              'Unsupported installation method', ...
              'warn');
      clear('global','BBDEFARG');
    end
    args={};
  else
    args=varargin;
  end

  % Grind input arguments.
  action=0; arg=0;
  while arg<length(args)
    arg=arg+1;
    switch lower(args{arg})
      case {'-i', '-install'},              action=1; ask=false;
      case {'-u', '-upgrade'},              action=2; ask=false;
      case {'-c', '-checkupgrade'},         action=3; ask=false;
      case {'-d', '-download'},             action=4; ask=false;
      case {'-r', '-uninstall', '-remove'}, action=5; ask=false;
      otherwise
        error('Unknown argument ''%s''', args{arg});
    end
  end

  % Start the GUI.
  fig=CreateGUI;
  BaseList={'Examine the system', 'Select task'};
  ProgressList(fig,BaseList);

  % Execute atstart-code.
  %
  % We use this for restart and selfdestruction.
  private=get(fig,'userdata');
  atstart='';
  if cmdline
    if isfield(private,'atstart_cmd')
      atstart=private.atstart_cmd;
    end
  else
    if isfield(private,'atstart_callback')
      atstart=private.atstart_callback;
    end
  end
  if ~isempty(atstart)
    eval(atstart,'');
  end
  private=get(fig,'userdata');
  private.atstart_cmd='';
  private.atstart_callback='';
  set(fig,'userdata',private);

  % Examine the system.
  ProgressAt(fig,1);
  if relaunch                                           % Extract info on a relaunch
    private=get(fig,'userdata');
    if isfield(private,'info')
      info=private.info;
    else
      warning('BBTOOLS:bbinstall:RelaunchFailed',...
              'Relaunching failed.');
      relaunch=false;
    end
  end
  if ~relaunch
    info=ExamineSys(fig,ask,action);
  end

  % Sanity check.
  if GetDeleteOnQuitStat(fig) && (info.inmatlab || info.inpack)
    warning('BBTOOLS:bbinstall:NoSelfDelete',...
            'Self-deletion has been turned off.');
    SetDeleteOnQuitStat(fig,false);
  end

  % Stash info in the figure data.
  info.BaseList=BaseList;
  if ishandle(fig)
    private=get(fig,'userdata');
    private.info=info;
    set(fig,'userdata',private);
  end

  % If the user specified an action from the command-line, we check if it
  % can be carried out. If it can not, then we drop the user in the menu,
  % otherwise we carry out the action.
  canAct=[info.canInst; info.canUpg; info.canChk; info.canDwn; info.canRem];
  if (action~=0) && ~canAct(action)
    action=0;
  end
  remWidgets(fig);
  if ask || (action==0)
    % bring up the main menu
    MainMenu(fig,info,action);
  else
    % Go through the moves.
    switch action
      case 1, Install(fig,info);
      case 2, Upgrade(fig,info);
      case 3, ChkUpgrade(fig,info);
      case 4, Download(fig,info);
      case 5, Remove(fig,info);
      otherwise
        close(fig);
        error('Internal failure.');
    end
  end

%-------------------------------------------------------
%
% MAIN MENU
%
%-------------------------------------------------------

function MainMenu(fig,info,def)
  % Reset progress list
  ProgressList(fig,info.BaseList);
  ProgressAt(fig,2);

  % Put info on.
  InfoText(fig,{...
    'The installer automates several administrative tasks for BBTools.', ...
    '', ...
    'INSTALL/UPGRADE FROM DISK: a common way to install BBTools', ...
    '  for the first time, is to download an installation package from the homepage.', ...
    '  After decompressing the archive, you can run the installer.', ...
    '', ...
    'INSTALL/UPGRADE FROM INTERNET: download the most recent version', ...
    '  of BBTools and install it. It BBTools is already installed, it will', ...
    '  be replaced with the new one.', ...
    '', ...
    'CHECK FOR NEW VERSION: check if a new version of BBTools is available.', ...
    '  It will not do anything to harm your current installation.', ...
    '', ...
    'DOWNLOAD: download the newest version of BBTools without installing it.', ...
    '  This allows you to evaluate it, before installation.', ...
    '', ...
    'REMOVE: remove an installed version of BBTools. The state of the toolbox', ...
    '  is not checked, so damaged or partially installed versions can be erased', ...
    '  completely erased if necessary.', ...
    '', ...
    'Note: the install functions do not check if the version is current.', ...
    'Therefore they can be used to repair a damaged installation simply by', ...
    'running them again.'
  });

  % Put on menu.
  push={'Install/Upgrade from disk', ...
        'Install/Upgrade from Internet', ...
        'Check for new version', ...
        'Download', ...
        'Remove', ...
        'Quit'};
  canAct=[info.canInst; info.canUpg; info.canChk; info.canDwn; info.canRem];
  Wid=MultiSelect(fig, {}, push, 1, {}, [canAct; true]);
  set(Wid{1},'Callback',{@InitInstall,1,'-i'});
  set(Wid{2},'Callback',{@InitInstall,1,'-u'});
  set(Wid{3},'Callback',{@InitInstall,1,'-c'});
  set(Wid{4},'Callback',{@InitInstall,1,'-d'});
  set(Wid{5},'Callback',{@InitInstall,1,'-r'});
  set(Wid{6},'Callback',QuitCmd(fig));


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% DELETE-ON-QUIT / COPY-AND-RUN
%

% We have a bit of dancing to do with the self-deletion
% process. It only works if the file is deleted
% directly by an instruction triggered as a call-back from
% a uicontrol, since a file can not delete itself while
% it is running.
%
% On the other hand, the option must be 'sticky', in the
% sense that it should be able to relaunch itself and
% still remember to selfdestruct. It also have to be
% if called from another installer.
%
% We keep a status on this in the figure handle, and
% use a function to create the "trigger string".

% Set the status of 'deleteonquit'.
function SetDeleteOnQuitStat(fig,stat)
  if ~ishandle(fig); return; end
  private=get(fig,'userdata');
  private.deleteonquit=stat;
  set(fig,'userdata',private);
  set(fig,'closerequestfcn', QuitCmd(fig));

% Get the status of 'deleteonquit'.
function stat=GetDeleteOnQuitStat(fig)
  stat=false;
  if ishandle(fig)
    private=get(fig,'userdata');
    if isfield(private,'deleteonquit')
      stat=private.deleteonquit;
    end
  end

% Get command for quitting by closing the figure.
% This is the normal mechanism to delete a copy of
% the installer when it is done.
function cmd=QuitCmd(fig)
  if GetDeleteOnQuitStat(fig)
    cmd=sprintf(['disp(''Deleting temporary installer.'');', ...
                 'delete(''%s'');', ...
                 'closereq'], ...
                 [mfilename('fullpath'), '.m']);
  else
    cmd='closereq';
  end

% Make sure the installer is started from
% a location outside the Matlab path.
%
% This is the reason we need the option in the
% first place, so it makes sense to put it
% in this section.
function mustRelaunch=CopyAndRun(fig,info,arg)
  % Check if we have a problem.
  if ~info.inmatlab
    mustRelaunch=false;
    return
  end
  mustRelaunch=true;
  % Sanity check.
  private=get(fig,'userdata');
  if GetDeleteOnQuitStat(fig)
    fprintf(['The current installer already ran as a copy.\n', ...
             'Please file a bug report and tell the developer\n', ...
             'about the circumstances that resulted in this error.\n']);
    error('Internal consistency failure.');
  end
  % Find a temporary file to put the installer.
  [instcopy,fname]=newfilename(info.tmpdir, 'bbinstallcopy', 'm');
  % Inform the user about the problem.
  InfoText(fig,{...
    'The installer can not complete this task because it is', ...
    'running from the Matlab path.', ...
    '', ...
    'However, the installer can copy itself to a temporary file and', ...
    'complete the action from that location. It will delete this file',...
    'if the installer terminates in an orderly manner.',...
    '', ...
    'The installer will copy itself to the following file:', ...
    ['   ', instcopy]});
  % Prepare for launch.
  private=get(fig,'userdata');
  private.atstart_cmd=[...
    'cd(''', cd, ''');', ...                     % Change back to original directory
    'SetDeleteOnQuitStat(fig,true);', ...        % Turn on self-deletion
    'disp(''NOTE: if the installer ends normally, it should delete itself.'');', ...
    'disp(''In case it terminates abnormally, you should manually delete the'');', ...
    'disp(''following file:'');', ...
    'disp(''', instcopy, ''');'];
  set(fig,'userdata',private);
  cmd=sprintf(['cd(''%s'');', ...
               'copyfile(''%s'',''%s'');', ...
               '%s(''%s'');'], ...
               info.tmpdir, ...
               [mfilename('fullpath'), '.m'], fname, ...
               fname(1:end-2), arg);
  % Put on the choices and exit.
  push={'Restart from new location', 'Return to main menu', 'Quit'};
  Wid=MultiSelect(fig, {}, push, 1, {}, true(3,1));
  set(Wid{1},'Callback',cmd);
  set(Wid{2},'Callback',{@InitInstall,1});
  set(Wid{3},'Callback',QuitCmd(fig));


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Install functions.
%

%-------------------------------------------------------
%
% INSTALL
%
%-------------------------------------------------------

function Install(fig,info)
  % Set the progress indicator
  num=length(info.BaseList);
  PList=[info.BaseList, {...
        'Review license', ...
        'Remove existing paths', ...
        'Delete existing toolbox', ...
        'Copy toolbox files', ...
        'Install documentation', ...
        'Add paths', ...
        'Finish'}];
  ProgressList(fig,PList);
  ProgressAt(fig,num);

  % Check if the installation is more recent
  % than the currently installed toolbox.
  if AskVersion(fig,info,'disk'); return; end

  % Ensure we are run outside the Matlab path.
  % This should never happen.
  if CopyAndRun(fig,info,'-i'); return; end

  % Ask user about the license.
  ProgressAt(fig,num+1);
  if ClickWrap(fig,info); return; end

  % Init
  ProgressAt(fig,num+2);
  InfoText(fig,{'BBTools is being installed.'});
  console(fig,{'Quit','Return'});
  conPrint(fig,'Installing BBTools.');
  abort=false;
  tooldir=info.tooldir;
  bbroot=info.bbroot;
  bbsrc=info.bbsrc;

  % Uninstall an existing toolbox if it exists.
  suc=1;
  if ~isempty(dir(bbroot))
    suc=TheRemove(fig,info,num+2);
  end
  if (suc~=1)
    conPrint(fig);
    conPrint(fig,'The currently installed toolbox was not deleted.');
    abort=true;
  end

  % Create the toolbox directory.
  if ~abort
    [suc,msg,id]=mkdir(tooldir,'bbtools');
    if suc==0
      conPrint(fig);
      conPrint(fig,'Failed to create the installation directory.');
      abort=true;
    end
  end

  % Copy toolbox.
  ProgressAt(fig,num+4);
  [FileList,DirList,PathList]=GetInstallFiles;          % Get filelist from the top
  if ~abort
    conPrint(fig);
    conPrint(fig,'Installing toolbox files.');
    % Copy separate files.
    L=FileList;
    for i=1:length(L)
      [suc,msg,id]=cp(fig,bbsrc,bbroot,L{i},'');
      if suc==0; abort=true; break; end
    end
    % Copy toolbox dir.
    [suc,msg,id]=cp(fig,bbsrc,matlabroot,'toolbox','bbtools','');
    if suc==0; abort=true; end
  end

  % Copy and install documentation.
  %
  % This is a release dependent operation.
  ProgressAt(fig,num+5);
  if ~abort
    conPrint(fig);
    conPrint(fig,'Installing documentation.');
    [suc,msg,id]=cp(fig,bbsrc,matlabroot,'help','toolbox','bbtools','');
    if suc==0; abort=true; end
  end
  if ~abort
    bbinstall=fullfile(bbsrc,'reldep','');
    mhelp=fullfile(matlabroot,'help','');
    if info.rel==13
      rhelp=fullfile(bbinstall,'R13','');
      suc1=cp(fig,rhelp,mhelp,'mwdoctoc_bbtools.xml');
      suc2=cp(fig,rhelp,mhelp,'mwdocindex_bbtools.xml');
      suc3=cp(fig,rhelp,bbroot,'info.xml');
      if (suc1==0) || (suc2==0) || (suc3==0); abort=true; end
    else
      rhelp=fullfile(bbinstall,'R14','');
      suc=cp(fig,rhelp,bbroot,'info.xml');
      if suc==0; abort=true; end
    end
  end

  % Add the toolbox to the startup path.
  ProgressAt(fig,num+6);
  if ~abort
    conPrint(fig);
    conPrint(fig,'Adding the toolbox to the current path.');
    for i=1:length(PathList)
      PathList{i}=fullfile(bbroot,PathList{i},'');
    end
    PathList=[{bbroot}, PathList];
    addpath(PathList{:},'-end');                         % Add the toolbox to the path
    clear('pathdef');                                    % Ensure we use the disk version
    start_path=pathdef;                                  % Get start path

    inx=strfind(pathdef,bbroot);
    if isempty(inx)
      conPrint(fig,'Adding the toolbox to the startup path.');
      current_path=path;                                 % Save current path
      path(start_path);                                  % Set path to the startup-path
      addpath(PathList{:},'-end');                       % Add the toolbox path
      if info.rel<=13                                    % Save the new startup path
        suc=path2rc;
      else
        suc=savepath;
      end
      clear('pathdef');                                  % Ensure we use the new version
      path(current_path);                                % Restore path
    else
      conPrint(fig,'The toolbox was already in the startup path.');
      suc=0;
    end
    if suc~=0
      conPrint(fig);
      conPrint(fig,'Could not add the toolbox to the startup path.');
      abort=true;
    end
  end
  UpdatePath(fig,info);

  % Finish.
  ProgressAt(fig,num+7);
  conPrint(fig);
  if ~abort
    conPrint(fig,'The toolbox was successfully installed.');
    conPrint(fig);
    conPrint(fig,'To get started, you may run one of the following commands:');
    if (info.hasjava)
      conPrint(fig,'  doc bbtools');
    else
      conPrint(fig,'  bbdoc');
    end
    conPrint(fig,'  demo toolbox black-box');
  else
    conPrint(fig,'A problem was encountered while installing BBTools.');
    conPrint(fig,'You may try to remove it, possibly manually, and try again.');
  end
  Finish(fig,info,2);

% Inform the user that an operation completed.
% mode: 1=relaunch, 2=quick-step (reexamine the system)
function Finish(fig,info,mode)
  Wid=conDone(fig,false);
  set(Wid{1},'Callback',QuitCmd(fig));
  set(Wid{2},'Callback',{@InitInstall,2});
  InfoText(fig,{...
    'The operation completed.', ...
    '', ...
    'RETURN: return to the main menu.', ...
    '', ...
    'QUIT: quit the installer.', ...
    });


% Ask user if he wants to reinstall or downgrade
% an existing installation.
function abort=AskVersion(fig,info,from)
  abort=false;
  if ~(info.isinstalled), return, end
  dst_stamp=info.dst_stamp;
  dst_vec=str2num(dst_stamp.STAMP);
  if strcmp(from,'disk')
    s=gettextfile(fullfile(info.bbsrc,'stamp.dat'));    % Read the installed stamp-file
    new_stamp=GetStamp(s);                              % Get the stamp (or use default)
  else
    new_stamp=GetNetStamp(fig,info);
  end
  new_vec=str2num(new_stamp.STAMP);
  if etime(new_vec,dst_vec)>0
    return
  elseif (etime(new_vec,dst_vec)<0)
    InfoText(fig,{...
      'The toolbox is about to be replaced by a version that appears', ...
      'to be older than what is currently installed.', ...
      '', ...
      'Proceeding with this action will downgrade the toolbox.', ...
      'If you know the installation package is recent, the installed', ...
      'toolbox is damaged and you should proceed.'});
      radio={'Downgrade / Repair','Cancel'};
  else
    InfoText(fig,{...
      'The toolbox is about to be replaced by a version that appears', ...
      'to be the same as the one that is currently installed.', ...
      '', ...
      'If you proceed, the toolbox will be removed and reinstalled.', ...
      'This can be used to repair a damaged installation, but is not', ...
      'recommended otherwise.'});
      radio={'Reinstall / Repair','Cancel'};
  end
  push={'Continue >>'};
  MultiSelect(fig, radio, push, 1, true(2,1), true);
  [radionum,pushnum]=MultiDone(fig);
  if (radionum~=1)
    MainMenu(fig,info,0);
    abort=true;
  end

% Ask user about the license.
function abort=ClickWrap(fig,info)
  InfoText(fig,{'BBTools is free software - but copyrights are reserved.', ...
                '',...
                'You may use BBTools as you wish internally, but copyright law', ...
                'requires that you obtain permission before you distribute it.', ...
                'The easiest way to get this permission is to adhere to the', ...
                'GPL-license shown above.', ...
                '',...
                'This is your last chance to abort this install.'});
  Wid=console(fig,{'Continue','Abort'});
  abort=true;
  try
    p=textread(fullfile(info.bbsrc,'GPL'), ...
      '%s','delimiter',char([10,12]),'whitespace','');
    p=strrep(p,char(9),blanks(8));
  catch
    console(fig,{'Quit','Return'});
    conPrint(fig,'The install was attempted from an incomplete installation package.');
    conPrint(fig,'Please download a full package or use upgrade.');
    pushnum=conDone(fig,true);
    if (pushnum==2)
      MainMenu(fig,info,0);
    else
      close(fig);
    end
    return;
  end
  set(Wid{1},'string',p);
  pushnum=conDone(fig,true);
  if pushnum~=1
    MainMenu(fig,info,0);
    return
  end
  abort=false;


%-------------------------------------------------------
%
% REMOVE
%
%-------------------------------------------------------

% The actual removal is also used by Install() and
% Upgrade(). It is therefore put in a separate
% function.
function Remove(fig,info)
  % Set the progress indicator
  num=length(info.BaseList);
  PList=[info.BaseList, {...
        'Confirm removal', ...
        'Remove paths', ...
        'Delete files', ...
        'Finish'}];
  ProgressList(fig,PList);

  % Ensure we are run outside the Matlab path.
  ProgressAt(fig,num);
  if CopyAndRun(fig,info,'-r'); return; end

  % Ask again if this is what the user want.
  ProgressAt(fig,num+1);
  InfoText(fig,{'This step will remove BBTools from the Matlab directory.', ...
                'Please confirm this action'});
  radio={'Cancel','Remove BBTools'};
  push={'Continue >>'};
  MultiSelect(fig, radio, push, 1, true(2,1), true);
  [radionum,pushnum]=MultiDone(fig);
  if (radionum~=2) || (pushnum~=1)
    MainMenu(fig,info,0);
    return;
  end

  % Remove the toolbox.
  InfoText(fig,{'BBTools is being uninstalled.'});
  console(fig,{'Quit','Return'});
  conPrint(fig,'Removing the installed toolbox.');
  res=TheRemove(fig,info,num+2);
  UpdatePath(fig,info);

  % Finish off
  ProgressAt(fig,num+4);
  conPrint(fig);
  if res==1
    conPrint(fig,'The toolbox was successfully removed.');
  else
    conPrint(fig,'A problem was encountered when removing the toolbox.');
  end
  Finish(fig,info,2);


% The actual function for removing.
function res=TheRemove(fig,info,num)
  res=1;                                               % Default is success
  bbroot=info.bbroot;

  % Remove the toolbox from the path.
  ProgressAt(fig,num);
  conPrint(fig);
  conPrint(fig,'Removing the toolbox from the current path.');
  rmpath2(fig,bbroot);                                 % Remove the toolbox from current path
  clear('pathdef');                                    % Ensure we use the disk version
  start_path=pathdef;                                  % Get the start path
  inx=strfind(start_path,bbroot);
  if ~isempty(inx)
    conPrint(fig,'Removing the toolbox from the startup path.');
    current_path=path;                                 % Save current path
    path(start_path);                                  % Set path to the startup-path
    rmpath2(fig,bbroot);                               % Remove the toolbox path(s)
    if info.rel<=13                                    % Save the new startup path
      suc=path2rc;
    else
      suc=savepath;
    end
    clear('pathdef');                                  % Ensure others use the new version
    path(current_path);                                % Restore the toolbox path
  else
    conPrint(fig,'The toolbox was not found in the startup path.');
    suc=0;
  end
  if suc~=0
    conPrint(fig,'Warning: could not remove the toolbox from the path.');
    res=0;
  end

  % Remove documentation.
  ProgressAt(fig,num+1);
  conPrint(fig);
  conPrint(fig,'Removing documentation.');
  helpdir=fullfile(matlabroot,'help','toolbox','bbtools','');
  if ~isempty(dir(helpdir))
    suc=rm(fig,helpdir);
    if suc==0, res=0; end
  end

  % Although TOC and index are only installed for Matlab R13,
  % it doesn't hurt to remove them unconditionally.
  suc1=rm(fig,fullfile(matlabroot,'help','mwdoctoc_bbtools.xml'));
  suc2=rm(fig,fullfile(matlabroot,'help','mwdocindex_bbtools.xml'));
  if (suc1==0) || (suc2==0), res=0; end

  % Remove the toolbox.
  if ~isempty(dir(bbroot))
    conPrint(fig);
    conPrint(fig,'Removing toolbox files.');
    suc=rm(fig,bbroot);
    if suc==0, res=0; end
  end

  % Print a meaningful message.
  if res==0
    conPrint(fig);
    conPrint(fig,'The removal is incomplete. The most likely cause is that you');
    conPrint(fig,'have insufficient rights to delete the toolbox at its');
    conPrint(fig,'current location. This may happen if another user installed');
    conPrint(fig,'the software, or if Matlab copied the toolbox in an upgrade.');
    conPrint(fig);
    conPrint(fig,'It is probably necessary to remove the toolbox manually.');
    conPrint(fig,'Please contact your system administrator. The files that');
    conPrint(fig,'needs to be deleted are listed above.');
  end


%-------------------------------------------------------
%
% CHECK FOR UPGRADES
%
%-------------------------------------------------------

function ChkUpgrade(fig,info)
  % Set the progress indicator
  num=length(info.BaseList);
  PList=[info.BaseList, ...
        {'Connect to Internet', ...
         'Find resident versions', ...
         'Finish'}];
  ProgressList(fig,PList);
  ProgressAt(fig,num);
  InfoText(fig,{'The current version of BBTools is being determined.'});
  console(fig,{'Quit','Return'});
  conPrint(fig,'Checking for new version.');

  % Get the stamp of the online version.
  ProgressAt(fig,num+1);
  [net_stamp,flag]=GetNetStamp(fig,info);

  % Print the stamp of the currently installed version.
  dst_stamp=info.dst_stamp;
  dst_vec=str2num(dst_stamp.STAMP);
  if (info.isinstalled) && (dst_vec(1)>=2000)
    conPrint(fig);
    conPrint(fig,'The time-stamp of the installed version of BBTools is:');
    conPrint(fig,'  %s', datestr(dst_vec,1));
  end

  % Print the stamp of the installation package.
  ProgressAt(fig,num+2);
  if info.inpack
    s=gettextfile(fullfile(info.bbsrc,'stamp.dat'));   % Read the installed stamp-file
    pack_stamp=GetStamp(s);                            % Get the stamp (or use default)
    pack_vec=str2num(pack_stamp.STAMP);
    conPrint(fig);
    conPrint(fig,'The time-stamp of the installation package is:');
    conPrint(fig,'  %s', datestr(pack_vec,1));
  end

  % Compare the online version with the others.
  ProgressAt(fig,num+3);
  conPrint(fig);
  if flag==1
    net_vec=str2num(net_stamp.STAMP);
    conPrint(fig,'The timestamp of the latest official release is:');
    conPrint(fig,'  %s', datestr(net_vec,1));
    conPrint(fig);
    dev_rel=false;
    % Say something meaningful about the current installation.
    if (info.isinstalled) && (etime(net_vec,dst_vec)==0)
      conPrint(fig,'The installed version of BBTools is current.');
      conPrint(fig,'We recommend no further action at this point.');
    elseif (info.isinstalled) && (etime(net_vec,dst_vec)<0)
      conPrint(fig,'The installed version of BBTools is either a developer version');
      conPrint(fig,'or broken. If you are not a developer or beta-tester, we recommend');
      conPrint(fig,'you install the latest official version from the net.');
      dev_rel=true;
    elseif (info.isinstalled) && (~info.inpack)
      % Leave the case with an installation package for later.
      conPrint(fig,'The installed version of BBTools is outdated.');
      conPrint(fig,'Please consider upgrading.');
    elseif ~(info.isinstalled)
      conPrint(fig,'BBTools was not currently installed.');
      conPrint(fig,'There are several ways to obtain it:');
      conPrint(fig,'  1) Use Install from Internet on the main menu,');
      conPrint(fig,'  2) Use Download, and install seperately,')
      conPrint(fig,'  3) Download a ZIP-archive from the homepage.');
    end

    % Say something meaningful about the installation package.
    if (info.inpack);conPrint(fig); end
    if (info.inpack) && (etime(net_vec,pack_vec)==0)
      conPrint(fig,'The installation package is current.');
      if ~(info.isinstalled)
        conPrint(fig,'Please consider installing it.');
      end
    elseif (info.inpack) && (etime(net_vec,pack_vec)<0)
      % Slightly tricky situation. This guy appear to be living on
      % the bleeding edge, but the install could be messed up.
      if ~dev_rel
        conPrint(fig,'The installation package contains an unofficial developer version.');
        conPrint(fig,'We can only recommend installing this package if you are a');
        conPrint(fig,'developer or beta-tester.');
      elseif (etime(pack_vec,dst_vec)<=0)
        conPrint(fig,'Your current installation is as recent as this package.');
      else
        conPrint(fig,'The package appears to be newer than your current installation.');
      end
    elseif (info.inpack)
      conPrint(fig,'The installation package is outdated.');
      if ~(info.isinstalled)
        conPrint(fig,'Please consider downloading a more recent version or ''Upgrade''');
        conPrint(fig,'rather than installing this one.');
      end
    end
  end

  % That's it. Ask the usert if he want to quit.
  ProgressAt(fig,num+3);
  Finish(fig,info,1);


%-------------------------------------------------------
%
% DOWNLOAD
%
%-------------------------------------------------------

% Most of the code in Download() is shared with
% Upgrade(). Therefore we put this in a
% separate function.
function Download(fig,info)
  % Setup progress indicator.
  num=length(info.BaseList);
  PList=[info.BaseList, ...
    {'Select directory', ...
     'Download', ...
     'Decompress', ...
     'Finish'}];
  ProgressList(fig,PList);

  % Do the actual download
  pushlist={'Quit','Return'};
  [respath,abort]=TheDownload(fig,info,info.dwndir,num,pushlist);
  if abort
    MainMenu(fig,info,0);
    return
  end

  % Done - setup buttons.
  conPrint(fig);
  conPrint(fig,'Done.');
  ProgressAt(fig,num+4);
  Finish(fig,info,1);

% The actual download function.
function [respath,abort]=TheDownload(fig,info,dwndir,num,pushlist)
  % Select download path.
  ProgressAt(fig,num+1);
  respath='';
  askdir=true;
  abort=false;
  while 1
    % Ask for download directory
    if askdir
      InfoText(fig,{...
        'Choose the directory where you want to download the toolbox.', ...
        'The toolbox will be created in a directory called ''bbtools''', ...
        'so the one you specify need not be empty.' ...
        });
      [dwndir,pushnum]=PathSelect(fig,{'Continue >>','Return'},dwndir);
      if pushnum~=1, abort=true; break; end
    end
    askdir=true;

    % Check if the user specified a directory.
    chk=dir(dwndir);
    if isempty(chk) || ~(chk(1).isdir)
      InfoText(fig,...
        {'A directory for the toolbox already exists in the specified directory.'});
      radio={'Specify another directory','Return to main menu'};
      push={'OK'};
      MultiSelect(fig, radio, push, 1, true(3,1), true(3,1));
      [radionum,pushnum]=MultiDone(fig);
      if pushnum~=1, abort=true; return; end
      switch radionum
        case 1, continue
        case 2, abort=true; return
      end
    end

    % Check if a toolbox already exist
    tmpzip=newfilename(dwndir, 'bbtools', 'zip');
    tmpdir=fullfile(dwndir, 'bbtools', '');
    respath=tmpdir;
    if isempty(dir(tmpdir)), break
    end

    % We are in trouble.
    InfoText(fig,...
      {'A directory for the toolbox already exists in the specified directory.'});
    radio={'Specify another directory', ...
           'Delete the existing directory', ...
           'Return to main menu'};
    push={'OK'};
    MultiSelect(fig, radio, push, 1, true(3,1), true(3,1));
    [radionum,pushnum]=MultiDone(fig);
    if pushnum~=1; abort=true; return; end
    switch radionum
      case 1, continue
      case 3, abort=true; return
    end

    % The user courageously asked us to remove an existing directory.
    InfoText(fig,{'The existing directory is being deleted.'});
    console(fig,{'Continue >>'});
    suc=rm(fig,tmpdir);
    if conDone(fig,true)~=1, abort=true; return; end
    askdir=false;
  end

  % Download installation package.
  InfoText(fig,{'Download in progress.'});
  console(fig,pushlist);
  if ~abort
    ProgressAt(fig,num+2);
    [net_stamp,flag]=GetNetStamp(fig,info);
    if flag~=1
      conPrint(fig, 'Trying previous location.');
      net_stamp=info.dst_stamp;
    end
    conPrint(fig);
    conPrint(fig, 'Downloading ''%s''...', tmpzip);
    [s,stat]=urlwrite(net_stamp.ZIPURL,tmpzip);
    if stat~=1
      conPrint(fig, 'Download failed.');
      abort=true;
    end
  end

  % Decompress.
  if ~abort
    ProgressAt(fig,num+3);
    conPrint(fig,'Decompressing.');
    unzip(tmpzip,dwndir);                               % No success code
    conPrint(fig,'Deleting archive file.');
    delete(tmpzip);                                     % No need to keep the .ZIP-file
  end

%-------------------------------------------------------
%
% UPGRADE
%
%-------------------------------------------------------

function Upgrade(fig,info)
  % Setup progress indicator.
  num=length(info.BaseList);
  PList=[info.BaseList, ...
    {'Check version', ...
     'Select directory', ...
     'Download', ...
     'Decompress', ...
     'Start installation'}];
  ProgressList(fig,PList);

  % Check if the installation is more recent
  % than the currently installed toolbox.
  ProgressAt(fig,num);
  console(fig,{'Continue >>'});
  if AskVersion(fig,info,'net'); return; end

  % Do the actual download / decompress
  pushlist={'Continue >>','Return'};
  [respath,abort]=TheDownload(fig,info,info.dwndir,num+1,pushlist);
  if abort
    MainMenu(fig,info,0);
    return;
  end

  % Launch the new installer.
  ProgressAt(fig,num+5);
  InfoText(fig,{
    'The actual installation will be performed by the upgraded installer.', ...
    'This will be launched when you press continue.', ...
    '', ...
    'Your current path will be changed by this operation', ...
    'We apologize for the inconvenience this may cause.', ...
    '', ...
    'The downloaded package remain downloaded so you can install/repair', ...
    'the toolbox if it becomes necessary.'});
  Wid=conDone(fig,false);
  cmd=sprintf(['%s;', ...
               'cd(''%s'');', ...
               'bbinstall(''-i'');'], ...
               QuitCmd(fig), respath);
  set(Wid{1},'Callback', cmd);
  set(Wid{2},'Callback',{@InitInstall,1});


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% SYSTEM EXAMINATION
%

function info=ExamineSys(fig,ask,action)
  % Initialize
  InfoText(fig,{...
    'Some tasks can only be accomplished when certain requirements', ...
    'are met.', ...
    '', ...
    'To install/uninstall/upgrade you must have write privileges to', ...
    'the Matlab directory.', ...
    '', ...
    'You can not install BBTools from an existing installation,', ...
    'although you may upgrade.',...
    '', ...
    'To upgrade/check for upgrade/download you must enable Java', ...
    'and have a working Internet connection.', ...
    '', ...
    'If the installer can not perform a task, then the information', ...
    'provided here should give a reason.'...
  });
  console(fig,{'Continue >>'});
  canInst=true; canUpg=true;
  canChk=true; canDwn=true;
  canRem=true;

  % Check platform.
  plat=computer;
  switch plat
    case {'PCWIN', 'GLNX86', 'HPUX'}
    otherwise
      conPrint(fig,'Sorry, the platform ''%s'' is not currently supported.', plat);
      conPrint(fig,'You will need to compile mex-files to make BBTools work');
      conPrint(fig,'Install is disabled.');
      conPrint(fig,'');
      ask=true;
      canInst=false;
  end

  % Check for Java.
  hasjava=(usejava('mwt')==1);
  if (~hasjava)
    conPrint(fig);
    conPrint(fig,'Java is not currently enabled, and the installer does');
    conPrint(fig,'not have access to the Internet. Therefore you you can');
    conPrint(fig,'not use the upgrade, check for new version, or download.');
    conPrint(fig,'');
    conPrint(fig,'TIP: If you disabled Java to avoid the user-interface');
    conPrint(fig,'you may start Matlab with the option ''-nodesktop'' instead');
    conPrint(fig,'of ''-nojava''. This will allow Matlab to be more functional.');
    canUpg=false; canChk=false; canDwn=false;
  end

  % Determine various paths.
  P=mfilename('fullpath');
  bbsrc=fileparts(P);                                  % Dir. containing BBINSTALL
  curdir=cd;                                           % Current directory
  bbroot=fullfile(matlabroot,'toolbox','bbtools','');  % Dir. to install the toolbox
  tooldir=fullfile(matlabroot,'toolbox','');           % Dir. containing Matlab toolboxes

  % Check if the toolbox is installed and get its stamp.
  %
  % You can not trust EXIST when you mess with
  % toolbox directories. Instead we use DIR.
  isinstalled=~isempty(dir(bbroot));
  s='';
  if isinstalled
    s=gettextfile(fullfile(bbroot,'stamp.dat'));       % Read the installed stamp-file
  end
  dst_stamp=GetStamp(s);                               % Get the stamp, or use default
  if isinstalled
    vec=str2num(dst_stamp.STAMP);
    if vec(1)>=2000
      conPrint(fig,'BBTools is installed, the time-stamp is %s.', datestr(vec,1));
    else
      conPrint(fig,'BBTools is installed, but a time-stamp could not be determined.');
      conPrint(fig,'You should probably install again or upgrade.');
    end
  else
    conPrint(fig,'BBTools is not currently installed.');
  end

  % Check if we have write privileges to the toolbox directory.
  %
  % This works well most of the time, but can fail to detect
  % delete-permissions on filesystems with fine-grained
  % permissions, such as recent versionsof Windows and Unix-systems
  % with support for ACL (access control lists).
  testfile=newfilename(tooldir,'testfile');
  fid=fopen(testfile,'w');
  if fid~=-1
    fclose(fid);
    rm(fig,testfile,0);
    haswriteperm=true;
  else
    conPrint(fig);
    conPrint(fig,'You do not have permission to change the Matlab directory.');
    conPrint(fig,'Installing and removing the toolbox has been disabled.');
    conPrint(fig,'Please contact your system administrator to perform');
    conPrint(fig,'these operators.');
    haswriteperm=false;
  end

  % Check if BBINSTALL runs from the installation path.
  %
  % On Windows the path may use a truncated form (for DOS compatibility),
  % and can mix upper- and lower-case. Unix can make a mess with symbolic links.
  %
  % To make the detection reliable we create a testfile. If this fails, we
  % don't have write permissions and the previous test failed us.
  inmatlab=false;
  cdinmatlab=false;
  if isinstalled && haswriteperm
    % Generate testfile with junk
    [testfile,fname]=newfilename(bbroot,'testfile');
    fid=fopen(testfile,'w');
    if fid==-1
      conPrint(fig);
      conPrint(fig,'The installer failed to create a file in the toolbox directory');
      conPrint(fig,'although you appear to have the required privileges.');
      conPrint(fig);
      conPrint(fig,'Please contact your system administrator or file a bug report.');
      ask=true;
      haswriteperm=false;
    else
      junk=uint8(256*rand(20,1));
      fwrite(fid,junk,'uint8');
      fclose(fid);
      % Open the file from the path of BBINSTALL.
      fid=fopen(fullfile(bbsrc,fname));
      if fid~=-1
        junk2=fread(fid,'uint8=>uint8');
        fclose(fid);
        inmatlab=isequal(junk,junk2);
      end
      % Open the file from the current path.
      fid=fopen(fullfile(curdir,fname));
      if fid~=-1
        junk2=fread(fid,'uint8=>uint8');
        fclose(fid);
        cdinmatlab=isequal(junk,junk2);
      end
      % Remove the testfile silently.
      suc=rm(fig,testfile,0);
      if suc==0
        conPrint(fig);
        conPrint(fig,'The installer failed to delete a testfile it created to');
        conPrint(fig,'check permissions. The privileges for the toolbox will');
        conPrint(fig,'not allow an ordinary installation.');
        conPrint(fig);
        conPrint(fig,'Please notify your system administrator of this problem.');
        conPrint(fig,'It may be necessary to remove the toolbox manually');
        conPrint(fig,'and reinstall.');
        ask=true;
        haswriteperm=false;
      end
    end
  end
  if inmatlab
    conPrint(fig);
    conPrint(fig,'The installer is running from the installation path.');
    conPrint(fig,'Install is disabled.');
    canInst=false;
  end

  % Check if BBINSTALL runs from an installation package.
  % We just check for a few critical files.
  inpack=false;
  if ~inmatlab
    inpack=IsInstallPack(bbsrc);
    if ~inpack
      conPrint(fig);
      conPrint(fig,'The installer does not appear to run from an installation package.');
      conPrint(fig,'Installation from disk is disabled; please upgrade from Internet');
      conPrint(fig,'or download a full installation package.');
      canInst=false;
    end
  end

  % Disable tasks that require write-permissions.
  if ~haswriteperm
    canInst=false; canRem=false; canUpg=false;
  end

  % Find a temporary directory.
  %
  % Sometimes TEMPDIR may return an empty string,
  % (particularly running as root on Unix).
  tmpdir=tempdir;
  if isempty(tmpdir)
    tmpdir=curdir;                                      % Final fall-back
    t={};
    if isunix
      % Try the regular Unix temp-dir and home-dir.
      t={'/tmp', getenv('HOME')};
    elseif ispc
      % On Windows first try local users temp, then the windows temp.
      t={getenv('TEMP'), fullfile(getenv('WINDIR'),'temp')};
    end
    for i=1:length(t)
      if IsDir(t{i})
        tmpdir=t{i};
        break
      end
    end
  end

  % Find a suitable download directory.
  % Normally the current directory is used, but
  % there are some slick cases.
  if ~inpack && ~inmatlab && length(bbsrc)>8 && strcmp(bbsrc(end-7:end),[filesep, 'bbtools'])
    % If BBINSTALL runs from an incomplete installation package,
    % we take take the directory containing it.
    dwndir=bbsrc(1:end-8);
  elseif ~cdinmatlab && isempty(fullfile(curdir,'stamp.dat'))
    % If the current dir does not contain a stamp (the exact files may
    % differ in new versions), and it is not in the matlab path, then
    % way may use it.
    dwndir=curdir;
  else
    % Last resort.
    dwndir=tmpdir;
  end

  % Print a path summary.
  if canInst
    conPrint(fig);
    conPrint(fig, 'The installation package is:');
    conPrint(fig, '  %s', bbsrc);
  end
  conPrint(fig);
  conPrint(fig, 'Toolbox installation directory is:');
  conPrint(fig, '  %s', bbroot);
  conPrint(fig);
  if isempty(dwndir)                                    % This can happen as root on Unix
    conPrint(fig,'A suitable download directory could not be determined.');
  else
    conPrint(fig,'Default download directory is:');
    conPrint(fig,'  %s', dwndir);
  end

  % Put the information in a struct.
  rel=str2num(version('-release'));                     % Get release number
  info=struct('bbsrc', bbsrc, ...
              'tooldir', tooldir, ...
              'bbroot', bbroot, ...
              'dwndir', dwndir, ...
              'tmpdir', tmpdir, ...
              'isinstalled', isinstalled, ...
              'inmatlab', inmatlab, ...
              'inpack', inpack, ...
              'hasjava', hasjava, ...
              'canInst', canInst, ...
              'canUpg', canUpg, ...
              'canChk', canChk, ...
              'canDwn', canDwn, ...
              'canRem', canRem, ...
              'dst_stamp', dst_stamp, ...
              'rel', rel);

  % Always ask if the action is infeasible.
  canAct=[canInst; canUpg; canChk; canDwn; canRem];
  if (action~=0) && ~canAct(action)
    ask=true;
  end
  conDone(fig,ask);                                     % We are done

%
% Check if a directory contains an installation package.
%
function inpack=IsInstallPack(testdir)
  FileList=GetInstallFiles;                             % We just check a few files
  for i=1:length(FileList)
    f=fullfile(testdir,FileList{i});
    if isempty(dir(f))                                  % Check if it exists
      inpack=false; return
    end
  end
  inpack=true;



%-------------------------------------------------------
%
% Routines for handling stamps
%
%-------------------------------------------------------

% Read a text-file as a string.
% This mimicks URLREAD for a text-file.
function s=gettextfile(file)
  fid=fopen(file,'r');
  if fid<0
    s='';
  else
    s=fread(fid,'char=>char')';
    fclose(fid);
  end

% Get a stamp from a string, or use
% default values.
function stamp=GetStamp(s,info)
  if nargin<2, info=struct; end
  stamp.STAMP='[0,0,0,0,0,0]';
  stamp.STAMPURL='http://nru.dk/software/bbtools/stamp.dat';
  stamp.ZIPURL='http://nru.dk/software/bbtools/bbtools.zip';
  stamp=parsestr(s,stamp);

% This function takes a string consisting of
% a complete text-file and extracts each
% line with an assignment into a struct.
function info=parsestr(s,info)
  if isempty(s), return, end
  p=strread(s,'%s','delimiter','\n');                    % Extract lines
  for i=1:length(p)
    l=unblank(p{i});                                     % Get line
    if isempty(l), continue, end                         % Skip empty lines
    if l(1)=='%', continue, end                          % Skip comment lines
    epos=find(l=='=');                                   % Find an equal sign
    if isempty(epos), continue, end                      % Skip non-assignment lines
    epos=epos(1);                                        % Only use the first sign
    field=unblank(l(1:epos-1));                          % Extract field-name
    if isempty(field), continue, end                     % Skip line: no field
    field(find(field==' '))='_';                         % Convert spaces to underscores
    if ~isvarname(field), continue, end                  % Skip line: illegal field-name
    info.(field)=unblank(l(epos+1:end));                 % Put result into the struct
  end

% Resembles deblank (for strings), except this
% also remove leading spaces.
function s=unblank(s)
  inx=find((s~=0) & ~isspace(s));                        % Find non-spaces
  if isempty(inx)
    s=s([]);
  else
    s=s(inx(1):inx(end));
  end

% Download the most recent stamp from the net.
%
% After applying this once, we store the result in
% the figure handle to avoid unnecessary downloads.
function [net_stamp,flag]=GetNetStamp(fig,info)
  private=get(fig,'userdata');
  if isfield(private,'netstamp')
    net_stamp=private.netstamp;
    flag=private.netstamp_flag;
    return
  end

  conPrint(fig);
  conPrint(fig,'Attempting to download stamp at:');
  conPrint(fig,'  %s', info.dst_stamp.STAMPURL);
  [s,stat]=urlread(info.dst_stamp.STAMPURL);
  if stat~=1
    conPrint(fig,'Failed to download update information.');
    flag=0;
  else
    net_stamp=GetStamp(s);                              % Get the stamp (or use default)
    flag=1;
  end
  
  private.netstamp=net_stamp;
  private.netstamp_flag=flag;
  set(fig,'userdata',private);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% FILE AND PATH FUNCTIONS
%

% Copy a file or directory from src to dst.
% If src and dst are directories, then sub-directories/files,
% common to both main-directories, may be specified as trailing
% arguments.
function [suc,msg,id]=cp(fig,src,dst,varargin)
  if nargin>=4
    src=fullfile(src,varargin{:});
    dst=fullfile(dst,varargin{:});
  end
  conPrint(fig,'Copying to %s', dst);
  [suc,msg,id]=copyfile(src,dst);
  if suc==0
    conPrint(fig,'Failed to copy %s', dst);
  end

% Remove a directory or file.
%
% For directories this recursively deletes all
% subdirectories and files if necessary.
%
% Verbosity levels:
%   0: Errors only
%   1: Print directories and top-level individual files
%   2: Print every file
%
% Return: 0 on failure.
function suc=rm(fig,target,verb)
  if nargin<3; verb=1; end                             % Default verbosity level
  suc=1;                                               % Default: OK
  files=dir(target);                                   % Get directory contents
  if isempty(files); return; end                       % Stop if it did not exist
  % Take directory if taget is a file
  isdir=(length(files)>1 || files.isdir);              % Check if the target is a dir
  if ~isdir
    t='file'; dir1=fileparts(target);
  else
    t='directory'; dir1=target;
  end
  if verb==1                                           % Print top-level files/dirs only
    conPrint(fig,'Deleting %s %s', t, target);
  end
  % Delete all files
  for i=1:length(files)
    name=files(i).name;
    if files(i).isdir==1                               % Delete a subdirectory
      if strcmp(name,'.') || strcmp(name,'..')         % Ignore self-dir and top-dir
        continue
      end
      suc=rm(fig,fullfile(dir1,name,''),verb);         % Remove subdirectory
      if suc==0; break; end                            % Stop on error
    else                                               % Delete a file
      fname=fullfile(dir1,name);                       % Get full filename
      if verb>=2                                       % Print individual files
        conPrint(fig,'Deleting %s', dir1);
      end
      % Clear file from memory.
      %
      % Workaround: a bug, confirmed for at least one versions of
      % Matlab running on Windows, causes CLEAR to crash if the
      % file starts with a dot.
      %
      % We skip this step for such files and hope they are not resident.
      if name(1)~='.'; clear(fname); end               % Rid it from memory
      % Delete does not have a success flag.
      % This makes the following necessary.
      t=evalc(['delete(''', fname, ''');']);
      if ~isempty(t); suc=0; break; end
    end
  end
  if suc && isdir; [suc,msg,id]=rmdir(dir1); end        % Delete the empty directory
  if suc==0                                             % Print error
    conPrint(fig,'Failed to delete %s', target);
  end

% Check if a target exist and if it is a directory.
function ret=IsDir(target)
  ret=false;
  if isempty(target), return, end
  files=dir(target);
  if length(files)>1 || files.isdir
    ret=true;
  end

% Find an unused filename by appending an integer to it.
function [newname,file]=newfilename(basedir, name, ext)
  if nargin<3, ext=''; end
  % First try the unmodified name.
  if isempty(ext)
    file=name;
  else
    file=[name, '.', ext];
  end
  newname=fullfile(basedir,file);
  if isempty(dir(newname)), return
  end
  % Otherwise append an integer starting from 0.
  i=0;
  while 1
    % Generate name
    if isempty(ext)
      file=sprintf('%s%i',name,i);
    else
      file=sprintf('%s%i.%s',name,i,ext);
    end
    newname=fullfile(basedir,file);
    % Check if file exists
    if isempty(dir(newname)); return; end
    % Try another name, but stop if it gets ridiculous.
    i=i+1;
    if i>50
      error('Unable to generate an unused file.');
    end
  end

% Remove a directory from the path. In contrast to
% RMPATH, this function also removes sub-directories of
% the dir from the path.
function rmpath2(fig,basedir)
  % Build a cell-array of all the paths.
  P=path;                                              % Get path
  if ~isunix                                           % Adjust for case sensitivity
    P=lower(path);
    basedir=lower(basedir);
  end
  P=strread(P,'%s','delimiter',pathsep);               % Get cell for each dir.
  % Find and remove base-dir.
  inx=find(strcmp(P,basedir));
  if ~isempty(inx); rmpath(basedir); end               % Remove base directory
  % Remove subdirs
  basedir=[basedir, filesep];                          % Sub-dir all start with this string
  inx=find(strncmp(P,basedir,length(basedir)));        % Find paths matching the basedir
  if ~isempty(inx); rmpath(P{inx}); end                % Remove all paths found


% Update the cache and give the help-browser a kick.
function UpdatePath(fig,info)
  conPrint(fig,'Updating paths.');
  rehash toolboxcache;
  if info.hasjava
    docroot(docroot);
  end


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% BASE GUI
%

function fig=CreateGUI
  % Try to take over a previous installer figure.
  % Otherwise create a new one.
  prop={'visible','off', ...
        'IntegerHandle','off', ...
        'Name','BBTools Installer', ...
        'Units', 'pixels', ...
        'NumberTitle', 'off', ...
        'MenuBar','none', ...
        'Tag','BBTools Installer'};
  prv=get(0,'ShowHiddenHandles');
  set(0,'ShowHiddenHandles','on');
  fig=findobj('tag','BBTools Installer');
  set(0,'ShowHiddenHandles',prv);
  if isempty(fig)
    fig=figure(prop{:});
  else
    % Only keep one install window
    for i=2:length(fig)
      close(fig(i));
    end
    fig=fig(1);
    set(fig,prop{:});
    list=findall(get(fig,'children'));
    if ~isempty(list)
      delete(list);
    end
  end

  % Create struct which includes handles
  private=get(fig,'userdata');
  private.HTaskText={};
  private.HWidgets={};
  bgcol=get(fig,'Color');
  private.progFrame = uicontrol(fig, ...                % Seperator between progress and action
    'Style','frame', ...
    'Units', 'pixels', ...
    'BackgroundColor', bgcol, ...
    'ForegroundColor', [0,0,0]);
  private.HStatInfo=uicontrol(fig, ...                  % Text-box with explanatory text
    'Style', 'listbox', ...
    'Max',2, 'Min',0, 'Value',[], 'Enable','inactive', ...
    'Units', 'pixels', ...
    'BackgroundColor','white', ...
    'ForegroundColor','black');

  % Put it on the screen.
  set(fig,'UserData',private);
  recomputePos(fig);
  set(fig,'ResizeFcn',@recomputePos);
  set(fig,'Visible','on','HandleVisibility','callback');
  drawnow; pause(0);

%
% Recompute widget positions.
%
function recomputePos(fig,event)
  % Get our data
  private=get(fig,'UserData');

  % Compute splits
  figPos=get(fig,'Position');
  x_split=round(figPos(3)*.3);
  y_split=round(figPos(4)*.3);
  margin=10;

  % Compute positions
  px1=margin;
  px2=max(px1+10,x_split-margin/2);
  py1=y_split+margin/2;
  py2=max(py1+10,figPos(4)-margin);
  sx1=margin;
  sx2=max(sx1+10,figPos(3)-margin);
  sy1=margin;
  sy2=max(sy1+10,y_split-margin/2);

  % Change position on progress frame and status box
  set(private.HStatInfo, 'Position', [sx1, sy1, sx2-sx1, sy2-sy1]);
  set(private.progFrame, 'Position', [x_split, py1, 2, py2-py1]);

  % Update positions of the task list.
  L=length(private.HTaskText);
  y=py2-margin-20;
  for i=1:L
    hdl=private.HTaskText{i};
    set(hdl, 'Position', [px1, y, px2-px1, 20]);
    ext=get(hdl,'extent');
    y=y-ext(4)-margin;
  end

  % Find extreme positions of all widgets and
  % center them in the action rectangle.
  L=length(private.HWidgets);
  x1=inf; y1=inf; x2=-inf; y2=-inf;
  for i=1:L
    hdl=private.HWidgets{i};
    pos=get(hdl,'position');
    x1=min(x1,pos(1));
    y1=min(y1,pos(2));
    x2=max(x2,pos(1)+pos(3));
    y2=max(y2,pos(2)+pos(4));
  end
  cx=(x_split+figPos(3))/2;
  cy=(y_split+figPos(4))/2;
  mx=cx-(x1+x2)/2;
  my=cy-(y1+y2)/2;
  for i=1:L
    hdl=private.HWidgets{i};
    pos=get(hdl,'position');
    set(hdl,'position',[pos(1)+mx,pos(2)+my,pos(3),pos(4)]);
  end

%
% Change text in the info box.
%
function InfoText(fig,explain)
  if ~ishandle(fig), return, end                        % Ignore closed windows
  private=get(fig,'UserData');
  set(private.HStatInfo,'String',explain);

%
% Remove custom widgets and reset.
%
function remWidgets(fig)
  if ishandle(fig)
    set(fig,'ResizeFcn',@recomputePos);
    private=get(fig,'userdata');
    Wid=private.HWidgets;
    if ~isempty(Wid); delete(Wid{:}); end
    private=get(fig,'UserData');
    private.HWidgets={};
    set(fig,'UserData',private);
  end


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Module: Create a list of actions and show how far we
% are at some point.
%

function ProgressList(fig,plist)
  if ~ishandle(fig); return; end
  % Delete a previous list.
  private=get(fig,'UserData');
  if ~isempty(private.HTaskText)
    delete(private.HTaskText{:});
  end
  % Setup the new list
  L=length(plist);
  Wid=cell(L,1);
  bgcol=get(fig,'Color');
  for i=1:L
    Wid{i}=uicontrol(fig, ...
      'style', 'text', ...
      'units', 'pixels', ...
      'string', plist{i}, ...
      'horizontalalignment', 'left', ...
      'backgroundcolor', bgcol);
  end
  private.HTaskText=Wid;
  set(fig,'UserData',private);
  recomputePos(fig);

function ProgressAt(fig,num)
  if ishandle(fig)
    private=get(fig,'UserData');
    Wid=private.HTaskText;
    if num>1
      set([Wid{1:num-1}],'fontweight','normal','enable','off');
    end
    if num>=1
      set(Wid{num},'fontweight','bold','enable','on');
    end
    set([Wid{num+1:end}],'fontweight','normal','enable','on');
  end
  drawnow; pause(0);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Module: Create a list of radio-buttons and
% push-buttons. It waits until a pushbutton is pressed,
% and return the number of the selected radiobutton and
% pushbutton.
%

function Wid=MultiSelect(fig,radiolist,pushlist,def,ract,pact)
  remWidgets(fig);
  Rnum=length(radiolist);
  Pnum=length(pushlist);
  Wid=cell(Rnum+Pnum,1);
  if ~ishandle(fig); return; end
  % Create radiobuttons and find height and width
  bgcol=get(fig,'Color');
  W=0; H=0;
  for i=1:Rnum
    Wid{i}=uicontrol(fig, ...
      'Style', 'radiobutton', ...
      'Units','pixels', ...
      'String', radiolist{i}, ...
      'BackgroundColor', bgcol, ...
      'callback', @radio_callback ...
    );
    if ~ract(i)
      set(Wid{i},'enable','off');
    end
    ext=get(Wid{i},'extent');
    W=max(W,ext(3));
    H=max(H,ext(4));
  end
  if def>0
    set(Wid{def},'value',1);                            % Activate the default choice
  end
  % Set positions now that the extent is known.
  W=W+20; H=H+5;                                        % Add extra margins
  for i=1:Rnum
    set(Wid{i}, 'Position', [0, H*(Rnum-i), W, H]);
  end
  % Add pushbuttons
  W=0; H=0;
  for i=1:Pnum
    Wid{Rnum+i}=uicontrol(fig, ...
      'Style', 'pushbutton', ...
      'Units','pixels', ...
      'String', pushlist{i}, ...  %'position', [0, -1.5*i*(H+5), W, 1.25*H], ...
      'HorizontalAlignment', 'center', ...
      'Interruptible', 'off', ...
      'UserData', 0, ...
      'Callback', @push_callback);
    if ~pact(i)
      set(Wid{i},'enable','off');
    end
    ext=get(Wid{Rnum+i},'extent');
    W=max(W,ext(3));
    H=max(H,ext(4));
  end
  % Set positions now that the extent is known.
  W=W*1.25+20; H=H+5;                                   % Add extra margins
  for i=1:Pnum
    set(Wid{Rnum+i}, 'Position', [0, -1.5*i*(H+5), W, 1.25*H]);
  end
  % Put it on screen.
  private=get(fig,'UserData');
  private.HWidgets=Wid;
  set(fig,'UserData',private);
  recomputePos(fig);

% Wait and get the answer
function [radionum,pushnum]=MultiDone(fig)
  % Get widgets
  private=get(fig,'UserData');
  Wid=private.HWidgets;
  % Get selection choice
  uiwait(fig);
  radionum=-1; pushnum=-1;
  if ishandle(fig)
    radionum=0;
    pushnum=0;
    pushc=0; radioc=0;
    for i=1:length(Wid) %Rnum
      switch lower(get(Wid{i},'style'))
        case 'radiobutton'
          radioc=radioc+1;
          if get(Wid{i},'value')==1, radionum=radioc; end
        case 'pushbutton'
          pushc=pushc+1;
          if get(Wid{i},'userdata')==1, pushnum=pushc; end
      end
    end
    remWidgets(fig);
  end

% Radio button callback: mutual exclusive selection.
function radio_callback(hdl,event)
  fig=get(hdl,'parent');
  private=get(fig,'UserData');
  Wid=private.HWidgets;
  for i=1:length(Wid)
    if strcmp(get(Wid{i},'Style'),'radiobutton')
      set(Wid{i},'value',0);
    end
  end
  set(hdl,'value',1);

% Push button callback: stop selection.
function push_callback(hdl,event)
  fig=get(hdl,'parent');
  set(hdl,'UserData',1);
  uiresume(fig);


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Module: Ask for a directory path. As usually, create
% a list of push-buttons and return which one was pushed.

function [newdir,pushnum]=PathSelect(fig,pushlist,defdir)
  remWidgets(fig);
  Wid=cell(2+length(pushlist),1);
  if ~iscell(pushlist)
    pushlist={pushlist};
  end
  % Create edit box
  Wid{1}=uicontrol(fig, ...
    'style', 'edit', ...
    'string', defdir, ...
    'horizontalalignment', 'left', ...
    'units', 'pixels', ...
    'BackgroundColor','white', ...
    'ForegroundColor','black');
  % Add browse pushbutton
  Wid{2}=uicontrol(fig, ...
    'style', 'pushbutton', ...
    'units','pixels', ...
    'string', 'Browse...', ...
    'horizontalalignment', 'center', ...
    'interruptible', 'off', ...
    'callback', @push_browse);
  % Add pushbuttons
  Pnum=length(pushlist);
  for i=1:Pnum
    Wid{2+i}=uicontrol(fig, ...
     'style', 'pushbutton', ...
     'units','pixels', ...
     'string', pushlist{i}, ...
     'horizontalalignment', 'center', ...
     'interruptible', 'off', ...
     'userdata', 0, ...
     'callback', @push_callback);
  end
  % Put it on screen
  private=get(fig,'UserData');
  private.HWidgets=Wid;
  set(fig,'UserData',private);
  set(fig,'ResizeFcn',@recomputePathPos);
  recomputePathPos(fig);
  % Wait and get user response.
  uiwait(fig);
  pushnum=1;                                            % Default
  newdir=defdir;
  if ishandle(fig)
    newdir=get(Wid{1},'string');
    for i=3:length(Wid)
      if get(Wid{i},'userdata')==1, pushnum=i-2;
      end
    end
    remWidgets(fig);
  end

% Resize function for PathSelect.
function recomputePathPos(fig,varargin)
  private=get(fig,'userdata');
  Wid=private.HWidgets;
  % Determine the size of the console
  figPos=get(fig,'position');
  widPos=get(Wid{1},'position');
  W=round(figPos(3)*.7)-20;
  W=max(10,W);
  set(Wid{1},'position',[0,0,W,widPos(4)]);
  % Move browse button.
  ext=get(Wid{2},'extent');
  py=-ext(4)*2; ph=1.5*ext(4);
  pw=2*ext(3);
  set(Wid{2},'Position',[0,py,pw,ph]);
  % Move pushbuttons accordingly.
  px=W;
  for i=3:length(Wid)
    ext=get(Wid{i},'extent');
    pw=2*ext(3);
    px=px-pw;
    set(Wid{i},'Position',[px,py,pw,ph]);
    px=px-10;
  end
  recomputePos(fig,varargin{:});

% Start Matlab's directory browser.
function push_browse(hdl,event)
  fig=get(hdl,'parent');
  private=get(fig,'userdata');
  Wid=private.HWidgets;
  newdir=uigetdir(get(Wid{1},'string'));
  if ~isequal(newdir,0)
    set(Wid{1},'string',newdir);
  end


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Module: Create a "console" which can be filled with
% any kind of information.
%

function Wid=console(fig,pushlist)
  remWidgets(fig);
  Wid=cell(1+length(pushlist),1);
  % Create console window with status info
  Wid{1}=uicontrol(fig, ...
     'Style', 'listbox', ...
     'Max', 2, 'Min',0, 'Value',[], ...
     'Enable', 'inactive', ...
     'Units', 'pixels', ...
     'BackgroundColor','white', ...
     'ForegroundColor','black');
  % Create push buttons
  for i=1:length(pushlist)
    Wid{i+1}=uicontrol(fig, ...
       'Style', 'pushbutton', ...
       'Units','pixels', ...
       'String', pushlist{i}, ...
       'Enable', 'off', ...
       'HorizontalAlignment', 'center', ...
       'Interruptible', 'off', ...
       'Callback', @push_callback);
  end
  % Put it on screen
  private=get(fig,'UserData');
  private.HWidgets=Wid;
  set(fig,'UserData',private);
  set(fig,'ResizeFcn',@recomputeConPos);
  recomputeConPos(fig);

% Resize function for console
function recomputeConPos(fig,varargin)
  private=get(fig,'UserData');
  Wid=private.HWidgets;
  % Determine the size of the console
  figPos=get(fig,'Position');
  ext=get(Wid{2},'extent');
  W=round(figPos(3)*.7)-20;
  H=round(figPos(4)*.7)-20-2*ext(4);
  W=max(10,W);
  H=max(10,H);
  set(Wid{1},'Position',[0,0,W,H]);
  % Move pushbuttons accordingly.
  py=-ext(4)*2; ph=1.5*ext(4);
  px=W;
  for i=2:length(Wid)
    ext=get(Wid{i},'extent');
    pw=2*ext(3);
    px=px-pw;
    set(Wid{i},'Position',[px,py,pw,ph]);
    px=px-10;
  end
  recomputePos(fig,varargin{:});

% Call this to print a line on the console.
function conPrint(fig,newstr,varargin)
  % Put the string in standard form.
  if nargin<2; newstr=''; end
  if nargin>2; newstr=sprintf(newstr,varargin{:}); end
  if ~iscell(newstr); newstr={newstr}; end
  % Fall back to stdout. FPRINTF is more reliable than
  % DISP when it comes to actually getting something printed.
  if ~ishandle(fig)
    fprintf('%s\n', newstr{:});
    return
  end
  % Get widget
  private=get(fig,'UserData');
  Wid=private.HWidgets;
  % Add new text
  str=get(Wid{1},'string');
  if isempty(str)
    str=newstr;
  else
    str=[str; newstr];
  end
  set(Wid{1},'string',str);
  % Ensure the new line is visible.
  pos=get(Wid{1},'position');
  H=max(1,pos(4)-20);                                   % Allow a bit of extra room
  ext=get(Wid{1},'extent');
  if ext(4)>H                                           % Is the new line visible?
    % Try to put the new line near the bottom.
    L=size(str,1);
    top=1+ceil((L+1)*(ext(4)-H)/ext(4));
    top=median([1,top,L]);
    set(Wid{1},'listboxtop',top);
  end
  drawnow; pause(0);

% Call this after finishing.
% ask=true:  wait and return the number of the pressed pushbutton
% ask=false: return immediately with the list of pusbutton widgets.
function out=conDone(fig,ask)
  if ~ishandle(fig); out={}; return; end            % Handle closed windows
  % Get widgets and enable them.
  private=get(fig,'UserData');
  Wid=private.HWidgets;
  set([Wid{2:end}],'Enable','on');
  % Stop here?
  if ~ask, out=Wid(2:end); return; end
  % Wait for user, if ask is provided
  pushnum=1;                                            % Default
  uiwait(fig);
  if ishandle(fig)
    % Check which pushbutton was pressed
    for i=2:length(Wid)
      if get(Wid{i},'userdata')==1, out=i-1; end
    end
  end
  remWidgets(fig);
