]> cygwin.com Git - cygwin-apps/setup.git/blob - package_db.cc
2003-03-16 Robert Collins <rbtcollins@hotmail.com>
[cygwin-apps/setup.git] / package_db.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 /* 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
22 static const char *cvsid =
23 "\n%%% $Id$\n";
24 #endif
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <strings.h>
29 #if HAVE_ERRNO_H
30 #include <errno.h>
31 #endif
32
33 #include "io_stream.h"
34 #include "compress.h"
35
36 #include "filemanip.h"
37
38 #include "package_version.h"
39 #include "cygpackage.h"
40 #include "package_db.h"
41 #include "package_meta.h"
42 #include "Exception.h"
43
44 using namespace std;
45
46 /* static members */
47
48 packagedb::packagedb ()
49 {
50 io_stream *db = 0;
51 if (!installeddbread)
52 {
53 /* no parameters. Read in the local installation database. */
54 db = io_stream::open ("cygfile:///etc/setup/installed.db", "rt");
55 if (!db)
56 return;
57 /* flush_local_db_package_data */
58 char line[1000], pkgname[1000], inst[1000], src[1000];
59 int instsz, srcsz;
60
61 if (db->gets (line, 1000))
62 {
63 int dbver;
64 sscanf (line, "%s %d", pkgname, &instsz);
65 if (!strcasecmp (pkgname, "INSTALLED.DB") && instsz == 2)
66 dbver = 2;
67 else
68 dbver = 1;
69 delete db;
70 db = 0;
71 /* Later versions may not use installed.db other than to record the version. */
72 if (dbver == 1 || dbver == 2)
73 {
74 db =
75 io_stream::open ("cygfile:///etc/setup/installed.db", "rt");
76 if (dbver == 2)
77 db->gets (line, 1000);
78 while (db->gets (line, 1000))
79 {
80 int parseable;
81 src[0] = '\0';
82 pkgname[0] = '\0';
83 inst[0] = '\0';
84 srcsz = 0;
85 instsz = 0;
86
87 sscanf (line, "%s %s %d %s %d", pkgname, inst, &instsz, src,
88 &srcsz);
89
90 if (pkgname[0] == '\0' || inst[0] == '\0')
91 continue;
92
93 fileparse f;
94 parseable = parse_filename (inst, f);
95 if (!parseable)
96 continue;
97
98 packagemeta *pkg = findBinary (PackageSpecification(pkgname));
99 if (!pkg)
100 {
101 pkg = new packagemeta (pkgname, inst);
102 packages.push_back (pkg);
103 /* we should install a new handler then not check this...
104 */
105 //if (!pkg)
106 //die badly
107 }
108
109 packageversion binary =
110 cygpackage::createInstance (pkgname, inst, instsz, f.ver,
111 package_installed,
112 package_binary);
113
114 pkg->add_version (binary);
115 pkg->set_installed (binary);
116 pkg->desired = pkg->installed;
117 }
118 delete db;
119 db = 0;
120 }
121 else
122 // unknown dbversion
123 exit (1);
124 }
125 installeddbread = 1;
126 }
127 }
128
129 int
130 packagedb::flush ()
131 {
132 /* naive approach - just dump the lot */
133 char const *odbn = "cygfile:///etc/setup/installed.db";
134 char const *ndbn = "cygfile:///etc/setup/installed.db.new";
135
136 io_stream::mkpath_p (PATH_TO_FILE, ndbn);
137
138 io_stream *ndb = io_stream::open (ndbn, "wb");
139
140 // XXX if this failed, try removing any existing .new database?
141 if (!ndb)
142 return errno ? errno : 1;
143
144 ndb->write ("INSTALLED.DB 2\n", strlen ("INSTALLED.DB 2\n"));
145 for (vector <packagemeta *>::iterator i = packages.begin ();
146 i != packages.end (); ++i)
147 {
148 packagemeta & pkgm = **i;
149 if (pkgm.installed)
150 {
151 /* size here is irrelevant - as we can assume that this install source
152 * no longer exists, and it does not correlate to used disk space
153 * also note that we are writing a fictional install source
154 * to keep cygcheck happy.
155 */
156 String line;
157 line = pkgm.name + " " + pkgm.name + "-" +
158 pkgm.installed.Canonical_version () + ".tar.bz2 0\n";
159 ndb->write (line.cstr_oneuse(), line.size());
160 }
161 }
162
163 delete ndb;
164
165 io_stream::remove (odbn);
166
167 if (io_stream::move (ndbn, odbn))
168 return errno ? errno : 1;
169 return 0;
170 }
171
172 packagemeta *
173 packagedb::findBinary (PackageSpecification const &spec) const
174 {
175 for (vector <packagemeta *>::iterator n = packages.begin ();
176 n != packages.end (); ++n)
177 {
178 packagemeta & pkgm = **n;
179 for (set<packageversion>::iterator i=pkgm.versions.begin();
180 i != pkgm.versions.end(); ++i)
181 if (spec.satisfies (*i))
182 return &pkgm;
183 }
184 return NULL;
185 }
186
187 packagemeta *
188 packagedb::findSource (PackageSpecification const &spec) const
189 {
190 for (vector <packagemeta *>::iterator n=sourcePackages.begin();
191 n != sourcePackages.end(); ++n)
192 {
193 for (set<packageversion>::iterator i = (*n)->versions.begin();
194 i != (*n)->versions.end(); ++i)
195 if (spec.satisfies (*i))
196 return *n;
197 }
198 return NULL;
199 }
200
201 int
202 packagedb::installeddbread =
203 0;
204 vector < packagemeta * > packagedb::packages;
205 packagedb::categoriesType
206 packagedb::categories;
207 vector <packagemeta *> packagedb::sourcePackages;
208 PackageDBActions
209 packagedb::task =
210 PackageDB_Install;
211 std::vector <packagemeta *>
212 packagedb::dependencyOrderedPackages;
213
214 #include "LogSingleton.h"
215 #include <stack>
216
217 class
218 ConnectedLoopFinder
219 {
220 public:
221 ConnectedLoopFinder();
222 void doIt();
223 packagedb db;
224 size_t visited;
225 std::vector<size_t> visitOrder;
226 size_t visit (size_t const nodeToVisit);
227 std::stack<size_t> nodesInStronglyConnectedComponent;
228 };
229
230 ConnectedLoopFinder::ConnectedLoopFinder() : visited(0)
231 {
232 for (size_t counter = 0; counter < db.packages.size(); ++counter)
233 visitOrder.push_back(0);
234 }
235
236 void
237 ConnectedLoopFinder::doIt()
238 {
239 /* XXX this could be done useing a class to hold both the visitedInIteration and the package
240 * meta reference. Then we could use a range, not an int loop.
241 */
242 for (size_t i = 0; i < db.packages.size(); ++i)
243 {
244 packagemeta &pkg (*db.packages[i]);
245 if (pkg.installed && ! visitOrder[i])
246 visit (i);
247 }
248 log (LOG_PLAIN) << "Visited: " << visited << " nodes out of " << db.packages.size() << "." << endLog;
249 }
250
251 static bool
252 checkForInstalled (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
266 size_t
267 ConnectedLoopFinder::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;
293 while (nodeJustVisited < db.packages.size() && db.packages[nodeJustVisited]->name.casecompare((*i)->packageName()))
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
327 PackageDBConnectedIterator
328 packagedb::connectedBegin()
329 {
330 if (!dependencyOrderedPackages.size())
331 {
332 ConnectedLoopFinder doMe;
333 doMe.doIt();
334 log(LOG_PLAIN) << "Dependency ordered install:" << endLog;
335 for (std::vector<packagemeta *>::iterator i = dependencyOrderedPackages.begin();
336 i != dependencyOrderedPackages.end(); ++i)
337 {
338 packagemeta &pkg (**i);
339 log(LOG_PLAIN) << pkg.name << endLog;
340 }
341 }
342
343 return dependencyOrderedPackages.begin();
344 }
345
346 PackageDBConnectedIterator
347 packagedb::connectedEnd()
348 {
349 return dependencyOrderedPackages.end();
350 }
This page took 0.045823 seconds and 5 git commands to generate.