2 * Copyright (c) 2001, Robert Collins.
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.
9 * A copy of the GNU General Public License can be found at
12 * Written by Robert Collins <rbtcollins@hotmail.com>
16 /* this is the package database class.
17 * It lists all known packages, including custom ones, ones from a mirror and
22 static const char *cvsid
=
33 #include "io_stream.h"
36 #include "filemanip.h"
38 #include "package_version.h"
39 #include "cygpackage.h"
40 #include "package_db.h"
41 #include "package_meta.h"
42 #include "Exception.h"
48 packagedb::packagedb ()
53 /* no parameters. Read in the local installation database. */
54 db
= io_stream::open ("cygfile:///etc/setup/installed.db", "rt");
57 /* flush_local_db_package_data */
58 char line
[1000], pkgname
[1000], inst
[1000], src
[1000];
61 if (db
->gets (line
, 1000))
64 sscanf (line
, "%s %d", pkgname
, &instsz
);
65 if (!strcasecmp (pkgname
, "INSTALLED.DB") && instsz
== 2)
71 /* Later versions may not use installed.db other than to record the version. */
72 if (dbver
== 1 || dbver
== 2)
75 io_stream::open ("cygfile:///etc/setup/installed.db", "rt");
77 db
->gets (line
, 1000);
78 while (db
->gets (line
, 1000))
87 sscanf (line
, "%s %s %d %s %d", pkgname
, inst
, &instsz
, src
,
90 if (pkgname
[0] == '\0' || inst
[0] == '\0')
94 parseable
= parse_filename (inst
, f
);
98 packagemeta
*pkg
= findBinary (PackageSpecification(pkgname
));
101 pkg
= new packagemeta (pkgname
, inst
);
102 packages
.push_back (pkg
);
103 /* we should install a new handler then not check this...
109 packageversion binary
=
110 cygpackage::createInstance (pkgname
, inst
, instsz
, f
.ver
,
114 pkg
->add_version (binary
);
115 pkg
->set_installed (binary
);
116 pkg
->desired
= pkg
->installed
;
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";
136 io_stream::mkpath_p (PATH_TO_FILE
, ndbn
);
138 io_stream
*ndb
= io_stream::open (ndbn
, "wb");
140 // XXX if this failed, try removing any existing .new database?
142 return errno
? errno
: 1;
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
)
148 packagemeta
& pkgm
= **i
;
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.
157 line
= pkgm
.name
+ " " + pkgm
.name
+ "-" +
158 pkgm
.installed
.Canonical_version () + ".tar.bz2 0\n";
159 ndb
->write (line
.cstr_oneuse(), line
.size());
165 io_stream::remove (odbn
);
167 if (io_stream::move (ndbn
, odbn
))
168 return errno
? errno
: 1;
173 packagedb::findBinary (PackageSpecification
const &spec
) const
175 for (vector
<packagemeta
*>::iterator n
= packages
.begin ();
176 n
!= packages
.end (); ++n
)
178 packagemeta
& pkgm
= **n
;
179 for (set
<packageversion
>::iterator i
=pkgm
.versions
.begin();
180 i
!= pkgm
.versions
.end(); ++i
)
181 if (spec
.satisfies (*i
))
188 packagedb::findSource (PackageSpecification
const &spec
) const
190 for (vector
<packagemeta
*>::iterator n
=sourcePackages
.begin();
191 n
!= sourcePackages
.end(); ++n
)
193 for (set
<packageversion
>::iterator i
= (*n
)->versions
.begin();
194 i
!= (*n
)->versions
.end(); ++i
)
195 if (spec
.satisfies (*i
))
202 packagedb::installeddbread
=
204 vector
< packagemeta
* > packagedb::packages
;
205 packagedb::categoriesType
206 packagedb::categories
;
207 vector
<packagemeta
*> packagedb::sourcePackages
;
211 std::vector
<packagemeta
*>
212 packagedb::dependencyOrderedPackages
;
214 #include "LogSingleton.h"
221 ConnectedLoopFinder();
225 std::vector
<size_t> visitOrder
;
226 size_t visit (size_t const nodeToVisit
);
227 std::stack
<size_t> nodesInStronglyConnectedComponent
;
230 ConnectedLoopFinder::ConnectedLoopFinder() : visited(0)
232 for (size_t counter
= 0; counter
< db
.packages
.size(); ++counter
)
233 visitOrder
.push_back(0);
237 ConnectedLoopFinder::doIt()
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.
242 for (size_t i
= 0; i
< db
.packages
.size(); ++i
)
244 packagemeta
&pkg (*db
.packages
[i
]);
245 if (pkg
.installed
&& ! visitOrder
[i
])
248 log (LOG_PLAIN
) << "Visited: " << visited
<< " nodes out of " << db
.packages
.size() << "." << endLog
;
252 checkForInstalled (PackageSpecification
*spec
)
255 packagemeta
*required
= db
.findBinary (*spec
);
258 if (spec
->satisfies (required
->installed
)
259 && required
->desired
== required
->installed
)
260 /* done, found a satisfactory installed version that will remain
267 ConnectedLoopFinder::visit(size_t const nodeToVisit
)
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;
273 visitOrder
[nodeToVisit
] = visited
;
275 size_t minimumVisitId
= visited
;
276 nodesInStronglyConnectedComponent
.push(nodeToVisit
);
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())
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())
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
292 size_t nodeJustVisited
= 0;
293 while (nodeJustVisited
< db
.packages
.size() && db
.packages
[nodeJustVisited
]->name
.casecompare((*i
)->packageName()))
295 if (nodeJustVisited
== db
.packages
.size())
296 log (LOG_PLAIN
) << "Search for package '" << (*i
)->packageName() << "' failed." << endLog
;
299 if (visitOrder
[nodeJustVisited
])
300 minimumVisitId
= std::min (minimumVisitId
, visitOrder
[nodeJustVisited
]);
302 minimumVisitId
= std::min (minimumVisitId
, visit (nodeJustVisited
));
304 /* next and clause */
308 /* not installed or not available we ignore */
312 if (minimumVisitId
== visitOrder
[nodeToVisit
])
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
);
324 return minimumVisitId
;
327 PackageDBConnectedIterator
328 packagedb::connectedBegin()
330 if (!dependencyOrderedPackages
.size())
332 ConnectedLoopFinder doMe
;
334 log(LOG_PLAIN
) << "Dependency ordered install:" << endLog
;
335 for (std::vector
<packagemeta
*>::iterator i
= dependencyOrderedPackages
.begin();
336 i
!= dependencyOrderedPackages
.end(); ++i
)
338 packagemeta
&pkg (**i
);
339 log(LOG_PLAIN
) << pkg
.name
<< endLog
;
343 return dependencyOrderedPackages
.begin();
346 PackageDBConnectedIterator
347 packagedb::connectedEnd()
349 return dependencyOrderedPackages
.end();