xref: /haiku/src/bin/listattr.cpp (revision 372b901dfeada686207d00bbcce456f748bbda12)
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 */
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 	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 
154 static const char *
155 get_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 
219 int
220 main(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