1/*
2 * Copyright 2010, Alexander Shagarov, alexander.shagarov@gmail.com.
3 * Copyright 2005, Stephan A��mus, superstippi@yellowbites.com.
4 * Copyright 2004-2010, Axel D��rfler, axeld@pinc-software.de.
5 * Copyright 2002, Sebastian Nozzi.
6 *
7 * Distributed under the terms of the MIT license.
8 */
9
10
11#include <ctype.h>
12#include <errno.h>
13#include <getopt.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17#include <unistd.h>
18
19#include <fs_attr.h>
20#include <Mime.h>
21#include <String.h>
22#include <TypeConstants.h>
23
24
25/*!	Used to present the characters in the raw data view */
26static void
27putCharOrDot(uchar c)
28{
29	putchar(isgraph(c) ? c : '.');
30}
31
32
33/*!	Dumps the contents of the attribute in the form of
34	raw data. This view is used for the type B_RAW_DATA_TYPE,
35	for custom types and for any type that is not directly
36	supported by the utility "addattr"
37*/
38static void
39dumpRawData(const char *buffer, size_t size)
40{
41	const uint32 kChunkSize = 16;
42	uint32 dumpPosition = 0;
43
44	while (dumpPosition < size) {
45		// Position for this line
46		printf("0x%06" B_PRIx32 ":  ", dumpPosition);
47
48		// Print the bytes in form of hexadecimal numbers
49		for (uint32 i = 0; i < kChunkSize; i++) {
50			if (dumpPosition + i < size) {
51				printf("%02x ", (uint8)buffer[dumpPosition + i]);
52			} else
53				printf("   ");
54		}
55
56		// Print the bytes in form of printable characters
57		// (whenever possible)
58		printf("  '");
59		for (uint32 i = 0; i < kChunkSize; i++) {
60			if (dumpPosition < size)
61				putCharOrDot(buffer[dumpPosition]);
62			else
63				putchar(' ');
64
65			dumpPosition++;
66		}
67		printf("'\n");
68	}
69}
70
71
72static const char*
73type_to_string(uint32 type)
74{
75	static char buffer[32];
76
77	int32 missed = 0, shift = 24;
78	uint8 value[4];
79	for (int32 i = 0; i < 4; i++, shift -= 8) {
80		value[i] = uint8(type >> shift);
81		if (value[i] < ' ' || value[i] > 127) {
82			value[i] = '.';
83			missed++;
84		}
85	}
86
87	if (missed < 2) {
88		sprintf(buffer, "'%c%c%c%c'", value[0], value[1], value[2],
89			value[3]);
90	} else
91		sprintf(buffer, "0x%08" B_PRIx32, type);
92
93	return buffer;
94}
95
96
97static BString
98type_name(uint32 type)
99{
100	switch (type) {
101		case B_INT8_TYPE:
102			return "int8";
103		case B_UINT8_TYPE:
104			return "uint8";
105		case B_INT16_TYPE:
106			return "int16";
107		case B_UINT16_TYPE:
108			return "uint16";
109		case B_INT32_TYPE:
110			return "int32";
111		case B_UINT32_TYPE:
112			return "uint32";
113		case B_INT64_TYPE:
114			return "int64";
115		case B_UINT64_TYPE:
116			return "uint64";
117		case B_FLOAT_TYPE:
118			return "float";
119		case B_DOUBLE_TYPE:
120			return "double";
121		case B_BOOL_TYPE:
122			return "bool";
123		case B_STRING_TYPE:
124			return "string";
125		case B_MESSAGE_TYPE:
126			return "message";
127		case B_RAW_TYPE:
128			return "raw_data";
129
130		default:
131			return type_to_string(type);
132	}
133}
134
135
136static status_t
137catAttr(const char *attribute, const char *fileName, bool keepRaw,
138	bool dataOnly, bool resolveLinks)
139{
140	int fd = open(fileName, O_RDONLY | (resolveLinks ? 0 : O_NOTRAVERSE));
141	if (fd < 0)
142		return errno;
143
144	attr_info info;
145	if (fs_stat_attr(fd, attribute, &info) < 0) {
146		close(fd);
147		return errno;
148	}
149
150	// limit size of the attribute, only the first 64k will make it on screen
151	off_t size = info.size;
152	bool cut = false;
153	if (size > 64 * 1024) {
154		size = 64 * 1024;
155		cut = true;
156	}
157
158	char* buffer = (char*)malloc(size);
159	if (!buffer) {
160		fprintf(stderr, "Could not allocate read buffer!\n");
161		close(fd);
162		return B_NO_MEMORY;
163	}
164
165	ssize_t bytesRead = fs_read_attr(fd, attribute, info.type, 0, buffer, size);
166	if (bytesRead < 0) {
167		free(buffer);
168		close(fd);
169		return errno;
170	}
171
172	if (bytesRead != size) {
173		fprintf(stderr, "Could only read %ld bytes from attribute!\n",
174			bytesRead);
175		free(buffer);
176		close(fd);
177		return B_ERROR;
178	}
179
180	if (keepRaw) {
181		off_t pos = 0;
182		ssize_t written = 0;
183		while (pos < info.size) {
184			// write what we have read so far
185			written = write(STDOUT_FILENO, buffer, bytesRead);
186			// check for write error
187			if (written < bytesRead) {
188				if (written >= 0) {
189					fprintf(stderr, "Could only write %ld bytes to stream!\n",
190						written);
191					written = B_ERROR;
192				} else {
193					fprintf(stderr, "Failed to write to stream: %s\n",
194						strerror(written));
195				}
196				break;
197			}
198			// read next chunk of data at pos
199			pos += bytesRead;
200			bytesRead = fs_read_attr(fd, attribute, info.type, pos, buffer,
201				size);
202			// check for read error
203			if (bytesRead < size && pos + bytesRead < info.size) {
204				if (bytesRead >= 0) {
205					fprintf(stderr, "Could only read %ld bytes from "
206						"attribute!\n", bytesRead);
207				} else {
208					fprintf(stderr, "Failed to read from attribute: %s\n",
209						strerror(bytesRead));
210				}
211				written = B_ERROR;
212				break;
213			}
214		}
215		free(buffer);
216		if (written > 0)
217			written = B_OK;
218		close(fd);
219		return written;
220	}
221
222	if (!dataOnly)
223		printf("%s : %s : ", fileName, type_name(info.type).String());
224
225	switch (info.type) {
226		case B_INT8_TYPE:
227			printf("%" B_PRId8 "\n", *((int8*)buffer));
228			break;
229		case B_UINT8_TYPE:
230			printf("%" B_PRIu8 "\n", *((uint8*)buffer));
231			break;
232		case B_INT16_TYPE:
233			printf("%" B_PRId16 "\n", *((int16*)buffer));
234			break;
235		case B_UINT16_TYPE:
236			printf("%" B_PRIu16 "\n", *((uint16*)buffer));
237			break;
238		case B_INT32_TYPE:
239			printf("%" B_PRId32 "\n", *((int32*)buffer));
240			break;
241		case B_UINT32_TYPE:
242			printf("%" B_PRIu32 "\n", *((uint32*)buffer));
243			break;
244		case B_INT64_TYPE:
245			printf("%" B_PRId64 "\n", *((int64*)buffer));
246			break;
247		case B_UINT64_TYPE:
248			printf("%" B_PRIu64 "\n", *((uint64*)buffer));
249			break;
250		case B_FLOAT_TYPE:
251			printf("%f\n", *((float*)buffer));
252			break;
253		case B_DOUBLE_TYPE:
254			printf("%f\n", *((double*)buffer));
255			break;
256		case B_BOOL_TYPE:
257			printf("%d\n", *((unsigned char*)buffer));
258			break;
259		case B_STRING_TYPE:
260		case B_MIME_STRING_TYPE:
261		case 'MSIG':
262		case 'MSDC':
263		case 'MPTH':
264			printf("%s\n", buffer);
265			break;
266
267		case B_MESSAGE_TYPE:
268		{
269			BMessage message;
270			if (!cut && message.Unflatten(buffer) == B_OK) {
271				message.PrintToStream();
272				break;
273			}
274			// supposed to fall through
275		}
276
277		default:
278			// The rest of the attributes types are displayed as raw data
279			dumpRawData(buffer, size);
280			break;
281	}
282
283	free(buffer);
284	close(fd);
285	return B_OK;
286}
287
288
289static int
290usage(const char* program, int returnCode)
291{
292	// Issue usage message
293	fprintf(returnCode == EXIT_SUCCESS ? stdout : stderr,
294		"usage: %s [-P] [--raw|-r] <attribute-name> <file1> [<file2>...]\n"
295		"  -P\t\tDon't resolve links\n"
296		"  --raw,-r\tGet the raw data of attributes\n"
297		"  --data,-d\tShow the attribute data only\n", program);
298
299	return returnCode;
300}
301
302
303int
304main(int argc, char *argv[])
305{
306	char *program = strrchr(argv[0], '/');
307	if (program == NULL)
308		program = argv[0];
309	else
310		program++;
311
312	const struct option kLongOptions[] = {
313		{"raw", no_argument, NULL, 'r'},
314		{"data", no_argument, NULL, 'd'},
315		{"help", no_argument, NULL, 'h'},
316		{NULL, 0, NULL, 0}
317	};
318
319	bool keepRaw = false;
320	bool resolveLinks = true;
321	bool dataOnly = false;
322
323	int option;
324	while ((option = getopt_long(argc, argv, "rdPh", kLongOptions, NULL))
325			!= -1) {
326		switch (option) {
327			case 'r':
328				keepRaw = true;
329				break;
330			case 'P':
331				resolveLinks = false;
332				break;
333			case 'd':
334				dataOnly = true;
335				break;
336			case 'h':
337				return usage(program, EXIT_SUCCESS);
338
339			default:
340				return usage(program, EXIT_FAILURE);
341		}
342	}
343
344	if (optind + 2 > argc)
345		return usage(program, EXIT_FAILURE);
346
347	int succeeded = 0;
348	const char* attrName = argv[optind++];
349
350	while (optind < argc) {
351		const char* fileName = argv[optind++];
352		status_t status = catAttr(attrName, fileName, keepRaw, dataOnly,
353			resolveLinks);
354		if (status != B_OK) {
355			fprintf(stderr, "%s: \"%s\", attribute \"%s\": %s\n",
356				program, fileName, attrName, strerror(status));
357		} else
358			succeeded++;
359	}
360
361	return succeeded != 0 ? EXIT_SUCCESS : EXIT_FAILURE;
362}
363