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