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 close(fd); 147 return errno; 148 } 149 150 // limit size of the attribute, only the first 64k will make it on screen 151 off_t size = info.size; 152 bool cut = false; 153 if (size > 64 * 1024) { 154 size = 64 * 1024; 155 cut = true; 156 } 157 158 char* buffer = (char*)malloc(size); 159 if (!buffer) { 160 fprintf(stderr, "Could not allocate read buffer!\n"); 161 close(fd); 162 return B_NO_MEMORY; 163 } 164 165 ssize_t bytesRead = fs_read_attr(fd, attribute, info.type, 0, buffer, size); 166 if (bytesRead < 0) { 167 free(buffer); 168 close(fd); 169 return errno; 170 } 171 172 if (bytesRead != size) { 173 fprintf(stderr, "Could only read %ld bytes from attribute!\n", 174 bytesRead); 175 free(buffer); 176 close(fd); 177 return B_ERROR; 178 } 179 180 if (keepRaw) { 181 off_t pos = 0; 182 ssize_t written = 0; 183 while (pos < info.size) { 184 // write what we have read so far 185 written = write(STDOUT_FILENO, buffer, bytesRead); 186 // check for write error 187 if (written < bytesRead) { 188 if (written >= 0) { 189 fprintf(stderr, "Could only write %ld bytes to stream!\n", 190 written); 191 written = B_ERROR; 192 } else { 193 fprintf(stderr, "Failed to write to stream: %s\n", 194 strerror(written)); 195 } 196 break; 197 } 198 // read next chunk of data at pos 199 pos += bytesRead; 200 bytesRead = fs_read_attr(fd, attribute, info.type, pos, buffer, 201 size); 202 // check for read error 203 if (bytesRead < size && pos + bytesRead < info.size) { 204 if (bytesRead >= 0) { 205 fprintf(stderr, "Could only read %ld bytes from " 206 "attribute!\n", bytesRead); 207 } else { 208 fprintf(stderr, "Failed to read from attribute: %s\n", 209 strerror(bytesRead)); 210 } 211 written = B_ERROR; 212 break; 213 } 214 } 215 free(buffer); 216 if (written > 0) 217 written = B_OK; 218 close(fd); 219 return written; 220 } 221 222 if (!dataOnly) 223 printf("%s : %s : ", fileName, type_name(info.type).String()); 224 225 switch (info.type) { 226 case B_INT8_TYPE: 227 printf("%" B_PRId8 "\n", *((int8*)buffer)); 228 break; 229 case B_UINT8_TYPE: 230 printf("%" B_PRIu8 "\n", *((uint8*)buffer)); 231 break; 232 case B_INT16_TYPE: 233 printf("%" B_PRId16 "\n", *((int16*)buffer)); 234 break; 235 case B_UINT16_TYPE: 236 printf("%" B_PRIu16 "\n", *((uint16*)buffer)); 237 break; 238 case B_INT32_TYPE: 239 printf("%" B_PRId32 "\n", *((int32*)buffer)); 240 break; 241 case B_UINT32_TYPE: 242 printf("%" B_PRIu32 "\n", *((uint32*)buffer)); 243 break; 244 case B_INT64_TYPE: 245 printf("%" B_PRId64 "\n", *((int64*)buffer)); 246 break; 247 case B_UINT64_TYPE: 248 printf("%" B_PRIu64 "\n", *((uint64*)buffer)); 249 break; 250 case B_FLOAT_TYPE: 251 printf("%f\n", *((float*)buffer)); 252 break; 253 case B_DOUBLE_TYPE: 254 printf("%f\n", *((double*)buffer)); 255 break; 256 case B_BOOL_TYPE: 257 printf("%d\n", *((unsigned char*)buffer)); 258 break; 259 case B_STRING_TYPE: 260 case B_MIME_STRING_TYPE: 261 case 'MSIG': 262 case 'MSDC': 263 case 'MPTH': 264 printf("%s\n", buffer); 265 break; 266 267 case B_MESSAGE_TYPE: 268 { 269 BMessage message; 270 if (!cut && message.Unflatten(buffer) == B_OK) { 271 message.PrintToStream(); 272 break; 273 } 274 // supposed to fall through 275 } 276 277 default: 278 // The rest of the attributes types are displayed as raw data 279 dumpRawData(buffer, size); 280 break; 281 } 282 283 free(buffer); 284 close(fd); 285 return B_OK; 286 } 287 288 289 static int 290 usage(const char* program, int returnCode) 291 { 292 // Issue usage message 293 fprintf(returnCode == EXIT_SUCCESS ? stdout : stderr, 294 "usage: %s [-P] [--raw|-r] <attribute-name> <file1> [<file2>...]\n" 295 " -P\t\tDon't resolve links\n" 296 " --raw,-r\tGet the raw data of attributes\n" 297 " --data,-d\tShow the attribute data only\n", program); 298 299 return returnCode; 300 } 301 302 303 int 304 main(int argc, char *argv[]) 305 { 306 char *program = strrchr(argv[0], '/'); 307 if (program == NULL) 308 program = argv[0]; 309 else 310 program++; 311 312 const struct option kLongOptions[] = { 313 {"raw", no_argument, NULL, 'r'}, 314 {"data", no_argument, NULL, 'd'}, 315 {"help", no_argument, NULL, 'h'}, 316 {NULL, 0, NULL, 0} 317 }; 318 319 bool keepRaw = false; 320 bool resolveLinks = true; 321 bool dataOnly = false; 322 323 int option; 324 while ((option = getopt_long(argc, argv, "rdPh", kLongOptions, NULL)) 325 != -1) { 326 switch (option) { 327 case 'r': 328 keepRaw = true; 329 break; 330 case 'P': 331 resolveLinks = false; 332 break; 333 case 'd': 334 dataOnly = true; 335 break; 336 case 'h': 337 return usage(program, EXIT_SUCCESS); 338 339 default: 340 return usage(program, EXIT_FAILURE); 341 } 342 } 343 344 if (optind + 2 > argc) 345 return usage(program, EXIT_FAILURE); 346 347 int succeeded = 0; 348 const char* attrName = argv[optind++]; 349 350 while (optind < argc) { 351 const char* fileName = argv[optind++]; 352 status_t status = catAttr(attrName, fileName, keepRaw, dataOnly, 353 resolveLinks); 354 if (status != B_OK) { 355 fprintf(stderr, "%s: \"%s\", attribute \"%s\": %s\n", 356 program, fileName, attrName, strerror(status)); 357 } else 358 succeeded++; 359 } 360 361 return succeeded != 0 ? EXIT_SUCCESS : EXIT_FAILURE; 362 } 363