]> git.saurik.com Git - apt.git/blob - apt-pkg/sourcelist.cc
62d5e6fcd5eb98b23757a2e2bbe9a7f30026140a
[apt.git] / apt-pkg / sourcelist.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: sourcelist.cc,v 1.1 1998/07/07 04:17:06 jgg Exp $
4 /* ######################################################################
5
6 List of Sources
7
8 ##################################################################### */
9 /*}}}*/
10 // Include Files /*{{{*/
11 #ifdef __GNUG__
12 #pragma implementation "pkglib/sourcelist.h"
13 #endif
14
15 #include <pkglib/sourcelist.h>
16 #include <pkglib/error.h>
17 #include <pkglib/fileutl.h>
18 #include <strutl.h>
19 #include <options.h>
20
21 #include <fstream.h>
22 #include <stdio.h>
23 #include <unistd.h>
24 #include <sys/stat.h>
25 /*}}}*/
26
27 // SourceList::pkgSourceList - Constructors /*{{{*/
28 // ---------------------------------------------------------------------
29 /* */
30 pkgSourceList::pkgSourceList()
31 {
32 }
33
34 pkgSourceList::pkgSourceList(string File)
35 {
36 Read(File);
37 }
38 /*}}}*/
39 // SourceList::ReadMainList - Read the main source list from etc /*{{{*/
40 // ---------------------------------------------------------------------
41 /* */
42 bool pkgSourceList::ReadMainList()
43 {
44 return Read(PKG_DEB_CF_SOURCELIST);
45 }
46 /*}}}*/
47 // SourceList::Read - Parse the sourcelist file /*{{{*/
48 // ---------------------------------------------------------------------
49 /* */
50 bool pkgSourceList::Read(string File)
51 {
52 // Open the stream for reading
53 ifstream F(File.c_str(),ios::in | ios::nocreate);
54 if (!F != 0)
55 return _error->Errno("ifstream::ifstream","Opening %s",File.c_str());
56
57 List.erase(List.begin(),List.end());
58 char Buffer[300];
59
60 int CurLine = 0;
61 while (F.eof() == false)
62 {
63 F.getline(Buffer,sizeof(Buffer));
64 CurLine++;
65 _strtabexpand(Buffer,sizeof(Buffer));
66 _strstrip(Buffer);
67
68 // Comment or blank
69 if (Buffer[0] == '#' || Buffer[0] == 0)
70 continue;
71
72 // Grok it
73 string Type;
74 string URI;
75 Item Itm;
76 char *C = Buffer;
77 if (ParseQuoteWord(C,Type) == false)
78 return _error->Error("Malformed line %u in source list %s (type)",CurLine,File.c_str());
79 if (ParseQuoteWord(C,URI) == false)
80 return _error->Error("Malformed line %u in source list %s (URI)",CurLine,File.c_str());
81 if (ParseQuoteWord(C,Itm.Dist) == false)
82 return _error->Error("Malformed line %u in source list %s (dist)",CurLine,File.c_str());
83 if (Itm.SetType(Type) == false)
84 return _error->Error("Malformed line %u in source list %s (type parse)",CurLine,File.c_str());
85 if (Itm.SetURI(URI) == false)
86 return _error->Error("Malformed line %u in source list %s (URI parse)",CurLine,File.c_str());
87
88 // Check for an absolute dists specification.
89 if (Itm.Dist.empty() == false && Itm.Dist[Itm.Dist.size() - 1] == '/')
90 {
91 if (ParseQuoteWord(C,Itm.Section) == true)
92 return _error->Error("Malformed line %u in source list %s (Absolute dist)",CurLine,File.c_str());
93 Itm.Dist = SubstVar(Itm.Dist,"$(ARCH)",PKG_DEB_ARCH);
94 List.push_back(Itm);
95 continue;
96 }
97
98 // Grab the rest of the dists
99 if (ParseQuoteWord(C,Itm.Section) == false)
100 return _error->Error("Malformed line %u in source list %s (dist parse)",CurLine,File.c_str());
101
102 do
103 {
104 List.push_back(Itm);
105 }
106 while (ParseQuoteWord(C,Itm.Section) == true);
107 }
108 return true;
109 }
110 /*}}}*/
111 // SourceList::SanitizeURI - Hash the uri /*{{{*/
112 // ---------------------------------------------------------------------
113 /* This converts a URI into a safe filename. It quotes all unsafe characters
114 and converts / to _ and removes the scheme identifier. */
115 string pkgSourceList::SanitizeURI(string URI)
116 {
117 string::const_iterator I = URI.begin() + URI.find(':') + 1;
118 for (; I < URI.end() && *I == '/'; I++);
119
120 // "\x00-\x20{}|\\\\^\\[\\]<>\"\x7F-\xFF";
121 URI = QuoteString(string(I,URI.end() - I),"\\|{}[]<>\"^~_=!@#$%^&*");
122 string::iterator J = URI.begin();
123 for (; J != URI.end(); J++)
124 if (*J == '/')
125 *J = '_';
126 return URI;
127 }
128 /*}}}*/
129 // SourceList::MatchPkgFile - Find the package file that has the ver /*{{{*/
130 // ---------------------------------------------------------------------
131 /* This will return List.end() if it could not find the matching
132 file */
133 pkgSourceList::const_iterator pkgSourceList::MatchPkgFile(pkgCache::VerIterator Ver)
134 {
135 string Base = PKG_DEB_ST_LIST;
136 for (const_iterator I = List.begin(); I != List.end(); I++)
137 {
138 string URI = I->PackagesURI();
139 switch (I->Type)
140 {
141 case Item::Deb:
142 if (Base + SanitizeURI(URI) == Ver.File().FileName())
143 return I;
144 break;
145 };
146 }
147 return List.end();
148 }
149 /*}}}*/
150 // SourceList::Item << - Writes the item to a stream /*{{{*/
151 // ---------------------------------------------------------------------
152 /* This is not suitable for rebuilding the sourcelist file but it good for
153 debugging. */
154 ostream &operator <<(ostream &O,pkgSourceList::Item &Itm)
155 {
156 O << Itm.Type << ' ' << Itm.URI << ' ' << Itm.Dist << ' ' << Itm.Section;
157 return O;
158 }
159 /*}}}*/
160 // SourceList::Item::SetType - Sets the distribution type /*{{{*/
161 // ---------------------------------------------------------------------
162 /* */
163 bool pkgSourceList::Item::SetType(string S)
164 {
165 if (S == "deb")
166 {
167 Type = Deb;
168 return true;
169 }
170
171 return true;
172 }
173 /*}}}*/
174 // SourceList::Item::SetURI - Set the URI /*{{{*/
175 // ---------------------------------------------------------------------
176 /* For simplicity we strip the scheme off the uri */
177 bool pkgSourceList::Item::SetURI(string S)
178 {
179 if (S.empty() == true)
180 return false;
181
182 if (S.find(':') == string::npos)
183 return false;
184
185 S = SubstVar(S,"$(ARCH)",PKG_DEB_ARCH);
186
187 // Make sure that the URN is / postfixed
188 URI = S;
189 if (URI[URI.size() - 1] != '/')
190 URI += '/';
191
192 return true;
193 }
194 /*}}}*/
195 // SourceList::Item::PackagesURI - Returns a URI to the packages file /*{{{*/
196 // ---------------------------------------------------------------------
197 /* */
198 string pkgSourceList::Item::PackagesURI() const
199 {
200 string Res;
201 switch (Type)
202 {
203 case Deb:
204 if (Dist[Dist.size() - 1] == '/')
205 Res = URI + Dist;
206 else
207 Res = URI + "dists/" + Dist + '/' + Section +
208 "/binary-" + PKG_DEB_ARCH + '/';
209
210 Res += "Packages";
211 break;
212 };
213 return Res;
214 }
215 /*}}}*/
216 // SourceList::Item::PackagesInfo - Shorter version of the URI /*{{{*/
217 // ---------------------------------------------------------------------
218 /* This is a shorter version that is designed to be < 60 chars or so */
219 string pkgSourceList::Item::PackagesInfo() const
220 {
221 string Res;
222 switch (Type)
223 {
224 case Deb:
225 Res += SiteOnly(URI) + ' ';
226 if (Dist[Dist.size() - 1] == '/')
227 Res += Dist;
228 else
229 Res += Dist + '/' + Section;
230
231 Res += " Packages";
232 break;
233 };
234 return Res;
235 }
236 /*}}}*/
237 // SourceList::Item::ArchiveInfo - Shorter version of the archive spec /*{{{*/
238 // ---------------------------------------------------------------------
239 /* This is a shorter version that is designed to be < 60 chars or so */
240 string pkgSourceList::Item::ArchiveInfo(pkgCache::VerIterator Ver) const
241 {
242 string Res;
243 switch (Type)
244 {
245 case Deb:
246 Res += SiteOnly(URI) + ' ';
247 if (Dist[Dist.size() - 1] == '/')
248 Res += Dist;
249 else
250 Res += Dist + '/' + Section;
251
252 Res += " ";
253 Res += Ver.ParentPkg().Name();
254 break;
255 };
256 return Res;
257 }
258 /*}}}*/
259 // SourceList::Item::ArchiveURI - Returns a URI to the given archive /*{{{*/
260 // ---------------------------------------------------------------------
261 /* */
262 string pkgSourceList::Item::ArchiveURI(string File) const
263 {
264 string Res;
265 switch (Type)
266 {
267 case Deb:
268 Res = URI + File;
269 break;
270 };
271 return Res;
272 }
273 /*}}}*/
274 // SourceList::Item::SiteOnly - Strip off the path part of a URI /*{{{*/
275 // ---------------------------------------------------------------------
276 /* */
277 string pkgSourceList::Item::SiteOnly(string URI) const
278 {
279 unsigned int Pos = URI.find(':');
280 if (Pos == string::npos || Pos + 3 > URI.length())
281 return URI;
282 if (URI[Pos + 1] != '/' || URI[Pos + 2] != '/')
283 return URI;
284
285 Pos = URI.find('/',Pos + 3);
286 if (Pos == string::npos)
287 return URI;
288 return string(URI,0,Pos);
289 }
290 /*}}}*/
291
292 // UpdateMeta - Update the meta information /*{{{*/
293 // ---------------------------------------------------------------------
294 /* The meta information is package files, revision information and mirror
295 lists. */
296 bool pkgUpdateMeta(pkgSourceList &List,pkgAquire &Engine)
297 {
298 if (Engine.OutputDir(PKG_DEB_ST_LIST) == false)
299 return false;
300
301 for (pkgSourceList::const_iterator I = List.begin(); I != List.end(); I++)
302 {
303 string URI = I->PackagesURI();
304 string GetInfo = I->PackagesInfo();
305 switch (I->Type)
306 {
307 case pkgSourceList::Item::Deb:
308 if (Engine.Get(URI + ".gz",List.SanitizeURI(URI),GetInfo) == false)
309 return false;
310 break;
311 };
312 }
313
314 return true;
315 }
316 /*}}}*/
317 // MakeSrcCache - Generate a cache file of all the package files /*{{{*/
318 // ---------------------------------------------------------------------
319 /* This goes over the source list and builds a cache of all the package
320 files. */
321 bool pkgMakeSrcCache(pkgSourceList &List)
322 {
323 // First we date check the cache
324 bool Bad = false;
325 while (Bad == false)
326 {
327 if (FileExists(PKG_DEB_CA_SRCCACHE) == false)
328 break;
329
330 pkgCache Cache(PKG_DEB_CA_SRCCACHE,true,true);
331 if (_error->PendingError() == true)
332 {
333 _error->Discard();
334 break;
335 }
336
337 // They are certianly out of sync
338 if (Cache.Head().PackageFileCount != List.size())
339 break;
340
341 for (pkgCache::PkgFileIterator F(Cache); F.end() == false; F++)
342 {
343 // Search for a match in the source list
344 Bad = true;
345 for (pkgSourceList::const_iterator I = List.begin();
346 I != List.end(); I++)
347 {
348 string File = string(PKG_DEB_ST_LIST) +
349 List.SanitizeURI(I->PackagesURI());
350 if (F.FileName() == File)
351 {
352 Bad = false;
353 break;
354 }
355 }
356
357 // Check if the file matches what was cached
358 Bad |= !F.IsOk();
359 if (Bad == true)
360 break;
361 }
362
363 if (Bad == false)
364 return true;
365 }
366
367 unlink(PKG_DEB_CA_SRCCACHE);
368 pkgCache::MergeState Merge(PKG_DEB_CA_SRCCACHE);
369 if (_error->PendingError() == true)
370 return false;
371
372 for (pkgSourceList::const_iterator I = List.begin(); I != List.end(); I++)
373 {
374 string File = string(PKG_DEB_ST_LIST) + List.SanitizeURI(I->PackagesURI());
375 if (Merge.MergePackageFile(File,"??","??") == false)
376 return false;
377 }
378
379 return true;
380 }
381 /*}}}*/
382 // MakeStatusCache - Generates a cache that includes the status files /*{{{*/
383 // ---------------------------------------------------------------------
384 /* This copies the package source cache and then merges the status and
385 xstatus files into it. */
386 bool pkgMakeStatusCache()
387 {
388 // Quickly check if the existing package cache is ok
389 bool Bad = false;
390 while (Bad == false)
391 {
392 if (FileExists(PKG_DEB_CA_PKGCACHE) == false)
393 break;
394
395 /* We check the dates of the two caches. This takes care of most things
396 quickly and easially */
397 struct stat Src;
398 struct stat Pkg;
399 if (stat(PKG_DEB_CA_PKGCACHE,&Pkg) != 0 ||
400 stat(PKG_DEB_CA_SRCCACHE,&Src) != 0)
401 break;
402 if (difftime(Src.st_mtime,Pkg.st_mtime) > 0)
403 break;
404
405 pkgCache Cache(PKG_DEB_CA_PKGCACHE,true,true);
406 if (_error->PendingError() == true)
407 {
408 _error->Discard();
409 break;
410 }
411
412 for (pkgCache::PkgFileIterator F(Cache); F.end() == false; F++)
413 {
414 if (F.IsOk() == false)
415 {
416 Bad = true;
417 break;
418 }
419 }
420
421 if (Bad == false)
422 return true;
423 }
424
425 // Check the integrity of the source cache.
426 {
427 pkgCache Cache(PKG_DEB_CA_SRCCACHE,true,true);
428 if (_error->PendingError() == true)
429 return false;
430 }
431
432 // Sub scope so that merge destructs before we rename the file...
433 string Cache = PKG_DEB_CA_PKGCACHE ".new";
434 {
435 if (CopyFile(PKG_DEB_CA_SRCCACHE,Cache) == false)
436 return false;
437
438 pkgCache::MergeState Merge(Cache);
439 if (_error->PendingError() == true)
440 return false;
441
442 // Merge in the user status file
443 if (FileExists(PKG_DEB_ST_USERSTATUS) == true)
444 if (Merge.MergePackageFile(PKG_DEB_ST_USERSTATUS,"status","0",
445 pkgFLAG_NotSource) == false)
446 return false;
447
448 // Merge in the extra status file
449 if (FileExists(PKG_DEB_ST_XSTATUS) == true)
450 if (Merge.MergePackageFile(PKG_DEB_ST_XSTATUS,"status","0",
451 pkgFLAG_NotSource) == false)
452 return false;
453
454 // Merge in the status file
455 if (Merge.MergePackageFile("/var/lib/dpkg/status","status","0",
456 pkgFLAG_NotSource) == false)
457 return false;
458 }
459
460 if (rename(Cache.c_str(),PKG_DEB_CA_PKGCACHE) != 0)
461 return false;
462
463 return true;
464 }
465 /*}}}*/