4 // Copyright (c) 2000-2005 Per Liden
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2 of the License, or
9 // (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
29 void pkgadd::run(int argc
, char** argv
)
32 // Check command line options
36 bool o_upgrade
= false;
39 for (int i
= 1; i
< argc
; i
++) {
40 string
option(argv
[i
]);
41 if (option
== "-r" || option
== "--root") {
42 assert_argument(argv
, argc
, i
);
45 } else if (option
== "-u" || option
== "--upgrade") {
47 } else if (option
== "-f" || option
== "--force") {
49 } else if (option
[0] == '-' || !o_package
.empty()) {
50 throw runtime_error("invalid option " + option
);
56 if (o_package
.empty())
57 throw runtime_error("option missing");
63 throw runtime_error("only root can install/upgrade packages");
66 // Install/upgrade package
69 db_lock
lock(o_root
, true);
72 pair
<string
, pkginfo_t
> package
= pkg_open(o_package
);
73 vector
<rule_t
> config_rules
= read_config();
75 bool installed
= db_find_pkg(package
.first
);
76 if (installed
&& !o_upgrade
)
77 throw runtime_error("package " + package
.first
+ " already installed (use -u to upgrade)");
78 else if (!installed
&& o_upgrade
)
79 throw runtime_error("package " + package
.first
+ " not previously installed (skip -u to install)");
81 set
<string
> non_install_files
= apply_install_rules(package
.first
, package
.second
, config_rules
);
82 set
<string
> conflicting_files
= db_find_conflicts(package
.first
, package
.second
);
84 if (!conflicting_files
.empty()) {
86 set
<string
> keep_list
;
87 if (o_upgrade
) // Don't remove files matching the rules in configuration
88 keep_list
= make_keep_list(conflicting_files
, config_rules
);
89 db_rm_files(conflicting_files
, keep_list
); // Remove unwanted conflicts
91 copy(conflicting_files
.begin(), conflicting_files
.end(), ostream_iterator
<string
>(cerr
, "\n"));
92 throw runtime_error("listed file(s) already installed (use -f to ignore and overwrite)");
96 set
<string
> keep_list
;
99 keep_list
= make_keep_list(package
.second
.files
, config_rules
);
100 db_rm_pkg(package
.first
, keep_list
);
103 db_add_pkg(package
.first
, package
.second
);
105 pkg_install(o_package
, keep_list
, non_install_files
);
110 void pkgadd::print_help() const
112 cout
<< "usage: " << utilname
<< " [options] <file>" << endl
113 << "options:" << endl
114 << " -u, --upgrade upgrade package with the same name" << endl
115 << " -f, --force force install, overwrite conflicting files" << endl
116 << " -r, --root <path> specify alternative installation root" << endl
117 << " -v, --version print version and exit" << endl
118 << " -h, --help print help and exit" << endl
;
121 vector
<rule_t
> pkgadd::read_config() const
123 vector
<rule_t
> rules
;
124 unsigned int linecount
= 0;
125 const string filename
= root
+ PKGADD_CONF
;
126 ifstream
in(filename
.c_str());
133 if (!line
.empty() && line
[0] != '#') {
134 if (line
.length() >= PKGADD_CONF_MAXLINE
)
135 throw runtime_error(filename
+ ":" + itos(linecount
) + ": line too long, aborting");
137 char event
[PKGADD_CONF_MAXLINE
];
138 char pattern
[PKGADD_CONF_MAXLINE
];
139 char action
[PKGADD_CONF_MAXLINE
];
140 char dummy
[PKGADD_CONF_MAXLINE
];
141 if (sscanf(line
.c_str(), "%s %s %s %s", event
, pattern
, action
, dummy
) != 3)
142 throw runtime_error(filename
+ ":" + itos(linecount
) + ": wrong number of arguments, aborting");
144 if (!strcmp(event
, "UPGRADE") || !strcmp(event
, "INSTALL")) {
146 rule
.event
= strcmp(event
, "UPGRADE") ? INSTALL
: UPGRADE
;
147 rule
.pattern
= pattern
;
148 if (!strcmp(action
, "YES")) {
150 } else if (!strcmp(action
, "NO")) {
153 throw runtime_error(filename
+ ":" + itos(linecount
) + ": '" +
154 string(action
) + "' unknown action, should be YES or NO, aborting");
156 rules
.push_back(rule
);
158 throw runtime_error(filename
+ ":" + itos(linecount
) + ": '" +
159 string(event
) + "' unknown event, aborting");
166 cerr
<< "Configuration:" << endl
;
167 for (vector
<rule_t
>::const_iterator j
= rules
.begin(); j
!= rules
.end(); j
++) {
168 cerr
<< "\t" << (*j
).pattern
<< "\t" << (*j
).action
<< endl
;
176 set
<string
> pkgadd::make_keep_list(const set
<string
>& files
, const vector
<rule_t
>& rules
) const
178 set
<string
> keep_list
;
179 vector
<rule_t
> found
;
181 find_rules(rules
, UPGRADE
, found
);
183 for (set
<string
>::const_iterator i
= files
.begin(); i
!= files
.end(); i
++) {
184 for (vector
<rule_t
>::reverse_iterator j
= found
.rbegin(); j
!= found
.rend(); j
++) {
185 if (rule_applies_to_file(*j
, *i
)) {
187 keep_list
.insert(keep_list
.end(), *i
);
195 cerr
<< "Keep list:" << endl
;
196 for (set
<string
>::const_iterator j
= keep_list
.begin(); j
!= keep_list
.end(); j
++) {
197 cerr
<< " " << (*j
) << endl
;
205 set
<string
> pkgadd::apply_install_rules(const string
& name
, pkginfo_t
& info
, const vector
<rule_t
>& rules
)
207 // TODO: better algo(?)
208 set
<string
> install_set
;
209 set
<string
> non_install_set
;
210 vector
<rule_t
> found
;
212 find_rules(rules
, INSTALL
, found
);
214 for (set
<string
>::const_iterator i
= info
.files
.begin(); i
!= info
.files
.end(); i
++) {
215 bool install_file
= true;
217 for (vector
<rule_t
>::reverse_iterator j
= found
.rbegin(); j
!= found
.rend(); j
++) {
218 if (rule_applies_to_file(*j
, *i
)) {
219 install_file
= (*j
).action
;
225 install_set
.insert(install_set
.end(), *i
);
227 non_install_set
.insert(*i
);
231 info
.files
= install_set
;
234 cerr
<< "Install set:" << endl
;
235 for (set
<string
>::iterator j
= info
.files
.begin(); j
!= info
.files
.end(); j
++) {
236 cerr
<< " " << (*j
) << endl
;
240 cerr
<< "Non-Install set:" << endl
;
241 for (set
<string
>::iterator j
= non_install_set
.begin(); j
!= non_install_set
.end(); j
++) {
242 cerr
<< " " << (*j
) << endl
;
247 return non_install_set
;
250 void pkgadd::find_rules(const vector
<rule_t
>& rules
, rule_event_t event
, vector
<rule_t
>& found
) const
252 for (vector
<rule_t
>::const_iterator i
= rules
.begin(); i
!= rules
.end(); i
++)
253 if (i
->event
== event
)
257 bool pkgadd::rule_applies_to_file(const rule_t
& rule
, const string
& file
) const
262 if (regcomp(&preg
, rule
.pattern
.c_str(), REG_EXTENDED
| REG_NOSUB
))
263 throw runtime_error("error compiling regular expression '" + rule
.pattern
+ "', aborting");
265 ret
= !regexec(&preg
, file
.c_str(), 0, 0, 0);