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