4 // Copyright (c) 2000-2005 Per Liden
5 // Copyright (c) 2006-2007 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
37 bool o_upgrade
= false;
40 for (int i
= 1; i
< argc
; i
++) {
41 string
option(argv
[i
]);
42 if (option
== "-r" || option
== "--root") {
43 assert_argument(argv
, argc
, i
);
46 } else if (option
== "-u" || option
== "--upgrade") {
48 } else if (option
== "-f" || option
== "--force") {
50 } else if (option
[0] == '-' || !o_package
.empty()) {
51 throw runtime_error("invalid option " + option
);
57 if (o_package
.empty())
58 throw runtime_error("option missing");
64 throw runtime_error("only root can install/upgrade packages");
67 // Install/upgrade package
70 db_lock
lock(o_root
, true);
73 pair
<string
, pkginfo_t
> package
= pkg_open(o_package
);
74 vector
<rule_t
> config_rules
= read_config();
76 bool installed
= db_find_pkg(package
.first
);
77 if (installed
&& !o_upgrade
)
78 throw runtime_error("package " + package
.first
+ " already installed (use -u to upgrade)");
79 else if (!installed
&& o_upgrade
)
80 throw runtime_error("package " + package
.first
+ " not previously installed (skip -u to install)");
82 set
<string
> non_install_files
= apply_install_rules(package
.first
, package
.second
, config_rules
);
83 set
<string
> conflicting_files
= db_find_conflicts(package
.first
, package
.second
);
85 if (!conflicting_files
.empty()) {
87 set
<string
> keep_list
;
88 if (o_upgrade
) // Don't remove files matching the rules in configuration
89 keep_list
= make_keep_list(conflicting_files
, config_rules
);
90 db_rm_files(conflicting_files
, keep_list
); // Remove unwanted conflicts
92 copy(conflicting_files
.begin(), conflicting_files
.end(), ostream_iterator
<string
>(cerr
, "\n"));
93 throw runtime_error("listed file(s) already installed (use -f to ignore and overwrite)");
97 set
<string
> keep_list
;
100 keep_list
= make_keep_list(package
.second
.files
, config_rules
);
101 db_rm_pkg(package
.first
, keep_list
);
104 db_add_pkg(package
.first
, package
.second
);
106 pkg_install(o_package
, keep_list
, non_install_files
);
111 void pkgadd::print_help() const
113 cout
<< "usage: " << utilname
<< " [options] <file>" << endl
114 << "options:" << endl
115 << " -u, --upgrade upgrade package with the same name" << endl
116 << " -f, --force force install, overwrite conflicting files" << endl
117 << " -r, --root <path> specify alternative installation root" << endl
118 << " -v, --version print version and exit" << endl
119 << " -h, --help print help and exit" << endl
;
122 vector
<rule_t
> pkgadd::read_config() const
124 vector
<rule_t
> rules
;
125 unsigned int linecount
= 0;
126 const string filename
= root
+ PKGADD_CONF
;
127 ifstream
in(filename
.c_str());
134 if (!line
.empty() && line
[0] != '#') {
135 if (line
.length() >= PKGADD_CONF_MAXLINE
)
136 throw runtime_error(filename
+ ":" + itos(linecount
) + ": line too long, aborting");
138 char event
[PKGADD_CONF_MAXLINE
];
139 char pattern
[PKGADD_CONF_MAXLINE
];
140 char action
[PKGADD_CONF_MAXLINE
];
141 char dummy
[PKGADD_CONF_MAXLINE
];
142 if (sscanf(line
.c_str(), "%s %s %s %s", event
, pattern
, action
, dummy
) != 3)
143 throw runtime_error(filename
+ ":" + itos(linecount
) + ": wrong number of arguments, aborting");
145 if (!strcmp(event
, "UPGRADE") || !strcmp(event
, "INSTALL")) {
147 rule
.event
= strcmp(event
, "UPGRADE") ? INSTALL
: UPGRADE
;
148 rule
.pattern
= pattern
;
149 if (!strcmp(action
, "YES")) {
151 } else if (!strcmp(action
, "NO")) {
154 throw runtime_error(filename
+ ":" + itos(linecount
) + ": '" +
155 string(action
) + "' unknown action, should be YES or NO, aborting");
157 rules
.push_back(rule
);
159 throw runtime_error(filename
+ ":" + itos(linecount
) + ": '" +
160 string(event
) + "' unknown event, aborting");
167 cerr
<< "Configuration:" << endl
;
168 for (vector
<rule_t
>::const_iterator j
= rules
.begin(); j
!= rules
.end(); j
++) {
169 cerr
<< "\t" << (*j
).pattern
<< "\t" << (*j
).action
<< endl
;
177 set
<string
> pkgadd::make_keep_list(const set
<string
>& files
, const vector
<rule_t
>& rules
) const
179 set
<string
> keep_list
;
180 vector
<rule_t
> found
;
182 find_rules(rules
, UPGRADE
, found
);
184 for (set
<string
>::const_iterator i
= files
.begin(); i
!= files
.end(); i
++) {
185 for (vector
<rule_t
>::reverse_iterator j
= found
.rbegin(); j
!= found
.rend(); j
++) {
186 if (rule_applies_to_file(*j
, *i
)) {
188 keep_list
.insert(keep_list
.end(), *i
);
196 cerr
<< "Keep list:" << endl
;
197 for (set
<string
>::const_iterator j
= keep_list
.begin(); j
!= keep_list
.end(); j
++) {
198 cerr
<< " " << (*j
) << endl
;
206 set
<string
> pkgadd::apply_install_rules(const string
& name
, pkginfo_t
& info
, const vector
<rule_t
>& rules
)
208 // TODO: better algo(?)
209 set
<string
> install_set
;
210 set
<string
> non_install_set
;
211 vector
<rule_t
> found
;
213 find_rules(rules
, INSTALL
, found
);
215 for (set
<string
>::const_iterator i
= info
.files
.begin(); i
!= info
.files
.end(); i
++) {
216 bool install_file
= true;
218 for (vector
<rule_t
>::reverse_iterator j
= found
.rbegin(); j
!= found
.rend(); j
++) {
219 if (rule_applies_to_file(*j
, *i
)) {
220 install_file
= (*j
).action
;
226 install_set
.insert(install_set
.end(), *i
);
228 non_install_set
.insert(*i
);
232 info
.files
= install_set
;
235 cerr
<< "Install set:" << endl
;
236 for (set
<string
>::iterator j
= info
.files
.begin(); j
!= info
.files
.end(); j
++) {
237 cerr
<< " " << (*j
) << endl
;
241 cerr
<< "Non-Install set:" << endl
;
242 for (set
<string
>::iterator j
= non_install_set
.begin(); j
!= non_install_set
.end(); j
++) {
243 cerr
<< " " << (*j
) << endl
;
248 return non_install_set
;
251 void pkgadd::find_rules(const vector
<rule_t
>& rules
, rule_event_t event
, vector
<rule_t
>& found
) const
253 for (vector
<rule_t
>::const_iterator i
= rules
.begin(); i
!= rules
.end(); i
++)
254 if (i
->event
== event
)
258 bool pkgadd::rule_applies_to_file(const rule_t
& rule
, const string
& file
) const
263 if (regcomp(&preg
, rule
.pattern
.c_str(), REG_EXTENDED
| REG_NOSUB
))
264 throw runtime_error("error compiling regular expression '" + rule
.pattern
+ "', aborting");
266 ret
= !regexec(&preg
, file
.c_str(), 0, 0, 0);