1/*
2 * Copyright 2004-2020, 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	buffer[min_c(bytesRead, kLimit - 1)] = '\0';
80
81	switch (info.type) {
82		case B_INT8_TYPE:
83			printf("%" B_PRId8 "\n", *((int8 *)buffer));
84			break;
85		case B_UINT8_TYPE:
86			printf("%" B_PRIu8 "\n", *((uint8 *)buffer));
87			break;
88		case B_INT16_TYPE:
89			printf("%" B_PRId16 "\n", *((int16 *)buffer));
90			break;
91		case B_UINT16_TYPE:
92			printf("%" B_PRIu16 "\n", *((uint16 *)buffer));
93			break;
94		case B_INT32_TYPE:
95			printf("%" B_PRId32 "\n", *((int32 *)buffer));
96			break;
97		case B_UINT32_TYPE:
98			printf("%" B_PRIu32 "\n", *((uint32 *)buffer));
99			break;
100		case B_INT64_TYPE:
101			printf("%" B_PRId64 "\n", *((int64 *)buffer));
102			break;
103		case B_UINT64_TYPE:
104			printf("%" B_PRIu64 "\n", *((uint64 *)buffer));
105			break;
106		case B_FLOAT_TYPE:
107			printf("%f\n", *((float *)buffer));
108			break;
109		case B_DOUBLE_TYPE:
110			printf("%f\n", *((double *)buffer));
111			break;
112		case B_BOOL_TYPE:
113			printf("%d\n", *((unsigned char *)buffer));
114			break;
115		case B_TIME_TYPE:
116		{
117			char stringBuffer[256];
118			struct tm timeInfo;
119			localtime_r((time_t *)buffer, &timeInfo);
120			strftime(stringBuffer, sizeof(stringBuffer), "%c", &timeInfo);
121			printf("%s\n", stringBuffer);
122			break;
123		}
124		case B_STRING_TYPE:
125		case B_MIME_STRING_TYPE:
126		case 'MSIG':
127		case 'MSDC':
128		case 'MPTH':
129			printf("%s\n", buffer);
130			break;
131
132		case B_MESSAGE_TYPE:
133		{
134			BMessage message;
135			if (!cut && message.Unflatten(buffer) == B_OK) {
136				putchar('\n');
137				message.PrintToStream();
138				putchar('\n');
139				break;
140			}
141			// supposed to fall through
142		}
143
144		default:
145			// The rest of the attributes types are displayed as raw data
146			putchar('\n');
147			dump_raw_data(buffer, size);
148			putchar('\n');
149			break;
150	}
151}
152
153
154static const char *
155get_type(type_code type)
156{
157	static char buffer[32];
158
159	switch (type) {
160		case B_MIME_STRING_TYPE:
161			return "MIME String";
162		case B_RAW_TYPE:
163			return "Raw Data";
164
165		case B_STRING_TYPE:
166			return "Text";
167		case B_INT64_TYPE:
168			return "Int-64";
169		case B_UINT64_TYPE:
170			return "Uint-64";
171		case B_INT32_TYPE:
172			return "Int-32";
173		case B_UINT32_TYPE:
174			return "Uint-32";
175		case B_INT16_TYPE:
176			return "Int-16";
177		case B_UINT16_TYPE:
178			return "Uint-16";
179		case B_INT8_TYPE:
180			return "Int-8";
181		case B_UINT8_TYPE:
182			return "Uint-8";
183		case B_BOOL_TYPE:
184			return "Boolean";
185		case B_FLOAT_TYPE:
186			return "Float";
187		case B_DOUBLE_TYPE:
188			return "Double";
189
190		case B_MINI_ICON_TYPE:
191			return "Mini Icon";
192		case B_LARGE_ICON_TYPE:
193			return "Icon";
194
195		default:
196		{
197			int32 missed = 0, shift = 24;
198			uint8 value[4];
199			for (int32 i = 0; i < 4; i++, shift -= 8) {
200				value[i] = uint8(type >> shift);
201				if (value[i] < ' ' || value[i] > 127) {
202					value[i] = '.';
203					missed++;
204				}
205			}
206
207			if (missed < 2) {
208				sprintf(buffer, "'%c%c%c%c'", value[0], value[1], value[2],
209					value[3]);
210			} else
211				sprintf(buffer, "0x%08" B_PRIx32, type);
212
213			return buffer;
214		}
215	}
216}
217
218
219int
220main(int argc, char *argv[])
221{
222	const char *program = strrchr(argv[0], '/');
223	if (program == NULL)
224		program = argv[0];
225	else
226		program++;
227
228	bool printContents = false;
229
230	if (argc > 2 && (!strcmp(argv[1], "--long") || !strcmp(argv[1], "-l"))) {
231		printContents = true;
232		argc--;
233		argv++;
234	}
235
236	if (argc < 2 || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) {
237		printf("usage: %s [-l|--long] 'filename' ['filename' ...]\n"
238			"  -l, --long  Shows the attribute contents as well.\n", program);
239		return argc == 2 ? 0 : 1;
240	}
241
242	off_t total = 0;
243
244	for (int i = 1; i < argc; ++i) {
245		BNode node(argv[i]);
246
247		status_t status = node.InitCheck();
248		if (status < B_OK) {
249			fprintf(stderr, "%s: initialization failed for \"%s\": %s\n",
250				program, argv[i], strerror(status));
251			return 0;
252		}
253
254		printf("File: %s\n", argv[i]);
255
256		const int kTypeWidth = 12;
257		const int kSizeWidth = 10;
258		const int kNameWidth = 36;
259		const int kContentsWidth = 21;
260		printf("%*s %*s  %-*s%s\n", kTypeWidth, "Type", kSizeWidth, "Size",
261			kNameWidth, "Name", printContents ? "Contents" : "");
262
263		BString separator;
264		separator.SetTo('-', kTypeWidth + kSizeWidth + kNameWidth
265			+ (printContents ? kContentsWidth : 0));
266		puts(separator.String());
267
268		char name[B_ATTR_NAME_LENGTH];
269		while (node.GetNextAttrName(name) == B_OK) {
270			attr_info attrInfo;
271
272			status = node.GetAttrInfo(name, &attrInfo);
273			if (status >= B_OK) {
274				printf("%*s", kTypeWidth, get_type(attrInfo.type));
275				printf("% *" B_PRId64 "  ", kSizeWidth, attrInfo.size);
276				printf("\"%s\"", name);
277
278				if (printContents) {
279					// padding
280					int length = kNameWidth - 2 - strlen(name);
281					if (length > 0)
282						printf("%*s", length, "");
283
284					show_attr_contents(node, name, attrInfo);
285				} else
286					putchar('\n');
287
288				total += attrInfo.size;
289			} else {
290				fprintf(stderr, "%s: stat failed for \"%s\": %s\n",
291					program, name, strerror(status));
292			}
293		}
294	}
295
296	printf("\n%" B_PRId64 " bytes total in attributes.\n", total);
297	return 0;
298}
299