4 // Copyright (c) 2000-2005 Per Liden
5 // Copyright (c) 2006-2013 by CRUX team (http://crux.nu)
7 // Patches for alternative pkgadd.conf file by Jose V Beneyto <sepen@crux.nu>
9 // This program is free software; you can redistribute it and/or modify
10 // it under the terms of the GNU General Public License as published by
11 // the Free Software Foundation; either version 2 of the License, or
12 // (at your option) any later version.
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU General Public License for more details.
19 // You should have received a copy of the GNU General Public License
20 // along with this program; if not, write to the Free Software
21 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
32 void pkgadd::run(int argc
, char** argv
)
35 // Check command line options
40 bool o_upgrade
= false;
43 for (int i
= 1; i
< argc
; i
++) {
44 string
option(argv
[i
]);
45 if (option
== "-r" || option
== "--root") {
46 assert_argument(argv
, argc
, i
);
49 } else if (option
== "-c" || option
== "--config") {
50 assert_argument(argv
, argc
, i
);
51 o_config
= argv
[i
+ 1];
53 } else if (option
== "-u" || option
== "--upgrade") {
55 } else if (option
== "-f" || option
== "--force") {
57 } else if (option
[0] == '-' || !o_package
.empty()) {
58 throw runtime_error("invalid option " + option
);
64 if (o_package
.empty())
65 throw runtime_error("option missing");
71 throw runtime_error("only root can install/upgrade packages");
74 // Install/upgrade package
77 db_lock
lock(o_root
, true);
80 pair
<string
, pkginfo_t
> package
= pkg_open(o_package
);
81 vector
<rule_t
> config_rules
= read_config(o_config
);
83 bool installed
= db_find_pkg(package
.first
);
84 if (installed
&& !o_upgrade
)
85 throw runtime_error("package " + package
.first
+ " already installed (use -u to upgrade)");
86 else if (!installed
&& o_upgrade
)
87 throw runtime_error("package " + package
.first
+ " not previously installed (skip -u to install)");
89 set
<string
> non_install_files
= apply_install_rules(package
.first
, package
.second
, config_rules
);
90 set
<string
> conflicting_files
= db_find_conflicts(package
.first
, package
.second
);
92 if (!conflicting_files
.empty()) {
94 set
<string
> keep_list
;
95 if (o_upgrade
) // Don't remove files matching the rules in configuration
96 keep_list
= make_keep_list(conflicting_files
, config_rules
);
97 db_rm_files(conflicting_files
, keep_list
); // Remove unwanted conflicts
99 copy(conflicting_files
.begin(), conflicting_files
.end(), ostream_iterator
<string
>(cerr
, "\n"));
100 throw runtime_error("listed file(s) already installed (use -f to ignore and overwrite)");
104 set
<string
> keep_list
;
107 keep_list
= make_keep_list(package
.second
.files
, config_rules
);
108 db_rm_pkg(package
.first
, keep_list
);
111 db_add_pkg(package
.first
, package
.second
);
113 pkg_install(o_package
, keep_list
, non_install_files
);
118 void pkgadd::print_help() const
120 cout
<< "usage: " << utilname
<< " [options] <file>" << endl
121 << "options:" << endl
122 << " -u, --upgrade upgrade package with the same name" << endl
123 << " -f, --force force install, overwrite conflicting files" << endl
124 << " -r, --root <path> specify alternative installation root" << endl
125 << " -c, --config <file> use alternate configuration file" << endl
126 << " -v, --version print version and exit" << endl
127 << " -h, --help print help and exit" << endl
;
130 vector
<rule_t
> pkgadd::read_config(string file
) const
132 vector
<rule_t
> rules
;
133 unsigned int linecount
= 0;
134 string filename
= root
+ PKGADD_CONF
;
136 if (!file
.empty()) filename
= file
;
137 ifstream
in(filename
.c_str());
144 if (!line
.empty() && line
[0] != '#') {
145 if (line
.length() >= PKGADD_CONF_MAXLINE
)
146 throw runtime_error(filename
+ ":" + itos(linecount
) + ": line too long, aborting");
148 char event
[PKGADD_CONF_MAXLINE
];
149 char pattern
[PKGADD_CONF_MAXLINE
];
150 char action
[PKGADD_CONF_MAXLINE
];
151 char dummy
[PKGADD_CONF_MAXLINE
];
152 if (sscanf(line
.c_str(), "%s %s %s %s", event
, pattern
, action
, dummy
) != 3)
153 throw runtime_error(filename
+ ":" + itos(linecount
) + ": wrong number of arguments, aborting");
155 if (!strcmp(event
, "UPGRADE") || !strcmp(event
, "INSTALL")) {
157 rule
.event
= strcmp(event
, "UPGRADE") ? INSTALL
: UPGRADE
;
158 rule
.pattern
= pattern
;
159 if (!strcmp(action
, "YES")) {
161 } else if (!strcmp(action
, "NO")) {
164 throw runtime_error(filename
+ ":" + itos(linecount
) + ": '" +
165 string(action
) + "' unknown action, should be YES or NO, aborting");
167 rules
.push_back(rule
);
169 throw runtime_error(filename
+ ":" + itos(linecount
) + ": '" +
170 string(event
) + "' unknown event, aborting");
177 cerr
<< "Configuration:" << endl
;
178 for (vector
<rule_t
>::const_iterator j
= rules
.begin(); j
!= rules
.end(); j
++) {
179 cerr
<< "\t" << (*j
).pattern
<< "\t" << (*j
).action
<< endl
;
187 set
<string
> pkgadd::make_keep_list(const set
<string
>& files
, const vector
<rule_t
>& rules
) const
189 set
<string
> keep_list
;
190 vector
<rule_t
> found
;
192 find_rules(rules
, UPGRADE
, found
);
194 for (set
<string
>::const_iterator i
= files
.begin(); i
!= files
.end(); i
++) {
195 for (vector
<rule_t
>::reverse_iterator j
= found
.rbegin(); j
!= found
.rend(); j
++) {
196 if (rule_applies_to_file(*j
, *i
)) {
198 keep_list
.insert(keep_list
.end(), *i
);
206 cerr
<< "Keep list:" << endl
;
207 for (set
<string
>::const_iterator j
= keep_list
.begin(); j
!= keep_list
.end(); j
++) {
208 cerr
<< " " << (*j
) << endl
;
216 set
<string
> pkgadd::apply_install_rules(const string
& name
, pkginfo_t
& info
, const vector
<rule_t
>& rules
)
218 // TODO: better algo(?)
219 set
<string
> install_set
;
220 set
<string
> non_install_set
;
221 vector
<rule_t
> found
;
223 find_rules(rules
, INSTALL
, found
);
225 for (set
<string
>::const_iterator i
= info
.files
.begin(); i
!= info
.files
.end(); i
++) {
226 bool install_file
= true;
228 for (vector
<rule_t
>::reverse_iterator j
= found
.rbegin(); j
!= found
.rend(); j
++) {
229 if (rule_applies_to_file(*j
, *i
)) {
230 install_file
= (*j
).action
;
236 install_set
.insert(install_set
.end(), *i
);
238 non_install_set
.insert(*i
);
242 info
.files
= install_set
;
245 cerr
<< "Install set:" << endl
;
246 for (set
<string
>::iterator j
= info
.files
.begin(); j
!= info
.files
.end(); j
++) {
247 cerr
<< " " << (*j
) << endl
;
251 cerr
<< "Non-Install set:" << endl
;
252 for (set
<string
>::iterator j
= non_install_set
.begin(); j
!= non_install_set
.end(); j
++) {
253 cerr
<< " " << (*j
) << endl
;
258 return non_install_set
;
261 void pkgadd::find_rules(const vector
<rule_t
>& rules
, rule_event_t event
, vector
<rule_t
>& found
) const
263 for (vector
<rule_t
>::const_iterator i
= rules
.begin(); i
!= rules
.end(); i
++)
264 if (i
->event
== event
)
268 bool pkgadd::rule_applies_to_file(const rule_t
& rule
, const string
& file
) const
273 if (regcomp(&preg
, rule
.pattern
.c_str(), REG_EXTENDED
| REG_NOSUB
))
274 throw runtime_error("error compiling regular expression '" + rule
.pattern
+ "', aborting");
276 ret
= !regexec(&preg
, file
.c_str(), 0, 0, 0);