1 /*
2 * Copyright 2011-2013, Oliver Tappe <zooey@hirschkaefer.de>
3 * Distributed under the terms of the MIT License.
4 */
5
6
7 #include <dirent.h>
8 #include <errno.h>
9 #include <getopt.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13
14 #include <map>
15
16 #include <Entry.h>
17 #include <ObjectList.h>
18 #include <Path.h>
19 #include <String.h>
20
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>
30
31 #include "package_repo.h"
32
33
34 using BPackageKit::BHPKG::BRepositoryWriterListener;
35 using BPackageKit::BHPKG::BRepositoryWriter;
36 using namespace BPackageKit::BHPKG;
37 using namespace BPackageKit;
38
39
40 static bool sTrustFilenames = false;
41
42
operator <(const BPackageInfo & left,const BPackageInfo & right)43 bool operator< (const BPackageInfo & left, const BPackageInfo & right)
44 {
45 if (sTrustFilenames)
46 return left.FileName().Compare(right.FileName()) < 0;
47
48 if (left.Name() != right.Name())
49 return left.Name() < right.Name();
50
51 return left.Version().Compare(right.Version()) < 0;
52 }
53
54
55 namespace
56 {
57
58
59 typedef std::map<BPackageInfo, bool> PackageInfos;
60
61
62 status_t
parsePackageListFile(const char * packageListFileName,BObjectList<BString> * packageFileNames)63 parsePackageListFile(const char* packageListFileName,
64 BObjectList<BString>* packageFileNames)
65 {
66 FILE* packageListFile = fopen(packageListFileName, "r");
67 if (packageListFile == NULL) {
68 printf("Error: Unable to open %s\n", packageListFileName);
69 return B_ENTRY_NOT_FOUND;
70 }
71 char buffer[128];
72 while (fgets(buffer, sizeof(buffer), packageListFile) != NULL) {
73 BString* packageFileName = new(std::nothrow) BString(buffer);
74 if (packageFileName == NULL) {
75 printf("Error: Out of memory when reading from %s\n",
76 packageListFileName);
77 fclose(packageListFile);
78 return B_NO_MEMORY;
79 }
80 packageFileName->Trim();
81 packageFileNames->AddItem(packageFileName);
82 }
83 fclose(packageListFile);
84 return B_OK;
85 }
86
87
88 struct PackageInfosCollector : BRepositoryContentHandler {
PackageInfosCollector__anonf4669b410111::PackageInfosCollector89 PackageInfosCollector(PackageInfos& packageInfos,
90 BHPKG::BErrorOutput* errorOutput)
91 :
92 fPackageInfos(packageInfos),
93 fErrorOutput(errorOutput),
94 fRepositoryInfo(),
95 fPackageInfo(),
96 fPackageInfoContentHandler(fPackageInfo, fErrorOutput)
97 {
98 }
99
HandlePackage__anonf4669b410111::PackageInfosCollector100 virtual status_t HandlePackage(const char* packageName)
101 {
102 fPackageInfo.Clear();
103 return B_OK;
104 }
105
HandlePackageAttribute__anonf4669b410111::PackageInfosCollector106 virtual status_t HandlePackageAttribute(
107 const BPackageInfoAttributeValue& value)
108 {
109 return fPackageInfoContentHandler.HandlePackageAttribute(value);
110 }
111
HandlePackageDone__anonf4669b410111::PackageInfosCollector112 virtual status_t HandlePackageDone(const char* packageName)
113 {
114 if (fPackageInfo.InitCheck() != B_OK) {
115 const BString& name = fPackageInfo.Name();
116 printf("Error: package-info in repository for '%s' is incomplete\n",
117 name.IsEmpty() ? "<unknown-package>" : name.String());
118 return B_BAD_DATA;
119 }
120
121 if (sTrustFilenames) {
122 // Cache the canonical name to avoid repeated fallbacks
123 fPackageInfo.SetFileName(fPackageInfo.CanonicalFileName());
124 }
125
126 fPackageInfos[fPackageInfo] = false;
127 return B_OK;
128 }
129
HandleRepositoryInfo__anonf4669b410111::PackageInfosCollector130 virtual status_t HandleRepositoryInfo(const BRepositoryInfo& repositoryInfo)
131 {
132 fRepositoryInfo = repositoryInfo;
133 return B_OK;
134 }
135
HandleErrorOccurred__anonf4669b410111::PackageInfosCollector136 virtual void HandleErrorOccurred()
137 {
138 }
139
RepositoryInfo__anonf4669b410111::PackageInfosCollector140 const BRepositoryInfo& RepositoryInfo() const
141 {
142 return fRepositoryInfo;
143 }
144
145 private:
146 PackageInfos& fPackageInfos;
147 BHPKG::BErrorOutput* fErrorOutput;
148 BRepositoryInfo fRepositoryInfo;
149 BPackageInfo fPackageInfo;
150 BPackageInfoContentHandler fPackageInfoContentHandler;
151 };
152
153
154 class RepositoryWriterListener : public BRepositoryWriterListener {
155 public:
RepositoryWriterListener(bool verbose,bool quiet)156 RepositoryWriterListener(bool verbose, bool quiet)
157 : fVerbose(verbose), fQuiet(quiet)
158 {
159 }
160
PrintErrorVarArgs(const char * format,va_list args)161 virtual void PrintErrorVarArgs(const char* format, va_list args)
162 {
163 vfprintf(stderr, format, args);
164 }
165
OnPackageAdded(const BPackageInfo & packageInfo)166 virtual void OnPackageAdded(const BPackageInfo& packageInfo)
167 {
168 }
169
OnRepositoryInfoSectionDone(uint32 uncompressedSize)170 virtual void OnRepositoryInfoSectionDone(uint32 uncompressedSize)
171 {
172 if (fQuiet || !fVerbose)
173 return;
174
175 printf("----- Repository Info Section --------------------\n");
176 printf("repository info size: %10" B_PRIu32 " (uncompressed)\n",
177 uncompressedSize);
178 }
179
OnPackageAttributesSectionDone(uint32 stringCount,uint32 uncompressedSize)180 virtual void OnPackageAttributesSectionDone(uint32 stringCount,
181 uint32 uncompressedSize)
182 {
183 if (fQuiet || !fVerbose)
184 return;
185
186 printf("----- Package Attribute Section -------------------\n");
187 printf("string count: %10" B_PRIu32 "\n", stringCount);
188 printf("package attributes size: %10" B_PRIu32 " (uncompressed)\n",
189 uncompressedSize);
190 }
191
OnRepositoryDone(uint32 headerSize,uint32 repositoryInfoSize,uint32 licenseCount,uint32 packageCount,uint32 packageAttributesSize,uint64 totalSize)192 virtual void OnRepositoryDone(uint32 headerSize, uint32 repositoryInfoSize,
193 uint32 licenseCount, uint32 packageCount, uint32 packageAttributesSize,
194 uint64 totalSize)
195 {
196 if (fQuiet || !fVerbose)
197 return;
198
199 printf("----- Package Repository Info -----\n");
200 if (fVerbose)
201 printf("embedded license count %10" B_PRIu32 "\n", licenseCount);
202 printf("package count %10" B_PRIu32 "\n", packageCount);
203 printf("-----------------------------------\n");
204 printf("header size: %10" B_PRIu32 "\n", headerSize);
205 printf("repository header size: %10" B_PRIu32 "\n",
206 repositoryInfoSize);
207 printf("package attributes size: %10" B_PRIu32 "\n",
208 packageAttributesSize);
209 printf("total size: %10" B_PRIu64 "\n", totalSize);
210 printf("-----------------------------------\n");
211 }
212
213 private:
214 bool fVerbose;
215 bool fQuiet;
216 };
217
218
219 } // anonymous namespace
220
221
222 int
command_update(int argc,const char * const * argv)223 command_update(int argc, const char* const* argv)
224 {
225 const char* changeToDirectory = NULL;
226 bool quiet = false;
227 bool verbose = false;
228
229 while (true) {
230 static struct option sLongOptions[] = {
231 { "help", no_argument, 0, 'h' },
232 { "quiet", no_argument, 0, 'q' },
233 { "verbose", no_argument, 0, 'v' },
234 { "trust-filenames", no_argument, 0, 't' },
235 { 0, 0, 0, 0 }
236 };
237
238 opterr = 0; // don't print errors
239 int c = getopt_long(argc, (char**)argv, "+C:hqvt", sLongOptions, NULL);
240 if (c == -1)
241 break;
242
243 switch (c) {
244 case 'C':
245 changeToDirectory = optarg;
246 break;
247
248 case 'h':
249 print_usage_and_exit(false);
250 break;
251
252 case 'q':
253 quiet = true;
254 break;
255
256 case 'v':
257 verbose = true;
258 break;
259
260 case 't':
261 sTrustFilenames = true;
262 break;
263
264 default:
265 print_usage_and_exit(true);
266 break;
267 }
268 }
269
270 // The remaining three arguments are the source and target repository file
271 // plus the package list file.
272 if (optind + 3 != argc)
273 print_usage_and_exit(true);
274
275 const char* sourceRepositoryFileName = argv[optind++];
276 const char* targetRepositoryFileName = argv[optind++];
277 const char* packageListFileName = argv[optind++];
278
279 BStandardErrorOutput errorOutput;
280 RepositoryWriterListener listener(verbose, quiet);
281
282 BEntry sourceRepositoryEntry(sourceRepositoryFileName);
283 if (!sourceRepositoryEntry.Exists()) {
284 listener.PrintError(
285 "Error: given source repository file '%s' doesn't exist!\n",
286 sourceRepositoryFileName);
287 return 1;
288 }
289 // determine path for 'repo.info' file from given new repository file
290 BString repositoryInfoFileName(targetRepositoryFileName);
291 repositoryInfoFileName.Append(".info");
292 BEntry repositoryInfoEntry(repositoryInfoFileName.String());
293 BRepositoryInfo repositoryInfo(repositoryInfoEntry);
294 status_t result = repositoryInfo.InitCheck();
295 if (result != B_OK) {
296 listener.PrintError(
297 "Error: can't parse/read repository-info file %s : %s\n",
298 repositoryInfoFileName.String(), strerror(result));
299 return 1;
300 }
301
302 // open source repository
303 BRepositoryReader repositoryReader(&errorOutput);
304 result = repositoryReader.Init(sourceRepositoryFileName);
305 if (result != B_OK) {
306 listener.PrintError(
307 "Error: can't read from old repository file : %s\n",
308 strerror(result));
309 return 1;
310 }
311
312 // collect package infos from source repository
313 PackageInfos packageInfos;
314 PackageInfosCollector packageInfosCollector(packageInfos, &errorOutput);
315 result = repositoryReader.ParseContent(&packageInfosCollector);
316 if (result != B_OK) {
317 listener.PrintError(
318 "Error: couldn't fetch package infos from old repository : %s\n",
319 strerror(result));
320 return 1;
321 }
322
323 // create new repository
324 BRepositoryWriter repositoryWriter(&listener, &repositoryInfo);
325 BString tempRepositoryFileName(targetRepositoryFileName);
326 tempRepositoryFileName += ".___new___";
327 if ((result = repositoryWriter.Init(tempRepositoryFileName.String()))
328 != B_OK) {
329 listener.PrintError("Error: can't initialize repository-writer : %s\n",
330 strerror(result));
331 return 1;
332 }
333
334 BEntry tempRepositoryFile(tempRepositoryFileName.String());
335 BPath targetRepositoryFilePath(targetRepositoryFileName);
336
337 BObjectList<BString> packageNames(100, true);
338 if ((result = parsePackageListFile(packageListFileName, &packageNames))
339 != B_OK) {
340 listener.PrintError(
341 "Error: Failed to read package-list-file \"%s\": %s\n",
342 packageListFileName, strerror(result));
343 return 1;
344 }
345
346 // change directory, if requested
347 if (changeToDirectory != NULL) {
348 if (chdir(changeToDirectory) != 0) {
349 listener.PrintError(
350 "Error: Failed to change the current working directory to "
351 "\"%s\": %s\n", changeToDirectory, strerror(errno));
352 return 1;
353 }
354 }
355
356 // add all given package files
357 for (int i = 0; i < packageNames.CountItems(); ++i) {
358 BPackageInfo packageInfo;
359
360 if (sTrustFilenames)
361 packageInfo.SetFileName(*packageNames.ItemAt(i));
362 else {
363 if ((result = packageInfo.ReadFromPackageFile(
364 packageNames.ItemAt(i)->String())) != B_OK) {
365 listener.PrintError(
366 "Error: Failed to read package-info from \"%s\": %s\n",
367 packageNames.ItemAt(i)->String(), strerror(result));
368 return 1;
369 }
370 }
371
372 PackageInfos::iterator infoIter = packageInfos.find(packageInfo);
373 if (infoIter != packageInfos.end()) {
374 infoIter->second = true;
375 if ((result = repositoryWriter.AddPackageInfo(infoIter->first))
376 != B_OK)
377 return 1;
378 if (verbose) {
379 printf("keeping '%s-%s'\n", infoIter->first.Name().String(),
380 infoIter->first.Version().ToString().String());
381 }
382 } else {
383 BEntry entry(packageNames.ItemAt(i)->String());
384 if ((result = repositoryWriter.AddPackage(entry)) != B_OK)
385 return 1;
386 if (!quiet) {
387 printf("added '%s' ...\n",
388 packageNames.ItemAt(i)->String());
389 }
390 }
391 }
392
393 // tell about packages dropped from repository
394 PackageInfos::const_iterator infoIter;
395 for (infoIter = packageInfos.begin(); infoIter != packageInfos.end();
396 ++infoIter) {
397 if (!infoIter->second) {
398 printf("dropped '%s-%s'\n", infoIter->first.Name().String(),
399 infoIter->first.Version().ToString().String());
400 }
401 }
402
403
404 // write the repository
405 result = repositoryWriter.Finish();
406 if (result != B_OK)
407 return 1;
408
409 result = tempRepositoryFile.Rename(targetRepositoryFilePath.Leaf(), true);
410 if (result != B_OK) {
411 printf("Error: unable to rename repository %s to %s - %s\n",
412 tempRepositoryFileName.String(), targetRepositoryFileName,
413 strerror(result));
414 return 1;
415 }
416
417 if (verbose) {
418 printf("\nsuccessfully created repository '%s'\n",
419 targetRepositoryFileName);
420 }
421
422 return 0;
423 }
424