+ // Load the list of packages from the command line into the show list
+ for (const char **I = CmdL.FileList + 1; *I != 0; I++)
+ {
+ // Process per-package flags
+ string P = *I;
+ bool Force = false;
+ if (P.length() > 3)
+ {
+ if (P.end()[-1] == '^')
+ {
+ Force = true;
+ P.erase(P.end()-1);
+ }
+
+ if (P.end()[-1] == ',')
+ P.erase(P.end()-1);
+ }
+
+ // Locate the package
+ pkgCache::PkgIterator Pkg = Cache.FindPkg(P);
+ if (Pkg.end() == true)
+ {
+ _error->Warning(_("Unable to locate package %s"),*I);
+ continue;
+ }
+ Show[Pkg->ID] = ToShow;
+
+ if (Force == true)
+ Flags[Pkg->ID] |= ForceNR;
+ }
+
+ // Little header
+ cout << "graph: { title: \"packages\"" << endl <<
+ "xmax: 700 ymax: 700 x: 30 y: 30" << endl <<
+ "layout_downfactor: 8" << endl;
+
+ bool Act = true;
+ while (Act == true)
+ {
+ Act = false;
+ for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; Pkg++)
+ {
+ // See we need to show this package
+ if (Show[Pkg->ID] == None || Show[Pkg->ID] >= DoneNR)
+ continue;
+
+ //printf ("node: { title: \"%s\" label: \"%s\" }\n", Pkg.Name(), Pkg.Name());
+
+ // Colour as done
+ if (Show[Pkg->ID] == ToShowNR || (Flags[Pkg->ID] & ForceNR) == ForceNR)
+ {
+ // Pure Provides and missing packages have no deps!
+ if (ShapeMap[Pkg->ID] == 0 || ShapeMap[Pkg->ID] == 1)
+ Show[Pkg->ID] = Done;
+ else
+ Show[Pkg->ID] = DoneNR;
+ }
+ else
+ Show[Pkg->ID] = Done;
+ Act = true;
+
+ // No deps to map out
+ if (Pkg->VersionList == 0 || Show[Pkg->ID] == DoneNR)
+ continue;
+
+ pkgCache::VerIterator Ver = Pkg.VersionList();
+ for (pkgCache::DepIterator D = Ver.DependsList(); D.end() == false; D++)
+ {
+ // See if anything can meet this dep
+ // Walk along the actual package providing versions
+ bool Hit = false;
+ pkgCache::PkgIterator DPkg = D.TargetPkg();
+ for (pkgCache::VerIterator I = DPkg.VersionList();
+ I.end() == false && Hit == false; I++)
+ {
+ if (Cache.VS->CheckDep(I.VerStr(),D->CompareOp,D.TargetVer()) == true)
+ Hit = true;
+ }
+
+ // Follow all provides
+ for (pkgCache::PrvIterator I = DPkg.ProvidesList();
+ I.end() == false && Hit == false; I++)
+ {
+ if (Cache.VS->CheckDep(I.ProvideVersion(),D->CompareOp,D.TargetVer()) == false)
+ Hit = true;
+ }
+
+
+ // Only graph critical deps
+ if (D.IsCritical() == true)
+ {
+ printf ("edge: { sourcename: \"%s\" targetname: \"%s\" class: 2 ",Pkg.Name(), D.TargetPkg().Name() );
+
+ // Colour the node for recursion
+ if (Show[D.TargetPkg()->ID] <= DoneNR)
+ {
+ /* If a conflicts does not meet anything in the database
+ then show the relation but do not recurse */
+ if (Hit == false &&
+ (D->Type == pkgCache::Dep::Conflicts ||
+ D->Type == pkgCache::Dep::Obsoletes))
+ {
+ if (Show[D.TargetPkg()->ID] == None &&
+ Show[D.TargetPkg()->ID] != ToShow)
+ Show[D.TargetPkg()->ID] = ToShowNR;
+ }
+ else
+ {
+ if (GivenOnly == true && Show[D.TargetPkg()->ID] != ToShow)
+ Show[D.TargetPkg()->ID] = ToShowNR;
+ else
+ Show[D.TargetPkg()->ID] = ToShow;
+ }
+ }
+
+ // Edge colour
+ switch(D->Type)
+ {
+ case pkgCache::Dep::Conflicts:
+ printf("label: \"conflicts\" color: lightgreen }\n");
+ break;
+ case pkgCache::Dep::Obsoletes:
+ printf("label: \"obsoletes\" color: lightgreen }\n");
+ break;
+
+ case pkgCache::Dep::PreDepends:
+ printf("label: \"predepends\" color: blue }\n");
+ break;
+
+ default:
+ printf("}\n");
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* Draw the box colours after the fact since we can not tell what colour
+ they should be until everything is finished drawing */
+ for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; Pkg++)
+ {
+ if (Show[Pkg->ID] < DoneNR)
+ continue;
+
+ if (Show[Pkg->ID] == DoneNR)
+ printf("node: { title: \"%s\" label: \"%s\" color: orange shape: %s }\n", Pkg.Name(), Pkg.Name(),
+ Shapes[ShapeMap[Pkg->ID]]);
+ else
+ printf("node: { title: \"%s\" label: \"%s\" shape: %s }\n", Pkg.Name(), Pkg.Name(),
+ Shapes[ShapeMap[Pkg->ID]]);
+
+ }
+
+ printf("}\n");
+ return true;
+}
+ /*}}}*/
+
+
+// Dotty - Generate a graph for Dotty /*{{{*/
+// ---------------------------------------------------------------------
+/* Dotty is the graphvis program for generating graphs. It is a fairly
+ simple queuing algorithm that just writes dependencies and nodes.
+ http://www.research.att.com/sw/tools/graphviz/ */
+bool Dotty(CommandLine &CmdL)
+{
+ pkgCache &Cache = *GCache;
+ bool GivenOnly = _config->FindB("APT::Cache::GivenOnly",false);
+
+ /* Normal packages are boxes
+ Pure Provides are triangles
+ Mixed are diamonds
+ Hexagons are missing packages*/
+ const char *Shapes[] = {"hexagon","triangle","box","diamond"};
+
+ /* Initialize the list of packages to show.
+ 1 = To Show
+ 2 = To Show no recurse
+ 3 = Emitted no recurse
+ 4 = Emitted
+ 0 = None */
+ enum States {None=0, ToShow, ToShowNR, DoneNR, Done};
+ enum TheFlags {ForceNR=(1<<0)};
+ unsigned char *Show = new unsigned char[Cache.Head().PackageCount];
+ unsigned char *Flags = new unsigned char[Cache.Head().PackageCount];
+ unsigned char *ShapeMap = new unsigned char[Cache.Head().PackageCount];
+
+ // Show everything if no arguments given
+ if (CmdL.FileList[1] == 0)
+ for (unsigned long I = 0; I != Cache.Head().PackageCount; I++)
+ Show[I] = ToShow;
+ else
+ for (unsigned long I = 0; I != Cache.Head().PackageCount; I++)
+ Show[I] = None;
+ memset(Flags,0,sizeof(*Flags)*Cache.Head().PackageCount);
+
+ // Map the shapes
+ for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; Pkg++)
+ {
+ if (Pkg->VersionList == 0)
+ {
+ // Missing
+ if (Pkg->ProvidesList == 0)
+ ShapeMap[Pkg->ID] = 0;
+ else
+ ShapeMap[Pkg->ID] = 1;
+ }
+ else
+ {
+ // Normal
+ if (Pkg->ProvidesList == 0)
+ ShapeMap[Pkg->ID] = 2;
+ else
+ ShapeMap[Pkg->ID] = 3;
+ }
+ }
+
+ // Load the list of packages from the command line into the show list
+ for (const char **I = CmdL.FileList + 1; *I != 0; I++)
+ {
+ // Process per-package flags
+ string P = *I;
+ bool Force = false;
+ if (P.length() > 3)
+ {
+ if (P.end()[-1] == '^')
+ {
+ Force = true;
+ P.erase(P.end()-1);
+ }
+
+ if (P.end()[-1] == ',')
+ P.erase(P.end()-1);
+ }
+
+ // Locate the package
+ pkgCache::PkgIterator Pkg = Cache.FindPkg(P);
+ if (Pkg.end() == true)
+ {
+ _error->Warning(_("Unable to locate package %s"),*I);
+ continue;
+ }
+ Show[Pkg->ID] = ToShow;
+
+ if (Force == true)
+ Flags[Pkg->ID] |= ForceNR;
+ }
+
+ // Little header
+ printf("digraph packages {\n");
+ printf("concentrate=true;\n");
+ printf("size=\"30,40\";\n");
+
+ bool Act = true;
+ while (Act == true)
+ {
+ Act = false;
+ for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; Pkg++)
+ {
+ // See we need to show this package
+ if (Show[Pkg->ID] == None || Show[Pkg->ID] >= DoneNR)
+ continue;
+
+ // Colour as done
+ if (Show[Pkg->ID] == ToShowNR || (Flags[Pkg->ID] & ForceNR) == ForceNR)
+ {
+ // Pure Provides and missing packages have no deps!
+ if (ShapeMap[Pkg->ID] == 0 || ShapeMap[Pkg->ID] == 1)
+ Show[Pkg->ID] = Done;
+ else
+ Show[Pkg->ID] = DoneNR;
+ }
+ else
+ Show[Pkg->ID] = Done;
+ Act = true;
+
+ // No deps to map out
+ if (Pkg->VersionList == 0 || Show[Pkg->ID] == DoneNR)
+ continue;
+
+ pkgCache::VerIterator Ver = Pkg.VersionList();
+ for (pkgCache::DepIterator D = Ver.DependsList(); D.end() == false; D++)
+ {
+ // See if anything can meet this dep
+ // Walk along the actual package providing versions
+ bool Hit = false;
+ pkgCache::PkgIterator DPkg = D.TargetPkg();
+ for (pkgCache::VerIterator I = DPkg.VersionList();
+ I.end() == false && Hit == false; I++)
+ {
+ if (Cache.VS->CheckDep(I.VerStr(),D->CompareOp,D.TargetVer()) == true)
+ Hit = true;
+ }
+
+ // Follow all provides
+ for (pkgCache::PrvIterator I = DPkg.ProvidesList();
+ I.end() == false && Hit == false; I++)
+ {
+ if (Cache.VS->CheckDep(I.ProvideVersion(),D->CompareOp,D.TargetVer()) == false)
+ Hit = true;
+ }
+
+ // Only graph critical deps
+ if (D.IsCritical() == true)
+ {
+ printf("\"%s\" -> \"%s\"",Pkg.Name(),D.TargetPkg().Name());
+
+ // Colour the node for recursion
+ if (Show[D.TargetPkg()->ID] <= DoneNR)
+ {
+ /* If a conflicts does not meet anything in the database
+ then show the relation but do not recurse */
+ if (Hit == false &&
+ (D->Type == pkgCache::Dep::Conflicts ||
+ D->Type == pkgCache::Dep::Obsoletes))
+ {
+ if (Show[D.TargetPkg()->ID] == None &&
+ Show[D.TargetPkg()->ID] != ToShow)
+ Show[D.TargetPkg()->ID] = ToShowNR;
+ }
+ else
+ {
+ if (GivenOnly == true && Show[D.TargetPkg()->ID] != ToShow)
+ Show[D.TargetPkg()->ID] = ToShowNR;
+ else
+ Show[D.TargetPkg()->ID] = ToShow;
+ }
+ }
+
+ // Edge colour
+ switch(D->Type)
+ {
+ case pkgCache::Dep::Conflicts:
+ case pkgCache::Dep::Obsoletes:
+ printf("[color=springgreen];\n");
+ break;
+
+ case pkgCache::Dep::PreDepends:
+ printf("[color=blue];\n");
+ break;
+
+ default:
+ printf(";\n");
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* Draw the box colours after the fact since we can not tell what colour
+ they should be until everything is finished drawing */
+ for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; Pkg++)
+ {
+ if (Show[Pkg->ID] < DoneNR)
+ continue;
+
+ // Orange box for early recursion stoppage
+ if (Show[Pkg->ID] == DoneNR)
+ printf("\"%s\" [color=orange,shape=%s];\n",Pkg.Name(),
+ Shapes[ShapeMap[Pkg->ID]]);
+ else
+ printf("\"%s\" [shape=%s];\n",Pkg.Name(),
+ Shapes[ShapeMap[Pkg->ID]]);
+ }
+
+ printf("}\n");
+ return true;
+}
+ /*}}}*/
+// DoAdd - Perform an adding operation /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool DoAdd(CommandLine &CmdL)
+{
+ return _error->Error("Unimplemented");
+#if 0
+ // Make sure there is at least one argument
+ if (CmdL.FileSize() <= 1)
+ return _error->Error("You must give at least one file name");
+
+ // Open the cache
+ FileFd CacheF(_config->FindFile("Dir::Cache::pkgcache"),FileFd::WriteAny);
+ if (_error->PendingError() == true)
+ return false;
+
+ DynamicMMap Map(CacheF,MMap::Public);
+ if (_error->PendingError() == true)
+ return false;
+
+ OpTextProgress Progress(*_config);
+ pkgCacheGenerator Gen(Map,Progress);
+ if (_error->PendingError() == true)
+ return false;
+
+ unsigned long Length = CmdL.FileSize() - 1;
+ for (const char **I = CmdL.FileList + 1; *I != 0; I++)
+ {
+ Progress.OverallProgress(I - CmdL.FileList,Length,1,"Generating cache");
+ Progress.SubProgress(Length);
+
+ // Do the merge
+ FileFd TagF(*I,FileFd::ReadOnly);
+ debListParser Parser(TagF);
+ if (_error->PendingError() == true)
+ return _error->Error("Problem opening %s",*I);
+
+ if (Gen.SelectFile(*I,"") == false)
+ return _error->Error("Problem with SelectFile");
+
+ if (Gen.MergeList(Parser) == false)
+ return _error->Error("Problem with MergeList");
+ }
+
+ Progress.Done();
+ GCache = &Gen.GetCache();
+ Stats(CmdL);
+
+ return true;
+#endif
+}
+ /*}}}*/
+// DisplayRecord - Displays the complete record for the package /*{{{*/
+// ---------------------------------------------------------------------
+/* This displays the package record from the proper package index file.
+ It is not used by DumpAvail for performance reasons. */
+bool DisplayRecord(pkgCache::VerIterator V)
+{
+ // Find an appropriate file
+ pkgCache::VerFileIterator Vf = V.FileList();
+ for (; Vf.end() == false; Vf++)
+ if ((Vf.File()->Flags & pkgCache::Flag::NotSource) == 0)
+ break;
+ if (Vf.end() == true)
+ Vf = V.FileList();
+
+ // Check and load the package list file
+ pkgCache::PkgFileIterator I = Vf.File();
+ if (I.IsOk() == false)
+ return _error->Error(_("Package file %s is out of sync."),I.FileName());
+
+ FileFd PkgF(I.FileName(),FileFd::ReadOnly);
+ if (_error->PendingError() == true)
+ return false;
+
+ // Read the record
+ unsigned char *Buffer = new unsigned char[GCache->HeaderP->MaxVerFileSize+1];
+ Buffer[V.FileList()->Size] = '\n';
+ if (PkgF.Seek(V.FileList()->Offset) == false ||
+ PkgF.Read(Buffer,V.FileList()->Size) == false)
+ {
+ delete [] Buffer;
+ return false;
+ }
+
+ // Get a pointer to start of Description field
+ const unsigned char *DescP = (unsigned char*)strstr((char*)Buffer, "Description:");
+
+ // Write all but Description
+ if (fwrite(Buffer,1,DescP - Buffer,stdout) < (size_t)(DescP - Buffer))
+ {
+ delete [] Buffer;
+ return false;
+ }
+
+ // Show the right description
+ pkgRecords Recs(*GCache);
+ pkgCache::DescIterator Desc = V.TranslatedDescription();
+ pkgRecords::Parser &P = Recs.Lookup(Desc.FileList());
+ cout << "Description" << ( (strcmp(Desc.LanguageCode(),"") != 0) ? "-" : "" ) << Desc.LanguageCode() << ": " << P.LongDesc();
+
+ // Find the first field after the description (if there is any)
+ for(DescP++;DescP != &Buffer[V.FileList()->Size];DescP++)
+ {
+ if(*DescP == '\n' && *(DescP+1) != ' ')
+ {
+ // write the rest of the buffer
+ const unsigned char *end=&Buffer[V.FileList()->Size];
+ if (fwrite(DescP,1,end-DescP,stdout) < (size_t)(end-DescP))
+ {
+ delete [] Buffer;
+ return false;
+ }
+
+ break;
+ }
+ }
+ // write a final newline (after the description)
+ cout<<endl;
+ delete [] Buffer;
+
+ return true;
+}
+ /*}}}*/
+// Search - Perform a search /*{{{*/
+// ---------------------------------------------------------------------
+/* This searches the package names and pacakge descriptions for a pattern */
+struct ExDescFile
+{
+ pkgCache::DescFile *Df;
+ bool NameMatch;
+};
+
+bool Search(CommandLine &CmdL)
+{
+ pkgCache &Cache = *GCache;
+ bool ShowFull = _config->FindB("APT::Cache::ShowFull",false);
+ bool NamesOnly = _config->FindB("APT::Cache::NamesOnly",false);
+ unsigned NumPatterns = CmdL.FileSize() -1;
+
+ pkgDepCache::Policy Plcy;
+
+ // Make sure there is at least one argument
+ if (NumPatterns < 1)
+ return _error->Error(_("You must give exactly one pattern"));
+
+ // Compile the regex pattern
+ regex_t *Patterns = new regex_t[NumPatterns];
+ memset(Patterns,0,sizeof(*Patterns)*NumPatterns);
+ for (unsigned I = 0; I != NumPatterns; I++)
+ {
+ if (regcomp(&Patterns[I],CmdL.FileList[I+1],REG_EXTENDED | REG_ICASE |
+ REG_NOSUB) != 0)
+ {
+ for (; I != 0; I--)
+ regfree(&Patterns[I]);
+ return _error->Error("Regex compilation error");
+ }
+ }
+
+ // Create the text record parser
+ pkgRecords Recs(Cache);
+ if (_error->PendingError() == true)
+ {
+ for (unsigned I = 0; I != NumPatterns; I++)
+ regfree(&Patterns[I]);
+ return false;
+ }
+
+ ExDescFile *DFList = new ExDescFile[Cache.HeaderP->PackageCount+1];
+ memset(DFList,0,sizeof(*DFList)*Cache.HeaderP->PackageCount+1);
+
+ // Map versions that we want to write out onto the VerList array.
+ for (pkgCache::PkgIterator P = Cache.PkgBegin(); P.end() == false; P++)
+ {
+ DFList[P->ID].NameMatch = NumPatterns != 0;
+ for (unsigned I = 0; I != NumPatterns; I++)
+ {
+ if (regexec(&Patterns[I],P.Name(),0,0,0) == 0)
+ DFList[P->ID].NameMatch &= true;
+ else
+ DFList[P->ID].NameMatch = false;
+ }
+
+ // Doing names only, drop any that dont match..
+ if (NamesOnly == true && DFList[P->ID].NameMatch == false)
+ continue;
+
+ // Find the proper version to use.
+ pkgCache::VerIterator V = Plcy.GetCandidateVer(P);
+ if (V.end() == false)
+ DFList[P->ID].Df = V.DescriptionList().FileList();
+ }
+
+ // Include all the packages that provide matching names too
+ for (pkgCache::PkgIterator P = Cache.PkgBegin(); P.end() == false; P++)
+ {
+ if (DFList[P->ID].NameMatch == false)
+ continue;
+
+ for (pkgCache::PrvIterator Prv = P.ProvidesList() ; Prv.end() == false; Prv++)
+ {
+ pkgCache::VerIterator V = Plcy.GetCandidateVer(Prv.OwnerPkg());
+ if (V.end() == false)
+ {
+ DFList[Prv.OwnerPkg()->ID].Df = V.DescriptionList().FileList();
+ DFList[Prv.OwnerPkg()->ID].NameMatch = true;
+ }
+ }
+ }
+
+ LocalitySort(&DFList->Df,Cache.HeaderP->PackageCount,sizeof(*DFList));
+
+ // Iterate over all the version records and check them
+ for (ExDescFile *J = DFList; J->Df != 0; J++)
+ {
+ pkgRecords::Parser &P = Recs.Lookup(pkgCache::DescFileIterator(Cache,J->Df));
+
+ bool Match = true;
+ if (J->NameMatch == false)
+ {
+ string LongDesc = P.LongDesc();
+ Match = NumPatterns != 0;
+ for (unsigned I = 0; I != NumPatterns; I++)
+ {
+ if (regexec(&Patterns[I],LongDesc.c_str(),0,0,0) == 0)
+ Match &= true;
+ else
+ Match = false;
+ }
+ }
+
+ if (Match == true)
+ {
+ if (ShowFull == true)
+ {
+ const char *Start;
+ const char *End;
+ P.GetRec(Start,End);
+ fwrite(Start,End-Start,1,stdout);
+ putc('\n',stdout);
+ }
+ else
+ printf("%s - %s\n",P.Name().c_str(),P.ShortDesc().c_str());
+ }
+ }
+
+ delete [] DFList;
+ for (unsigned I = 0; I != NumPatterns; I++)
+ regfree(&Patterns[I]);
+ if (ferror(stdout))
+ return _error->Error("Write to stdout failed");
+ return true;
+}
+ /*}}}*/
+// ShowPackage - Dump the package record to the screen /*{{{*/
+// ---------------------------------------------------------------------