4 // Copyright (c) 2000-2005 Per Liden
5 // Copyright (c) 2006-2010 by CRUX team (http://crux.nu)
7 // This program is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by
9 // the Free Software Foundation; either version 2 of the License, or
10 // (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
30 void pkgadd::run(int argc
, char** argv
)
33 // Check command line options
38 bool o_upgrade
= false;
41 for (int i
= 1; i
< argc
; i
++) {
42 string
option(argv
[i
]);
43 if (option
== "-r" || option
== "--root") {
44 assert_argument(argv
, argc
, i
);
47 } else if (option
== "-c" || option
== "--config") {
48 assert_argument(argv
, argc
, i
);
49 o_config
= argv
[i
+ 1];
51 } else if (option
== "-u" || option
== "--upgrade") {
53 } else if (option
== "-f" || option
== "--force") {
55 } else if (option
[0] == '-' || !o_package
.empty()) {
56 throw runtime_error("invalid option " + option
);
62 if (o_package
.empty())
63 throw runtime_error("option missing");
69 throw runtime_error("only root can install/upgrade packages");
72 // Install/upgrade package
75 db_lock
lock(o_root
, true);
78 pair
<string
, pkginfo_t
> package
= pkg_open(o_package
);
79 vector
<rule_t
> config_rules
= read_config(o_config
);
81 bool installed
= db_find_pkg(package
.first
);
82 if (installed
&& !o_upgrade
)
83 throw runtime_error("package " + package
.first
+ " already installed (use -u to upgrade)");
84 else if (!installed
&& o_upgrade
)
85 throw runtime_error("package " + package
.first
+ " not previously installed (skip -u to install)");
87 set
<string
> non_install_files
= apply_install_rules(package
.first
, package
.second
, config_rules
);
88 set
<string
> conflicting_files
= db_find_conflicts(package
.first
, package
.second
);
90 if (!conflicting_files
.empty()) {
92 set
<string
> keep_list
;
93 if (o_upgrade
) // Don't remove files matching the rules in configuration
94 keep_list
= make_keep_list(conflicting_files
, config_rules
);
95 db_rm_files(conflicting_files
, keep_list
); // Remove unwanted conflicts
97 copy(conflicting_files
.begin(), conflicting_files
.end(), ostream_iterator
<string
>(cerr
, "\n"));
98 throw runtime_error("listed file(s) already installed (use -f to ignore and overwrite)");
102 set
<string
> keep_list
;
105 keep_list
= make_keep_list(package
.second
.files
, config_rules
);
106 db_rm_pkg(package
.first
, keep_list
);
109 db_add_pkg(package
.first
, package
.second
);
111 pkg_install(o_package
, keep_list
, non_install_files
);
116 void pkgadd::print_help() const
118 cout
<< "usage: " << utilname
<< " [options] <file>" << endl
119 << "options:" << endl
120 << " -u, --upgrade upgrade package with the same name" << endl
121 << " -f, --force force install, overwrite conflicting files" << endl
122 << " -r, --root <path> specify alternative installation root" << endl
123 << " -c, --config <file> use alternate configuration file" << endl
124 << " -v, --version print version and exit" << endl
125 << " -h, --help print help and exit" << endl
;
128 vector
<rule_t
> pkgadd::read_config(string file
) const
130 vector
<rule_t
> rules
;
131 unsigned int linecount
= 0;
132 string filename
= root
+ PKGADD_CONF
;
134 if (!file
.empty()) filename
= file
;
135 ifstream
in(filename
.c_str());
142 if (!line
.empty() && line
[0] != '#') {
143 if (line
.length() >= PKGADD_CONF_MAXLINE
)
144 throw runtime_error(filename
+ ":" + itos(linecount
) + ": line too long, aborting");
146 char event
[PKGADD_CONF_MAXLINE
];
147 char pattern
[PKGADD_CONF_MAXLINE
];
148 char action
[PKGADD_CONF_MAXLINE
];
149 char dummy
[PKGADD_CONF_MAXLINE
];
150 if (sscanf(line
.c_str(), "%s %s %s %s", event
, pattern
, action
, dummy
) != 3)
151 throw runtime_error(filename
+ ":" + itos(linecount
) + ": wrong number of arguments, aborting");
153 if (!strcmp(event
, "UPGRADE") || !strcmp(event
, "INSTALL")) {
155 rule
.event
= strcmp(event
, "UPGRADE") ? INSTALL
: UPGRADE
;
156 rule
.pattern
= pattern
;
157 if (!strcmp(action
, "YES")) {
159 } else if (!strcmp(action
, "NO")) {
162 throw runtime_error(filename
+ ":" + itos(linecount
) + ": '" +
163 string(action
) + "' unknown action, should be YES or NO, aborting");
165 rules
.push_back(rule
);
167 throw runtime_error(filename
+ ":" + itos(linecount
) + ": '" +
168 string(event
) + "' unknown event, aborting");
175 cerr
<< "Configuration:" << endl
;
176 for (vector
<rule_t
>::const_iterator j
= rules
.begin(); j
!= rules
.end(); j
++) {
177 cerr
<< "\t" << (*j
).pattern
<< "\t" << (*j
).action
<< endl
;
185 set
<string
> pkgadd::make_keep_list(const set
<string
>& files
, const vector
<rule_t
>& rules
) const
187 set
<string
> keep_list
;
188 vector
<rule_t
> found
;
190 find_rules(rules
, UPGRADE
, found
);
192 for (set
<string
>::const_iterator i
= files
.begin(); i
!= files
.end(); i
++) {
193 for (vector
<rule_t
>::reverse_iterator j
= found
.rbegin(); j
!= found
.rend(); j
++) {
194 if (rule_applies_to_file(*j
, *i
)) {
196 keep_list
.insert(keep_list
.end(), *i
);
204 cerr
<< "Keep list:" << endl
;
205 for (set
<string
>::const_iterator j
= keep_list
.begin(); j
!= keep_list
.end(); j
++) {
206 cerr
<< " " << (*j
) << endl
;
214 set
<string
> pkgadd::apply_install_rules(const string
& name
, pkginfo_t
& info
, const vector
<rule_t
>& rules
)
216 // TODO: better algo(?)
217 set
<string
> install_set
;
218 set
<string
> non_install_set
;
219 vector
<rule_t
> found
;
221 find_rules(rules
, INSTALL
, found
);
223 for (set
<string
>::const_iterator i
= info
.files
.begin(); i
!= info
.files
.end(); i
++) {
224 bool install_file
= true;
226 for (vector
<rule_t
>::reverse_iterator j
= found
.rbegin(); j
!= found
.rend(); j
++) {
227 if (rule_applies_to_file(*j
, *i
)) {
228 install_file
= (*j
).action
;
234 install_set
.insert(install_set
.end(), *i
);
236 non_install_set
.insert(*i
);
240 info
.files
= install_set
;
243 cerr
<< "Install set:" << endl
;
244 for (set
<string
>::iterator j
= info
.files
.begin(); j
!= info
.files
.end(); j
++) {
245 cerr
<< " " << (*j
) << endl
;
249 cerr
<< "Non-Install set:" << endl
;
250 for (set
<string
>::iterator j
= non_install_set
.begin(); j
!= non_install_set
.end(); j
++) {
251 cerr
<< " " << (*j
) << endl
;
256 return non_install_set
;
259 void pkgadd::find_rules(const vector
<rule_t
>& rules
, rule_event_t event
, vector
<rule_t
>& found
) const
261 for (vector
<rule_t
>::const_iterator i
= rules
.begin(); i
!= rules
.end(); i
++)
262 if (i
->event
== event
)
266 bool pkgadd::rule_applies_to_file(const rule_t
& rule
, const string
& file
) const
271 if (regcomp(&preg
, rule
.pattern
.c_str(), REG_EXTENDED
| REG_NOSUB
))
272 throw runtime_error("error compiling regular expression '" + rule
.pattern
+ "', aborting");
274 ret
= !regexec(&preg
, file
.c_str(), 0, 0, 0);