1/*
2 * Copyright 2004-2009, Axel D��rfler, axeld@pinc-software.de.
3 * Copyright 2002, Ryan Fleet.
4 *
5 * Distributed under the terms of the MIT license.
6 */
7
8
9#include <String.h>
10#include <TypeConstants.h>
11#include <Mime.h>
12
13#include <fs_attr.h>
14
15#include <ctype.h>
16#include <string.h>
17#include <stdio.h>
18
19
20/*!	Dumps the contents of the attribute in the form of raw data. This view
21	is used for the type B_RAW_TYPE, for custom types and for any type that
22	is not directly supported by the utility "addattr".
23*/
24static void
25dump_raw_data(const char *buffer, size_t size)
26{
27	const uint32 kChunkSize = 16;
28	uint32 dumpPosition = 0;
29
30	while (dumpPosition < size) {
31		// Position for this line
32		printf("\t%04" B_PRIx32 ": ", dumpPosition);
33
34		// Print the bytes in form of hexadecimal numbers
35		for (uint32 i = 0; i < kChunkSize; i++) {
36			if (dumpPosition + i < size) {
37				printf("%02x ", (uint8)buffer[dumpPosition + i]);
38			} else
39				printf("   ");
40		}
41
42		// Print the bytes in form of printable characters
43		// (whenever possible)
44		printf(" ");
45		for (uint32 i = 0; i < kChunkSize; i++) {
46			if (dumpPosition < size) {
47				char c = buffer[dumpPosition];
48				putchar(isgraph(c) ? c : '.');
49			} else
50				putchar(' ');
51
52			dumpPosition++;
53		}
54		printf("\n");
55	}
56}
57
58
59static void
60show_attr_contents(BNode& node, const char* attribute, const attr_info& info)
61{
62	// limit size of the attribute, only the first kLimit byte will make it on
63	// screen
64	int kLimit = 256;
65	bool cut = false;
66	off_t size = info.size;
67	if (size > kLimit) {
68		size = kLimit;
69		cut = true;
70	}
71
72	char buffer[kLimit];
73	ssize_t bytesRead = node.ReadAttr(attribute, info.type, 0, buffer, size);
74	if (bytesRead != size) {
75		fprintf(stderr, "Could only read %" B_PRIdOFF " bytes from attribute!\n",
76			size);
77		return;
78	}
79
80	switch (info.type) {
81		case B_INT8_TYPE:
82			printf("%" B_PRId8 "\n", *((int8 *)buffer));
83			break;
84		case B_UINT8_TYPE:
85			printf("%" B_PRIu8 "\n", *((uint8 *)buffer));
86			break;
87		case B_INT16_TYPE:
88			printf("%" B_PRId16 "\n", *((int16 *)buffer));
89			break;
90		case B_UINT16_TYPE:
91			printf("%" B_PRIu16 "\n", *((uint16 *)buffer));
92			break;
93		case B_INT32_TYPE:
94			printf("%" B_PRId32 "\n", *((int32 *)buffer));
95			break;
96		case B_UINT32_TYPE:
97			printf("%" B_PRIu32 "\n", *((uint32 *)buffer));
98			break;
99		case B_INT64_TYPE:
100			printf("%" B_PRId64 "\n", *((int64 *)buffer));
101			break;
102		case B_UINT64_TYPE:
103			printf("%" B_PRIu64 "\n", *((uint64 *)buffer));
104			break;
105		case B_FLOAT_TYPE:
106			printf("%f\n", *((float *)buffer));
107			break;
108		case B_DOUBLE_TYPE:
109			printf("%f\n", *((double *)buffer));
110			break;
111		case B_BOOL_TYPE:
112			printf("%d\n", *((unsigned char *)buffer));
113			break;
114		case B_STRING_TYPE:
115		case B_MIME_STRING_TYPE:
116		case 'MSIG':
117		case 'MSDC':
118		case 'MPTH':
119			printf("%s\n", buffer);
120			break;
121
122		case B_MESSAGE_TYPE:
123		{
124			BMessage message;
125			if (!cut && message.Unflatten(buffer) == B_OK) {
126				putchar('\n');
127				message.PrintToStream();
128				putchar('\n');
129				break;
130			}
131			// supposed to fall through
132		}
133
134		default:
135			// The rest of the attributes types are displayed as raw data
136			putchar('\n');
137			dump_raw_data(buffer, size);
138			putchar('\n');
139			break;
140	}
141}
142
143
144static const char *
145get_type(type_code type)
146{
147	static char buffer[32];
148
149	switch (type) {
150		case B_MIME_STRING_TYPE:
151			return "MIME String";
152		case B_RAW_TYPE:
153			return "Raw Data";
154
155		case B_STRING_TYPE:
156			return "Text";
157		case B_INT64_TYPE:
158			return "Int-64";
159		case B_UINT64_TYPE:
160			return "Uint-64";
161		case B_INT32_TYPE:
162			return "Int-32";
163		case B_UINT32_TYPE:
164			return "Uint-32";
165		case B_INT16_TYPE:
166			return "Int-16";
167		case B_UINT16_TYPE:
168			return "Uint-16";
169		case B_INT8_TYPE:
170			return "Int-8";
171		case B_UINT8_TYPE:
172			return "Uint-8";
173		case B_BOOL_TYPE:
174			return "Boolean";
175		case B_FLOAT_TYPE:
176			return "Float";
177		case B_DOUBLE_TYPE:
178			return "Double";
179
180		case B_MINI_ICON_TYPE:
181			return "Mini Icon";
182		case B_LARGE_ICON_TYPE:
183			return "Icon";
184
185		default:
186		{
187			int32 missed = 0, shift = 24;
188			uint8 value[4];
189			for (int32 i = 0; i < 4; i++, shift -= 8) {
190				value[i] = uint8(type >> shift);
191				if (value[i] < ' ' || value[i] > 127) {
192					value[i] = '.';
193					missed++;
194				}
195			}
196
197			if (missed < 2) {
198				sprintf(buffer, "'%c%c%c%c'", value[0], value[1], value[2],
199					value[3]);
200			} else
201				sprintf(buffer, "0x%08" B_PRIx32, type);
202
203			return buffer;
204		}
205	}
206}
207
208
209int
210main(int argc, char *argv[])
211{
212	const char *program = strrchr(argv[0], '/');
213	if (program == NULL)
214		program = argv[0];
215	else
216		program++;
217
218	bool printContents = false;
219
220	if (argc > 2 && (!strcmp(argv[1], "--long") || !strcmp(argv[1], "-l"))) {
221		printContents = true;
222		argc--;
223		argv++;
224	}
225
226	if (argc < 2 || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) {
227		printf("usage: %s [-l|--long] 'filename' ['filename' ...]\n"
228			"  -l, --long  Shows the attribute contents as well.\n", program);
229		return argc == 2 ? 0 : 1;
230	}
231
232	off_t total = 0;
233
234	for (int i = 1; i < argc; ++i) {
235		BNode node(argv[i]);
236
237		status_t status = node.InitCheck();
238		if (status < B_OK) {
239			fprintf(stderr, "%s: initialization failed for \"%s\": %s\n",
240				program, argv[i], strerror(status));
241			return 0;
242		}
243
244		printf("File: %s\n", argv[i]);
245
246		const int kTypeWidth = 12;
247		const int kSizeWidth = 10;
248		const int kNameWidth = 36;
249		const int kContentsWidth = 21;
250		printf("%*s %*s  %-*s%s\n", kTypeWidth, "Type", kSizeWidth, "Size",
251			kNameWidth, "Name", printContents ? "Contents" : "");
252
253		BString separator;
254		separator.SetTo('-', kTypeWidth + kSizeWidth + kNameWidth
255			+ (printContents ? kContentsWidth : 0));
256		puts(separator.String());
257
258		char name[B_ATTR_NAME_LENGTH];
259		while (node.GetNextAttrName(name) == B_OK) {
260			attr_info attrInfo;
261
262			status = node.GetAttrInfo(name, &attrInfo);
263			if (status >= B_OK) {
264				printf("%*s", kTypeWidth, get_type(attrInfo.type));
265				printf("% *" B_PRId64 "  ", kSizeWidth, attrInfo.size);
266				printf("\"%s\"", name);
267
268				if (printContents) {
269					// padding
270					int length = kNameWidth - 2 - strlen(name);
271					if (length > 0)
272						printf("%*s", length, "");
273
274					show_attr_contents(node, name, attrInfo);
275				} else
276					putchar('\n');
277
278				total += attrInfo.size;
279			} else {
280				fprintf(stderr, "%s: stat failed for \"%s\": %s\n",
281					program, name, strerror(status));
282			}
283		}
284	}
285
286	printf("\n%" B_PRId64 " bytes total in attributes.\n", total);
287	return 0;
288}
289