xref: /haiku/src/bin/catattr.cpp (revision 508f54795f39c3e7552d87c95aae9dd8ec6f505b)
1 /*
2  * Copyright 2010, Alexander Shagarov, alexander.shagarov@gmail.com.
3  * Copyright 2005, Stephan Aßmus, superstippi@yellowbites.com.
4  * Copyright 2004-2009, 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 <Mime.h>
12 #include <TypeConstants.h>
13 
14 #include <fs_attr.h>
15 
16 #include <ctype.h>
17 #include <errno.h>
18 #include <getopt.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.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%06lx:  ", 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 	if (type == B_RAW_TYPE)
76 		return "raw_data";
77 
78 	static char buffer[32];
79 
80 	int32 missed = 0, shift = 24;
81 	uint8 value[4];
82 	for (int32 i = 0; i < 4; i++, shift -= 8) {
83 		value[i] = uint8(type >> shift);
84 		if (value[i] < ' ' || value[i] > 127) {
85 			value[i] = '.';
86 			missed++;
87 		}
88 	}
89 
90 	if (missed < 2) {
91 		sprintf(buffer, "'%c%c%c%c'", value[0], value[1], value[2],
92 			value[3]);
93 	} else
94 		sprintf(buffer, "0x%08lx", type);
95 
96 	return buffer;
97 }
98 
99 
100 static status_t
101 catAttr(const char *attribute, const char *fileName, bool keepRaw = false,
102 	bool resolveLinks = true)
103 {
104 	int fd = open(fileName, O_RDONLY | (resolveLinks ? 0 : O_NOTRAVERSE));
105 	if (fd < 0)
106 		return errno;
107 
108 	attr_info info;
109 	if (fs_stat_attr(fd, attribute, &info) < 0)
110 		return errno;
111 
112 	// limit size of the attribute, only the first 64k will make it on screen
113 	off_t size = info.size;
114 	bool cut = false;
115 	if (size > 64 * 1024) {
116 		size = 64 * 1024;
117 		cut = true;
118 	}
119 
120 	char* buffer = (char*)malloc(size);
121 	if (!buffer) {
122 		fprintf(stderr, "Could not allocate read buffer!\n");
123 		return B_NO_MEMORY;
124 	}
125 
126 	ssize_t bytesRead = fs_read_attr(fd, attribute, info.type, 0, buffer, size);
127 	if (bytesRead < 0) {
128 		free(buffer);
129 		return errno;
130 	}
131 
132 	if (bytesRead != size) {
133 		fprintf(stderr, "Could only read %ld bytes from attribute!\n",
134 			bytesRead);
135 		free(buffer);
136 		return B_ERROR;
137 	}
138 
139 	if (keepRaw) {
140 		off_t pos = 0;
141 		ssize_t written = 0;
142 		while (pos < info.size) {
143 			// write what we have read so far
144 			written = write(STDOUT_FILENO, buffer, bytesRead);
145 			// check for write error
146 			if (written < bytesRead) {
147 				if (written >= 0) {
148 					fprintf(stderr, "Could only write %ld bytes to stream!\n",
149 						written);
150 					written = B_ERROR;
151 				} else {
152 					fprintf(stderr, "Failed to write to stream: %s\n",
153 						strerror(written));
154 				}
155 				break;
156 			}
157 			// read next chunk of data at pos
158 			pos += bytesRead;
159 			bytesRead = fs_read_attr(fd, attribute, info.type, pos, buffer,
160 				size);
161 			// check for read error
162 			if (bytesRead < size && pos + bytesRead < info.size) {
163 				if (bytesRead >= 0) {
164 					fprintf(stderr, "Could only read %ld bytes from "
165 						"attribute!\n", bytesRead);
166 				} else {
167 					fprintf(stderr, "Failed to read from attribute: %s\n",
168 						strerror(bytesRead));
169 				}
170 				written = B_ERROR;
171 				break;
172 			}
173 		}
174 		free(buffer);
175 		if (written > 0)
176 			written = B_OK;
177 		return written;
178 	}
179 
180 	switch (info.type) {
181 		case B_INT8_TYPE:
182 			printf("%s : int8 : %d\n", fileName, *((int8 *)buffer));
183 			break;
184 		case B_UINT8_TYPE:
185 			printf("%s : uint8 : %u\n", fileName, *((uint8 *)buffer));
186 			break;
187 		case B_INT16_TYPE:
188 			printf("%s : int16 : %d\n", fileName, *((int16 *)buffer));
189 			break;
190 		case B_UINT16_TYPE:
191 			printf("%s : uint16 : %u\n", fileName, *((uint16 *)buffer));
192 			break;
193 		case B_INT32_TYPE:
194 			printf("%s : int32 : %ld\n", fileName, *((int32 *)buffer));
195 			break;
196 		case B_UINT32_TYPE:
197 			printf("%s : uint32 : %lu\n", fileName, *((uint32 *)buffer));
198 			break;
199 		case B_INT64_TYPE:
200 			printf("%s : int64 : %Ld\n", fileName, *((int64 *)buffer));
201 			break;
202 		case B_UINT64_TYPE:
203 			printf("%s : uint64 : %Lu\n", fileName, *((uint64 *)buffer));
204 			break;
205 		case B_FLOAT_TYPE:
206 			printf("%s : float : %f\n", fileName, *((float *)buffer));
207 			break;
208 		case B_DOUBLE_TYPE:
209 			printf("%s : double : %f\n", fileName, *((double *)buffer));
210 			break;
211 		case B_BOOL_TYPE:
212 			printf("%s : bool : %d\n", fileName, *((unsigned char *)buffer));
213 			break;
214 		case B_STRING_TYPE:
215 			printf("%s : string : %s\n", fileName, buffer);
216 			break;
217 
218 		case B_MIME_STRING_TYPE:
219 		case 'MSIG':
220 		case 'MSDC':
221 		case 'MPTH':
222 			printf("%s : %s : %s\n", fileName, type_to_string(info.type),
223 				buffer);
224 			break;
225 
226 		case B_MESSAGE_TYPE:
227 		{
228 			BMessage message;
229 			if (!cut && message.Unflatten(buffer) == B_OK) {
230 				printf("%s : message :\n", fileName);
231 				message.PrintToStream();
232 				break;
233 			}
234 			// supposed to fall through
235 		}
236 
237 		default:
238 			// The rest of the attributes types are displayed as raw data
239 			printf("%s : %s : \n", fileName, type_to_string(info.type));
240 			dumpRawData(buffer, size);
241 			break;
242 	}
243 
244 	free(buffer);
245 	return B_OK;
246 }
247 
248 
249 static int
250 usage(const char* program)
251 {
252 	// Issue usage message
253 	fprintf(stderr, "usage: %s [-P] [--raw|-r] <attribute-name> <file1> "
254 		"[<file2>...]\n"
255 		"\t-P : Don't resolve links\n"
256 		"\t--raw|-r : Get the raw data of attributes\n", program);
257 	// Be's original version -only- returned 1 if the
258 	// amount of parameters was wrong, not if the file
259 	// or attribute couldn't be found (!)
260 	// In all other cases it returned 0
261 	return 1;
262 }
263 
264 
265 int
266 main(int argc, char *argv[])
267 {
268 	char *program = strrchr(argv[0], '/');
269 	if (program == NULL)
270 		program = argv[0];
271 	else
272 		program++;
273 
274 	bool keepRaw = false;
275 	bool resolveLinks = true;
276 	const struct option longOptions[] = {
277 		{"raw", no_argument, NULL, 'r'},
278 		{NULL, 0, NULL, 0}
279 	};
280 
281 	int rez;
282 	while ((rez = getopt_long(argc, argv, "rP", longOptions, NULL)) != -1) {
283 		switch (rez) {
284 			case 'r':
285 				keepRaw = true;
286 				break;
287 			case 'P':
288 				resolveLinks = false;
289 				break;
290 			default:
291 				return usage(program);
292 		}
293 	}
294 
295 	if (optind + 2 > argc)
296 		return usage(program);
297 
298 	const char* attrName = argv[optind++];
299 	while (optind < argc) {
300 		const char* fileName = argv[optind++];
301 		status_t status = catAttr(attrName, fileName, keepRaw,
302 				resolveLinks);
303 		if (status != B_OK) {
304 			fprintf(stderr, "%s: \"%s\", attribute \"%s\": %s\n",
305 					program, fileName, attrName, strerror(status));
306 		}
307 	}
308 
309 	return 0;
310 }
311