xref: /haiku/src/bin/catattr.cpp (revision 9760dcae2038d47442f4658c2575844c6cf92c40)
1 /*
2  * Copyright 2005, Stephan Aßmus, superstippi@yellowbites.com.
3  * Copyright 2004-2009, Axel Dörfler, axeld@pinc-software.de.
4  * Copyright 2002, Sebastian Nozzi.
5  *
6  * Distributed under the terms of the MIT license.
7  */
8 
9 
10 #include <Mime.h>
11 #include <TypeConstants.h>
12 
13 #include <fs_attr.h>
14 
15 #include <ctype.h>
16 #include <errno.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 
22 
23 /*!	Used to present the characters in the raw data view */
24 static void
25 putCharOrDot(uchar c)
26 {
27 	putchar(isgraph(c) ? c : '.');
28 }
29 
30 
31 /*!	Dumps the contents of the attribute in the form of
32 	raw data. This view is used for the type B_RAW_DATA_TYPE,
33 	for custom types and for any type that is not directly
34 	supported by the utility "addattr"
35 */
36 static void
37 dumpRawData(const char *buffer, size_t size)
38 {
39 	const uint32 kChunkSize = 16;
40 	uint32 dumpPosition = 0;
41 
42 	while (dumpPosition < size) {
43 		// Position for this line
44 		printf("0x%06lx:  ", dumpPosition);
45 
46 		// Print the bytes in form of hexadecimal numbers
47 		for (uint32 i = 0; i < kChunkSize; i++) {
48 			if (dumpPosition + i < size) {
49 				printf("%02x ", (uint8)buffer[dumpPosition + i]);
50 			} else
51 				printf("   ");
52 		}
53 
54 		// Print the bytes in form of printable characters
55 		// (whenever possible)
56 		printf("  '");
57 		for (uint32 i = 0; i < kChunkSize; i++) {
58 			if (dumpPosition < size)
59 				putCharOrDot(buffer[dumpPosition]);
60 			else
61 				putchar(' ');
62 
63 			dumpPosition++;
64 		}
65 		printf("'\n");
66 	}
67 }
68 
69 
70 static const char*
71 type_to_string(uint32 type)
72 {
73 	if (type == B_RAW_TYPE)
74 		return "raw_data";
75 
76 	static char buffer[32];
77 
78 	int32 missed = 0, shift = 24;
79 	uint8 value[4];
80 	for (int32 i = 0; i < 4; i++, shift -= 8) {
81 		value[i] = uint8(type >> shift);
82 		if (value[i] < ' ' || value[i] > 127) {
83 			value[i] = '.';
84 			missed++;
85 		}
86 	}
87 
88 	if (missed < 2) {
89 		sprintf(buffer, "'%c%c%c%c'", value[0], value[1], value[2],
90 			value[3]);
91 	} else
92 		sprintf(buffer, "0x%08lx", type);
93 
94 	return buffer;
95 }
96 
97 
98 static status_t
99 catAttr(const char *attribute, const char *fileName, bool keepRaw = false)
100 {
101 	int fd = open(fileName, O_RDONLY);
102 	if (fd < 0)
103 		return errno;
104 
105 	attr_info info;
106 	if (fs_stat_attr(fd, attribute, &info) < 0)
107 		return errno;
108 
109 	// limit size of the attribute, only the first 64k will make it on screen
110 	off_t size = info.size;
111 	bool cut = false;
112 	if (size > 64 * 1024) {
113 		size = 64 * 1024;
114 		cut = true;
115 	}
116 
117 	char* buffer = (char*)malloc(size);
118 	if (!buffer) {
119 		fprintf(stderr, "Could not allocate read buffer!\n");
120 		return B_NO_MEMORY;
121 	}
122 
123 	ssize_t bytesRead = fs_read_attr(fd, attribute, info.type, 0, buffer, size);
124 	if (bytesRead < 0) {
125 		free(buffer);
126 		return errno;
127 	}
128 
129 	if (bytesRead != size) {
130 		fprintf(stderr, "Could only read %ld bytes from attribute!\n",
131 			bytesRead);
132 		free(buffer);
133 		return B_ERROR;
134 	}
135 
136 	if (keepRaw) {
137 		off_t pos = 0;
138 		ssize_t written = 0;
139 		while (pos < info.size) {
140 			// write what we have read so far
141 			written = write(STDOUT_FILENO, buffer, bytesRead);
142 			// check for write error
143 			if (written < bytesRead) {
144 				if (written >= 0) {
145 					fprintf(stderr, "Could only write %ld bytes to stream!\n",
146 						written);
147 					written = B_ERROR;
148 				} else {
149 					fprintf(stderr, "Failed to write to stream: %s\n",
150 						strerror(written));
151 				}
152 				break;
153 			}
154 			// read next chunk of data at pos
155 			pos += bytesRead;
156 			bytesRead = fs_read_attr(fd, attribute, info.type, pos, buffer,
157 				size);
158 			// check for read error
159 			if (bytesRead < size && pos + bytesRead < info.size) {
160 				if (bytesRead >= 0) {
161 					fprintf(stderr, "Could only read %ld bytes from "
162 						"attribute!\n", bytesRead);
163 				} else {
164 					fprintf(stderr, "Failed to read from attribute: %s\n",
165 						strerror(bytesRead));
166 				}
167 				written = B_ERROR;
168 				break;
169 			}
170 		}
171 		free(buffer);
172 		if (written > 0)
173 			written = B_OK;
174 		return written;
175 	}
176 
177 	switch (info.type) {
178 		case B_INT8_TYPE:
179 			printf("%s : int8 : %d\n", fileName, *((int8 *)buffer));
180 			break;
181 		case B_UINT8_TYPE:
182 			printf("%s : uint8 : %u\n", fileName, *((uint8 *)buffer));
183 			break;
184 		case B_INT16_TYPE:
185 			printf("%s : int16 : %d\n", fileName, *((int16 *)buffer));
186 			break;
187 		case B_UINT16_TYPE:
188 			printf("%s : uint16 : %u\n", fileName, *((uint16 *)buffer));
189 			break;
190 		case B_INT32_TYPE:
191 			printf("%s : int32 : %ld\n", fileName, *((int32 *)buffer));
192 			break;
193 		case B_UINT32_TYPE:
194 			printf("%s : uint32 : %lu\n", fileName, *((uint32 *)buffer));
195 			break;
196 		case B_INT64_TYPE:
197 			printf("%s : int64 : %Ld\n", fileName, *((int64 *)buffer));
198 			break;
199 		case B_UINT64_TYPE:
200 			printf("%s : uint64 : %Lu\n", fileName, *((uint64 *)buffer));
201 			break;
202 		case B_FLOAT_TYPE:
203 			printf("%s : float : %f\n", fileName, *((float *)buffer));
204 			break;
205 		case B_DOUBLE_TYPE:
206 			printf("%s : double : %f\n", fileName, *((double *)buffer));
207 			break;
208 		case B_BOOL_TYPE:
209 			printf("%s : bool : %d\n", fileName, *((unsigned char *)buffer));
210 			break;
211 		case B_STRING_TYPE:
212 			printf("%s : string : %s\n", fileName, buffer);
213 			break;
214 
215 		case B_MIME_STRING_TYPE:
216 		case 'MSIG':
217 		case 'MSDC':
218 		case 'MPTH':
219 			printf("%s : %s : %s\n", fileName, type_to_string(info.type),
220 				buffer);
221 			break;
222 
223 		case B_MESSAGE_TYPE:
224 		{
225 			BMessage message;
226 			if (!cut && message.Unflatten(buffer) == B_OK) {
227 				printf("%s : message :\n", fileName);
228 				message.PrintToStream();
229 				break;
230 			}
231 			// supposed to fall through
232 		}
233 
234 		default:
235 			// The rest of the attributes types are displayed as raw data
236 			printf("%s : %s : \n", fileName, type_to_string(info.type));
237 			dumpRawData(buffer, size);
238 			break;
239 	}
240 
241 	free(buffer);
242 	return B_OK;
243 }
244 
245 
246 int
247 main(int argc, char *argv[])
248 {
249 	char *program = strrchr(argv[0], '/');
250 	if (program == NULL)
251 		program = argv[0];
252 	else
253 		program++;
254 
255 	if (argc > 2) {
256 		int32 attrNameIndex = 1;
257 		bool keepRaw = false;
258 
259 		// see if user wants to get to the raw data of the attribute
260 		if (strcmp(argv[attrNameIndex], "--raw") == 0 ||
261 			strcmp(argv[attrNameIndex], "-r") == 0) {
262 			attrNameIndex++;
263 			keepRaw = true;
264 		}
265 
266 		// Cat the attribute for every file given
267 		for (int32 i = attrNameIndex + 1; i < argc; i++) {
268 			status_t status = catAttr(argv[attrNameIndex], argv[i], keepRaw);
269 			if (status != B_OK) {
270 				fprintf(stderr, "%s: file \"%s\", attribute \"%s\": %s\n",
271 					program, argv[i], argv[attrNameIndex], strerror(status));
272 			}
273 		}
274 	} else {
275 		// Issue usage message
276 		fprintf(stderr, "usage: %s [--raw|-r] <attribute-name> <file1> "
277 			"[<file2>...]\n", program);
278 		// Be's original version -only- returned 1 if the
279 		// amount of parameters was wrong, not if the file
280 		// or attribute couldn't be found (!)
281 		// In all other cases it returned 0
282 		return 1;
283 	}
284 
285 	return 0;
286 }
287