1/*
2 * Copyright 2009-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <ctype.h>
8#include <errno.h>
9#include <getopt.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <time.h>
14
15#include <package/hpkg/PackageContentHandler.h>
16#include <package/hpkg/PackageEntry.h>
17#include <package/hpkg/PackageEntryAttribute.h>
18#include <package/hpkg/PackageInfoAttributeValue.h>
19#include <package/hpkg/PackageReader.h>
20
21#include <package/PackageInfo.h>
22
23#include "package.h"
24#include "StandardErrorOutput.h"
25
26
27using namespace BPackageKit::BHPKG;
28using namespace BPackageKit;
29
30
31struct PackageContentListHandler : BPackageContentHandler {
32	PackageContentListHandler(bool listAttributes)
33		:
34		fLevel(0),
35		fListAttribute(listAttributes)
36	{
37	}
38
39	virtual status_t HandleEntry(BPackageEntry* entry)
40	{
41		fLevel++;
42
43		int indentation = (fLevel - 1) * 2;
44		printf("%*s", indentation, "");
45
46		// name and size
47		printf("%-*s", indentation < 32 ? 32 - indentation : 0, entry->Name());
48		printf("  %8llu", (unsigned long long)entry->Data().UncompressedSize());
49
50		// time
51		struct tm* time = localtime(&entry->ModifiedTime().tv_sec);
52		printf("  %04d-%02d-%02d %02d:%02d:%02d",
53			1900 + time->tm_year, time->tm_mon + 1, time->tm_mday,
54			time->tm_hour, time->tm_min, time->tm_sec);
55
56		// file type
57		mode_t mode = entry->Mode();
58		if (S_ISREG(mode))
59			printf("  -");
60		else if (S_ISDIR(mode))
61			printf("  d");
62		else if (S_ISLNK(mode))
63			printf("  l");
64		else
65			printf("  ?");
66
67		// permissions
68		char buffer[4];
69		printf("%s", _PermissionString(buffer, mode >> 6,
70			(mode & S_ISUID) != 0));
71		printf("%s", _PermissionString(buffer, mode >> 3,
72			(mode & S_ISGID) != 0));
73		printf("%s", _PermissionString(buffer, mode, false));
74
75		// print the symlink path
76		if (S_ISLNK(mode))
77			printf("  -> %s", entry->SymlinkPath());
78
79		printf("\n");
80		return B_OK;
81	}
82
83	virtual status_t HandleEntryAttribute(BPackageEntry* entry,
84		BPackageEntryAttribute* attribute)
85	{
86		if (!fListAttribute)
87			return B_OK;
88
89		int indentation = fLevel * 2;
90		printf("%*s<", indentation, "");
91		printf("%-*s  %8llu", indentation < 31 ? 31 - indentation : 0,
92			attribute->Name(),
93			(unsigned long long)attribute->Data().UncompressedSize());
94
95		uint32 type = attribute->Type();
96		if (isprint(type & 0xff) && isprint((type >> 8) & 0xff)
97			 && isprint((type >> 16) & 0xff) && isprint(type >> 24)) {
98			printf("  '%c%c%c%c'", int(type >> 24), int((type >> 16) & 0xff),
99				int((type >> 8) & 0xff), int(type & 0xff));
100		} else
101			printf("  %#" B_PRIx32, type);
102
103		printf(">\n");
104		return B_OK;
105	}
106
107	virtual status_t HandleEntryDone(BPackageEntry* entry)
108	{
109		fLevel--;
110		return B_OK;
111	}
112
113	virtual status_t HandlePackageAttribute(
114		const BPackageInfoAttributeValue& value)
115	{
116		switch (value.attributeID) {
117			case B_PACKAGE_INFO_NAME:
118				printf("package-attributes:\n");
119				printf("\tname: %s\n", value.string);
120				break;
121
122			case B_PACKAGE_INFO_SUMMARY:
123				printf("\tsummary: %s\n", value.string);
124				break;
125
126			case B_PACKAGE_INFO_DESCRIPTION:
127				printf("\tdescription: %s\n", value.string);
128				break;
129
130			case B_PACKAGE_INFO_VENDOR:
131				printf("\tvendor: %s\n", value.string);
132				break;
133
134			case B_PACKAGE_INFO_PACKAGER:
135				printf("\tpackager: %s\n", value.string);
136				break;
137
138			case B_PACKAGE_INFO_FLAGS:
139				if (value.unsignedInt == 0)
140					break;
141				printf("\tflags:\n");
142				if ((value.unsignedInt & B_PACKAGE_FLAG_APPROVE_LICENSE) != 0)
143					printf("\t\tapprove_license\n");
144				if ((value.unsignedInt & B_PACKAGE_FLAG_SYSTEM_PACKAGE) != 0)
145					printf("\t\tsystem_package\n");
146				break;
147
148			case B_PACKAGE_INFO_ARCHITECTURE:
149				printf("\tarchitecture: %s\n",
150					BPackageInfo::kArchitectureNames[value.unsignedInt]);
151				break;
152
153			case B_PACKAGE_INFO_VERSION:
154				printf("\tversion: ");
155				_PrintPackageVersion(value.version);
156				printf("\n");
157				break;
158
159			case B_PACKAGE_INFO_COPYRIGHTS:
160				printf("\tcopyright: %s\n", value.string);
161				break;
162
163			case B_PACKAGE_INFO_LICENSES:
164				printf("\tlicense: %s\n", value.string);
165				break;
166
167			case B_PACKAGE_INFO_URLS:
168				printf("\tURL: %s\n", value.string);
169				break;
170
171			case B_PACKAGE_INFO_SOURCE_URLS:
172				printf("\tsource URL: %s\n", value.string);
173				break;
174
175			case B_PACKAGE_INFO_PROVIDES:
176				printf("\tprovides: %s", value.resolvable.name);
177				if (value.resolvable.haveVersion) {
178					printf(" = ");
179					_PrintPackageVersion(value.resolvable.version);
180				}
181				if (value.resolvable.haveCompatibleVersion) {
182					printf(" (compatible >= ");
183					_PrintPackageVersion(value.resolvable.compatibleVersion);
184					printf(")");
185				}
186				printf("\n");
187				break;
188
189			case B_PACKAGE_INFO_REQUIRES:
190				printf("\trequires: %s", value.resolvableExpression.name);
191				if (value.resolvableExpression.haveOpAndVersion) {
192					printf(" %s ", BPackageResolvableExpression::kOperatorNames[
193							value.resolvableExpression.op]);
194					_PrintPackageVersion(value.resolvableExpression.version);
195				}
196				printf("\n");
197				break;
198
199			case B_PACKAGE_INFO_SUPPLEMENTS:
200				printf("\tsupplements: %s", value.resolvableExpression.name);
201				if (value.resolvableExpression.haveOpAndVersion) {
202					printf(" %s ", BPackageResolvableExpression::kOperatorNames[
203							value.resolvableExpression.op]);
204					_PrintPackageVersion(value.resolvableExpression.version);
205				}
206				printf("\n");
207				break;
208
209			case B_PACKAGE_INFO_CONFLICTS:
210				printf("\tconflicts: %s", value.resolvableExpression.name);
211				if (value.resolvableExpression.haveOpAndVersion) {
212					printf(" %s ", BPackageResolvableExpression::kOperatorNames[
213							value.resolvableExpression.op]);
214					_PrintPackageVersion(value.resolvableExpression.version);
215				}
216				printf("\n");
217				break;
218
219			case B_PACKAGE_INFO_FRESHENS:
220				printf("\tfreshens: %s", value.resolvableExpression.name);
221				if (value.resolvableExpression.haveOpAndVersion) {
222					printf(" %s ", BPackageResolvableExpression::kOperatorNames[
223							value.resolvableExpression.op]);
224					_PrintPackageVersion(value.resolvableExpression.version);
225				}
226				printf("\n");
227				break;
228
229			case B_PACKAGE_INFO_REPLACES:
230				printf("\treplaces: %s\n", value.string);
231				break;
232
233			case B_PACKAGE_INFO_INSTALL_PATH:
234				printf("\tinstall path: %s\n", value.string);
235				break;
236
237			default:
238				printf(
239					"*** Invalid package attribute section: unexpected "
240					"package attribute id %d encountered\n", value.attributeID);
241				return B_BAD_DATA;
242		}
243
244		return B_OK;
245	}
246
247	virtual void HandleErrorOccurred()
248	{
249	}
250
251private:
252	static const char* _PermissionString(char* buffer, uint32 mode, bool sticky)
253	{
254		buffer[0] = (mode & 0x4) != 0 ? 'r' : '-';
255		buffer[1] = (mode & 0x2) != 0 ? 'w' : '-';
256
257		if ((mode & 0x1) != 0)
258			buffer[2] = sticky ? 's' : 'x';
259		else
260			buffer[2] = '-';
261
262		buffer[3] = '\0';
263		return buffer;
264	}
265
266	static void _PrintPackageVersion(const BPackageVersionData& version)
267	{
268		printf("%s", version.major);
269		if (version.minor != NULL && version.minor[0] != '\0')
270			printf(".%s", version.minor);
271		if (version.micro != NULL && version.micro[0] != '\0')
272			printf(".%s", version.micro);
273		if (version.preRelease != NULL && version.preRelease[0] != '\0')
274			printf("-%s", version.preRelease);
275		if (version.release > 0)
276			printf("-%d", version.release);
277	}
278
279private:
280	int		fLevel;
281	bool	fListAttribute;
282};
283
284
285int
286command_list(int argc, const char* const* argv)
287{
288	bool listAttributes = false;
289
290	while (true) {
291		static struct option sLongOptions[] = {
292			{ "help", no_argument, 0, 'h' },
293			{ 0, 0, 0, 0 }
294		};
295
296		opterr = 0; // don't print errors
297		int c = getopt_long(argc, (char**)argv, "+ha", sLongOptions, NULL);
298		if (c == -1)
299			break;
300
301		switch (c) {
302			case 'a':
303				listAttributes = true;
304				break;
305
306			case 'h':
307				print_usage_and_exit(false);
308				break;
309
310			default:
311				print_usage_and_exit(true);
312				break;
313		}
314	}
315
316	// One argument should remain -- the package file name.
317	if (optind + 1 != argc)
318		print_usage_and_exit(true);
319
320	const char* packageFileName = argv[optind++];
321
322	// open package
323	StandardErrorOutput errorOutput;
324	BPackageReader packageReader(&errorOutput);
325	status_t error = packageReader.Init(packageFileName);
326	if (error != B_OK)
327		return 1;
328
329	// list
330	PackageContentListHandler handler(listAttributes);
331	error = packageReader.ParseContent(&handler);
332	if (error != B_OK)
333		return 1;
334
335	return 0;
336}
337