xref: /haiku/src/bin/catattr.cpp (revision ebeab0cf7032dd3f09d4588aa05e0618482999b8)
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 */
26 static void
27 putCharOrDot(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 */
38 static void
39 dumpRawData(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 
72 static const char*
73 type_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 
97 static BString
98 type_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 
136 static status_t
137 catAttr(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 
289 static int
290 usage(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 
303 int
304 main(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