help-octave
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: pkg install/uninstall again


From: David Bateman
Subject: Re: pkg install/uninstall again
Date: Sun, 15 Jul 2007 13:32:51 +0200
User-agent: Thunderbird 1.5.0.7 (X11/20060921)

address@hidden wrote:
> I've got one more time trouble with pkg
> I tried to uninstall one package!
> <<pkg uninstall -verbose nnet-0.1.0

That should read "pkg uninstall -verbose nnet". You don't need the
version numbering.

> after this command, ALL packages are uninstalled, this means octave 
> couldn't find the
> packages but the directories are still in 
> /usr/local/share/octave/packages/..

This definitely should happen and I've never seen it. However, I'm not
sure its worth debugging it with the 2.9.12 version of pkg as pkg has
evolved significantly.. Can you try the attached version of pkg.m from
the CVS and see if it has these problems.

> 
> so the complete install and uninstall started again...

If the packages are in the directory it is just the database that is
corrupt. Again try "pkg -rebuild" to fix the database, though that only
worked correctly in 2.9.12+ and not the 2.9.12 version.

> 
> <<help pkg 
> doesn't show me a way to update a package. This means if I want to 
> update a package
> 1. uninstall old package
> 2. install new package
> is this correct?

If you do a "pkg install" on an installed package it should ignore the
request if the version is the same or older than the version installed
or it should update the version installed if it is newer.


Regards
David

> 
> 
> System:
> gentoo/coLinux on WinXP Prof.
> Octave-2.9.12
> 
> thanks again
> Michael

## Copyright (C) 2005 Søren Hauberg
## 
## This file is part of Octave.
##
## Octave is free software; you can redistribute it and/or modify it
## under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2, or (at your option)
## any later version.
##
## Octave is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Octave; see the file COPYING.  If not, write to the Free
## Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
## 02110-1301, USA.

## -*- texinfo -*-
## @deftypefn  {Command} pkg @var{command} @var{pkg_name}
## @deftypefnx {Command} pkg @var{command} @var{option} @var{pkg_name}
## This command interacts with the package manager. Different actions will
## be taken depending on the value of @var{command}.
##
## @table @samp
## @item install
## Install named packages.  For example,
## @example
## pkg install image-1.0.0.tar.gz
## @end example
## @noindent
## installs the package found in the file @code{image-1.0.0.tar.gz}.
##
## The @var{option} variable can contain options that affect the manner
## in which a package is installed. These options can be one or more of
##
## @table @code
## @item -nodeps
## The package manager will disable the dependency checking. That way it 
## is possible to install a package even if it depends on another package 
## that's not installed on the system. @strong{Use this option with care.}
##
## @item -noauto
## The package manager will not automatically load the installed package 
## when starting Octave, even if the package requests that it is.
##
## @item -auto
## The package manager will automatically load the installed package when 
## starting Octave, even if the package requests that it isn't.
##
## @item -local
## A local installation is forced, even if the user has system privileges.
##
## @item -global
## A global installation is forced, even if the user doesn't normally have
## system privileges
##
## @item -verbose
## The package manager will print the output of all of the commands that are 
## performed.
## @end table
##
## @item uninstall
## Uninstall named packages.  For example,
## @example
## pkg uninstall image
## @end example
## @noindent
## removes the @code{image} package from the system. If another installed
## package depends on the @code{image} package an error will be issued.
## The package can be uninstalled anyway by using the @code{-nodeps} option.
## @item load
## Add named packages to the path. After loading a package it is
## possible to use the functions provided by the package. For example,
## @example
## pkg load image
## @end example
## @noindent
## adds the @code{image} package to the path. It is possible to load all
## installed packages at once with the command
## @example
## pkg load all
## @end example
## @item unload
## Removes named packages from the path. After unloading a package it is
## no longer possible to use the functions provided by the package.
## This command behaves like the @code{load} command.
## @item list
## Show a list of the currently installed packages. By requesting one or two
## output argument it is possible to get a list of the currently installed
## packages. For example,
## @example
## installed_packages = pkg list;
## @end example
## @noindent
## returns a cell array containing a structure for each installed package.
## The command
## @example
## address@hidden, @var{system_packages}] = pkg list
## @end example
## @noindent
## splits the list of installed packages into those who are installed by
## the current user, and those installed by the system administrator.
## @item prefix
## Set the installation prefix directory. For example,
## @example
## pkg prefix ~/my_octave_packages
## @end example
## @noindent
## sets the installation prefix to @code{~/my_octave_packages}.
## Packages will be installed in this directory.
##
## It is possible to get the current installation prefix by requesting an
## output argument.  For example,
## @example
## p = pkg prefix
## @end example
## @item local_list
## Set the file in which to look for information on the locally
## installed packages. Locally installed packages are those that are
## typically available only to the current user. For example
## @example
## pkg local_list ~/.octave_packages
## @end example
## It is possible to get the current value of local_list with the following
## @example
## pkg local_list
## @end example
## @item global_list
## Set the file in which to look for, for information on the globally
## installed packages. Globally installed packages are those that are
## typically available to all users. For example
## @example
## pkg global_list /usr/share/octave/octave_packages
## @end example
## It is possible to get the current value of global_list with the following
## @example
## pkg global_list
## @end example
## @item rebuild
## Rebuilds the package database from the installed directories. This can 
## be used in cases where for some reason the package database is corrupted.
## It can also take the @code{-auto} and @code{-noauto} options to allow the
## autolaoding state of a package to be changed. For example
##
## @example
## pkg rebuild -noauto image
## @end example
##
## will remove the autoloading status of the image package.
## @item build
## Builds a binary form of a package or packages. The binary file produced
## will itself be an Octave package that can be installed normally with
## @code{pkg}. The form of the command to build a binary package is
##
## @example
## pkg build builddir image-1.0.0.tar.gz @dots{}
## @end example
##
## @noindent
## where @code{buiddir} is the name of a directory where the temporary
## installation will be produced and the binary packages will be found.
## The options @code{-verbose} and @code{-nodeps} are respected, while 
## the other options are ignored.
## @end table
## @end deftypefn

## PKG_ADD: mark_as_command pkg
## PKG_ADD: pkg ("load", "auto");

