]> cygwin.com Git - cygwin-apps/setup.git/blame - package_db.cc
* CHANGES: Update.
[cygwin-apps/setup.git] / package_db.cc
CommitLineData
7939f6d1 1/*
31f0ccce 2 * Copyright (c) 2001, 2003 Robert Collins.
7939f6d1
RC
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/* this is the package database class.
17 * It lists all known packages, including custom ones, ones from a mirror and
18 * installed ones.
19 */
20
21#if 0
22static const char *cvsid =
23 "\n%%% $Id$\n";
24#endif
7939f6d1
RC
25#include <stdio.h>
26#include <stdlib.h>
27#include <unistd.h>
28#include <strings.h>
45e01f23
RC
29#if HAVE_ERRNO_H
30#include <errno.h>
31#endif
7939f6d1
RC
32
33#include "io_stream.h"
34#include "compress.h"
35
36#include "filemanip.h"
37
fa0c0d10 38#include "package_version.h"
7939f6d1
RC
39#include "cygpackage.h"
40#include "package_db.h"
41#include "package_meta.h"
ad646f43 42#include "Exception.h"
2f18f94d 43#include "Generic.h"
7939f6d1 44
6625e635
RC
45using namespace std;
46
7939f6d1
RC
47/* static members */
48
7939f6d1
RC
49packagedb::packagedb ()
50{
df62e023 51 io_stream *db = 0;
7939f6d1
RC
52 if (!installeddbread)
53 {
54 /* no parameters. Read in the local installation database. */
55 db = io_stream::open ("cygfile:///etc/setup/installed.db", "rt");
82573872 56 installeddbread = 1;
7939f6d1
RC
57 if (!db)
58 return;
59 /* flush_local_db_package_data */
0ec7cbc9
BD
60 char line[1000], pkgname[1000], inst[1000];
61 int instsz;
7939f6d1 62
24cbae7f
RC
63 if (db->gets (line, 1000))
64 {
65 int dbver;
66 sscanf (line, "%s %d", pkgname, &instsz);
67 if (!strcasecmp (pkgname, "INSTALLED.DB") && instsz == 2)
68 dbver = 2;
69 else
70 dbver = 1;
71 delete db;
fa0c0d10 72 db = 0;
24cbae7f
RC
73 /* Later versions may not use installed.db other than to record the version. */
74 if (dbver == 1 || dbver == 2)
7939f6d1 75 {
24cbae7f
RC
76 db =
77 io_stream::open ("cygfile:///etc/setup/installed.db", "rt");
78 if (dbver == 2)
79 db->gets (line, 1000);
80 while (db->gets (line, 1000))
7939f6d1 81 {
24cbae7f 82 int parseable;
0ec7cbc9 83 int ign;
4e868a01
RC
84 pkgname[0] = '\0';
85 inst[0] = '\0';
4e868a01 86
0ec7cbc9 87 sscanf (line, "%s %s %d", pkgname, inst, &ign);
4e868a01
RC
88
89 if (pkgname[0] == '\0' || inst[0] == '\0')
90 continue;
91
24cbae7f
RC
92 fileparse f;
93 parseable = parse_filename (inst, f);
94 if (!parseable)
95 continue;
96
cfae3b8d 97 packagemeta *pkg = findBinary (PackageSpecification(pkgname));
24cbae7f
RC
98 if (!pkg)
99 {
fa0c0d10 100 pkg = new packagemeta (pkgname, inst);
cfae3b8d 101 packages.push_back (pkg);
24cbae7f
RC
102 /* we should install a new handler then not check this...
103 */
104 //if (!pkg)
105 //die badly
106 }
107
3c196821 108 packageversion binary =
0ec7cbc9 109 cygpackage::createInstance (pkgname, inst, f.ver,
3c196821
RC
110 package_installed,
111 package_binary);
24cbae7f 112
3c196821
RC
113 pkg->add_version (binary);
114 pkg->set_installed (binary);
bb849dbd 115 pkg->desired = pkg->installed;
7939f6d1 116 }
24cbae7f 117 delete db;
fa0c0d10 118 db = 0;
24cbae7f
RC
119 }
120 else
121 // unknown dbversion
122 exit (1);
7939f6d1 123 }
7939f6d1 124 }
7939f6d1
RC
125}
126
7c7034e8
RC
127int
128packagedb::flush ()
129{
130 /* naive approach - just dump the lot */
131 char const *odbn = "cygfile:///etc/setup/installed.db";
132 char const *ndbn = "cygfile:///etc/setup/installed.db.new";
133
134 io_stream::mkpath_p (PATH_TO_FILE, ndbn);
135
136 io_stream *ndb = io_stream::open (ndbn, "wb");
137
3c054baf 138 // XXX if this failed, try removing any existing .new database?
7c7034e8
RC
139 if (!ndb)
140 return errno ? errno : 1;
141
142 ndb->write ("INSTALLED.DB 2\n", strlen ("INSTALLED.DB 2\n"));
cfae3b8d
RC
143 for (vector <packagemeta *>::iterator i = packages.begin ();
144 i != packages.end (); ++i)
7c7034e8 145 {
cfae3b8d 146 packagemeta & pkgm = **i;
df62e023
RC
147 if (pkgm.installed)
148 {
df62e023
RC
149 /* size here is irrelevant - as we can assume that this install source
150 * no longer exists, and it does not correlate to used disk space
151 * also note that we are writing a fictional install source
152 * to keep cygcheck happy.
153 */
d19f12fd 154 std::string line;
3c196821 155 line = pkgm.name + " " + pkgm.name + "-" +
1eb2461a 156 std::string(pkgm.installed.Canonical_version()) + ".tar.bz2 0\n";
d2a3615c 157 ndb->write (line.c_str(), line.size());
df62e023 158 }
7c7034e8
RC
159 }
160
161 delete ndb;
162
163 io_stream::remove (odbn);
164
165 if (io_stream::move (ndbn, odbn))
166 return errno ? errno : 1;
167 return 0;
168}
169
3c196821
RC
170packagemeta *
171packagedb::findBinary (PackageSpecification const &spec) const
172{
cfae3b8d
RC
173 for (vector <packagemeta *>::iterator n = packages.begin ();
174 n != packages.end (); ++n)
3c196821 175 {
cfae3b8d 176 packagemeta & pkgm = **n;
3c196821
RC
177 for (set<packageversion>::iterator i=pkgm.versions.begin();
178 i != pkgm.versions.end(); ++i)
179 if (spec.satisfies (*i))
180 return &pkgm;
181 }
182 return NULL;
183}
184
185packagemeta *
186packagedb::findSource (PackageSpecification const &spec) const
187{
188 for (vector <packagemeta *>::iterator n=sourcePackages.begin();
189 n != sourcePackages.end(); ++n)
190 {
191 for (set<packageversion>::iterator i = (*n)->versions.begin();
192 i != (*n)->versions.end(); ++i)
193 if (spec.satisfies (*i))
194 return *n;
195 }
196 return NULL;
197}
198
df62e023
RC
199int
200 packagedb::installeddbread =
201 0;
cfae3b8d 202vector < packagemeta * > packagedb::packages;
0cf68afd 203packagedb::categoriesType
4fe323f9 204 packagedb::categories;
08cd08c3 205vector <packagemeta *> packagedb::sourcePackages;
df62e023
RC
206PackageDBActions
207 packagedb::task =
208 PackageDB_Install;
ad646f43
RC
209std::vector <packagemeta *>
210packagedb::dependencyOrderedPackages;
211
212#include "LogSingleton.h"
213#include <stack>
214
215class
216ConnectedLoopFinder
217{
218 public:
219 ConnectedLoopFinder();
220 void doIt();
221 packagedb db;
222 size_t visited;
223 std::vector<size_t> visitOrder;
224 size_t visit (size_t const nodeToVisit);
225 std::stack<size_t> nodesInStronglyConnectedComponent;
226};
227
228ConnectedLoopFinder::ConnectedLoopFinder() : visited(0)
229{
230 for (size_t counter = 0; counter < db.packages.size(); ++counter)
231 visitOrder.push_back(0);
232}
233
234void
235ConnectedLoopFinder::doIt()
236{
237 /* XXX this could be done useing a class to hold both the visitedInIteration and the package
238 * meta reference. Then we could use a range, not an int loop.
239 */
240 for (size_t i = 0; i < db.packages.size(); ++i)
241 {
242 packagemeta &pkg (*db.packages[i]);
243 if (pkg.installed && ! visitOrder[i])
244 visit (i);
245 }
92ef6cf8
BD
246 log (LOG_BABBLE) << "Visited: " << visited << " nodes out of "
247 << db.packages.size() << " while creating dependency order."
248 << endLog;
ad646f43
RC
249}
250
251static bool
252checkForInstalled (PackageSpecification *spec)
253{
254 packagedb db;
255 packagemeta *required = db.findBinary (*spec);
256 if (!required)
257 return false;
258 if (spec->satisfies (required->installed)
259 && required->desired == required->installed )
260 /* done, found a satisfactory installed version that will remain
261 installed */
262 return true;
263 return false;
264}
265
266size_t
267ConnectedLoopFinder::visit(size_t const nodeToVisit)
268{
269 if (!db.packages[nodeToVisit]->installed)
270 /* Can't visit this node, and it is not less than any visted node */
271 return db.packages.size() + 1;
272 ++visited;
273 visitOrder[nodeToVisit] = visited;
274
275 size_t minimumVisitId = visited;
276 nodesInStronglyConnectedComponent.push(nodeToVisit);
277
278 vector <vector <PackageSpecification *> *>::iterator dp = db.packages[nodeToVisit]->installed.depends ()->begin();
279 /* walk through each and clause (a link in the graph) */
280 while (dp != db.packages[nodeToVisit]->installed.depends ()->end())
281 {
282 /* check each or clause for an installed match */
283 vector <PackageSpecification *>::iterator i =
284 find_if ((*dp)->begin(), (*dp)->end(), checkForInstalled);
285 if (i != (*dp)->end())
286 {
287 /* we found an installed ok package */
288 /* visit it if needed */
289 /* UGLY. Need to refactor. iterators in the outer would help as we could simply
290 * vist the iterator
291 */
292 size_t nodeJustVisited = 0;
afa76033 293 while (nodeJustVisited < db.packages.size() && casecompare(db.packages[nodeJustVisited]->name, (*i)->packageName()))
ad646f43
RC
294 ++nodeJustVisited;
295 if (nodeJustVisited == db.packages.size())
296 log (LOG_PLAIN) << "Search for package '" << (*i)->packageName() << "' failed." << endLog;
297 else
298 {
299 if (visitOrder[nodeJustVisited])
300 minimumVisitId = std::min (minimumVisitId, visitOrder[nodeJustVisited]);
301 else
302 minimumVisitId = std::min (minimumVisitId, visit (nodeJustVisited));
303 }
304 /* next and clause */
305 ++dp;
306 continue;
307 }
308 /* not installed or not available we ignore */
309 ++dp;
310 }
311
312 if (minimumVisitId == visitOrder[nodeToVisit])
313 {
314 size_t popped;
315 do {
316 popped = nodesInStronglyConnectedComponent.top();
317 nodesInStronglyConnectedComponent.pop();
318 db.dependencyOrderedPackages.push_back(db.packages[popped]);
319 /* mark as displayed in a connected component */
320 visitOrder[popped] = db.packages.size() + 2;
321 } while (popped != nodeToVisit);
322 }
323
324 return minimumVisitId;
325}
326
327PackageDBConnectedIterator
328packagedb::connectedBegin()
329{
330 if (!dependencyOrderedPackages.size())
ad646f43 331 {
92ef6cf8
BD
332 ConnectedLoopFinder doMe;
333 doMe.doIt();
334 std::string s = "Dependency order of packages: ";
335
336 for (std::vector<packagemeta *>::iterator i =
337 dependencyOrderedPackages.begin();
338 i != dependencyOrderedPackages.end(); ++i)
339 s = s + (*i)->name + " ";
340 log (LOG_BABBLE) << s << endLog;
ad646f43 341 }
ad646f43
RC
342 return dependencyOrderedPackages.begin();
343}
344
345PackageDBConnectedIterator
346packagedb::connectedEnd()
347{
348 return dependencyOrderedPackages.end();
349}
8c242540
RC
350
351void
352packagedb::markUnVisited()
353{
354 for (vector <packagemeta *>::iterator n = packages.begin ();
355 n != packages.end (); ++n)
356 {
357 packagemeta & pkgm = **n;
358 pkgm.visited(false);
359 }
360}
31f0ccce
RC
361
362void
363packagedb::setExistence ()
364{
365 /* binary packages */
366 /* Remove packages that are in the db, not installed, and have no
367 mirror info and are not cached for both binary and source packages. */
368 vector <packagemeta *>::iterator i = packages.begin ();
369 while (i != packages.end ())
370 {
371 packagemeta & pkg = **i;
372 if (!pkg.installed && !pkg.accessible() &&
373 !pkg.sourceAccessible() )
374 {
375 packagemeta *pkgm = *i;
376 delete pkgm;
377 i = packages.erase (i);
378 }
379 else
380 ++i;
381 }
382#if 0
383 /* remove any source packages which are not accessible */
384 vector <packagemeta *>::iterator i = db.sourcePackages.begin();
385 while (i != db.sourcePackages.end())
386 {
387 packagemeta & pkg = **i;
388 if (!packageAccessible (pkg))
389 {
390 packagemeta *pkgm = *i;
391 delete pkgm;
392 i = db.sourcePackages.erase (i);
393 }
394 else
395 ++i;
396 }
397#endif
398}
2f18f94d
RC
399
400void
401packagedb::fillMissingCategory ()
402{
403 for_each(packages.begin(), packages.end(), visit_if(mem_fun(&packagemeta::setDefaultCategories), mem_fun(&packagemeta::hasNoCategories)));
404 for_each(packages.begin(), packages.end(), mem_fun(&packagemeta::addToCategoryAll));
405}
406
This page took 0.088784 seconds and 5 git commands to generate.