xref: /haiku/src/bin/listattr.cpp (revision 13581b3d2a71545960b98fefebc5225b5bf29072)
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 #include <time.h>
19 
20 
21 /*!	Dumps the contents of the attribute in the form of raw data. This view
22 	is used for the type B_RAW_TYPE, for custom types and for any type that
23 	is not directly supported by the utility "addattr".
24 */
25 static void
26 dump_raw_data(const char *buffer, size_t size)
27 {
28 	const uint32 kChunkSize = 16;
29 	uint32 dumpPosition = 0;
30 
31 	while (dumpPosition < size) {
32 		// Position for this line
33 		printf("\t%04" B_PRIx32 ": ", dumpPosition);
34 
35 		// Print the bytes in form of hexadecimal numbers
36 		for (uint32 i = 0; i < kChunkSize; i++) {
37 			if (dumpPosition + i < size) {
38 				printf("%02x ", (uint8)buffer[dumpPosition + i]);
39 			} else
40 				printf("   ");
41 		}
42 
43 		// Print the bytes in form of printable characters
44 		// (whenever possible)
45 		printf(" ");
46 		for (uint32 i = 0; i < kChunkSize; i++) {
47 			if (dumpPosition < size) {
48 				char c = buffer[dumpPosition];
49 				putchar(isgraph(c) ? c : '.');
50 			} else
51 				putchar(' ');
52 
53 			dumpPosition++;
54 		}
55 		printf("\n");
56 	}
57 }
58 
59 
60 static void
61 show_attr_contents(BNode& node, const char* attribute, const attr_info& info)
62 {
63 	// limit size of the attribute, only the first kLimit byte will make it on
64 	// screen
65 	int kLimit = 256;
66 	bool cut = false;
67 	off_t size = info.size;
68 	if (size > kLimit) {
69 		size = kLimit;
70 		cut = true;
71 	}
72 
73 	char buffer[kLimit];
74 	ssize_t bytesRead = node.ReadAttr(attribute, info.type, 0, buffer, size);
75 	if (bytesRead != size) {
76 		fprintf(stderr, "Could only read %" B_PRIdOFF " bytes from attribute!\n",
77 			size);
78 		return;
79 	}
80 	buffer[min_c(bytesRead, kLimit - 1)] = '\0';
81 
82 	switch (info.type) {
83 		case B_INT8_TYPE:
84 			printf("%" B_PRId8 "\n", *((int8 *)buffer));
85 			break;
86 		case B_UINT8_TYPE:
87 			printf("%" B_PRIu8 "\n", *((uint8 *)buffer));
88 			break;
89 		case B_INT16_TYPE:
90 			printf("%" B_PRId16 "\n", *((int16 *)buffer));
91 			break;
92 		case B_UINT16_TYPE:
93 			printf("%" B_PRIu16 "\n", *((uint16 *)buffer));
94 			break;
95 		case B_INT32_TYPE:
96 			printf("%" B_PRId32 "\n", *((int32 *)buffer));
97 			break;
98 		case B_UINT32_TYPE:
99 			printf("%" B_PRIu32 "\n", *((uint32 *)buffer));
100 			break;
101 		case B_INT64_TYPE:
102 			printf("%" B_PRId64 "\n", *((int64 *)buffer));
103 			break;
104 		case B_UINT64_TYPE:
105 			printf("%" B_PRIu64 "\n", *((uint64 *)buffer));
106 			break;
107 		case B_FLOAT_TYPE:
108 			printf("%f\n", *((float *)buffer));
109 			break;
110 		case B_DOUBLE_TYPE:
111 			printf("%f\n", *((double *)buffer));
112 			break;
113 		case B_BOOL_TYPE:
114 			printf("%d\n", *((unsigned char *)buffer));
115 			break;
116 		case B_TIME_TYPE:
117 		{
118 			char stringBuffer[256];
119 			struct tm timeInfo;
120 			localtime_r((time_t *)buffer, &timeInfo);
121 			strftime(stringBuffer, sizeof(stringBuffer), "%c", &timeInfo);
122 			printf("%s\n", stringBuffer);
123 			break;
124 		}
125 		case B_STRING_TYPE:
126 		case B_MIME_STRING_TYPE:
127 		case 'MSIG':
128 		case 'MSDC':
129 		case 'MPTH':
130 			printf("%s\n", buffer);
131 			break;
132 
133 		case B_MESSAGE_TYPE:
134 		{
135 			BMessage message;
136 			if (!cut && message.Unflatten(buffer) == B_OK) {
137 				putchar('\n');
138 				message.PrintToStream();
139 				putchar('\n');
140 				break;
141 			}
142 			// supposed to fall through
143 		}
144 
145 		default:
146 			// The rest of the attributes types are displayed as raw data
147 			putchar('\n');
148 			dump_raw_data(buffer, size);
149 			putchar('\n');
150 			break;
151 	}
152 }
153 
154 
155 static const char *
156 get_type(type_code type)
157 {
158 	static char buffer[32];
159 
160 	switch (type) {
161 		case B_MIME_STRING_TYPE:
162 			return "MIME String";
163 		case B_RAW_TYPE:
164 			return "Raw Data";
165 
166 		case B_STRING_TYPE:
167 			return "Text";
168 		case B_INT64_TYPE:
169 			return "Int-64";
170 		case B_UINT64_TYPE:
171 			return "Uint-64";
172 		case B_INT32_TYPE:
173 			return "Int-32";
174 		case B_UINT32_TYPE:
175 			return "Uint-32";
176 		case B_INT16_TYPE:
177 			return "Int-16";
178 		case B_UINT16_TYPE:
179 			return "Uint-16";
180 		case B_INT8_TYPE:
181 			return "Int-8";
182 		case B_UINT8_TYPE:
183 			return "Uint-8";
184 		case B_BOOL_TYPE:
185 			return "Boolean";
186 		case B_FLOAT_TYPE:
187 			return "Float";
188 		case B_DOUBLE_TYPE:
189 			return "Double";
190 
191 		case B_MINI_ICON_TYPE:
192 			return "Mini Icon";
193 		case B_LARGE_ICON_TYPE:
194 			return "Icon";
195 
196 		default:
197 		{
198 			int32 missed = 0, shift = 24;
199 			uint8 value[4];
200 			for (int32 i = 0; i < 4; i++, shift -= 8) {
201 				value[i] = uint8(type >> shift);
202 				if (value[i] < ' ' || value[i] > 127) {
203 					value[i] = '.';
204 					missed++;
205 				}
206 			}
207 
208 			if (missed < 2) {
209 				sprintf(buffer, "'%c%c%c%c'", value[0], value[1], value[2],
210 					value[3]);
211 			} else
212 				sprintf(buffer, "0x%08" B_PRIx32, type);
213 
214 			return buffer;
215 		}
216 	}
217 }
218 
219 
220 int
221 main(int argc, char *argv[])
222 {
223 	const char *program = strrchr(argv[0], '/');
224 	if (program == NULL)
225 		program = argv[0];
226 	else
227 		program++;
228 
229 	bool printContents = false;
230 
231 	if (argc > 2 && (!strcmp(argv[1], "--long") || !strcmp(argv[1], "-l"))) {
232 		printContents = true;
233 		argc--;
234 		argv++;
235 	}
236 
237 	if (argc < 2 || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) {
238 		printf("usage: %s [-l|--long] 'filename' ['filename' ...]\n"
239 			"  -l, --long  Shows the attribute contents as well.\n", program);
240 		return argc == 2 ? 0 : 1;
241 	}
242 
243 	off_t total = 0;
244 
245 	for (int i = 1; i < argc; ++i) {
246 		BNode node(argv[i]);
247 
248 		status_t status = node.InitCheck();
249 		if (status < B_OK) {
250 			fprintf(stderr, "%s: initialization failed for \"%s\": %s\n",
251 				program, argv[i], strerror(status));
252 			return 0;
253 		}
254 
255 		printf("File: %s\n", argv[i]);
256 
257 		const int kTypeWidth = 12;
258 		const int kSizeWidth = 10;
259 		const int kNameWidth = 36;
260 		const int kContentsWidth = 21;
261 		printf("%*s %*s  %-*s%s\n", kTypeWidth, "Type", kSizeWidth, "Size",
262 			kNameWidth, "Name", printContents ? "Contents" : "");
263 
264 		BString separator;
265 		separator.SetTo('-', kTypeWidth + kSizeWidth + kNameWidth
266 			+ (printContents ? kContentsWidth : 0));
267 		puts(separator.String());
268 
269 		char name[B_ATTR_NAME_LENGTH];
270 		while (node.GetNextAttrName(name) == B_OK) {
271 			attr_info attrInfo;
272 
273 			status = node.GetAttrInfo(name, &attrInfo);
274 			if (status >= B_OK) {
275 				printf("%*s", kTypeWidth, get_type(attrInfo.type));
276 				printf("% *" B_PRId64 "  ", kSizeWidth, attrInfo.size);
277 				printf("\"%s\"", name);
278 
279 				if (printContents) {
280 					// padding
281 					int length = kNameWidth - 2 - strlen(name);
282 					if (length > 0)
283 						printf("%*s", length, "");
284 
285 					show_attr_contents(node, name, attrInfo);
286 				} else
287 					putchar('\n');
288 
289 				total += attrInfo.size;
290 			} else {
291 				fprintf(stderr, "%s: stat failed for \"%s\": %s\n",
292 					program, name, strerror(status));
293 			}
294 		}
295 	}
296 
297 	printf("\n%" B_PRId64 " bytes total in attributes.\n", total);
298 	return 0;
299 }
300