xref: /haiku/src/bin/package/command_list.cpp (revision 73254051b196497dfee9ab89eb0c2f60cc305819)
1 /*
2  * Copyright 2009-2013, 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 <Entry.h>
16 #include <package/hpkg/PackageContentHandler.h>
17 #include <package/hpkg/PackageEntry.h>
18 #include <package/hpkg/PackageEntryAttribute.h>
19 #include <package/hpkg/PackageInfoAttributeValue.h>
20 #include <package/hpkg/PackageReader.h>
21 #include <package/hpkg/StandardErrorOutput.h>
22 #include <package/hpkg/v1/PackageContentHandler.h>
23 #include <package/hpkg/v1/PackageEntry.h>
24 #include <package/hpkg/v1/PackageEntryAttribute.h>
25 #include <package/hpkg/v1/PackageReader.h>
26 
27 #include <package/PackageInfo.h>
28 
29 #include "package.h"
30 #include "PackageInfoPrinter.h"
31 
32 
33 using namespace BPackageKit;
34 using BPackageKit::BHPKG::BErrorOutput;
35 using BPackageKit::BHPKG::BPackageInfoAttributeValue;
36 using BPackageKit::BHPKG::BStandardErrorOutput;
37 
38 
39 struct VersionPolicyV1 {
40 	typedef BPackageKit::BHPKG::V1::BPackageContentHandler
41 		PackageContentHandler;
42 	typedef BPackageKit::BHPKG::V1::BPackageEntry PackageEntry;
43 	typedef BPackageKit::BHPKG::V1::BPackageEntryAttribute
44 		PackageEntryAttribute;
45 	typedef BPackageKit::BHPKG::V1::BPackageReader PackageReader;
46 
47 	static inline uint64 PackageDataSize(
48 		const BPackageKit::BHPKG::V1::BPackageData& data)
49 	{
50 		return data.UncompressedSize();
51 	}
52 
53 	static inline status_t InitReader(PackageReader& packageReader,
54 		const char* fileName)
55 	{
56 		return packageReader.Init(fileName);
57 	}
58 };
59 
60 struct VersionPolicyV2 {
61 	typedef BPackageKit::BHPKG::BPackageContentHandler PackageContentHandler;
62 	typedef BPackageKit::BHPKG::BPackageEntry PackageEntry;
63 	typedef BPackageKit::BHPKG::BPackageEntryAttribute PackageEntryAttribute;
64 	typedef BPackageKit::BHPKG::BPackageReader PackageReader;
65 
66 	static inline uint64 PackageDataSize(
67 		const BPackageKit::BHPKG::BPackageData& data)
68 	{
69 		return data.Size();
70 	}
71 
72 	static inline status_t InitReader(PackageReader& packageReader,
73 		const char* fileName)
74 	{
75 		return packageReader.Init(fileName,
76 			BPackageKit::BHPKG
77 				::B_HPKG_READER_DONT_PRINT_VERSION_MISMATCH_MESSAGE);
78 	}
79 };
80 
81 
82 enum ListMode {
83 	LIST_ALL,
84 	LIST_PATHS_ONLY,
85 	LIST_META_INFO_ONLY
86 };
87 
88 
89 template<typename VersionPolicy>
90 struct PackageContentListHandler : VersionPolicy::PackageContentHandler {
91 	PackageContentListHandler(bool listEntries, bool listAttributes)
92 		:
93 		fPrinter(),
94 		fLevel(0),
95 		fListEntries(listEntries),
96 		fListAttribute(listEntries && listAttributes)
97 	{
98 	}
99 
100 	virtual status_t HandleEntry(typename VersionPolicy::PackageEntry* entry)
101 	{
102 		if (!fListEntries)
103 			return B_OK;
104 
105 		fLevel++;
106 
107 		int indentation = (fLevel - 1) * 2;
108 		printf("%*s", indentation, "");
109 
110 		// name and size
111 		printf("%-*s", indentation < 32 ? 32 - indentation : 0, entry->Name());
112 		printf("  %8llu",
113 			(unsigned long long)VersionPolicy::PackageDataSize(entry->Data()));
114 
115 		// time
116 		struct tm* time = localtime(&entry->ModifiedTime().tv_sec);
117 		printf("  %04d-%02d-%02d %02d:%02d:%02d",
118 			1900 + time->tm_year, time->tm_mon + 1, time->tm_mday,
119 			time->tm_hour, time->tm_min, time->tm_sec);
120 
121 		// file type
122 		mode_t mode = entry->Mode();
123 		if (S_ISREG(mode))
124 			printf("  -");
125 		else if (S_ISDIR(mode))
126 			printf("  d");
127 		else if (S_ISLNK(mode))
128 			printf("  l");
129 		else
130 			printf("  ?");
131 
132 		// permissions
133 		char buffer[4];
134 		printf("%s", _PermissionString(buffer, mode >> 6,
135 			(mode & S_ISUID) != 0));
136 		printf("%s", _PermissionString(buffer, mode >> 3,
137 			(mode & S_ISGID) != 0));
138 		printf("%s", _PermissionString(buffer, mode, false));
139 
140 		// print the symlink path
141 		if (S_ISLNK(mode))
142 			printf("  -> %s", entry->SymlinkPath());
143 
144 		printf("\n");
145 		return B_OK;
146 	}
147 
148 	virtual status_t HandleEntryAttribute(
149 		typename VersionPolicy::PackageEntry* entry,
150 		typename VersionPolicy::PackageEntryAttribute* attribute)
151 	{
152 		if (!fListAttribute)
153 			return B_OK;
154 
155 		int indentation = fLevel * 2;
156 		printf("%*s<", indentation, "");
157 		printf("%-*s  %8llu", indentation < 31 ? 31 - indentation : 0,
158 			attribute->Name(),
159 			(unsigned long long)VersionPolicy::PackageDataSize(
160 				attribute->Data()));
161 
162 		uint32 type = attribute->Type();
163 		if (isprint(type & 0xff) && isprint((type >> 8) & 0xff)
164 			 && isprint((type >> 16) & 0xff) && isprint(type >> 24)) {
165 			printf("  '%c%c%c%c'", int(type >> 24), int((type >> 16) & 0xff),
166 				int((type >> 8) & 0xff), int(type & 0xff));
167 		} else
168 			printf("  %#" B_PRIx32, type);
169 
170 		printf(">\n");
171 		return B_OK;
172 	}
173 
174 	virtual status_t HandleEntryDone(
175 		typename VersionPolicy::PackageEntry* entry)
176 	{
177 		if (!fListEntries)
178 			return B_OK;
179 
180 		fLevel--;
181 		return B_OK;
182 	}
183 
184 	virtual status_t HandlePackageAttribute(
185 		const BPackageInfoAttributeValue& value)
186 	{
187 		if (value.attributeID == B_PACKAGE_INFO_NAME)
188 			printf("package-attributes:\n");
189 
190 		if (!fPrinter.PrintAttribute(value)) {
191 			printf("*** Invalid package attribute section: unexpected "
192 				"package attribute id %d encountered\n", value.attributeID);
193 			return B_BAD_DATA;
194 		}
195 
196 		return B_OK;
197 	}
198 
199 	virtual void HandleErrorOccurred()
200 	{
201 	}
202 
203 private:
204 	static const char* _PermissionString(char* buffer, uint32 mode, bool sticky)
205 	{
206 		buffer[0] = (mode & 0x4) != 0 ? 'r' : '-';
207 		buffer[1] = (mode & 0x2) != 0 ? 'w' : '-';
208 
209 		if ((mode & 0x1) != 0)
210 			buffer[2] = sticky ? 's' : 'x';
211 		else
212 			buffer[2] = '-';
213 
214 		buffer[3] = '\0';
215 		return buffer;
216 	}
217 
218 	static void _PrintPackageVersion(const BPackageVersionData& version)
219 	{
220 		printf("%s", BPackageVersion(version).ToString().String());
221 	}
222 
223 private:
224 	PackageInfoPrinter	fPrinter;
225 	int					fLevel;
226 	bool				fListEntries;
227 	bool				fListAttribute;
228 };
229 
230 
231 template<typename VersionPolicy>
232 struct PackageContentListPathsHandler : VersionPolicy::PackageContentHandler {
233 	PackageContentListPathsHandler()
234 		:
235 		fPathComponents()
236 	{
237 	}
238 
239 	virtual status_t HandleEntry(typename VersionPolicy::PackageEntry* entry)
240 	{
241 		fPathComponents.Add(entry->Name());
242 		printf("%s\n", fPathComponents.Join("/").String());
243 		return B_OK;
244 	}
245 
246 	virtual status_t HandleEntryAttribute(
247 		typename VersionPolicy::PackageEntry* entry,
248 		typename VersionPolicy::PackageEntryAttribute* attribute)
249 	{
250 		return B_OK;
251 	}
252 
253 	virtual status_t HandleEntryDone(
254 		typename VersionPolicy::PackageEntry* entry)
255 	{
256 		fPathComponents.Remove(fPathComponents.CountStrings() - 1);
257 		return B_OK;
258 	}
259 
260 	virtual status_t HandlePackageAttribute(
261 		const BPackageInfoAttributeValue& value)
262 	{
263 		return B_OK;
264 	}
265 
266 	virtual void HandleErrorOccurred()
267 	{
268 	}
269 
270 private:
271 	BStringList	fPathComponents;
272 };
273 
274 
275 template<typename VersionPolicy>
276 static void
277 do_list(const char* packageFileName, bool listAttributes, ListMode listMode,
278 	bool ignoreVersionError)
279 {
280 	// open package
281 	BStandardErrorOutput errorOutput;
282 	typename VersionPolicy::PackageReader packageReader(&errorOutput);
283 	status_t error = VersionPolicy::InitReader(packageReader, packageFileName);
284 	if (error != B_OK) {
285 		if (ignoreVersionError && error == B_MISMATCHED_VALUES)
286 			return;
287 		exit(1);
288 	}
289 
290 	// list
291 	switch (listMode) {
292 		case LIST_PATHS_ONLY:
293 		{
294 			PackageContentListPathsHandler<VersionPolicy> handler;
295 			error = packageReader.ParseContent(&handler);
296 			break;
297 		}
298 
299 		case LIST_ALL:
300 		case LIST_META_INFO_ONLY:
301 		{
302 			PackageContentListHandler<VersionPolicy> handler(
303 				listMode != LIST_META_INFO_ONLY, listAttributes);
304 			error = packageReader.ParseContent(&handler);
305 		}
306 	}
307 
308 	if (error != B_OK)
309 		exit(1);
310 
311 	exit(0);
312 }
313 
314 
315 int
316 command_list(int argc, const char* const* argv)
317 {
318 	ListMode listMode = LIST_ALL;
319 	bool listAttributes = false;
320 
321 	while (true) {
322 		static struct option sLongOptions[] = {
323 			{ "help", no_argument, 0, 'h' },
324 			{ 0, 0, 0, 0 }
325 		};
326 
327 		opterr = 0; // don't print errors
328 		int c = getopt_long(argc, (char**)argv, "+ahip", sLongOptions, NULL);
329 		if (c == -1)
330 			break;
331 
332 		switch (c) {
333 			case 'a':
334 				listAttributes = true;
335 				break;
336 
337 			case 'i':
338 				listMode = LIST_META_INFO_ONLY;
339 				break;
340 
341 			case 'h':
342 				print_usage_and_exit(false);
343 				break;
344 
345 			case 'p':
346 				listMode = LIST_PATHS_ONLY;
347 				break;
348 
349 			default:
350 				print_usage_and_exit(true);
351 				break;
352 		}
353 	}
354 
355 	// One argument should remain -- the package file name.
356 	if (optind + 1 != argc)
357 		print_usage_and_exit(true);
358 
359 	const char* packageFileName = argv[optind++];
360 
361 	// If the file doesn't look like a package file, try to load it as a
362 	// package info file.
363 	if (!BString(packageFileName).EndsWith(".hpkg")) {
364 		struct ErrorListener : BPackageInfo::ParseErrorListener {
365 			virtual void OnError(const BString& msg, int line, int col)
366 			{
367 				fprintf(stderr, "%s:%d:%d: %s\n", fPath, line, col,
368 					msg.String());
369 			}
370 
371 			const char*	fPath;
372 		} errorListener;
373 		errorListener.fPath = packageFileName;
374 
375 		BPackageInfo info;
376 		if (info.ReadFromConfigFile(BEntry(packageFileName), &errorListener)
377 				!= B_OK) {
378 			return 1;
379 		}
380 
381 		printf("package-attributes:\n");
382 		PackageInfoPrinter().PrintPackageInfo(info);
383 		return 0;
384 	}
385 
386 	BHPKG::BStandardErrorOutput errorOutput;
387 
388 	// current package file format version
389 	do_list<VersionPolicyV2>(packageFileName, listAttributes, listMode, true);
390 	do_list<VersionPolicyV1>(packageFileName, listAttributes, listMode, false);
391 
392 	return 0;
393 }
394