function [local_packages, global_packages] = pkg (varargin)
  ## Installation prefix (XXX: what should these be on windows?)
  persistent user_prefix = false;
  persistent prefix = -1;
  persistent local_list = tilde_expand (fullfile("~", ".octave_packages"));
  persistent global_list = fullfile (OCTAVE_HOME (), "share", "octave",
                                     "octave_packages");
  mlock ();

  if (ispc () && ! isunix ())
    global_install = 1;
  else
    global_install = (geteuid() == 0);
  endif

  if (prefix == -1)
    if (global_install)
      prefix = fullfile (OCTAVE_HOME (), "share", "octave", "packages");
    else
      prefix = fullfile ("~", "octave");
    endif
    prefix = tilde_expand (prefix);
  endif

  ## Handle input
  if (length (varargin) == 0 || ! iscellstr (varargin))
    print_usage ();
  endif
  files = {};
  deps = true;
  auto = 0;
  action = "none";
  verbose = false;
  for i = 1:length (varargin)
    switch (varargin{i})
      case "-nodeps"
        deps = false;
      case "-noauto"
        auto = -1;
      case "-auto"
        auto = 1;
      case "-verbose"
        verbose = true;
      case "-local"
        global_install = false;
        if (! user_prefix)
          prefix = tilde_expand (fullfile ("~", "octave"));
        endif
      case "-global"
        global_install = true;
        if (! user_prefix)
          prefix = fullfile (OCTAVE_HOME (), "share", "octave", "packages");
        endif
      case {"list", "install", "uninstall", "load", "unload", ...
            "prefix", "local_list", "global_list", "rebuild", "build"}
        if (strcmp (action,"none"))
          action = varargin{i};
        else
          error ("more than one action specified");
        endif
      otherwise
        files{end+1} = varargin{i};
    endswitch
  endfor

  ## Take action
  switch (action)
    case "list"
      if (nargout == 0)
        installed_packages (local_list, global_list);
      elseif (nargout == 1)
        local_packages = installed_packages (local_list, global_list);
      elseif (nargout == 2)
        [local_packages, global_packages] = installed_packages (local_list,
                                                                global_list);
      else
        error ("too many output arguments requested");
      endif

    case "install"
      if (length (files) == 0)
        error ("you must specify at least one filename when calling 'pkg 
install'");
      endif
      install (files, deps, auto, prefix, verbose, local_list, 
               global_list, global_install);

    case "uninstall"
      if (length (files) == 0)
        error ("you must specify at least one package when calling 'pkg 
uninstall'");
      endif
      uninstall (files, deps, verbose, local_list, 
                 global_list, global_install);

    case "load"
      if (length (files) == 0)
        error ("you must specify at least one package, 'all' or 'auto' when 
calling 'pkg load'");
      endif
      load_packages (files, deps, local_list, global_list);

    case "unload"
      if (length (files) == 0)
        error ("you must specify at least one package or 'all' when calling 
'pkg unload'");
      endif
      unload_packages (files, deps, local_list, global_list);

    case "prefix"
      if (length (files) == 0 && nargout == 0)
        disp (prefix);
      elseif (length (files) == 0 && nargout == 1)
        local_packages = prefix;
      elseif (length (files) == 1 && nargout <= 1 && ischar (files{1}))
        prefix = files{1};
        ## if (!strcmp(prefix(end), filesep))
        ##   prefix(end+1) = filesep;
        ## endif
        prefix = absolute_pathname (prefix);
        local_packages = prefix;
        user_prefix = true;
      else
        error ("you must specify a prefix directory, or request an output 
argument");
      endif

    case "local_list"
      if (length (files) == 0 && nargout == 0)
        disp (local_list);
      elseif (length (files) == 0 && nargout == 1)
        local_packages = local_list;
      elseif (length (files) == 1 && nargout == 0 && ischar (files{1}))
        try
          local_list = absolute_pathname (files{1});
        catch
          ## Force file to be created
          fclose (fopen (files{1}, 'wt'));
          local_list = absolute_pathname (files{1});
        end_try_catch
      else
        error ("you must specify a local_list file, or request an output 
argument");
      endif

    case "global_list"
      if (length (files) == 0 && nargout == 0)
        disp(global_list);
      elseif (length (files) == 0 && nargout == 1)
        local_packages = global_list;
      elseif (length (files) == 1 && nargout == 0 && ischar (files{1}))
        try
          global_list = absolute_pathname (files{1});
        catch
          ## Force file to be created
          fclose (fopen (files{1}, 'wt'));
          global_list = absolute_pathname (files{1});
        end_try_catch
      else
        error ("you must specify a global_list file, or request an output 
argument");
      endif

    case "rebuild"
      if (global_install)
        global_packages = rebuild (prefix, global_list, files, auto, verbose);
        global_packages = save_order (global_packages);
        save (global_list, "global_packages");
        if (nargout > 0)
          local_packages = global_packages;
        endif
      else
        local_packages = rebuild (prefix, local_list, files, auto, verbose);
        local_packages = save_order (local_packages);
        save (local_list, "local_packages");
        if (nargout == 0)
          clear ("local_packages");
        endif
      endif

    case "build"
      if (length (files) < 2)
        error ("you must specify at least the build directory and one 
filename\nwhen calling 'pkg build'");
      endif
      build (files, deps, auto, verbose);

    otherwise
      error ("you must specify a valid action for 'pkg'. See 'help pkg' for 
details");
  endswitch
endfunction

function descriptions = rebuild (prefix, list, files, auto, verbose)
  if (isempty (files))
    [dirlist, err, msg] = readdir (prefix);
    if (err)
      error ("couldn't read directory %s: %s", prefix, msg);
    endif
    ## the two first entries of dirlist are "." and ".."
    dirlist([1,2]) = [];
  else
    old_descriptions = installed_packages (list, list);
    wd = pwd ();
    unwind_protect
      cd (prefix);
      dirlist = glob (cellfun(@(x) strcat(x, '-*'), files, 'UniformOutput', 0));
    unwind_protect_cleanup
      cd (wd);
    end_unwind_protect
  endif
  descriptions = {};
  for k = 1:length (dirlist)
    descfile = fullfile (prefix, dirlist{k}, "packinfo", "DESCRIPTION");
    if (verbose)
      printf ("recreating package description from %s\n", dirlist{k});
    endif
    if (exist (descfile, "file"))
      desc = get_description (descfile);
      desc.dir = fullfile (prefix, dirlist{k});
      if (auto != 0)
        if (exist (fullfile (desc.dir, "packinfo", ".autoload"), "file"))
          unlink (fullfile (desc.dir, "packinfo", ".autoload"));
        endif
        if (auto < 0)
          desc.autoload = 0;
        elseif (auto > 0)
          desc.autoload = 1;
          fclose (fopen (fullfile (desc.dir, "packinfo", ".autoload"), "wt"));
        endif
      else
        if (exist (fullfile (desc.dir, "packinfo", ".autoload"), "file"))
          desc.autoload = 1;
        else
          desc.autoload = 0;
        endif
      endif
      descriptions{end + 1} = desc;
    elseif (verbose)
      warning ("directory %s is not a valid package", dirlist{k});
    endif
  endfor

  if (! isempty (files))
    ## We are rebuilding for a particular package(s) so we should take
    ## care to keep the other untouched packages in the descriptions
    descriptions = {descriptions{:}, old_descriptions{:}};

    dup = [];
    for i = 1:length (descriptions)
      if (find (dup, i))
        continue;
      endif
      for j = (i+1):length (descriptions)
        if (find (dup, j))
          continue;
        endif
        if (strcmp (descriptions{i}.name, descriptions{j}.name))
          dup = [dup, j];
        endif
      endfor
    endfor
    if (! isempty (dup))
      descriptions (dup) = [];
    endif  
  endif
endfunction

function build (files, handle_deps, autoload, verbose)
  if (length (files) < 1)
    error ("insufficient number of files");
  endif
  builddir = files{1};
  if (! exist (builddir, "dir"))
    warning ("creating build directory %s", builddir);
    [status, msg] = mkdir (builddir);
    if (status != 1)
      error ("could not create installation directory: %s", msg);
    endif
  endif
  builddir = absolute_pathname (builddir);
  installdir = fullfile (builddir, "install");
  if (! exist (installdir, "dir"))
    [status, msg] = mkdir (installdir);
    if (status != 1)
      error ("could not create installation directory: %s", msg);
    endif
  endif
  files(1) = [];
  buildlist = fullfile (builddir, "octave_packages");
  install (files, handle_deps, autoload, installdir, verbose, 
           buildlist, "", false);
  unwind_protect
    repackage (builddir, buildlist);
  unwind_protect_cleanup
    unload_packages ({"all"}, handle_deps, buildlist, "");
    if (exist (installdir, "dir"))
      rm_rf (installdir);
    endif
    if (exist (buildlist, "file"))
      unlink (buildlist);
    endif
  end_unwind_protect
endfunction

function install (files, handle_deps, autoload, prefix, verbose, local_list, 
global_list, global_install)

  # Check that the directory in prefix exist. If it doesn't: create it!
  if (! exist (prefix, "dir"))
    warning ("creating installation directory %s", prefix);
    [status, msg] = mkdir (prefix);
    if (status != 1)
      error ("could not create installation directory: %s", msg);
    endif
  endif

  ## Get the list of installed packages
  [local_packages, global_packages] = installed_packages (local_list, 
                                                          global_list);

  installed_packages = {local_packages{:}, global_packages{:}};        

  if (global_install)
    packages = global_packages;
  else
    packages = local_packages;
  endif

  ## Uncompress the packages and read the DESCRIPTION files
  tmpdirs = packdirs = descriptions = {};
  try

    ## Warn about non existent files 
    for i = 1:length (files)
      if (isempty (glob(files{i}))) 
        warning ("file %s does not exist", files{i});
      endif
    endfor

    ## Unpack the package files and read the DESCRIPTION files
    files = glob (files);
    packages_to_uninstall = [];
    for i = 1:length (files)
      tgz = files{i};

      if (exist (tgz, "file"))
        ## Create a temporary directory 
        tmpdir = tmpnam ();
        tmpdirs{end+1} = tmpdir;
        if (verbose)
          printf ("mkdir (%s)\n", tmpdir);
        endif
        [status, msg] = mkdir (tmpdir);
        if (status != 1)
          error ("couldn't create temporary directory: %s", msg);
        endif

        ## Uncompress the package
        if (verbose)
          printf ("untar (%s, %s)\n", tgz, tmpdir);
        endif
        untar (tgz, tmpdir);

        ## Get the name of the directories produced by tar
        [dirlist, err, msg] = readdir (tmpdir);
        if (err)
          error ("couldn't read directory produced by tar: %s", msg);
        endif

        if (length (dirlist) > 3)
          error ("bundles of packages are not allowed")
        endif

        ## the two first entries of dirlist are "." and ".."
        for k = 3:length (dirlist)
          packdir = fullfile (tmpdir, dirlist{k});
          packdirs{end+1} = packdir;

          ## Make sure the package contains necessary files
          verify_directory (packdir);

          ## Read the DESCRIPTION file
          filename = fullfile (packdir, "DESCRIPTION");
          desc = get_description (filename);

          ## Verify that package name corresponds with filename
          [dummy, nm] = fileparts (tgz); 
          if ((length (nm) >= length (desc.name))
              && ! strcmp (desc.name, nm(1:length(desc.name))))
            error ("package name '%s' doesn't correspond to its filename '%s'", 
desc.name, nm);
          endif

          ## Set default installation directory
          desc.dir = fullfile (prefix, strcat (desc.name, "-", desc.version));

          ## Save desc
          descriptions{end+1} = desc;

          ## Are any of the new packages already installed?
          ## If so we'll remove the old version.
          for j = 1:length (packages)
            if (strcmp (packages{j}.name, desc.name))
              packages_to_uninstall(end+1) = j;
            endif
          endfor
        endfor        
      endif
    endfor
  catch
    ## Something went wrong, delete tmpdirs
    for i = 1:length (tmpdirs)
      rm_rf (tmpdirs{i});
    endfor
    rethrow (lasterror ());
  end_try_catch

  ## Check dependencies
  if (handle_deps)
    ok = true;
    error_text = "";
    for i = 1:length (descriptions)
      desc = descriptions{i};
      idx2 = complement (i, 1:length(descriptions));
      if (global_install)
        ## Global installation is not allowed to have dependencies on locally
        ## installed packages
        idx1 = complement (packages_to_uninstall, 
                           1:length(global_packages));
        pseudo_installed_packages = {global_packages{idx1}, ...
                                     descriptions{idx2}};
      else
        idx1 = complement (packages_to_uninstall, 
                           1:length(local_packages));
        pseudo_installed_packages = {local_packages{idx1}, ... 
                                     global_packages{:}, ...
                                     descriptions{idx2}};
      endif
      bad_deps = get_unsatisfied_deps (desc, pseudo_installed_packages);
      ## Are there any unsatisfied dependencies?
      if (! isempty (bad_deps))
        ok = false;
        for i = 1:length (bad_deps)
          dep = bad_deps{i};
          error_text = strcat (error_text, " ", desc.name, " needs ",
                               dep.package, " ", dep.operator, " ",
                               dep.version, "\n");
        endfor
      endif
    endfor

    ## Did we find any unsatisfied dependencies?
    if (! ok)
      error ("the following dependencies where unsatisfied:\n  %s", error_text);
    endif
  endif

  ## Prepare each package for installation
  try
    for i = 1:length (descriptions)
      desc = descriptions{i};
      pdir = packdirs{i};
      prepare_installation (desc, pdir);
      configure_make (desc, pdir, verbose);
    endfor
  catch
    ## Something went wrong, delete tmpdirs
    for i = 1:length (tmpdirs)
      rm_rf (tmpdirs{i});
    endfor
    rethrow (lasterror ());
  end_try_catch

  ## Uninstall the packages that will be replaced
  try
    for i = packages_to_uninstall
      if (global_install)
        uninstall ({global_packages{i}.name}, false, verbose, local_list, 
                   global_list, global_install);
      else
        uninstall ({local_packages{i}.name}, false, verbose, local_list, 
                   global_list, global_install);
      endif
    endfor
  catch
    ## Something went wrong, delete tmpdirs
    for i = 1:length (tmpdirs)
      rm_rf (tmpdirs{i});
    endfor
    rethrow (lasterror ());
  end_try_catch

  ## Install each package
  try
    for i = 1:length (descriptions)
      desc = descriptions{i};
      pdir = packdirs{i};
      copy_files (desc, pdir);
      create_pkgadddel (desc, pdir, "PKG_ADD");
      create_pkgadddel (desc, pdir, "PKG_DEL");
      finish_installation (desc, pdir)
    endfor
  catch
    ## Something went wrong, delete tmpdirs
    for i = 1:length (tmpdirs)
      rm_rf (tmpdirs{i});
    endfor
    for i = 1:length (descriptions)
      rm_rf (descriptions{i}.dir);
    endfor
    rethrow (lasterror ());
  end_try_catch

  ## Check if the installed directory is empty. If it is remove it
  ## from the list
  for i = length (descriptions):-1:1
    if (dirempty (descriptions{i}.dir, {"packinfo", "doc"}))
      warning ("package %s is empty\n", descriptions{i}.name);
      rm_rf (descriptions{i}.dir);
      descriptions(i) = [];
    endif
  endfor

  ## If the package requested that it is autoloaded, or the installer
  ## requested that it is, then mark the package as autoloaded.
  for i = length (descriptions):-1:1
    if (autoload > 0 || (autoload == 0 && isautoload (descriptions(i))))
      fclose (fopen (fullfile (descriptions{i}.dir, "packinfo", 
                               ".autoload"), "wt"));
      descriptions{i}.autoload = 1;
    endif
  endfor

  ## Add the packages to the package list
  try
    if (global_install)
      idx = complement (packages_to_uninstall, 1:length(global_packages));
      global_packages = save_order ({global_packages{idx}, descriptions{:}});
      save (global_list, "global_packages");
      installed_packages = {local_packages{:}, global_packages{:}};
    else
      idx = complement (packages_to_uninstall, 1:length(local_packages));
      local_packages = save_order ({local_packages{idx}, descriptions{:}});
      save (local_list, "local_packages");
      installed_packages = {local_packages{:}, global_packages{:}};
    endif
  catch
    ## Something went wrong, delete tmpdirs
    for i = 1:length (tmpdirs)
      rm_rf (tmpdirs{i});
    endfor
    for i = 1:length (descriptions)
      rm_rf (descriptions{i}.dir);
    endfor
    if (global_install)
      printf ("error: couldn't append to %s\n", global_list);
    else
      printf ("error: couldn't append to %s\n", local_list);
    endif
    rethrow (lasterror ());
  end_try_catch

  ## All is well, let's clean up
  for i = 1:length (tmpdirs)
    [status, msg] = rm_rf (tmpdirs{i});
    if (status != 1)
      warning ("couldn't clean up after my self: %s\n", msg);
    endif
  endfor

  ## Add the newly installed packages to the path, so the user
  ## can begin usings them. Only load them if they are marked autoload
  if (length (descriptions) > 0)
    idx = [];
    for i = 1:length (descriptions)
      if (isautoload (descriptions(i)))
        idx (end + 1) = i;
      endif
    endfor
    load_packages_and_dependencies (idx, handle_deps, installed_packages);
  endif
endfunction

function uninstall (pkgnames, handle_deps, verbose, local_list, 
                    global_list, global_install)
  ## Get the list of installed packages
  [local_packages, global_packages] = installed_packages(local_list, 
                                                         global_list);
  if (global_install)
    installed_packages = {local_packages{:}, global_packages{:}};
  else
    installed_packages = local_packages;
  endif

  num_packages = length (installed_packages);
  delete_idx = [];
  for i = 1:num_packages
    cur_name = installed_packages{i}.name;
    if (any (strcmp (cur_name, pkgnames)))
      delete_idx(end+1) = i;
    endif
  endfor

  ## Are all the packages that should be uninstalled already installed?
  if (length (delete_idx) != length (pkgnames))
    if (global_install)
      ## Try again for a locally installed package
      installed_packages = local_packages;

      num_packages = length (installed_packages);
      delete_idx = [];
      for i = 1:num_packages
        cur_name = installed_packages{i}.name;
        if (any (strcmp (cur_name, pkgnames)))
          delete_idx(end+1) = i;
        endif
      endfor
      if (length (delete_idx) != length (pkgnames))
        ## XXX: We should have a better error message
        warning ("some of the packages you want to uninstall are not 
installed");
      endif
    else
      ## XXX: We should have a better error message
      warning ("some of the packages you want to uninstall are not installed.");
    endif
  endif

  ## Compute the packages that will remain installed
  idx = complement (delete_idx, 1:num_packages);
  remaining_packages = {installed_packages{idx}};

  ## Check dependencies
  if (handle_deps)
    error_text = "";
    for i = 1:length (remaining_packages)
      desc = remaining_packages{i};
      bad_deps = get_unsatisfied_deps (desc, remaining_packages);

      ## Will the uninstallation break any dependencies?
      if (! isempty (bad_deps))
        for i = 1:length (bad_deps)
          dep = bad_deps{i};
          error_text = strcat (error_text, " ", desc.name, " needs ",
                               dep.package, " ", dep.operator, " ",
                               dep.version, "\n");
        endfor
      endif
    endfor

    if (! isempty (error_text))
      error ("the following dependencies where unsatisfied:\n  %s", error_text);
    endif
  endif

  ## Delete the directories containing the packages
  for i = delete_idx
    desc = installed_packages{i};
    ## If an 'on_uninstall.m' exist, call it!
    if (exist (fullfile (desc.dir, "packinfo", "on_uninstall.m"), "file"))
      wd = pwd ();
      cd (fullfile(desc.dir, "packinfo"));
      on_uninstall (desc);
      cd (wd);
    endif
    ## Do the actual deletion
    if (desc.loaded)
      rmpath (desc.dir);
      if (exist (fullfile (desc.dir, getarch()), "dir"))
        rmpath (fullfile (desc.dir, getarch ()));
      endif
    endif
    if (exist (desc.dir, "dir"))
      [status, msg] = rm_rf (desc.dir);
      if (status != 1)
        error ("couldn't delete directory %s: %s", desc.dir, msg);
      endif
    else
      warning ("directory %s previously lost", desc.dir);
    endif
  endfor

  ## Write a new ~/.octave_packages
  if (global_install)
    if (length (remaining_packages) == 0)
      unlink (global_list);
    else
      global_packages = save_order (remaining_packages);
      save (global_list, "global_packages");
    endif
  else
    if (length (remaining_packages) == 0)
      unlink (local_list);
    else
      local_packages = save_order (remaining_packages);
      save (local_list, "local_packages");
    endif
  endif

endfunction

##########################################################
##        A U X I L I A R Y    F U N C T I O N S        ##
##########################################################

function pth = absolute_pathname (pth)
  [status, msg, msgid] = fileattrib(pth);
  if (status != 1)
    error ("could not find the file or path %s", pth);
  else
    pth = msg.Name;
  endif
endfunction

function repackage (builddir, buildlist)
  packages = installed_packages (buildlist, buildlist);

  wd = pwd();
  for i = 1 : length(packages)
    pack = packages{i};
    unwind_protect
      cd (builddir);
      mkdir (pack.name);
      mkdir (fullfile (pack.name, "inst"));
      copyfile (fullfile (pack.dir, "*"), fullfile (pack.name, "inst"));
      movefile (fullfile (pack.name, "inst","packinfo", "*"), pack.name);
      if (exist (fullfile (pack.name, "inst","packinfo", ".autoload"), "file"))
        unlink (fullfile (pack.name, "inst","packinfo", ".autoload"));
      endif
      rmdir (fullfile (pack.name, "inst", "packinfo"));
      if (exist (fullfile (pack.name, "inst", "doc"), "dir"))
        movefile (fullfile (pack.name, "inst", "doc"), pack.name);
      endif
      if (exist (fullfile (pack.name, "inst", "bin"), "dir"))
        movefile (fullfile (pack.name, "inst", "bin"), pack.name);
      endif
      archdir = fullfile (pack.name, "inst", getarch ());
      if (exist (archdir, "dir"))
        if (exist (fullfile (pack.name, "inst", "PKG_ADD"), "file"))
          unlink (fullfile (pack.name, "inst", "PKG_ADD"));
        endif
        if (exist (fullfile (pack.name, "inst", "PKG_DEL"), "file"))
          unlink (fullfile (pack.name, "inst", "PKG_DEL"));
        endif
        if (exist (fullfile (archdir, "PKG_ADD"), "file"))
          movefile (fullfile (archdir, "PKG_ADD"), 
                    fullfile (pack.name, "PKG_ADD"));
        endif
        if (exist (fullfile (archdir, "PKG_DEL"), "file"))
          movefile (fullfile (archdir, "PKG_DEL"), 
                    fullfile (pack.name, "PKG_DEL")); 
        endif
      else
        if (exist (fullfile (pack.name, "inst", "PKG_ADD"), "file"))
          movefile (fullfile (pack.name, "inst", "PKG_ADD"), 
                    fullfile (pack.name, "PKG_ADD"));
        endif 
        if (exist (fullfile (pack.name, "inst", "PKG_DEL"), "file"))
          movefile (fullfile (pack.name, "inst", "PKG_DEL"), 
                    fullfile (pack.name, "PKG_DEL")); 
        endif   
      endif     
      tfile = strcat (pack.name, "-", pack.version, ".tar");
      tar (tfile, pack.name);

    unwind_protect_cleanup
      if (exist (pack.name, "dir"))
        rm_rf (pack.name);
      endif
      cd (wd);
    end_unwind_protect
  endfor
endfunction

function auto = isautoload (desc)
  auto = false;
  if (isfield (desc{1}, "autoload"))
    a = desc{1}.autoload;
    if ((isnumeric (a) && a > 0)
        || (ischar (a) && (strcmpi (a, "true")
                         || strcmpi (a, "on")
                         || strcmpi (a, "yes")
                         || strcmpi (a, "1"))))
      auto = true;
    endif
  endif
endfunction

function prepare_installation (desc, packdir)
  ## Is there a pre_install to call?
  if (exist (fullfile (packdir, "pre_install.m"), "file"))
    wd = pwd ();
    try
      cd (packdir);
      pre_install (desc); 
      cd (wd);
    catch
      cd (wd);
      rethrow (lasterror ());
    end_try_catch
  endif

  ## If the directory "inst" doesn't exist, we create it
  inst_dir = fullfile (packdir, "inst");
  if (! exist (inst_dir, "dir"))
    [status, msg] = mkdir (inst_dir);
    if (status != 1)
      rm_rf (desc.dir);
      error ("the 'inst' directory did not exist and could not be created: %s", 
             msg);
    endif
  endif
endfunction

function configure_make (desc, packdir, verbose)   
  ## Perform ./configure, make, make install in "src"
  if (exist (fullfile (packdir, "src"), "dir"))
    src = fullfile (packdir, "src");
    ## configure
    if (exist (fullfile (src, "configure"), "file"))
      [status, output] = shell (strcat ("cd ", src, "; ./configure --prefix=\"",
                                         desc.dir, "\"",
                                         " CC=", octave_config_info ("CC"),
                                         " CXX=", octave_config_info ("CXX"),
                                         " AR=", octave_config_info ("AR"),
                                         " RANLIB=", octave_config_info 
("RANLIB")));
      if (status != 0)
        rm_rf (desc.dir);
        error ("the configure script returned the following error: %s", output);
      elseif (verbose)
        printf("%s", output);
      endif

    endif

    ## make
    if (exist (fullfile (src, "Makefile"), "file"))
      [status, output] = shell (strcat ("export INSTALLDIR=\"", desc.dir,
                                         "\"; make -C ", src));
      if (status != 0)
        rm_rf (desc.dir);
        error ("'make' returned the following error: %s", output);
      elseif (verbose)
        printf("%s", output);
      endif
    endif

    ## Copy files to "inst" and "inst/arch" (this is instead of 'make install')
    files = fullfile (src, "FILES");
    instdir = fullfile (packdir, "inst");
    archdir = fullfile (packdir, "inst", getarch ());
    ## Get file names
    if (exist (files, "file"))
      [fid, msg] = fopen (files, "r");
      if (fid < 0)
        error ("couldn't open %s: %s", files, msg);
      endif
      filenames = char (fread (fid))';
      fclose (fid);
      if (filenames(end) == "\n")
        filenames(end) = [];
      endif
      filenames = split_by (filenames, "\n");
      delete_idx =  [];
      for i = 1:length (filenames)
        if (! all (isspace (filenames{i})))
          filenames{i} = fullfile (src, filenames{i});
        else
          delete_idx(end+1) = i;
        endif
      endfor
      filenames(delete_idx) = [];
      idx1 = cellfun ("isempty", regexp (filenames, '^.*\.mex'));
      idx2 = cellfun ("isempty", regexp (filenames, '^.*\.oct'));
      mex = filenames;
      mex(idx1 != 0) = [];
      oct = filenames;
      oct(idx2 != 0) = [];
      archindependent = filenames;
      archindependent(idx1 == 0 | idx2 == 0) = [];
      archdependent = [oct, mex];
    else
      m = dir (fullfile (src, "*.m"));
      oct = dir (fullfile (src, "*.oct"));
      mex = dir (fullfile (src, "*.mex"));
      archdependent = "";
      archindependent = "";
      filenames = "";
      if (length (m) > 0)
        filenames = sprintf (fullfile (src, "%s "), m.name);
        archindependent = sprintf (fullfile (src, "%s "), m.name);
      endif
      if (length (oct) > 0)
        filenames = strcat (filenames, " ", sprintf(fullfile(src, "%s "), ...
                                                    oct.name));
        archdependent = strcat (archdependent, " ", ...
                         sprintf(fullfile(src, "%s "), oct.name));
      endif
      if (length (mex) > 0)
        filenames = strcat (filenames, " ", sprintf(fullfile(src, "%s "), ...
                                                    mex.name));
        archdependent = strcat (archdependent, " ", ...
                         sprintf(fullfile(src, "%s "), mex.name));
      endif
      filenames = split_by (filenames, " ");
      archdependent = split_by (archdependent, " ");
      archindependent = split_by (archindependent, " ");
    endif

    ## Copy the files
    if (! all (isspace (filenames)))
        if (! exist (instdir, "dir")) 
          mkdir (instdir);
        endif
        if (! all (isspace (archindependent)))
          if (verbose)
            printf ("copyfile");
            printf (" %s", archindependent{:});
            printf ("%s\n", instdir);
          endif
          [status, output] = copyfile (archindependent, instdir);
          if (status != 1)
            rm_rf (desc.dir);
            error ("Couldn't copy files from 'src' to 'inst': %s", output);
          endif
        endif
        if (! all (isspace (archdependent)))
          if (verbose)
            printf ("copyfile");
            printf (" %s", archdependent{:});
            printf (" %s\n", archdir);
          endif
          if (! exist (archdir, "dir")) 
            mkdir (archdir);
          endif
          [status, output] = copyfile (archdependent, archdir);
          if (status != 1)
            rm_rf (desc.dir);
            error ("Couldn't copy files from 'src' to 'inst': %s", output);
          endif
        endif
    endif
  endif
endfunction

function pkg = extract_pkg (nm, pat)
  fid = fopen (nm, "rt");
  pkg = "";
  if (fid >= 0)
    while (! feof (fid))
      ln = fgetl (fid);
      if (ln > 0)
        t = regexp (ln, pat, "tokens");
        if (! isempty (t))
          pkg = strcat (pkg, "\n", t{1}{1});
        endif
      endif
    endwhile
    if (! isempty (pkg))
      pkg = strcat (pkg, "\n");
    endif
    fclose (fid);
  endif
endfunction

function create_pkgadddel (desc, packdir, nm)
  instpkg = fullfile (desc.dir, nm);
  instfid = fopen (instpkg, "wt");
  ## If it is exists, most of the  PKG_* file should go into the 
  ## architecture dependent directory so that the autoload/mfilename 
  ## commands work as expected. The only part that doesn't is the
  ## part in the main directory.
  if (exist (fullfile (desc.dir, getarch ()), "dir"))
    archpkg = fullfile (desc.dir, getarch (), nm);
    archfid = fopen (archpkg, "wt");
  else
    archpkg = instpkg;
    archfid = instfid;
  endif

  if (archfid >= 0 && instfid >= 0)
    ## Search all dot-m files for PKG commands
    lst = dir (fullfile(packdir, "inst", "*.m"));
    for i = 1:length (lst)
      nam = fullfile(packdir, "inst", lst(i).name);
      fwrite (instfid, extract_pkg (nam, ['^[#%][#%]* *' nm ': *(.*)$']));
    endfor

    ## Search all C++ source files for PKG commands
    lst = dir (fullfile(packdir, "src", "*.cc"));
    for i = 1:length (lst)
      nam = fullfile(packdir, "src", lst(i).name);
      fwrite (archfid, extract_pkg (nam, ['^//* *' nm ': *(.*)$']));
      fwrite (archfid, extract_pkg (nam, ['^/\** *' nm ': *(.*) *\*/$']));
    endfor

    ## Add developer included PKG commands
    packdirnm = fullfile (packdir, nm);
    if (exist (packdirnm, "file"))
      fid = fopen (packdirnm, "rt");
      if (fid >= 0)
        while (! feof (fid))
          ln = fgets (fid);
          if (ln > 0)
            fwrite (archfid, ln);
          endif
        endwhile
        fclose (fid);
      endif
    endif

    ## If the files is empty remove it
    fclose (instfid);
    t = dir (instpkg);
    if (t.bytes <= 0)
      unlink (instpkg);
    endif

    if (instfid != archfid)
      fclose (archfid);
      t = dir (archpkg);
      if (t.bytes <= 0)
        unlink (archpkg);
      endif
    endif
  endif
endfunction

function copy_files (desc, packdir, bindir)
  ## Create the installation directory
  if (! exist (desc.dir, "dir"))
    [status, output] = mkdir (desc.dir);
    if (status != 1)
      error ("couldn't create installation directory %s : %s", 
      desc.dir, output);
    endif
  endif

  ## Copy the files from "inst" to installdir
  instdir = fullfile (packdir, "inst");
  if (! dirempty (instdir))
    [status, output] = copyfile (fullfile (instdir, "*"), desc.dir);
    if (status != 1)
      rm_rf (desc.dir);
      error ("couldn't copy files to the installation directory");
    endif
  endif

  ## Create the "packinfo" directory
  packinfo = fullfile (desc.dir, "packinfo");
  [status, msg] = mkdir (packinfo);
  if (status != 1)
    rm_rf (desc.dir);
    error ("couldn't create packinfo directory: %s", msg);
  endif

  ## Copy DESCRIPTION
  [status, output] = copyfile (fullfile (packdir, "DESCRIPTION"), packinfo);
  if (status != 1)
   rm_rf (desc.dir);
   error ("couldn't copy DESCRIPTION: %s", output);
  endif

  ## Copy COPYING
  [status, output] = copyfile (fullfile (packdir, "COPYING"), packinfo);
  if (status != 1)
   rm_rf (desc.dir);
   error ("couldn't copy COPYING: %s", output);
  endif

  ## If the file ChangeLog exists, copy it
  fChangeLog = fullfile(packdir, "ChangeLog");
  if (exist (fChangeLog, "file"))
    [status, output] = copyfile (fChangeLog, packinfo);
    if (status != 1)
      rm_rf (desc.dir);
      error ("couldn't copy ChangeLog file: %s", output);
    endif
  endif

  ## Is there an INDEX file to copy or should we generate one?
  fINDEX = fullfile (packdir, "INDEX");
  if (exist(fINDEX, "file"))
    [status, output] = copyfile (fINDEX, packinfo);
    if (status != 1)
      rm_rf (desc.dir);
      error ("couldn't copy INDEX file: %s", output);
    endif
  else
    try
      write_INDEX (desc, fullfile (packdir, "inst"),
                   fullfile (packinfo, "INDEX"));
    catch
      rm_rf (desc.dir);
      rethrow (lasterror ());
    end_try_catch
  endif

  ## Is there an 'on_uninstall.m' to install?
  fon_uninstall = fullfile(packdir, "on_uninstall.m");
  if (exist (fon_uninstall, "file"))
    [status, output] = copyfile (fon_uninstall, packinfo);
    if (status != 1)
      rm_rf (desc.dir);
      error ("couldn't copy on_uninstall.m: %s", output);
    endif
  endif

  ## Is there a doc/ directory that needs to be installed
  docdir = fullfile (packdir, "doc");
  if (exist (docdir, "dir") && ! dirempty (docdir))
   [status, output] = copyfile (docdir, desc.dir);
  endif

  ## Is there a bin/ directory that needs to be installed
  bindir = fullfile (packdir, "bin");
  if (exist (bindir, "dir") && ! dirempty (bindir))
   [status, output] = copyfile (bindir, desc.dir);
  endif
endfunction

function finish_installation (desc, packdir)
  ## Is there a post-install to call?
  if (exist (fullfile (packdir, "post_install.m"), "file"))
    wd = pwd ();
    try
      cd (packdir);
      post_install (desc);
      cd (wd);
    catch
      cd (wd);
      rm_rf (desc.dir);
      rethrow (lasterror ());
    end_try_catch
  endif
endfunction

## This function makes sure the package contains the
## essential files.
function verify_directory (dir)
  needed_files = {"COPYING", "DESCRIPTION"};
  for f = needed_files
    if (! exist (fullfile (dir, f{1}), "file"))
      error ("package is missing file: %s", f{1});
    endif
  endfor
endfunction

## This function parses the DESCRIPTION file
function desc = get_description (filename)
  [fid, msg] = fopen (filename, "r");
  if (fid == -1)
    error ("the DESCRIPTION file %s could not be read: %s", filename, msg);
  endif

  desc = struct ();

  line = fgetl (fid);
  while (line != -1)
    if (line(1) == "#")
      ## Comments, do nothing
    elseif (isspace(line(1)))
      ## Continuation lines
      if (exist ("keyword", "var") && isfield (desc, keyword))
        desc.(keyword) = strcat (desc.(keyword), " ", rstrip(line));
      endif
    else
      ## Keyword/value pair
      colon = find (line == ":");
      if (length (colon) == 0)
        disp ("skipping line");
      else
        colon = colon(1);
        keyword = tolower (strip (line(1:colon-1)));
        value   = strip (line (colon+1:end));
        if (length (value) == 0)
            fclose (fid);
            error ("the keyword %s has an empty value", desc.keywords{end});
        endif
        desc.(keyword) = value;
      endif
    endif
    line = fgetl (fid);
  endwhile
  fclose (fid);

  ## Make sure all is okay
  needed_fields = {"name", "version", "date", "title", ...
                   "author", "maintainer", "description"};
  for f = needed_fields
    if (! isfield (desc, f{1}))
      error ("description is missing needed field %s", f{1});
    endif
  endfor
  desc.version = fix_version (desc.version);
  if (isfield (desc, "depends"))
    desc.depends = fix_depends (desc.depends);
  else
    desc.depends = "";
  endif
  desc.name = tolower (desc.name);
endfunction

## Makes sure the version string v is a valid x.y.z version string
## Examples: "0.1" => "0.1.0", "monkey" => error(...)
function out = fix_version (v)
  dots = find (v == ".");
  if (length (dots) == 1)
    major = str2num (v(1:dots-1));
    minor = str2num (v(dots+1:end));
    if (length (major) != 0 && length (minor) != 0)
      out = sprintf ("%d.%d.0", major, minor);
      return;
    endif
  elseif (length (dots) == 2)
    major = str2num (v(1:dots(1)-1));
    minor = str2num (v(dots(1)+1:dots(2)-1));
    rev   = str2num (v(dots(2)+1:end));
    if (length (major) != 0 && length (minor) != 0 && length (rev) != 0)
      out = sprintf ("%d.%d.%d", major, minor, rev);
      return;
    endif
  endif
  error ("bad version string: %s", v);
endfunction

## Makes sure the depends field is of the right format.
## This function returns a cell of structures with the following fields:
##   package, version, operator
function deps_cell = fix_depends (depends)
  deps = split_by (tolower (depends), ",");
  deps_cell = cell (1, length (deps));

  ## For each dependency
  for i = 1:length (deps)
    dep = deps{i};
    lpar = find (dep == "(");
    rpar = find (dep == ")");
    ## Does the dependency specify a version
    ## Example: package(>= version)
    if (length (lpar) == 1 && length (rpar) == 1)
      package = tolower (strip (dep(1:lpar-1)));
      sub = dep(lpar(1)+1:rpar(1)-1);
      parts = split_by (sub, " ");
      idx = [];
      for r = 1:size (parts, 1)
        if (length (parts{r}) > 0)
          idx(end+1) = r;
        endif
      endfor

      if (length (idx) != 2)
        error ("incorrect syntax for dependency `%s' in the DESCRIPTION file\n",
               dep);
      endif
      operator = parts{idx(1)};
      if (! any (strcmp (operator, {">", ">=", "<=", "<", "=="})))
        error ("unsupported operator: %s", operator);
      endif
      version  = fix_version (parts{idx(2)});

  ## If no version is specified for the dependency
  ## we say that the version should be greater than 
  ## or equal to 0.0.0
  else
    package = tolower (strip (dep));
    operator = ">=";
    version  = "0.0.0";
  endif
  deps_cell{i} = struct ("package", package, "operator", operator,
                         "version", version);
  endfor
endfunction

## Strips the text of spaces from the right
## Example: "  hello world  " => "  hello world" (XXX: is this the same as 
deblank?)
function text = rstrip (text)
  chars = find (! isspace (text));
  if (length (chars) > 0)
    ## XXX: shouldn't it be text = text(1:chars(end));
    text = text (chars(1):end);
  else
    text = "";
  endif
endfunction

## Strips the text of spaces from the left and the right
## Example: "  hello world  " => "hello world"
function text = strip (text)
  chars = find (! isspace (text));
  if (length (chars) > 0)
    text = text(chars(1):chars(end));
  else
    text = "";
  endif
endfunction

## Splits the text into a cell array of strings by sep
## Example: "A, B" => {"A", "B"} (with sep = ",")
function out = split_by (text, sep)
  text_matrix = split (text, sep);
  num_words = size (text_matrix, 1);
  out = cell (num_words, 1);
  for i = 1:num_words
    out{i} = strip (text_matrix(i, :));
  endfor
endfunction

## Creates an INDEX file for a package that doesn't provide one.
##   'desc'  describes the package.
##   'dir'   is the 'inst' direcotyr in temporary directory.
##   'INDEX' is the name (including path) of resulting INDEX file.
function write_INDEX (desc, dir, INDEX)
  ## Get names of functions in dir
  [files, err, msg] = readdir (dir);
  if (err)
    error ("couldn't read directory %s: %s", dir, msg);
  endif

  ## Check for architecture dependent files
  arch = getarch();
  tmpdir = fullfile (dir, arch);
  if (exist (tmpdir, "dir"))
    [files2, err, msg] = readdir (tmpdir);
    if (err)
      error ("couldn't read directory %s: %s", tmpdir, msg);
    endif
    files = [files; files2];    
  endif

  functions = {};
  for i = 1:length (files)
    file = files{i};
    lf = length (file);
    if (lf > 2 && strcmp (file(end-1:end), ".m"))
      functions{end+1} = file(1:end-2);
    elseif (lf > 4 && strcmp (file(end-3:end), ".oct"))
      functions{end+1} = file(1:end-4);
    endif
  endfor

  ## Does desc have a categories field?
  if (! isfield (desc, "categories"))
    error ("the DESCRIPTION file must have a Categories field, when no INDEX 
file is given");
  endif
  categories = split_by (desc.categories, ",");
  if (length (categories) < 1)
      error ("the Category field is empty");
  endif

  ## Write INDEX
  fid = fopen (INDEX, "w");
  if (fid == -1)
    error ("couldn't open %s for writing.", INDEX);
  endif
  fprintf (fid, "%s >> %s\n", desc.name, desc.title);
  fprintf (fid, "%s\n", categories{1});
  fprintf (fid, "  %s\n", functions{:});
  fclose (fid);
endfunction

function bad_deps = get_unsatisfied_deps (desc, installed_packages)
  bad_deps = {};

  ## For each dependency
  for i = 1:length (desc.depends)
    dep = desc.depends{i};

    ## Is the current dependency Octave?
    if (strcmp (dep.package, "octave"))
      if (! compare_versions (OCTAVE_VERSION, dep.version, dep.operator))
        bad_deps{end+1} = dep;
      endif
    ## Is the current dependency not Octave?
    else
      ok = false;
      for i = 1:length (installed_packages)
        cur_name = installed_packages{i}.name;
        cur_version = installed_packages{i}.version;
        if (strcmp (dep.package, cur_name)
            && compare_versions (cur_version, dep.version, dep.operator))
          ok = true;
          break;
        endif
      endfor
      if (! ok)
        bad_deps{end+1} = dep;
      endif
    endif
  endfor
endfunction

function [out1, out2] = installed_packages (local_list, global_list)
  ## Get the list of installed packages
  try
    local_packages = load (local_list).local_packages;
  catch
    local_packages = {};
  end_try_catch
  try
    global_packages = load (global_list).global_packages;
  catch
    global_packages = {};
  end_try_catch
  installed_packages = {local_packages{:}, global_packages{:}};

  ## Eliminate duplicates in the installed package list.
  ## Locally installed packages take precedence
  dup = [];
  for i = 1:length (installed_packages)
    if (find (dup, i))
      continue;
    endif
    for j = (i+1):length (installed_packages)
      if (find (dup, j))
        continue;
      endif
      if (strcmp (installed_packages{i}.name, installed_packages{j}.name))
        dup = [dup, j];
      endif
    endfor
  endfor
  if (! isempty(dup))
    installed_packages(dup) = [];
  endif  

  ## Now check if the package is loaded
  tmppath = strrep (path(), "\\", "/");
  for i = 1:length (installed_packages)
    if (regexp (tmppath, strrep (installed_packages{i}.dir, "\\", "/")))
      installed_packages{i}.loaded = true;
    else
      installed_packages{i}.loaded = false;
    endif
  endfor
  for i = 1:length (local_packages)
    if (regexp (tmppath, strrep (local_packages{i}.dir, "\\", "/")))
      local_packages{i}.loaded = true;
    else
      local_packages{i}.loaded = false;
    endif
  endfor
  for i = 1:length (global_packages)
    if (regexp (tmppath, strrep (global_packages{i}.dir, "\\", "/")))
      global_packages{i}.loaded = true;
    else
      global_packages{i}.loaded = false;
    endif
  endfor

  ## Should we return something?
  if (nargout == 2)
    out1 = local_packages;
    out2 = global_packages;
    return;
  elseif (nargout == 1)
    out1 = installed_packages;
    return;
  endif

  ## We shouldn't return something, so we'll print something
  num_packages = length (installed_packages);
  if (num_packages == 0)
    printf ("no packages installed.\n");
    return;
  endif

  ## Compute the maximal lengths of name, version, and dir
  h1 = "Package Name";
  h2 = "Version";
  h3 = "Installation directory";
  max_name_length = length (h1); 
  max_version_length = length (h2);
  names = cell (num_packages, 1); 
  for i = 1:num_packages
    max_name_length = max (max_name_length,
                           length (installed_packages{i}.name));
    max_version_length = max (max_version_length,
                              length (installed_packages{i}.version));
    names{i} = installed_packages{i}.name;
  endfor
  max_dir_length = terminal_size()(2) - max_name_length - ...
                                             max_version_length - 7;
  if (max_dir_length < 20)
     max_dir_length = Inf;
  endif

  h1 = postpad (h1, max_name_length + 1, " ");
  h2 = postpad (h2, max_version_length, " ");;

  ## Print a header
  header = sprintf("%s | %s | %s\n", h1, h2, h3);
  printf (header);
  tmp = sprintf (repmat ("-", 1, length(header)-1));
  tmp(length(h1)+2) = "+";
  tmp(length(h1)+length(h2)+5) = "+";
  printf ("%s\n", tmp);

  ## Print the packages
  format = sprintf ("%%%ds %%1s| %%%ds | %%s\n", max_name_length,
                    max_version_length);
  [dummy, idx] = sort (names);
  for i = 1:num_packages
    cur_name = installed_packages{idx(i)}.name;
    cur_version = installed_packages{idx(i)}.version;
    cur_dir = installed_packages{idx(i)}.dir;
    if (length (cur_dir) > max_dir_length)
      first_char = length (cur_dir) - max_dir_length + 4;
      first_filesep = strfind (cur_dir(first_char:end), filesep());
      if (! isempty (first_filesep))
        cur_dir = strcat ("...", 
                          cur_dir((first_char + first_filesep(1) - 1):end));
      else
        cur_dir = strcat ("...", cur_dir(first_char:end));
      endif
    endif
    if (installed_packages{idx(i)}.loaded)
      cur_loaded = "*";
    else
      cur_loaded = " ";
    endif
    printf (format, cur_name, cur_loaded, cur_version, cur_dir);
  endfor
endfunction

function load_packages (files, handle_deps, local_list, global_list)
  installed_packages = installed_packages (local_list, global_list);
  num_packages = length (installed_packages);

  ## Read package names and installdirs into a more convenient format
  pnames = pdirs = cell (1, num_packages);
  for i = 1:num_packages
    pnames{i} = installed_packages{i}.name;
    pdirs{i} = installed_packages{i}.dir;
  endfor

  ## load all
  if (length (files) == 1 && strcmp (files{1}, "all"))
    idx = [1:length(installed_packages)];
  ## load auto
  elseif (length (files) == 1 && strcmp (files{1}, "auto")) 
    idx = [];
    for i = 1:length (installed_packages)
      if (exist (fullfile (pdirs{i}, "packinfo", ".autoload"), "file"))
        idx (end + 1) = i;
      endif
    endfor
  ## load package_name1 ...
  else
    idx = [];
    for i = 1:length (files)
      idx2 = find (strcmp (pnames, files{i}));
      if (! any (idx2))
          error ("package %s is not installed", files{i});
      endif
      idx (end + 1) = idx2;
    endfor
  endif

  ## Load the packages, but take care of the ordering of dependencies
  load_packages_and_dependencies (idx, handle_deps, installed_packages);
endfunction

function unload_packages (files, handle_deps, local_list, global_list)
  installed_packages = installed_packages (local_list, global_list);
  num_packages = length (installed_packages);

  ## Read package names and installdirs into a more convenient format
  pnames = pdirs = cell (1, num_packages);
  for i = 1:num_packages
    pnames{i} = installed_packages{i}.name;
    pdirs{i} = installed_packages{i}.dir;
    pdeps{i} = installed_packages{i}.depends;
  endfor

  ## Get the current octave path
  p = split_by (path(), pathsep ());

  ## unload all
  if (length (files) == 1 && strcmp (files{1}, "all"))
      dirs = pdirs;
  ## unload package_name1 ...
  else
    dirs = {};
    for i = 1:length (files)
      idx = strcmp (pnames, files{i});
      if (! any (idx))
        error ("package %s is not installed", files{i});
      endif
        dirs{end+1} = pdirs{idx};
      endfor
  endif

  ## Check for architecture dependent directories
  arch = getarch();
  archdirs = {};
  for i = 1:length (dirs)
    tmpdir = fullfile (dirs{i}, arch);
    if (exist (tmpdir, "dir"))
      archdirs{end + 1} = dirs{i};
      archdirs{end + 1} = tmpdir;
    endif
  endfor
  if (length (archdirs) > 0)
    dirs = archdirs;
  endif

  ## Unload the packages
  for i = 1:length (dirs)
    d = dirs{i};
    idx = strcmp (p, d);
    if (any (idx))
      rmpath (d);
      ## XXX: We should also check if we need to remove items from EXEC_PATH
    endif
  endfor
endfunction

function [status_out, msg_out] = rm_rf (dir)
  crr = confirm_recursive_rmdir ();
  unwind_protect
    confirm_recursive_rmdir (false);
    [status, msg] = rmdir (dir, "s");
  unwind_protect_cleanup
    confirm_recursive_rmdir (crr);
  end_unwind_protect
  if (nargout > 0)
    status_out = status;
  endif
  if (nargout > 1)
    msg_out = msg;
  endif
endfunction

function emp = dirempty (nm, ign)
  if (nargin < 2)
    ign = {".", ".."};
  else
    ign = [{".", ".."}, ign];
  endif
  l = dir (nm);
  for i = 1:length (l)
    found = false;
    for j = 1:length (ign)
      if (strcmp (l(i).name, ign{j}))
        found = true;
        break;
      endif
    endfor
    if (! found)
      emp = false;
      return
    endif
  endfor
  emp = true;
endfunction

function arch = getarch ()
  persistent _arch = strcat (octave_config_info("canonical_host_type"), ...
                             "-", octave_config_info("api_version"));
  arch = _arch;
endfunction

function [status, output] = shell (cmd)
  persistent have_sh;

  cmd = strrep (cmd, "\\", "/");
  if (ispc () && ! isunix ())
    if (isempty(have_sh))
      if (system("sh.exe -c \"exit\""))
        have_sh = false;
      else
        have_sh = true;
      endif
    endif
    if (have_sh)
      [status, output] = system (strcat ("sh.exe -c \"", cmd, "\""));
    else
      error ("Can not find the command shell")
    endif
  else
    [status, output] = system (cmd);
  endif
endfunction

function newdesc = save_order (desc)
  newdesc = {};
  for i = 1 : length(desc)
    deps = desc{i}.depends;
    if (isempty (deps) || (length (deps) == 1 && 
                          strcmp(deps{1}.package, "octave")))
      newdesc {end + 1} = desc{i};
    else
      tmpdesc = {};
      for k = 1 : length (deps)
        for j = 1 : length (desc)
          if (strcmp (desc{j}.name, deps{k}.package))
            tmpdesc {end + 1} = desc{j};
            break;
          endif
        endfor
      endfor
      if (! isempty (tmpdesc))                                       
        newdesc = {newdesc{:}, save_order(tmpdesc){:}, desc{i}};
      else
        newdesc {end + 1} = desc{i};
      endif
    endif
  endfor
  ## Eliminate the duplicates
  idx = [];
  for i = 1 : length (newdesc)
    for j = (i + 1) : length (newdesc)
      if (strcmp (newdesc{i}.name, newdesc{j}.name))
        idx (end + 1) = j;
      endif
    endfor
  endfor
  newdesc(idx) = [];
endfunction

function load_packages_and_dependencies (idx, handle_deps, installed_packages)
  idx = load_package_dirs (idx, [], handle_deps, installed_packages);
  arch = getarch ();
  dirs = {};
  execpath = EXEC_PATH ();
  for i = idx;
    ndir = installed_packages{i}.dir;
    dirs {end + 1} = ndir;
    if (exist (fullfile (dirs{end}, "bin"), "dir"))
      execpath = strcat (fullfile(dirs{end}, "bin"), ":", execpath);
    endif
    tmpdir = fullfile (ndir, arch);
    if (exist (tmpdir, "dir"))
      dirs{end + 1} = tmpdir;
    endif
  endfor

  ## Load the packages
  if (length (dirs) > 0)
    addpath (dirs{:});
  endif

  ## Add the binaries to exec_path
  if (! strcmp (EXEC_PATH, execpath))
    EXEC_PATH (execpath);
  endif
endfunction

function idx = load_package_dirs (lidx, idx, handle_deps, installed_packages)
  for i = lidx
    if (isfield (installed_packages{i}, "loaded") &&
        installed_packages{i}.loaded)
      continue;
    else
      if (handle_deps)
        deps = installed_packages{i}.depends;
        if ((length (deps) > 1) || (length (deps) == 1 && 
                          ! strcmp(deps{1}.package, "octave")))
          tmplidx = [];
          for k = 1 : length (deps)
            for j = 1 : length (installed_packages)
              if (strcmp (installed_packages{j}.name, deps{k}.package))
                tmplidx (end + 1) = j;
                break;
              endif
            endfor
          endfor
          idx = load_package_dirs (tmplidx, idx, handle_deps, 
                                 installed_packages);
        endif
      endif
      if (isempty (find(idx == i)))
        idx (end + 1) = i;
      endif
    endif
  endfor
endfunction

reply via email to

[Prev in Thread] Current Thread [Next in Thread]