1 // 2 // dump.c - dumping partition maps 3 // 4 // Written by Eryk Vershen (eryk@apple.com) 5 // 6 7 /* 8 * Copyright 1996,1997,1998 by Apple Computer, Inc. 9 * All Rights Reserved 10 * 11 * Permission to use, copy, modify, and distribute this software and 12 * its documentation for any purpose and without fee is hereby granted, 13 * provided that the above copyright notice appears in all copies and 14 * that both the copyright notice and this permission notice appear in 15 * supporting documentation. 16 * 17 * APPLE COMPUTER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE 18 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 19 * FOR A PARTICULAR PURPOSE. 20 * 21 * IN NO EVENT SHALL APPLE COMPUTER BE LIABLE FOR ANY SPECIAL, INDIRECT, OR 22 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 23 * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, 24 * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 25 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 26 */ 27 28 // for *printf() 29 #include <stdio.h> 30 31 // for malloc() & free() 32 #ifndef __linux__ 33 #include <stdlib.h> 34 //#include <unistd.h> 35 #else 36 #include <malloc.h> 37 #endif 38 39 // for strcmp() 40 #include <string.h> 41 // for O_RDONLY 42 #include <fcntl.h> 43 // for errno 44 #include <errno.h> 45 46 #include "dump.h" 47 #include "pathname.h" 48 #include "io.h" 49 #include "errors.h" 50 51 52 // 53 // Defines 54 // 55 #if DPISTRLEN != 32 56 #error Change in strlen in partition entries! Fix constants 57 #endif 58 59 #define get_align_long(x) (*(x)) 60 61 62 // 63 // Types 64 // 65 typedef struct names { 66 char *abbr; 67 char *full; 68 } NAMES; 69 70 71 // 72 // Global Constants 73 // 74 NAMES plist[] = { 75 {"Drvr", "Apple_Driver"}, 76 {"Free", "Apple_Free"}, 77 {" HFS", "Apple_HFS"}, 78 {" MFS", "Apple_MFS"}, 79 {"PDOS", "Apple_PRODOS"}, 80 {"junk", "Apple_Scratch"}, 81 {"unix", "Apple_UNIX_SVR2"}, 82 {" map", "Apple_partition_map"}, 83 {0, 0}, 84 }; 85 86 const char * kStringEmpty = ""; 87 const char * kStringNot = " not"; 88 89 90 // 91 // Global Variables 92 // 93 int aflag = AFLAG_DEFAULT; /* abbreviate partition types */ 94 int pflag = PFLAG_DEFAULT; /* show physical limits of partition */ 95 96 97 // 98 // Forward declarations 99 // 100 void adjust_value_and_compute_prefix(double *value, int *prefix); 101 void dump_block_zero(partition_map_header *map); 102 void dump_partition_entry(partition_map *entry, int type_length, int name_length, int digits); 103 int get_max_base_or_length(partition_map_header *map); 104 int get_max_name_string_length(partition_map_header *map); 105 int get_max_type_string_length(partition_map_header *map); 106 int strnlen(char *s, int n); 107 108 109 // 110 // Routines 111 // 112 int 113 dump(char *name) 114 { 115 partition_map_header *map; 116 int junk; 117 118 map = open_partition_map(name, &junk, 0); 119 if (map == NULL) { 120 //error(-1, "No partition map in '%s'", name); 121 return 0; 122 } 123 124 dump_partition_map(map, 1); 125 126 close_partition_map(map); 127 128 return 1; 129 } 130 131 132 void 133 dump_block_zero(partition_map_header *map) 134 { 135 Block0 *p; 136 DDMap *m; 137 int i; 138 double value; 139 int prefix; 140 141 p = map->misc; 142 if (p->sbSig != BLOCK0_SIGNATURE) { 143 return; 144 } 145 146 value = ((double)p->sbBlkCount) * p->sbBlkSize; 147 adjust_value_and_compute_prefix(&value, &prefix); 148 printf("\nDevice block size=%u, Number of Blocks=%lu (%1.1f%c)\n", 149 p->sbBlkSize, p->sbBlkCount, value, prefix); 150 151 printf("DeviceType=0x%x, DeviceId=0x%x\n", 152 p->sbDevType, p->sbDevId); 153 if (p->sbDrvrCount > 0) { 154 printf("Drivers-\n"); 155 m = (DDMap *) p->sbMap; 156 for (i = 0; i < p->sbDrvrCount; i++) { 157 printf("%u: @ %lu for %u, type=0x%x\n", i+1, 158 get_align_long(&m[i].ddBlock), 159 m[i].ddSize, m[i].ddType); 160 } 161 } 162 printf("\n"); 163 } 164 165 166 void 167 dump_partition_map(partition_map_header *map, int disk_order) 168 { 169 partition_map * entry; 170 int max_type_length; 171 int max_name_length; 172 int digits; 173 char *alternate; 174 175 if (map == NULL) { 176 bad_input("No partition map exists"); 177 return; 178 } 179 alternate = get_mklinux_name(map->name); 180 if (alternate) { 181 printf("\nPartition map (with %d byte blocks) on '%s' (%s)\n", 182 map->logical_block, map->name, alternate); 183 free(alternate); 184 } else { 185 printf("\nPartition map (with %d byte blocks) on '%s'\n", 186 map->logical_block, map->name); 187 } 188 189 digits = number_of_digits(get_max_base_or_length(map)); 190 if (digits < 6) { 191 digits = 6; 192 } 193 if (aflag) { 194 max_type_length = 4; 195 } else { 196 max_type_length = get_max_type_string_length(map); 197 if (max_type_length < 4) { 198 max_type_length = 4; 199 } 200 } 201 max_name_length = get_max_name_string_length(map); 202 if (max_name_length < 6) { 203 max_name_length = 6; 204 } 205 printf(" #: %*s %-*s %*s %-*s ( size )\n", 206 max_type_length, "type", 207 max_name_length, "name", 208 digits, "length", digits, "base"); 209 210 if (disk_order) { 211 for (entry = map->disk_order; entry != NULL; 212 entry = entry->next_on_disk) { 213 214 dump_partition_entry(entry, max_type_length, max_name_length, digits); 215 } 216 } else { 217 for (entry = map->base_order; entry != NULL; 218 entry = entry->next_by_base) { 219 220 dump_partition_entry(entry, max_type_length, max_name_length, digits); 221 } 222 } 223 dump_block_zero(map); 224 } 225 226 227 void 228 dump_partition_entry(partition_map *entry, int type_length, int name_length, int digits) 229 { 230 partition_map_header *map; 231 int j; 232 DPME *p; 233 char *s; 234 u32 size; 235 double bytes; 236 int driver; 237 238 map = entry->the_map; 239 p = entry->data; 240 driver = entry->contains_driver? '*': ' '; 241 if (aflag) { 242 s = "????"; 243 for (j = 0; plist[j].abbr != 0; j++) { 244 if (strcmp(p->dpme_type, plist[j].full) == 0) { 245 s = plist[j].abbr; 246 break; 247 } 248 } 249 printf("%2ld: %.4s%c%-*.32s ", 250 entry->disk_address, s, driver, name_length, p->dpme_name); 251 } else { 252 printf("%2ld: %*.32s%c%-*.32s ", 253 entry->disk_address, type_length, p->dpme_type, 254 driver, name_length, p->dpme_name); 255 } 256 257 if (pflag) { 258 printf("%*lu ", digits, p->dpme_pblocks); 259 size = p->dpme_pblocks; 260 } else if (p->dpme_lblocks + p->dpme_lblock_start != p->dpme_pblocks) { 261 printf("%*lu+", digits, p->dpme_lblocks); 262 size = p->dpme_lblocks; 263 } else if (p->dpme_lblock_start != 0) { 264 printf("%*lu ", digits, p->dpme_lblocks); 265 size = p->dpme_lblocks; 266 } else { 267 printf("%*lu ", digits, p->dpme_pblocks); 268 size = p->dpme_pblocks; 269 } 270 if (pflag || p->dpme_lblock_start == 0) { 271 printf("@ %-*lu", digits, p->dpme_pblock_start); 272 } else { 273 printf("@~%-*lu", digits, p->dpme_pblock_start + p->dpme_lblock_start); 274 } 275 276 bytes = ((double)size) * map->logical_block; 277 adjust_value_and_compute_prefix(&bytes, &j); 278 if (j != ' ' && j != 'K') { 279 printf(" (%#5.1f%c)", bytes, j); 280 } 281 282 #if 0 283 // Old A/UX fields that no one pays attention to anymore. 284 bp = (BZB *) (p->dpme_bzb); 285 j = -1; 286 if (bp->bzb_magic == BZBMAGIC) { 287 switch (bp->bzb_type) { 288 case FSTEFS: 289 s = "EFS"; 290 break; 291 case FSTSFS: 292 s = "SFS"; 293 j = 1; 294 break; 295 case FST: 296 default: 297 if (bzb_root_get(bp) != 0) { 298 if (bzb_usr_get(bp) != 0) { 299 s = "RUFS"; 300 } else { 301 s = "RFS"; 302 } 303 j = 0; 304 } else if (bzb_usr_get(bp) != 0) { 305 s = "UFS"; 306 j = 2; 307 } else { 308 s = "FS"; 309 } 310 break; 311 } 312 if (bzb_slice_get(bp) != 0) { 313 printf(" s%1d %4s", bzb_slice_get(bp)-1, s); 314 } else if (j >= 0) { 315 printf(" S%1d %4s", j, s); 316 } else { 317 printf(" %4s", s); 318 } 319 if (bzb_crit_get(bp) != 0) { 320 printf(" K%1d", bp->bzb_cluster); 321 } else if (j < 0) { 322 printf(" "); 323 } else { 324 printf(" k%1d", bp->bzb_cluster); 325 } 326 if (bp->bzb_mount_point[0] != 0) { 327 printf(" %.64s", bp->bzb_mount_point); 328 } 329 } 330 #endif 331 printf("\n"); 332 } 333 334 335 void 336 list_all_disks() 337 { 338 MEDIA_ITERATOR iter; 339 MEDIA m; 340 DPME * data; 341 char *name; 342 long mark; 343 344 data = (DPME *) malloc(PBLOCK_SIZE); 345 if (data == NULL) { 346 error(errno, "can't allocate memory for try buffer"); 347 return; 348 } 349 350 for (iter = first_media_kind(&mark); iter != 0; iter = next_media_kind(&mark)) { 351 352 while ((name = step_media_iterator(iter)) != 0) { 353 354 if ((m = open_pathname_as_media(name, O_RDONLY)) == 0) { 355 error(errno, "can't open file '%s'", name); 356 } else { 357 close_media(m); 358 359 dump(name); 360 } 361 free(name); 362 } 363 364 delete_media_iterator(iter); 365 } 366 367 free(data); 368 } 369 370 371 void 372 show_data_structures(partition_map_header *map) 373 { 374 Block0 *zp; 375 DDMap *m; 376 int i; 377 int j; 378 partition_map * entry; 379 DPME *p; 380 BZB *bp; 381 char *s; 382 383 if (map == NULL) { 384 printf("No partition map exists\n"); 385 return; 386 } 387 printf("Header:\n"); 388 printf("map %d blocks out of %d, media %lu blocks (%d byte blocks)\n", 389 map->blocks_in_map, map->maximum_in_map, 390 map->media_size, map->logical_block); 391 printf("Map is%s writeable", (map->writeable)?kStringEmpty:kStringNot); 392 printf(", but%s changed\n", (map->changed)?kStringEmpty:kStringNot); 393 printf("\n"); 394 395 if (map->misc == NULL) { 396 printf("No block zero\n"); 397 } else { 398 zp = map->misc; 399 400 printf("Block0:\n"); 401 printf("signature 0x%x", zp->sbSig); 402 if (zp->sbSig == BLOCK0_SIGNATURE) { 403 printf("\n"); 404 } else { 405 printf(" should be 0x%x\n", BLOCK0_SIGNATURE); 406 } 407 printf("Block size=%u, Number of Blocks=%lu\n", 408 zp->sbBlkSize, zp->sbBlkCount); 409 printf("DeviceType=0x%x, DeviceId=0x%x, sbData=0x%lx\n", 410 zp->sbDevType, zp->sbDevId, zp->sbData); 411 if (zp->sbDrvrCount == 0) { 412 printf("No drivers\n"); 413 } else { 414 printf("%u driver%s-\n", zp->sbDrvrCount, 415 (zp->sbDrvrCount>1)?"s":kStringEmpty); 416 m = (DDMap *) zp->sbMap; 417 for (i = 0; i < zp->sbDrvrCount; i++) { 418 printf("%u: @ %lu for %u, type=0x%x\n", i+1, 419 get_align_long(&m[i].ddBlock), 420 m[i].ddSize, m[i].ddType); 421 } 422 } 423 } 424 printf("\n"); 425 426 /* 427 u32 dpme_boot_args[32] ; 428 u32 dpme_reserved_3[62] ; 429 */ 430 printf(" #: type length base " 431 "flags (logical)\n"); 432 for (entry = map->disk_order; entry != NULL; entry = entry->next_on_disk) { 433 p = entry->data; 434 printf("%2ld: %20.32s ", 435 entry->disk_address, p->dpme_type); 436 printf("%7lu @ %-7lu ", p->dpme_pblocks, p->dpme_pblock_start); 437 printf("%c%c%c%c%c%c%c%c%c%c%c%c ", 438 (dpme_valid_get(p))?'V':'.', 439 (dpme_allocated_get(p))?'A':'.', 440 (dpme_in_use_get(p))?'I':'.', 441 (dpme_bootable_get(p))?'B':'.', 442 (dpme_readable_get(p))?'R':'.', 443 (dpme_writable_get(p))?'W':'.', 444 (dpme_os_pic_code_get(p))?'P':'.', 445 (dpme_os_specific_2_get(p))?'2':'.', 446 (dpme_chainable_get(p))?'C':'.', 447 (dpme_diskdriver_get(p))?'D':'.', 448 (bitfield_get(p->dpme_flags, 30, 1))?'M':'.', 449 (bitfield_get(p->dpme_flags, 31, 1))?'X':'.'); 450 if (p->dpme_lblock_start != 0 || p->dpme_pblocks != p->dpme_lblocks) { 451 printf("(%lu @ %lu)", p->dpme_lblocks, p->dpme_lblock_start); 452 } 453 printf("\n"); 454 } 455 printf("\n"); 456 printf(" #: booter bytes load_address " 457 "goto_address checksum processor\n"); 458 for (entry = map->disk_order; entry != NULL; entry = entry->next_on_disk) { 459 p = entry->data; 460 printf("%2ld: ", entry->disk_address); 461 printf("%7lu ", p->dpme_boot_block); 462 printf("%7lu ", p->dpme_boot_bytes); 463 printf("%8lx ", (u32)p->dpme_load_addr); 464 printf("%8lx ", (u32)p->dpme_load_addr_2); 465 printf("%8lx ", (u32)p->dpme_goto_addr); 466 printf("%8lx ", (u32)p->dpme_goto_addr_2); 467 printf("%8lx ", p->dpme_checksum); 468 printf("%.32s", p->dpme_process_id); 469 printf("\n"); 470 } 471 printf("\n"); 472 /* 473 xx: cccc RU *dd s... 474 */ 475 printf(" #: type RU *slice mount_point (A/UX only fields)\n"); 476 for (entry = map->disk_order; entry != NULL; entry = entry->next_on_disk) { 477 p = entry->data; 478 printf("%2ld: ", entry->disk_address); 479 480 bp = (BZB *) (p->dpme_bzb); 481 j = -1; 482 if (bp->bzb_magic == BZBMAGIC) { 483 switch (bp->bzb_type) { 484 case FSTEFS: 485 s = "esch"; 486 break; 487 case FSTSFS: 488 s = "swap"; 489 j = 1; 490 break; 491 case FST: 492 default: 493 s = "fsys"; 494 if (bzb_root_get(bp) != 0) { 495 j = 0; 496 } else if (bzb_usr_get(bp) != 0) { 497 j = 2; 498 } 499 break; 500 } 501 printf("%4s ", s); 502 printf("%c%c ", 503 (bzb_root_get(bp))?'R':' ', 504 (bzb_usr_get(bp))?'U':' '); 505 if (bzb_slice_get(bp) != 0) { 506 printf(" %2ld", bzb_slice_get(bp)-1); 507 } else if (j >= 0) { 508 printf(" *%2d", j); 509 } else { 510 printf(" "); 511 } 512 if (bp->bzb_mount_point[0] != 0) { 513 printf(" %.64s", bp->bzb_mount_point); 514 } 515 } 516 printf("\n"); 517 } 518 } 519 520 521 void 522 full_dump_partition_entry(partition_map_header *map, int index) 523 { 524 partition_map * cur; 525 DPME *p; 526 int i; 527 u32 t; 528 529 cur = find_entry_by_disk_address(index, map); 530 if (cur == NULL) { 531 printf("No such partition\n"); 532 return; 533 } 534 535 p = cur->data; 536 printf(" signature: 0x%x\n", p->dpme_signature); 537 printf(" reserved1: 0x%x\n", p->dpme_reserved_1); 538 printf(" number of map entries: %ld\n", p->dpme_map_entries); 539 printf(" physical start: %10lu length: %10lu\n", p->dpme_pblock_start, p->dpme_pblocks); 540 printf(" logical start: %10lu length: %10lu\n", p->dpme_lblock_start, p->dpme_lblocks); 541 542 printf(" flags: 0x%lx\n", (u32)p->dpme_flags); 543 printf(" "); 544 if (dpme_valid_get(p)) printf("valid "); 545 if (dpme_allocated_get(p)) printf("alloc "); 546 if (dpme_in_use_get(p)) printf("in-use "); 547 if (dpme_bootable_get(p)) printf("boot "); 548 if (dpme_readable_get(p)) printf("read "); 549 if (dpme_writable_get(p)) printf("write "); 550 if (dpme_os_pic_code_get(p)) printf("pic "); 551 t = p->dpme_flags >> 7; 552 for (i = 7; i <= 31; i++) { 553 if (t & 0x1) { 554 printf("%d ", i); 555 } 556 t = t >> 1; 557 } 558 printf("\n"); 559 560 printf(" name: '%.32s'\n", p->dpme_name); 561 printf(" type: '%.32s'\n", p->dpme_type); 562 563 printf(" boot start block: %10lu\n", p->dpme_boot_block); 564 printf("boot length (in bytes): %10lu\n", p->dpme_boot_bytes); 565 printf(" load address: 0x%08lx 0x%08lx\n", 566 (u32)p->dpme_load_addr, (u32)p->dpme_load_addr_2); 567 printf(" start address: 0x%08lx 0x%08lx\n", 568 (u32)p->dpme_goto_addr, (u32)p->dpme_goto_addr_2); 569 printf(" checksum: 0x%08lx\n", p->dpme_checksum); 570 printf(" processor: '%.32s'\n", p->dpme_process_id); 571 printf("boot args field -"); 572 dump_block((unsigned char *)p->dpme_boot_args, 32*4); 573 printf("dpme_reserved_3 -"); 574 dump_block((unsigned char *)p->dpme_reserved_3, 62*4); 575 } 576 577 578 void 579 dump_block(unsigned char *addr, int len) 580 { 581 int i; 582 int j; 583 int limit1; 584 int limit; 585 #define LINE_LEN 16 586 #define UNIT_LEN 4 587 #define OTHER_LEN 8 588 589 for (i = 0; i < len; i = limit) { 590 limit1 = i + LINE_LEN; 591 if (limit1 > len) { 592 limit = len; 593 } else { 594 limit = limit1; 595 } 596 printf("\n%03x: ", i); 597 for (j = i; j < limit1; j++) { 598 if (j % UNIT_LEN == 0) { 599 printf(" "); 600 } 601 if (j < limit) { 602 printf("%02x", addr[j]); 603 } else { 604 printf(" "); 605 } 606 } 607 printf(" "); 608 for (j = i; j < limit; j++) { 609 if (j % OTHER_LEN == 0) { 610 printf(" "); 611 } 612 if (addr[j] < ' ') { 613 printf("."); 614 } else { 615 printf("%c", addr[j]); 616 } 617 } 618 } 619 printf("\n"); 620 } 621 622 void 623 full_dump_block_zero(partition_map_header *map) 624 { 625 Block0 *zp; 626 DDMap *m; 627 int i; 628 629 if (map == NULL) { 630 printf("No partition map exists\n"); 631 return; 632 } 633 634 if (map->misc == NULL) { 635 printf("No block zero\n"); 636 return; 637 } 638 zp = map->misc; 639 640 printf(" signature: 0x%x\n", zp->sbSig); 641 printf(" size of a block: %d\n", zp->sbBlkSize); 642 printf(" number of blocks: %ld\n", zp->sbBlkCount); 643 printf(" device type: 0x%x\n", zp->sbDevType); 644 printf(" device id: 0x%x\n", zp->sbDevId); 645 printf(" data: 0x%lx\n", zp->sbData); 646 printf(" driver count: %d\n", zp->sbDrvrCount); 647 m = (DDMap *) zp->sbMap; 648 for (i = 0; &m[i].ddType < &zp->sbMap[247]; i++) { 649 if (m[i].ddBlock == 0 && m[i].ddSize == 0 && m[i].ddType == 0) { 650 break; 651 } 652 printf(" driver %3u block: %ld\n", i+1, m[i].ddBlock); 653 printf(" size in blocks: %d\n", m[i].ddSize); 654 printf(" driver type: 0x%x\n", m[i].ddType); 655 } 656 printf("remainder of block -"); 657 dump_block((unsigned char *)&m[i].ddBlock, (&zp->sbMap[247]-((unsigned short *)&m[i].ddBlock))*2); 658 } 659 660 void 661 display_patches(partition_map *entry) 662 { 663 long long offset; 664 MEDIA m; 665 static unsigned char *patch_block; 666 667 offset = entry->data->dpme_pblock_start; 668 m = entry->the_map->m; 669 offset = ((long long) entry->data->dpme_pblock_start) * entry->the_map->logical_block; 670 if (patch_block == NULL) { 671 patch_block = (unsigned char *) malloc(PBLOCK_SIZE); 672 if (patch_block == NULL) { 673 error(errno, "can't allocate memory for patch block buffer"); 674 return; 675 } 676 } 677 if (read_media(m, (long long)offset, PBLOCK_SIZE, (char *)patch_block) == 0) { 678 error(errno, "Can't read patch block"); 679 return; 680 } 681 dump_block(patch_block, PBLOCK_SIZE); 682 } 683 684 int 685 strnlen(char *s, int n) 686 { 687 int i; 688 689 for (i = 0; i < n; i++) { 690 if (*s == 0) { 691 break; 692 } 693 s++; 694 } 695 return i; 696 } 697 698 int 699 get_max_type_string_length(partition_map_header *map) 700 { 701 partition_map * entry; 702 int max; 703 int length; 704 705 if (map == NULL) { 706 return 0; 707 } 708 709 max = 0; 710 711 for (entry = map->disk_order; entry != NULL; entry = entry->next_on_disk) { 712 length = strnlen(entry->data->dpme_type, DPISTRLEN); 713 if (length > max) { 714 max = length; 715 } 716 } 717 718 return max; 719 } 720 721 int 722 get_max_name_string_length(partition_map_header *map) 723 { 724 partition_map * entry; 725 int max; 726 int length; 727 728 if (map == NULL) { 729 return 0; 730 } 731 732 max = 0; 733 734 for (entry = map->disk_order; entry != NULL; entry = entry->next_on_disk) { 735 length = strnlen(entry->data->dpme_name, DPISTRLEN); 736 if (length > max) { 737 max = length; 738 } 739 } 740 741 return max; 742 } 743 744 int 745 get_max_base_or_length(partition_map_header *map) 746 { 747 partition_map * entry; 748 int max; 749 750 if (map == NULL) { 751 return 0; 752 } 753 754 max = 0; 755 756 for (entry = map->disk_order; entry != NULL; entry = entry->next_on_disk) { 757 if (entry->data->dpme_pblock_start > max) { 758 max = entry->data->dpme_pblock_start; 759 } 760 if (entry->data->dpme_pblocks > max) { 761 max = entry->data->dpme_pblocks; 762 } 763 if (entry->data->dpme_lblock_start > max) { 764 max = entry->data->dpme_lblock_start; 765 } 766 if (entry->data->dpme_lblocks > max) { 767 max = entry->data->dpme_lblocks; 768 } 769 } 770 771 return max; 772 } 773 774 void 775 adjust_value_and_compute_prefix(double *value, int *prefix) 776 { 777 double bytes; 778 int multiplier; 779 780 bytes = *value; 781 if (bytes < 1024.0) { 782 multiplier = ' '; 783 } else { 784 bytes = bytes / 1024.0; 785 if (bytes < 1024.0) { 786 multiplier = 'K'; 787 } else { 788 bytes = bytes / 1024.0; 789 if (bytes < 1024.0) { 790 multiplier = 'M'; 791 } else { 792 bytes = bytes / 1024.0; 793 if (bytes < 1024.0) { 794 multiplier = 'G'; 795 } else { 796 bytes = bytes / 1024.0; 797 multiplier = 'T'; 798 } 799 } 800 } 801 } 802 *value = bytes; 803 *prefix = multiplier; 804 } 805