]> cygwin.com Git - cygwin-apps/setup.git/blob - package_meta.cc
2002-11-04 Max Bowsher <maxb@ukf.net>
[cygwin-apps/setup.git] / package_meta.cc
1 /*
2 * Copyright (c) 2001, Robert Collins.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * A copy of the GNU General Public License can be found at
10 * http://www.gnu.org/
11 *
12 * Written by Robert Collins <rbtcollins@hotmail.com>
13 *
14 */
15
16 #if 0
17 static const char *cvsid = "\n%%% $Id$\n";
18 #endif
19
20 #include "package_meta.h"
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <strings.h>
25
26 #include "io_stream.h"
27 #include "compress.h"
28
29 #include "filemanip.h"
30 #include "hash.h"
31 #include "LogSingleton.h"
32 /* io_stream needs a bit of tweaking to get rid of this. TODO */
33 #include "mount.h"
34 /* this goes at the same time */
35 #include "win32.h"
36
37
38 #include "category.h"
39 #include "script.h"
40
41 #include "package_version.h"
42 #include "cygpackage.h"
43 #include "package_db.h"
44
45 #include <algorithm>
46
47 using namespace std;
48
49 static const char *standard_dirs[] = {
50 "bin",
51 "etc",
52 "lib",
53 "tmp",
54 "usr",
55 "usr/bin",
56 "usr/lib",
57 "usr/src",
58 "usr/local",
59 "usr/local/bin",
60 "usr/local/etc",
61 "usr/local/lib",
62 "usr/tmp",
63 "var/run",
64 "var/tmp",
65 0
66 };
67
68 void
69 hash::add_subdirs (String const &tpath)
70 {
71 char *nonp, *pp;
72 char *path = tpath.cstr();
73 for (nonp = path; *nonp == '\\' || *nonp == '/'; nonp++);
74 for (pp = path + strlen (path) - 1; pp > nonp; pp--)
75 if (*pp == '/' || *pp == '\\')
76 {
77 int i, s = 0;
78 char c = *pp;
79 *pp = 0;
80 for (i = 0; standard_dirs[i]; i++)
81 if (strcmp (standard_dirs[i] + 1, path) == 0)
82 {
83 s = 1;
84 break;
85 }
86 if (s == 0)
87 add (path);
88 *pp = c;
89 }
90 }
91
92 /*****************/
93
94 const
95 packagemeta::_actions
96 packagemeta::Default_action (0);
97 const
98 packagemeta::_actions
99 packagemeta::Install_action (1);
100 const
101 packagemeta::_actions
102 packagemeta::Reinstall_action (2);
103 const
104 packagemeta::_actions
105 packagemeta::Uninstall_action (3);
106
107 char const *
108 packagemeta::_actions::caption ()
109 {
110 switch (_value)
111 {
112 case 0:
113 return "Default";
114 case 1:
115 return "Install";
116 case 2:
117 return "Reinstall";
118 case 3:
119 return "Uninstall";
120 }
121 // Pacify GCC: (all case options are checked above)
122 return 0;
123 }
124
125 packagemeta::packagemeta (packagemeta const &rhs) :
126 name (rhs.name), key (rhs.name), installed_from (),
127 categories (rhs.categories), versions (rhs.versions),
128 installed (rhs.installed), prev (rhs.prev),
129 prevtimestamp (rhs.prevtimestamp), curr (rhs.curr),
130 currtimestamp (rhs.currtimestamp), exp (rhs.exp),
131 exptimestamp (rhs.exptimestamp), desired (rhs.desired),
132 architecture (rhs.architecture), priority (rhs.priority)
133 {
134
135 }
136
137 packagemeta::_actions & packagemeta::_actions::operator++ ()
138 {
139 ++_value;
140 if (_value > 3)
141 _value = 0;
142 return *this;
143 }
144
145 template<class T> struct removeCategory : public unary_function<T, void>
146 {
147 removeCategory(packagemeta *pkg) : _pkg (pkg) {}
148 void operator() (T x)
149 {
150 vector <packagemeta *> &aList = packagedb::categories[x];
151 aList.erase (find (aList.begin(), aList.end(), _pkg));
152 }
153 packagemeta *_pkg;
154 };
155
156
157 packagemeta::~packagemeta()
158 {
159 for_each (categories.begin (), categories.end (), removeCategory<String> (this));
160 categories.clear ();
161 versions.clear ();
162 }
163
164 void
165 packagemeta::add_version (packageversion & thepkg)
166 {
167 /* todo: check return value */
168 versions.insert (thepkg);
169 }
170
171 /* assumption: package thepkg is already in the metadata list. */
172 void
173 packagemeta::set_installed (packageversion & thepkg)
174 {
175 set<packageversion>::const_iterator temp = versions.find (thepkg);
176 if (temp != versions.end())
177 installed = thepkg;
178 }
179
180 /* uninstall a package if it's installed */
181 void
182 packagemeta::uninstall ()
183 {
184 if (installed)
185 {
186 /* this will need to be pushed down to the version, or even the source level
187 * to allow differences between formats to be seamlessly managed
188 * but for now: here is ok
189 */
190 hash dirs;
191 String line = installed.getfirstfile ();
192
193 try_run_script ("/etc/preremove/", name);
194 while (line.size())
195 {
196 dirs.add_subdirs (line);
197
198 String d = cygpath (String ("/") + line);
199 DWORD dw = GetFileAttributes (d.cstr_oneuse());
200 if (dw != INVALID_FILE_ATTRIBUTES
201 && !(dw & FILE_ATTRIBUTE_DIRECTORY))
202 {
203 log (LOG_BABBLE) << "unlink " << d << endLog;
204 SetFileAttributes (d.cstr_oneuse(), dw & ~FILE_ATTRIBUTE_READONLY);
205 DeleteFile (d.cstr_oneuse());
206 }
207 /* Check for Windows shortcut of same name. */
208 d += ".lnk";
209 dw = GetFileAttributes (d.cstr_oneuse());
210 if (dw != INVALID_FILE_ATTRIBUTES
211 && !(dw & FILE_ATTRIBUTE_DIRECTORY))
212 {
213 log (LOG_BABBLE) << "unlink " << d << endLog;
214 SetFileAttributes (d.cstr_oneuse(),
215 dw & ~FILE_ATTRIBUTE_READONLY);
216 DeleteFile (d.cstr_oneuse());
217 }
218 line = installed.getnextfile ();
219 }
220 installed.uninstall ();
221
222 dirs.reverse_sort ();
223 char *subdir = 0;
224 while ((subdir = dirs.enumerate (subdir)) != 0)
225 {
226 String d = cygpath (String ("/") + subdir);
227 if (RemoveDirectory (d.cstr_oneuse()))
228 log (LOG_BABBLE) << "rmdir " << d << endLog;
229 }
230 try_run_script ("/etc/postremove/", name);
231 }
232 installed = packageversion();
233 }
234
235
236 void
237 packagemeta::add_category (String const &cat)
238 {
239 if (categories.find (cat) != categories.end())
240 return;
241 /* add a new record for the package list */
242 packagedb::categories[cat].push_back (this);
243 categories.insert (cat);
244 }
245
246 static bool
247 hasSDesc(packageversion const &pkg)
248 {
249 return pkg.SDesc().size();
250 }
251
252 String const
253 packagemeta::SDesc () const
254 {
255 set<packageversion>::iterator i = find_if (versions.begin(), versions.end(), hasSDesc);
256 if (i == versions.end())
257 return String();
258 return i->SDesc ();
259 };
260
261 /* Return an appropriate caption given the current action. */
262 String
263 packagemeta::action_caption ()
264 {
265 if (!desired && installed)
266 return "Uninstall";
267 else if (!desired)
268 return "Skip";
269 else if (desired == installed && desired.picked())
270 {
271 packagedb db;
272 return db.task == PackageDB_Install ? "Reinstall" : "Retrieve";
273 }
274 else if (desired == installed && desired.sourcePackage() && desired.sourcePackage().picked())
275 /* FIXME: Redo source should come up if the tarball is already present locally */
276 return "Source";
277 else if (desired == installed) /* and neither src nor bin */
278 return "Keep";
279 else
280 return desired.Canonical_version ();
281 }
282
283 /* Set the next action given a current action. */
284 void
285 packagemeta::set_action (packageversion const &default_version)
286 {
287 /* actions are the following:
288
289 for install modes (from net/local)
290 for each version:
291 install this version
292 install the source for this version
293 and a boolean flag - force install to allow reinstallation, or bypassing requirements
294 globally:
295 install the source for the current version.
296
297 to uninstall a package, the desired version is set to NULL;
298
299 for mirroring modes (download only)
300 for each version
301 download this version
302 download source for this version
303
304 these are represented by the following:
305 the desired pointer in the packagemetadata indicated which version we are operating on.
306 if we are operating on the installed version, reinstall is a valid option.
307 for the selected version, forceinstall means Do an install no matter what, and
308 srcpicked means download the source.
309
310 The default action for any installed package is to install the 'curr version'
311 if it is not already installed.
312
313 The default action for any non-installed package is to do nothing.
314
315 To achieve a no-op, set desired==installed, and if (installed) set forceinstall=0 and
316 srcpicked = 0;
317
318 Iteration through versions should follow the following rules:
319 selected radio button (prev/curr/test) (show as reinstall if that is the
320 current version) ->source only (only if the package is installed) ->oldest version....s
321 kip version of radio button...
322 newest version->uninstall->no-op->selected radio button.
323
324 If any state cannot be set (ie because (say) no prev entry exists for a package
325 simply progress to the next option.
326
327 */
328
329 /* We were set to uninstall the package */
330 if (!desired && installed)
331 {
332 /* No-op - keep whatever we've got */
333 desired = installed;
334 if (desired)
335 {
336 desired.pick (false);
337 desired.sourcePackage().pick (false);
338 }
339 return;
340 }
341 else if (desired == installed &&
342 (!installed ||
343 // neither bin nor source are being installed
344 (!(installed.picked() || installed.sourcePackage().picked()) &&
345 // bin or source are available
346 (installed.accessible() || installed.sourcePackage().accessible()) ))
347 )
348 /* Install the default trust version - this is a 'reinstall' for installed
349 * packages */
350 {
351 /* No-op */
352 desired = default_version;
353 if (desired)
354 {
355 if (desired.accessible())
356 desired.pick (true);
357 else
358 desired.sourcePackage().pick (true);
359 return;
360 }
361 }
362 /* are we currently on the radio button selection and installed */
363 if (desired == default_version && installed &&
364 (!desired || desired.picked())
365 && (desired && desired.sourcePackage().accessible())
366 )
367 {
368 /* source only this file */
369 desired = installed;
370 desired.pick (false);
371 desired.sourcePackage().pick (true);
372 return;
373 }
374 /* are we currently on source only or on the radio button but not installed */
375 else if ((desired == installed
376 && installed.sourcePackage().picked ()) || desired == default_version)
377 {
378 /* move onto the loop through versions */
379 set<packageversion>::iterator i = versions.begin();
380 if (*i == default_version)
381 ++i;
382 if (i != versions.end())
383 {
384 desired = *i;
385 desired.pick (desired.accessible());
386 desired.sourcePackage ().pick (false);
387 }
388 else
389 desired = packageversion ();
390 return;
391 }
392 else
393 {
394 /* preserve the src tick box */
395 bool sourceticked = desired.sourcePackage().picked();
396 /* bump the version selected, skipping the radio button trust along the way */
397 set<packageversion>::iterator i;
398 for (i=versions.begin(); i != versions.end() && *i != desired; ++i);
399 /* i points at desired in the versions set */
400 ++i;
401 if (i != versions.end ())
402 {
403 if (default_version == *i)
404 ++i;
405 if (i != versions.end ())
406 {
407 desired = *i;
408 if (desired.sourcePackage().accessible ())
409 desired.sourcePackage ().pick (sourceticked);
410 else
411 desired.sourcePackage ().pick (false);
412 return;
413 }
414 }
415 /* went past the end - uninstall the package */
416 desired = packageversion ();
417 }
418 }
419
420 int
421 packagemeta::set_requirements (trusts deftrust, size_t depth)
422 {
423 int changed = 0;
424 /* handle build-depends */
425 if (depth == 0 && desired.sourcePackage ().picked())
426 changed += desired.sourcePackage ().set_requirements (deftrust, depth + 1);
427 if (!desired || (desired != installed && !desired.picked ()))
428 /* uninstall || source only */
429 return changed;
430
431 return changed + desired.set_requirements (deftrust, depth);
432 }
433
434
435 // Set a particular type of action.
436 void
437 packagemeta::set_action (_actions action, packageversion const &default_version)
438 {
439 if (action == Default_action)
440 {
441 if (installed
442 || categories.find ("Base") != categories.end ()
443 || categories.find ("Misc") != categories.end ())
444 {
445 desired = default_version;
446 if (desired)
447 {
448 desired.pick (desired == installed);
449 desired.sourcePackage ().pick (false);
450 }
451 }
452 else
453 desired = packageversion ();
454 return;
455 }
456 else if (action == Install_action)
457 {
458 desired = default_version;
459 if (desired)
460 {
461 if (desired != installed)
462 if (desired.accessible ())
463 {
464 desired.pick (true);
465 desired.sourcePackage ().pick (false);
466 }
467 else
468 {
469 desired.pick (false);
470 desired.sourcePackage ().pick (true);
471 }
472 else
473 {
474 desired.pick (false);
475 desired.sourcePackage ().pick (false);
476 }
477 }
478 return;
479 }
480 else if (action == Reinstall_action)
481 {
482 desired = installed;
483 if (desired)
484 {
485 desired.pick (true);
486 desired.sourcePackage ().pick (false);
487 }
488 }
489 else if (action == Uninstall_action)
490 {
491 desired = packageversion ();
492 }
493 }
494
495 bool
496 packagemeta::accessible () const
497 {
498 for (set<packageversion>::iterator i=versions.begin();
499 i != versions.end(); ++i)
500 if (i->accessible())
501 return true;
502 return false;
503 }
504
505 bool
506 packagemeta::sourceAccessible () const
507 {
508 for (set<packageversion>::iterator i=versions.begin();
509 i != versions.end(); ++i)
510 {
511 packageversion bin=*i;
512 if (bin.sourcePackage().accessible())
513 return true;
514 }
515 return false;
516 }
This page took 0.060103 seconds and 5 git commands to generate.