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