btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / bin / package_repo / command_update.cpp
blob3e06ee69cb73927914bc58d4c6679b52278c899f
1 /*
2 * Copyright 2011-2013, Oliver Tappe <zooey@hirschkaefer.de>
3 * Distributed under the terms of the MIT License.
4 */
7 #include <dirent.h>
8 #include <errno.h>
9 #include <getopt.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
14 #include <map>
16 #include <Entry.h>
17 #include <ObjectList.h>
18 #include <Path.h>
19 #include <String.h>
21 #include <package/hpkg/HPKGDefs.h>
22 #include <package/hpkg/PackageInfoAttributeValue.h>
23 #include <package/hpkg/RepositoryContentHandler.h>
24 #include <package/hpkg/RepositoryReader.h>
25 #include <package/hpkg/RepositoryWriter.h>
26 #include <package/hpkg/StandardErrorOutput.h>
27 #include <package/PackageInfo.h>
28 #include <package/PackageInfoContentHandler.h>
29 #include <package/RepositoryInfo.h>
31 #include "package_repo.h"
34 using BPackageKit::BHPKG::BRepositoryWriterListener;
35 using BPackageKit::BHPKG::BRepositoryWriter;
36 using namespace BPackageKit::BHPKG;
37 using namespace BPackageKit;
40 bool operator< (const BPackageInfo & left, const BPackageInfo & right)
42 if (left.Name() != right.Name())
43 return left.Name() < right.Name();
44 return left.Version().Compare(right.Version()) < 0;
48 namespace
52 typedef std::map<BPackageInfo, bool> PackageInfos;
55 status_t
56 parsePackageListFile(const char* packageListFileName,
57 BObjectList<BString>* packageFileNames)
59 FILE* packageListFile = fopen(packageListFileName, "r");
60 if (packageListFile == NULL) {
61 printf("Error: Unable to open %s\n", packageListFileName);
62 return B_ENTRY_NOT_FOUND;
64 char buffer[128];
65 while (fgets(buffer, sizeof(buffer), packageListFile) != NULL) {
66 BString* packageFileName = new(std::nothrow) BString(buffer);
67 if (packageFileName == NULL) {
68 printf("Error: Out of memory when reading from %s\n",
69 packageListFileName);
70 fclose(packageListFile);
71 return B_NO_MEMORY;
73 packageFileName->Trim();
74 packageFileNames->AddItem(packageFileName);
76 fclose(packageListFile);
77 return B_OK;
81 struct PackageInfosCollector : BRepositoryContentHandler {
82 PackageInfosCollector(PackageInfos& packageInfos,
83 BHPKG::BErrorOutput* errorOutput)
85 fPackageInfos(packageInfos),
86 fErrorOutput(errorOutput),
87 fRepositoryInfo(),
88 fPackageInfo(),
89 fPackageInfoContentHandler(fPackageInfo, fErrorOutput)
93 virtual status_t HandlePackage(const char* packageName)
95 fPackageInfo.Clear();
96 return B_OK;
99 virtual status_t HandlePackageAttribute(
100 const BPackageInfoAttributeValue& value)
102 return fPackageInfoContentHandler.HandlePackageAttribute(value);
105 virtual status_t HandlePackageDone(const char* packageName)
107 if (fPackageInfo.InitCheck() != B_OK) {
108 const BString& name = fPackageInfo.Name();
109 printf("Error: package-info in repository for '%s' is incomplete\n",
110 name.IsEmpty() ? "<unknown-package>" : name.String());
111 return B_BAD_DATA;
113 fPackageInfos[fPackageInfo] = false;
114 return B_OK;
117 virtual status_t HandleRepositoryInfo(const BRepositoryInfo& repositoryInfo)
119 fRepositoryInfo = repositoryInfo;
120 return B_OK;
123 virtual void HandleErrorOccurred()
127 const BRepositoryInfo& RepositoryInfo() const
129 return fRepositoryInfo;
132 private:
133 PackageInfos& fPackageInfos;
134 BHPKG::BErrorOutput* fErrorOutput;
135 BRepositoryInfo fRepositoryInfo;
136 BPackageInfo fPackageInfo;
137 BPackageInfoContentHandler fPackageInfoContentHandler;
141 class RepositoryWriterListener : public BRepositoryWriterListener {
142 public:
143 RepositoryWriterListener(bool verbose, bool quiet)
144 : fVerbose(verbose), fQuiet(quiet)
148 virtual void PrintErrorVarArgs(const char* format, va_list args)
150 vfprintf(stderr, format, args);
153 virtual void OnPackageAdded(const BPackageInfo& packageInfo)
157 virtual void OnRepositoryInfoSectionDone(uint32 uncompressedSize)
159 if (fQuiet || !fVerbose)
160 return;
162 printf("----- Repository Info Section --------------------\n");
163 printf("repository info size: %10" B_PRIu32 " (uncompressed)\n",
164 uncompressedSize);
167 virtual void OnPackageAttributesSectionDone(uint32 stringCount,
168 uint32 uncompressedSize)
170 if (fQuiet || !fVerbose)
171 return;
173 printf("----- Package Attribute Section -------------------\n");
174 printf("string count: %10" B_PRIu32 "\n", stringCount);
175 printf("package attributes size: %10" B_PRIu32 " (uncompressed)\n",
176 uncompressedSize);
179 virtual void OnRepositoryDone(uint32 headerSize, uint32 repositoryInfoSize,
180 uint32 licenseCount, uint32 packageCount, uint32 packageAttributesSize,
181 uint64 totalSize)
183 if (fQuiet || !fVerbose)
184 return;
186 printf("----- Package Repository Info -----\n");
187 if (fVerbose)
188 printf("embedded license count %10" B_PRIu32 "\n", licenseCount);
189 printf("package count %10" B_PRIu32 "\n", packageCount);
190 printf("-----------------------------------\n");
191 printf("header size: %10" B_PRIu32 "\n", headerSize);
192 printf("repository header size: %10" B_PRIu32 "\n",
193 repositoryInfoSize);
194 printf("package attributes size: %10" B_PRIu32 "\n",
195 packageAttributesSize);
196 printf("total size: %10" B_PRIu64 "\n", totalSize);
197 printf("-----------------------------------\n");
200 private:
201 bool fVerbose;
202 bool fQuiet;
206 } // anonymous namespace
210 command_update(int argc, const char* const* argv)
212 const char* changeToDirectory = NULL;
213 bool quiet = false;
214 bool verbose = false;
216 while (true) {
217 static struct option sLongOptions[] = {
218 { "help", no_argument, 0, 'h' },
219 { "quiet", no_argument, 0, 'q' },
220 { "verbose", no_argument, 0, 'v' },
221 { 0, 0, 0, 0 }
224 opterr = 0; // don't print errors
225 int c = getopt_long(argc, (char**)argv, "+C:hqv", sLongOptions, NULL);
226 if (c == -1)
227 break;
229 switch (c) {
230 case 'C':
231 changeToDirectory = optarg;
232 break;
234 case 'h':
235 print_usage_and_exit(false);
236 break;
238 case 'q':
239 quiet = true;
240 break;
242 case 'v':
243 verbose = true;
244 break;
246 default:
247 print_usage_and_exit(true);
248 break;
252 // The remaining three arguments are the source and target repository file
253 // plus the package list file.
254 if (optind + 3 != argc)
255 print_usage_and_exit(true);
257 const char* sourceRepositoryFileName = argv[optind++];
258 const char* targetRepositoryFileName = argv[optind++];
259 const char* packageListFileName = argv[optind++];
261 BStandardErrorOutput errorOutput;
262 RepositoryWriterListener listener(verbose, quiet);
264 BEntry sourceRepositoryEntry(sourceRepositoryFileName);
265 if (!sourceRepositoryEntry.Exists()) {
266 listener.PrintError(
267 "Error: given source repository file '%s' doesn't exist!\n",
268 sourceRepositoryFileName);
269 return 1;
271 // determine path for 'repo.info' file from given new repository file
272 BString repositoryInfoFileName(targetRepositoryFileName);
273 repositoryInfoFileName.Append(".info");
274 BEntry repositoryInfoEntry(repositoryInfoFileName.String());
275 BRepositoryInfo repositoryInfo(repositoryInfoEntry);
276 status_t result = repositoryInfo.InitCheck();
277 if (result != B_OK) {
278 listener.PrintError(
279 "Error: can't parse/read repository-info file %s : %s\n",
280 repositoryInfoFileName.String(), strerror(result));
281 return 1;
284 // open source repository
285 BRepositoryReader repositoryReader(&errorOutput);
286 result = repositoryReader.Init(sourceRepositoryFileName);
287 if (result != B_OK) {
288 listener.PrintError(
289 "Error: can't read from old repository file : %s\n",
290 strerror(result));
291 return 1;
294 // collect package infos from source repository
295 PackageInfos packageInfos;
296 PackageInfosCollector packageInfosCollector(packageInfos, &errorOutput);
297 result = repositoryReader.ParseContent(&packageInfosCollector);
298 if (result != B_OK) {
299 listener.PrintError(
300 "Error: couldn't fetch package infos from old repository : %s\n",
301 strerror(result));
302 return 1;
305 // create new repository
306 BRepositoryWriter repositoryWriter(&listener, &repositoryInfo);
307 BString tempRepositoryFileName(targetRepositoryFileName);
308 tempRepositoryFileName += ".___new___";
309 if ((result = repositoryWriter.Init(tempRepositoryFileName.String()))
310 != B_OK) {
311 listener.PrintError("Error: can't initialize repository-writer : %s\n",
312 strerror(result));
313 return 1;
316 BEntry tempRepositoryFile(tempRepositoryFileName.String());
317 BPath targetRepositoryFilePath(targetRepositoryFileName);
319 BObjectList<BString> packageNames(100, true);
320 if ((result = parsePackageListFile(packageListFileName, &packageNames))
321 != B_OK) {
322 listener.PrintError(
323 "Error: Failed to read package-list-file \"%s\": %s\n",
324 packageListFileName, strerror(result));
325 return 1;
328 // change directory, if requested
329 if (changeToDirectory != NULL) {
330 if (chdir(changeToDirectory) != 0) {
331 listener.PrintError(
332 "Error: Failed to change the current working directory to "
333 "\"%s\": %s\n", changeToDirectory, strerror(errno));
334 return 1;
338 // add all given package files
339 for (int i = 0; i < packageNames.CountItems(); ++i) {
340 BPackageInfo packageInfo;
341 if ((result = packageInfo.ReadFromPackageFile(
342 packageNames.ItemAt(i)->String())) != B_OK) {
343 listener.PrintError(
344 "Error: Failed to read package-info from \"%s\": %s\n",
345 packageNames.ItemAt(i)->String(), strerror(result));
346 return 1;
348 PackageInfos::iterator infoIter = packageInfos.find(packageInfo);
349 if (infoIter != packageInfos.end()) {
350 infoIter->second = true;
351 if ((result = repositoryWriter.AddPackageInfo(infoIter->first))
352 != B_OK)
353 return 1;
354 if (verbose) {
355 printf("keeping '%s-%s'\n", infoIter->first.Name().String(),
356 infoIter->first.Version().ToString().String());
358 } else {
359 BEntry entry(packageNames.ItemAt(i)->String());
360 if ((result = repositoryWriter.AddPackage(entry)) != B_OK)
361 return 1;
362 if (!quiet) {
363 printf("added '%s' ...\n",
364 packageNames.ItemAt(i)->String());
369 // tell about packages dropped from repository
370 PackageInfos::const_iterator infoIter;
371 for (infoIter = packageInfos.begin(); infoIter != packageInfos.end();
372 ++infoIter) {
373 if (!infoIter->second) {
374 printf("dropped '%s-%s'\n", infoIter->first.Name().String(),
375 infoIter->first.Version().ToString().String());
380 // write the repository
381 result = repositoryWriter.Finish();
382 if (result != B_OK)
383 return 1;
385 result = tempRepositoryFile.Rename(targetRepositoryFilePath.Leaf(), true);
386 if (result != B_OK) {
387 printf("Error: unable to rename repository %s to %s - %s\n",
388 tempRepositoryFileName.String(), targetRepositoryFileName,
389 strerror(result));
390 return 1;
393 if (verbose) {
394 printf("\nsuccessfully created repository '%s'\n",
395 targetRepositoryFileName);
398 return 0;