]> cygwin.com Git - cygwin-apps/setup.git/blob - package_meta.cc
2002-07-02 Robert Collins <rbtcollins@hotmail.com>
[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 static const char *standard_dirs[] = {
48 "bin",
49 "etc",
50 "lib",
51 "tmp",
52 "usr",
53 "usr/bin",
54 "usr/lib",
55 "usr/src",
56 "usr/local",
57 "usr/local/bin",
58 "usr/local/etc",
59 "usr/local/lib",
60 "usr/tmp",
61 "var/run",
62 "var/tmp",
63 0
64 };
65
66 void
67 hash::add_subdirs (String const &tpath)
68 {
69 char *nonp, *pp;
70 char *path = tpath.cstr();
71 for (nonp = path; *nonp == '\\' || *nonp == '/'; nonp++);
72 for (pp = path + strlen (path) - 1; pp > nonp; pp--)
73 if (*pp == '/' || *pp == '\\')
74 {
75 int i, s = 0;
76 char c = *pp;
77 *pp = 0;
78 for (i = 0; standard_dirs[i]; i++)
79 if (strcmp (standard_dirs[i] + 1, path) == 0)
80 {
81 s = 1;
82 break;
83 }
84 if (s == 0)
85 add (path);
86 *pp = c;
87 }
88 }
89
90 /*****************/
91
92 CategoryPackage::~CategoryPackage()
93 {
94 CategoryPackage **temp = &key.packages;
95 while (*temp != this)
96 temp = &((*temp)->next);
97 *temp = next;
98 }
99
100 /*****************/
101
102 const
103 packagemeta::_actions
104 packagemeta::Default_action (0);
105 const
106 packagemeta::_actions
107 packagemeta::Install_action (1);
108 const
109 packagemeta::_actions
110 packagemeta::Reinstall_action (2);
111 const
112 packagemeta::_actions
113 packagemeta::Uninstall_action (3);
114
115 char const *
116 packagemeta::_actions::caption ()
117 {
118 switch (_value)
119 {
120 case 0:
121 return "Default";
122 case 1:
123 return "Install";
124 case 2:
125 return "Reinstall";
126 case 3:
127 return "Uninstall";
128 }
129 // Pacify GCC: (all case options are checked above)
130 return 0;
131 }
132
133 packagemeta::packagemeta (packagemeta const &rhs) :
134 name (rhs.name), key (rhs.name), installed_from (),
135 Categories(rhs.Categories), versions (rhs.versions),
136 installed (rhs.installed), prev (rhs.prev),
137 prevtimestamp (rhs.prevtimestamp), curr (rhs.curr),
138 currtimestamp (rhs.currtimestamp), exp (rhs.exp),
139 exptimestamp (rhs.exptimestamp), desired (rhs.desired),
140 architecture (rhs.architecture), priority (rhs.priority)
141 {
142
143 }
144
145 packagemeta::_actions & packagemeta::_actions::operator++ ()
146 {
147 ++_value;
148 if (_value > 3)
149 _value = 0;
150 return *this;
151 }
152
153 packagemeta::~packagemeta()
154 {
155 while (Categories.number ())
156 {
157 CategoryPackage *catpkg = Categories.removebyindex (1);
158 delete catpkg;
159 }
160 versions.clear();
161 }
162
163 void
164 packagemeta::add_version (packageversion & thepkg)
165 {
166 /* todo: check return value */
167 versions.insert (thepkg);
168 }
169
170 /* assumption: package thepkg is already in the metadata list. */
171 void
172 packagemeta::set_installed (packageversion & thepkg)
173 {
174 set<packageversion>::const_iterator temp = versions.find (thepkg);
175 if (temp != versions.end())
176 installed = thepkg;
177 }
178
179 /* uninstall a package if it's installed */
180 void
181 packagemeta::uninstall ()
182 {
183 if (installed)
184 {
185 /* this will need to be pushed down to the version, or even the source level
186 * to allow differences between formats to be seamlessly managed
187 * but for now: here is ok
188 */
189 hash dirs;
190 String line = installed.getfirstfile ();
191
192 try_run_script ("/etc/preremove/", name);
193 while (line.size())
194 {
195 dirs.add_subdirs (line);
196
197 String d = cygpath (String ("/") + line);
198 DWORD dw = GetFileAttributes (d.cstr_oneuse());
199 if (dw != INVALID_FILE_ATTRIBUTES
200 && !(dw & FILE_ATTRIBUTE_DIRECTORY))
201 {
202 log (LOG_BABBLE) << "unlink " << d << endLog;
203 SetFileAttributes (d.cstr_oneuse(), dw & ~FILE_ATTRIBUTE_READONLY);
204 DeleteFile (d.cstr_oneuse());
205 }
206 /* Check for Windows shortcut of same name. */
207 d += ".lnk";
208 dw = GetFileAttributes (d.cstr_oneuse());
209 if (dw != INVALID_FILE_ATTRIBUTES
210 && !(dw & FILE_ATTRIBUTE_DIRECTORY))
211 {
212 log (LOG_BABBLE) << "unlink " << d << endLog;
213 SetFileAttributes (d.cstr_oneuse(),
214 dw & ~FILE_ATTRIBUTE_READONLY);
215 DeleteFile (d.cstr_oneuse());
216 }
217 line = installed.getnextfile ();
218 }
219 installed.uninstall ();
220
221 dirs.reverse_sort ();
222 char *subdir = 0;
223 while ((subdir = dirs.enumerate (subdir)) != 0)
224 {
225 String d = cygpath (String ("/") + subdir);
226 if (RemoveDirectory (d.cstr_oneuse()))
227 log (LOG_BABBLE) << "rmdir " << d << endLog;
228 }
229 try_run_script ("/etc/postremove/", name);
230 }
231 installed = packageversion();
232 }
233
234
235 void
236 packagemeta::add_category (Category & cat)
237 {
238 /* add a new record for the package list */
239 CategoryPackage & catpack = Categories.registerbykey (cat);
240 catpack.pkg = this;
241 }
242
243 static bool
244 hasSDesc(packageversion const &pkg)
245 {
246 return pkg.SDesc().size();
247 }
248
249 String const
250 packagemeta::SDesc () const
251 {
252 set<packageversion>::iterator i = find_if (versions.begin(), versions.end(), hasSDesc);
253 if (i == versions.end())
254 return String();
255 return i->SDesc ();
256 };
257
258 /* Return an appropriate caption given the current action. */
259 String
260 packagemeta::action_caption ()
261 {
262 if (!desired && installed)
263 return "Uninstall";
264 else if (!desired)
265 return "Skip";
266 else if (desired == installed && desired.picked())
267 {
268 packagedb db;
269 return db.task == PackageDB_Install ? "Reinstall" : "Retrieve";
270 }
271 else if (desired == installed && desired.sourcePackage() && desired.sourcePackage().picked())
272 /* FIXME: Redo source should come up if the tarball is already present locally */
273 return "Source";
274 else if (desired == installed) /* and neither src nor bin */
275 return "Keep";
276 else
277 return desired.Canonical_version ();
278 }
279
280 /* Set the next action given a current action. */
281 void
282 packagemeta::set_action (packageversion const &default_version)
283 {
284 /* actions are the following:
285
286 for install modes (from net/local)
287 for each version:
288 install this version
289 install the source for this version
290 and a boolean flag - force install to allow reinstallation, or bypassing requirements
291 globally:
292 install the source for the current version.
293
294 to uninstall a package, the desired version is set to NULL;
295
296 for mirroring modes (download only)
297 for each version
298 download this version
299 download source for this version
300
301 these are represented by the following:
302 the desired pointer in the packagemetadata indicated which version we are operating on.
303 if we are operating on the installed version, reinstall is a valid option.
304 for the selected version, forceinstall means Do an install no matter what, and
305 srcpicked means download the source.
306
307 The default action for any installed package is to install the 'curr version'
308 if it is not already installed.
309
310 The default action for any non-installed package is to do nothing.
311
312 To achieve a no-op, set desired==installed, and if (installed) set forceinstall=0 and
313 srcpicked = 0;
314
315 Iteration through versions should follow the following rules:
316 selected radio button (prev/curr/test) (show as reinstall if that is the
317 current version) ->source only (only if the package is installed) ->oldest version....s
318 kip version of radio button...
319 newest version->uninstall->no-op->selected radio button.
320
321 If any state cannot be set (ie because (say) no prev entry exists for a package
322 simply progress to the next option.
323
324 */
325
326 /* We were set to uninstall the package */
327 if (!desired && installed)
328 {
329 /* No-op - keep whatever we've got */
330 desired = installed;
331 if (desired)
332 {
333 desired.pick (false);
334 desired.sourcePackage().pick (false);
335 }
336 return;
337 }
338 else if (desired == installed &&
339 (!installed ||
340 // neither bin nor source are being installed
341 (!(installed.picked() || installed.sourcePackage().picked()) &&
342 // bin or source are available
343 (installed.accessible() || installed.sourcePackage().accessible()) ))
344 )
345 /* Install the default trust version - this is a 'reinstall' for installed
346 * packages */
347 {
348 /* No-op */
349 desired = default_version;
350 if (desired)
351 {
352 if (desired.accessible())
353 desired.pick (true);
354 else
355 desired.sourcePackage().pick (true);
356 return;
357 }
358 }
359 /* are we currently on the radio button selection and installed */
360 if (desired == default_version && installed &&
361 (!desired || desired.picked())
362 && (desired && desired.sourcePackage().accessible())
363 )
364 {
365 /* source only this file */
366 desired = installed;
367 desired.pick (false);
368 desired.sourcePackage().pick (true);
369 return;
370 }
371 /* are we currently on source only or on the radio button but not installed */
372 else if ((desired == installed
373 && installed.sourcePackage().picked ()) || desired == default_version)
374 {
375 /* move onto the loop through versions */
376 set<packageversion>::iterator i = versions.begin();
377 if (*i == default_version)
378 ++i;
379 if (i != versions.end())
380 {
381 desired = *i;
382 desired.pick (true);
383 desired.sourcePackage ().pick (false);
384 }
385 else
386 desired = packageversion ();
387 return;
388 }
389 else
390 {
391 /* preserve the src tick box */
392 bool sourceticked = desired.sourcePackage().picked();
393 /* bump the version selected, skipping the radio button trust along the way */
394 set<packageversion>::iterator i;
395 for (i=versions.begin(); i != versions.end() && *i != desired; ++i);
396 /* i points at desired in the versions set */
397 ++i;
398 if (i != versions.end ())
399 {
400 if (default_version == *i)
401 ++i;
402 if (i != versions.end ())
403 {
404 desired = *i;
405 if (desired.sourcePackage().accessible ())
406 desired.sourcePackage ().pick (sourceticked);
407 else
408 desired.sourcePackage ().pick (false);
409 return;
410 }
411 }
412 /* went past the end - uninstall the package */
413 desired = packageversion ();
414 }
415 }
416
417 static bool
418 checkForInstalled (PackageSpecification *spec)
419 {
420 packagedb db;
421 packagemeta *required = db.findBinary (*spec);
422 if (!required)
423 return false;
424 if (spec->satisfies (required->installed))
425 /* done, found a satisfactory installed version */
426 return true;
427 return false;
428 }
429
430 static bool
431 checkForUpgradeable (PackageSpecification *spec)
432 {
433 packagedb db;
434 packagemeta *required = db.findBinary (*spec);
435 if (!required || !required->installed)
436 return false;
437 for (set <packageversion>::iterator i = required->versions.begin();
438 i != required->versions.end(); ++i)
439 if (spec->satisfies (*i))
440 return true;
441 return false;
442 }
443
444 static bool
445 checkForSatisfiable (PackageSpecification *spec)
446 {
447 packagedb db;
448 packagemeta *required = db.findBinary (*spec);
449 if (!required)
450 return false;
451 for (set <packageversion>::iterator i = required->versions.begin();
452 i != required->versions.end(); ++i)
453 if (spec->satisfies (*i))
454 return true;
455 return false;
456 }
457
458 int
459 packagemeta::set_requirements (trusts deftrust = TRUST_CURR, size_t depth = 0)
460 {
461 int changed = 0;
462 if (!desired || (desired != installed && !desired.picked ()))
463 /* uninstall || source only */
464 return 0;
465
466 vector <vector <PackageSpecification *> *>::iterator dp = desired.depends ()->begin();
467 // packagedb db;
468 /* cheap test for too much recursion */
469 if (depth > 5)
470 return 0;
471 /* walk through each and clause */
472 while (dp != desired.depends ()->end())
473 {
474 /* three step:
475 1) is a satisfactory or clause installed?
476 2) is an unsatisfactory version of an or clause which has
477 a satisfactory version available installed?
478 3) is a satisfactory package available?
479 */
480 /* check each or clause for an installed match */
481 vector <PackageSpecification *>::iterator i =
482 find_if ((*dp)->begin(), (*dp)->end(), checkForInstalled);
483 if (i != (*dp)->end())
484 {
485 /* we found an installed ok package */
486 /* todo: ensure that it will remain installed */
487 /* next and clause */
488 ++dp;
489 continue;
490 }
491 /* check each or clause for an upgradeable version */
492 i = find_if ((*dp)->begin(), (*dp)->end(), checkForUpgradeable);
493 if (i != (*dp)->end())
494 {
495 /* we found a package that can be up/downgraded to meet the
496 requirement
497 */
498 /* TODO: set an appropriate desired version */
499 ++dp;
500 ++changed;
501 continue;
502 }
503 /* check each or clause for an installable version */
504 i = find_if ((*dp)->begin(), (*dp)->end(), checkForSatisfiable);
505 if (i != (*dp)->end())
506 {
507 /* we found a package that can be installed to meet the
508 requirement
509 */
510 /* TODO: set an appropriate desired version */
511 ++dp;
512 ++changed;
513 continue;
514 }
515 #if 0
516 // version updating and installing code, once the package is selected
517 if (!required->desired)
518 {
519 /* it's set to uninstall */
520 required->set_action (required->trustp (deftrust));
521 }
522 else if (required->desired != required->installed
523 && !required->desired.picked())
524 {
525 /* it's set to change to a different version source only */
526 required->desired.pick (true);
527 }
528 /* does this requirement have requirements? */
529 changed += required->set_requirements (deftrust, depth + 1);
530 #endif
531 ++dp;
532 }
533 return changed;
534 }
535
536
537 // Set a particular type of action.
538 void
539 packagemeta::set_action (_actions action, packageversion const &default_version)
540 {
541 packagedb db;
542 if (action == Default_action)
543 {
544 // XXX fix the list use to allow const ref usage.
545 Category tempCategory("Misc");
546 if (installed
547 || Categories.getbykey (db.categories.registerbykey ("Base"))
548 || Categories.getbykey (tempCategory))
549 {
550 desired = default_version;
551 if (desired)
552 {
553 desired.pick (desired == installed);
554 desired.sourcePackage ().pick (false);
555 }
556 }
557 else
558 desired = packageversion ();
559 return;
560 }
561 else if (action == Install_action)
562 {
563 desired = default_version;
564 if (desired)
565 {
566 if (desired != installed)
567 if (desired.source()->sites.number())
568 {
569 desired.pick (true);
570 desired.sourcePackage ().pick (false);
571 }
572 else
573 {
574 desired.pick (false);
575 desired.sourcePackage ().pick (true);
576 }
577 else
578 {
579 desired.pick (false);
580 desired.sourcePackage ().pick (false);
581 }
582 }
583 return;
584 }
585 else if (action == Reinstall_action)
586 {
587 desired = installed;
588 if (desired)
589 {
590 desired.pick (true);
591 desired.sourcePackage ().pick (false);
592 }
593 }
594 else if (action == Uninstall_action)
595 {
596 desired = packageversion ();
597 }
598 }
599
600 bool
601 packagemeta::accessible () const
602 {
603 for (set<packageversion>::iterator i=versions.begin();
604 i != versions.end(); ++i)
605 if (i->accessible())
606 return true;
607 return false;
608 }
609
610 bool
611 packagemeta::sourceAccessible () const
612 {
613 for (set<packageversion>::iterator i=versions.begin();
614 i != versions.end(); ++i)
615 {
616 packageversion bin=*i;
617 if (bin.sourcePackage().accessible())
618 return true;
619 }
620 return false;
621 }
This page took 0.112314 seconds and 5 git commands to generate.