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