1########################################################################
2##
3## Copyright (C) 2005-2021 The Octave Project Developers
4##
5## See the file COPYRIGHT.md in the top-level directory of this
6## distribution or <https://octave.org/copyright/>.
7##
8## This file is part of Octave.
9##
10## Octave is free software: you can redistribute it and/or modify it
11## under the terms of the GNU General Public License as published by
12## the Free Software Foundation, either version 3 of the License, or
13## (at your option) any later version.
14##
15## Octave is distributed in the hope that it will be useful, but
16## WITHOUT ANY WARRANTY; without even the implied warranty of
17## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18## GNU General Public License for more details.
19##
20## You should have received a copy of the GNU General Public License
21## along with Octave; see the file COPYING.  If not, see
22## <https://www.gnu.org/licenses/>.
23##
24########################################################################
25
26## -*- texinfo -*-
27## @deftypefn {} {} uninstall (@var{pkgnames}, @var{handle_deps}, @var{verbose}, @var{local_list}, @var{global_list}, @var{global_install})
28## Undocumented internal function.
29## @end deftypefn
30
31function uninstall (pkgnames, handle_deps, verbose, local_list,
32                    global_list, global_install)
33
34  ## Get the list of installed packages.
35  [local_packages, global_packages] = installed_packages(local_list,
36                                                         global_list);
37  if (global_install)
38    installed_pkgs_lst = {local_packages{:}, global_packages{:}};
39  else
40    installed_pkgs_lst = local_packages;
41  endif
42
43  num_packages = length (installed_pkgs_lst);
44  delete_idx = [];
45  for i = 1:num_packages
46    cur_name = installed_pkgs_lst{i}.name;
47    if (any (strcmp (cur_name, pkgnames)))
48      delete_idx(end+1) = i;
49    endif
50  endfor
51
52  ## Are all the packages that should be uninstalled already installed?
53  if (length (delete_idx) != length (pkgnames))
54    if (global_install)
55      ## Try again for a locally installed package.
56      installed_pkgs_lst = local_packages;
57
58      num_packages = length (installed_pkgs_lst);
59      delete_idx = [];
60      for i = 1:num_packages
61        cur_name = installed_pkgs_lst{i}.name;
62        if (any (strcmp (cur_name, pkgnames)))
63          delete_idx(end+1) = i;
64        endif
65      endfor
66      if (length (delete_idx) != length (pkgnames))
67        ## FIXME: We should have a better error message.
68        warning ("some of the packages you want to uninstall are not installed");
69      endif
70    else
71      ## FIXME: We should have a better error message.
72      warning ("some of the packages you want to uninstall are not installed");
73    endif
74  endif
75
76  if (isempty (delete_idx))
77    warning ("no packages will be uninstalled");
78  else
79
80    ## Compute the packages that will remain installed.
81    idx = setdiff (1:num_packages, delete_idx);
82    remaining_packages = {installed_pkgs_lst{idx}};
83    to_delete_packages = {installed_pkgs_lst{delete_idx}};
84
85    ## Check dependencies.
86    if (handle_deps)
87      error_text = "";
88      for i = 1:length (remaining_packages)
89        desc = remaining_packages{i};
90        bad_deps = get_unsatisfied_deps (desc, to_delete_packages, true);
91
92        ## Will the uninstallation break any dependencies?
93        if (! isempty (bad_deps))
94          for i = 1:length (bad_deps)
95            dep = bad_deps{i};
96            error_text = [error_text " " desc.name " needs " ...
97                          dep.package " " dep.operator " " dep.version "\n"];
98          endfor
99        endif
100      endfor
101
102      if (! isempty (error_text))
103        error ("the following dependencies where unsatisfied:\n  %s", error_text);
104      endif
105    endif
106
107    ## Delete the directories containing the packages.
108    for i = delete_idx
109      desc = installed_pkgs_lst{i};
110      ## If an 'on_uninstall.m' exist, call it!
111      if (exist (fullfile (desc.dir, "packinfo", "on_uninstall.m"), "file"))
112        wd = pwd ();
113        cd (fullfile (desc.dir, "packinfo"));
114        on_uninstall (desc);
115        cd (wd);
116      endif
117      ## Do the actual deletion.
118      if (desc.loaded)
119        rmpath (desc.dir);
120        if (isfolder (getarchdir (desc)))
121          rmpath (getarchdir (desc));
122        endif
123      endif
124      if (isfolder (desc.dir))
125        [status, msg] = rmdir (desc.dir, "s");
126        if (status != 1 && isfolder (desc.dir))
127          error ("couldn't delete directory %s: %s", desc.dir, msg);
128        endif
129        [status, msg] = rmdir (getarchdir (desc), "s");
130        if (status != 1 && isfolder (getarchdir (desc)))
131          error ("couldn't delete directory %s: %s", getarchdir (desc), msg);
132        endif
133        if (dirempty (desc.archprefix))
134          rmdir (desc.archprefix, "s");
135        endif
136      else
137        warning ("directory %s previously lost", desc.dir);
138      endif
139    endfor
140
141    ## Write a new ~/.octave_packages.
142    if (global_install)
143      if (length (remaining_packages) == 0)
144        unlink (global_list);
145      else
146        global_packages = save_order (remaining_packages);
147        if (ispc)
148          ## On Windows ensure LFN paths are saved rather than 8.3 style paths
149          global_packages = standardize_paths (global_packages);
150        endif
151        global_packages = make_rel_paths (global_packages);
152        save (global_list, "global_packages");
153      endif
154    else
155      if (length (remaining_packages) == 0)
156        unlink (local_list);
157      else
158        local_packages = save_order (remaining_packages);
159        if (ispc)
160          local_packages = standardize_paths (local_packages);
161        endif
162        save (local_list, "local_packages");
163      endif
164    endif
165  endif
166
167endfunction
168