1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * Copyright 2015 Nexenta Systems, Inc. All rights reserved. 25 */ 26 27 /* 28 * This file contains I/O related functions. 29 */ 30 #include "global.h" 31 32 #include <unistd.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <signal.h> 36 #include <ctype.h> 37 #include <stdarg.h> 38 #include <sys/tty.h> 39 #include <sys/termio.h> 40 #include <sys/termios.h> 41 #include <sys/efi_partition.h> 42 43 #include "startup.h" 44 #include "misc.h" 45 #include "menu_partition.h" 46 #include "param.h" 47 #include "menu.h" 48 49 50 extern int data_lineno; 51 extern char *space2str(); 52 extern long strtol(); 53 54 /* 55 * This variable is used to determine whether a token is present in the pipe 56 * already. 57 */ 58 static char token_present = 0; 59 60 /* 61 * This variable always gives us access to the most recent token type 62 */ 63 int last_token_type = 0; 64 65 #ifdef __STDC__ 66 /* 67 * Prototypes for ANSI C compilers 68 */ 69 static int sup_get_token(char *); 70 static void pushchar(int c); 71 static int checkeof(void); 72 static void flushline(void); 73 static int strcnt(char *s1, char *s2); 74 static int getbn(char *str, diskaddr_t *iptr); 75 static void print_input_choices(int type, u_ioparam_t *param); 76 static int slist_widest_str(slist_t *slist); 77 static void ljust_print(char *str, int width); 78 static int sup_inputchar(void); 79 static void sup_pushchar(int c); 80 static int geti64(char *str, uint64_t *iptr, uint64_t *wild); 81 82 #else /* __STDC__ */ 83 /* 84 * Prototypes for non-ANSI C compilers 85 */ 86 87 static int sup_get_token(); 88 static void pushchar(int c); 89 static int checkeof(void); 90 static void flushline(void); 91 static int strcnt(char *s1, char *s2); 92 static int getbn(char *str, diskaddr_t *iptr); 93 static void print_input_choices(int type, u_ioparam_t *param); 94 static int slist_widest_str(slist_t *slist); 95 static void ljust_print(char *str, int width); 96 static int sup_inputchar(void); 97 static void sup_pushchar(int c); 98 static int geti64(char *str, uint64_t *iptr, uint64_t *wild); 99 100 #endif /* __STDC__ */ 101 102 103 /* 104 * This routine pushes the given character back onto the input stream. 105 */ 106 static void 107 pushchar(c) 108 int c; 109 { 110 (void) ungetc(c, stdin); 111 } 112 113 /* 114 * This routine checks the input stream for an eof condition. 115 */ 116 static int 117 checkeof() 118 { 119 return (feof(stdin)); 120 } 121 122 /* 123 * This routine gets the next token off the input stream. A token is 124 * basically any consecutive non-white characters. 125 */ 126 char * 127 gettoken(inbuf) 128 char *inbuf; 129 { 130 char *ptr = inbuf; 131 int c, quoted = 0; 132 133 retoke: 134 /* 135 * Remove any leading white-space. 136 */ 137 while ((isspace(c = getchar())) && (c != '\n')) 138 ; 139 /* 140 * If we are at the beginning of a line and hit the comment character, 141 * flush the line and start again. 142 */ 143 if (!token_present && c == COMMENT_CHAR) { 144 token_present = 1; 145 flushline(); 146 goto retoke; 147 } 148 /* 149 * Loop on each character until we hit unquoted white-space. 150 */ 151 while (!isspace(c) || quoted && (c != '\n')) { 152 /* 153 * If we hit eof, get out. 154 */ 155 if (checkeof()) 156 return (NULL); 157 /* 158 * If we hit a double quote, change the state of quotedness. 159 */ 160 if (c == '"') 161 quoted = !quoted; 162 /* 163 * If there's room in the buffer, add the character to the end. 164 */ 165 else if (ptr - inbuf < TOKEN_SIZE) 166 *ptr++ = (char)c; 167 /* 168 * Get the next character. 169 */ 170 c = getchar(); 171 } 172 /* 173 * Null terminate the token. 174 */ 175 *ptr = '\0'; 176 /* 177 * Peel off white-space still in the pipe. 178 */ 179 while (isspace(c) && (c != '\n')) 180 c = getchar(); 181 /* 182 * If we hit another token, push it back and set state. 183 */ 184 if (c != '\n') { 185 pushchar(c); 186 token_present = 1; 187 } else 188 token_present = 0; 189 /* 190 * Return the token. 191 */ 192 return (inbuf); 193 } 194 195 /* 196 * This routine removes the leading and trailing spaces from a token. 197 */ 198 void 199 clean_token(cleantoken, token) 200 char *cleantoken, *token; 201 { 202 char *ptr; 203 204 /* 205 * Strip off leading white-space. 206 */ 207 for (ptr = token; isspace(*ptr); ptr++) 208 ; 209 /* 210 * Copy it into the clean buffer. 211 */ 212 (void) strcpy(cleantoken, ptr); 213 /* 214 * Strip off trailing white-space. 215 */ 216 for (ptr = cleantoken + strlen(cleantoken) - 1; 217 isspace(*ptr) && (ptr >= cleantoken); ptr--) { 218 *ptr = '\0'; 219 } 220 } 221 222 /* 223 * This routine checks if a token is already present on the input line 224 */ 225 int 226 istokenpresent() 227 { 228 return (token_present); 229 } 230 231 /* 232 * This routine flushes the rest of an input line if there is known 233 * to be data in it. The flush has to be qualified because the newline 234 * may have already been swallowed by the last gettoken. 235 */ 236 static void 237 flushline() 238 { 239 if (token_present) { 240 /* 241 * Flush the pipe to eol or eof. 242 */ 243 while ((getchar() != '\n') && !checkeof()) 244 ; 245 /* 246 * Mark the pipe empty. 247 */ 248 token_present = 0; 249 } 250 } 251 252 /* 253 * This routine returns the number of characters that are identical 254 * between s1 and s2, stopping as soon as a mismatch is found. 255 */ 256 static int 257 strcnt(s1, s2) 258 char *s1, *s2; 259 { 260 int i = 0; 261 262 while ((*s1 != '\0') && (*s1++ == *s2++)) 263 i++; 264 return (i); 265 } 266 267 /* 268 * This routine converts the given token into an integer. The token 269 * must convert cleanly into an integer with no unknown characters. 270 * If the token is the wildcard string, and the wildcard parameter 271 * is present, the wildcard value will be returned. 272 */ 273 int 274 geti(str, iptr, wild) 275 char *str; 276 int *iptr, *wild; 277 { 278 char *str2; 279 280 /* 281 * If there's a wildcard value and the string is wild, return the 282 * wildcard value. 283 */ 284 if (wild != NULL && strcmp(str, WILD_STRING) == 0) 285 *iptr = *wild; 286 else { 287 /* 288 * Conver the string to an integer. 289 */ 290 *iptr = (int)strtol(str, &str2, 0); 291 /* 292 * If any characters didn't convert, it's an error. 293 */ 294 if (*str2 != '\0') { 295 err_print("`%s' is not an integer.\n", str); 296 return (-1); 297 } 298 } 299 return (0); 300 } 301 302 /* 303 * This routine converts the given token into a long long. The token 304 * must convert cleanly into a 64-bit integer with no unknown characters. 305 * If the token is the wildcard string, and the wildcard parameter 306 * is present, the wildcard value will be returned. 307 */ 308 static int 309 geti64(str, iptr, wild) 310 char *str; 311 uint64_t *iptr, *wild; 312 { 313 char *str2; 314 315 /* 316 * If there's a wildcard value and the string is wild, return the 317 * wildcard value. 318 */ 319 if ((wild != NULL) && (strcmp(str, WILD_STRING)) == 0) { 320 *iptr = *wild; 321 } else { 322 /* 323 * Conver the string to an integer. 324 */ 325 *iptr = (uint64_t)strtoll(str, &str2, 0); 326 /* 327 * If any characters didn't convert, it's an error. 328 */ 329 if (*str2 != '\0') { 330 err_print("`%s' is not an integer.\n", str); 331 return (-1); 332 } 333 } 334 return (0); 335 } 336 337 /* 338 * This routine converts the given string into a block number on the 339 * current disk. The format of a block number is either a self-based 340 * number, or a series of self-based numbers separated by slashes. 341 * Any number preceeding the first slash is considered a cylinder value. 342 * Any number succeeding the first slash but preceeding the second is 343 * considered a head value. Any number succeeding the second slash is 344 * considered a sector value. Any of these numbers can be wildcarded 345 * to the highest possible legal value. 346 */ 347 static int 348 getbn(str, iptr) 349 char *str; 350 diskaddr_t *iptr; 351 { 352 char *cptr, *hptr, *sptr; 353 int cyl, head, sect; 354 int wild; 355 diskaddr_t wild64; 356 TOKEN buf; 357 358 /* 359 * Set cylinder pointer to beginning of string. 360 */ 361 cptr = str; 362 /* 363 * Look for the first slash. 364 */ 365 while ((*str != '\0') && (*str != '/')) 366 str++; 367 /* 368 * If there wasn't one, convert string to an integer and return it. 369 */ 370 if (*str == '\0') { 371 wild64 = physsects() - 1; 372 if (geti64(cptr, iptr, &wild64)) 373 return (-1); 374 return (0); 375 } 376 /* 377 * Null out the slash and set head pointer just beyond it. 378 */ 379 *str++ = '\0'; 380 hptr = str; 381 /* 382 * Look for the second slash. 383 */ 384 while ((*str != '\0') && (*str != '/')) 385 str++; 386 /* 387 * If there wasn't one, sector pointer points to a . 388 */ 389 if (*str == '\0') 390 sptr = str; 391 /* 392 * If there was, null it out and set sector point just beyond it. 393 */ 394 else { 395 *str++ = '\0'; 396 sptr = str; 397 } 398 /* 399 * Convert the cylinder part to an integer and store it. 400 */ 401 clean_token(buf, cptr); 402 wild = ncyl + acyl - 1; 403 if (geti(buf, &cyl, &wild)) 404 return (-1); 405 if ((cyl < 0) || (cyl >= (ncyl + acyl))) { 406 err_print("`%d' is out of range.\n", cyl); 407 return (-1); 408 } 409 /* 410 * Convert the head part to an integer and store it. 411 */ 412 clean_token(buf, hptr); 413 wild = nhead - 1; 414 if (geti(buf, &head, &wild)) 415 return (-1); 416 if ((head < 0) || (head >= nhead)) { 417 err_print("`%d' is out of range.\n", head); 418 return (-1); 419 } 420 /* 421 * Convert the sector part to an integer and store it. 422 */ 423 clean_token(buf, sptr); 424 wild = sectors(head) - 1; 425 if (geti(buf, §, &wild)) 426 return (-1); 427 if ((sect < 0) || (sect >= sectors(head))) { 428 err_print("`%d' is out of range.\n", sect); 429 return (-1); 430 } 431 /* 432 * Combine the pieces into a block number and return it. 433 */ 434 *iptr = chs2bn(cyl, head, sect); 435 return (0); 436 } 437 438 /* 439 * This routine is the basis for all input into the program. It 440 * understands the semantics of a set of input types, and provides 441 * consistent error messages for all input. It allows for default 442 * values and prompt strings. 443 */ 444 uint64_t 445 input(type, promptstr, delim, param, deflt, cmdflag) 446 int type; 447 char *promptstr; 448 int delim; 449 u_ioparam_t *param; 450 int *deflt; 451 int cmdflag; 452 { 453 int interactive, help, i, length, index, tied; 454 blkaddr_t bn; 455 diskaddr_t bn64; 456 char **str, **strings; 457 TOKEN token, cleantoken; 458 TOKEN token2, cleantoken2; 459 char *arg; 460 struct bounds *bounds; 461 char *s; 462 int value; 463 int cyls, cylno; 464 uint64_t blokno; 465 float nmegs; 466 float ngigs; 467 char shell_argv[MAXPATHLEN]; 468 part_deflt_t *part_deflt; 469 efi_deflt_t *efi_deflt; 470 471 /* 472 * Optional integer input has been added as a hack. 473 * Function result is 1 if user typed anything. 474 * Whatever they typed is returned in *deflt. 475 * This permits us to distinguish between "no value", 476 * and actually entering in some value, for instance. 477 */ 478 if (type == FIO_OPINT) { 479 assert(deflt != NULL); 480 } 481 reprompt: 482 help = interactive = 0; 483 /* 484 * If we are inputting a command, flush any current input in the pipe. 485 */ 486 if (cmdflag == CMD_INPUT) 487 flushline(); 488 /* 489 * Note whether the token is already present. 490 */ 491 if (!token_present) 492 interactive = 1; 493 /* 494 * Print the prompt. 495 */ 496 fmt_print(promptstr); 497 /* 498 * If there is a default value, print it in a format appropriate 499 * for the input type. 500 */ 501 if (deflt != NULL) { 502 switch (type) { 503 case FIO_BN: 504 #if !defined(lint) /* caller has aligned the pointer specifying FIO_BN */ 505 fmt_print("[%llu, ", *(diskaddr_t *)deflt); 506 pr_dblock(fmt_print, *(diskaddr_t *)deflt); 507 fmt_print("]"); 508 #endif 509 break; 510 case FIO_INT: 511 fmt_print("[%d]", *deflt); 512 break; 513 case FIO_INT64: 514 #if defined(lint) 515 /* caller is longlong aligned specifying FIO_INT64 */ 516 efi_deflt = NULL; 517 #else 518 efi_deflt = (efi_deflt_t *)deflt; 519 #endif 520 fmt_print("[%llu]", efi_deflt->start_sector); 521 break; 522 case FIO_CSTR: 523 case FIO_MSTR: 524 strings = (char **)param->io_charlist; 525 for (i = 0, str = strings; i < *deflt; i++, str++) 526 ; 527 fmt_print("[%s]", *str); 528 break; 529 case FIO_OSTR: 530 fmt_print("[\"%s\"]", (char *)deflt); 531 break; 532 case FIO_SLIST: 533 /* 534 * Search for a string matching the default 535 * value. If found, use it. Otherwise 536 * assume the default value is actually 537 * an illegal choice, and default to 538 * the first item in the list. 539 */ 540 s = find_string(param->io_slist, *deflt); 541 if (s == (char *)NULL) { 542 s = (param->io_slist)->str; 543 } 544 fmt_print("[%s]", s); 545 break; 546 case FIO_CYL: 547 /* 548 * Old-style partition size input, used to 549 * modify complete partition tables 550 */ 551 blokno = *(blkaddr32_t *)deflt; 552 fmt_print("[%llub, %uc, %1.2fmb, %1.2fgb]", blokno, 553 bn2c(blokno), bn2mb(blokno), bn2gb(blokno)); 554 break; 555 case FIO_ECYL: 556 /* 557 * set up pointer to partition defaults 558 * structure 559 */ 560 part_deflt = (part_deflt_t *)deflt; 561 562 /* 563 * Build print format specifier. We use the 564 * starting cylinder number which was entered 565 * before this call to input(), in case the 566 * user has changed it from the value in the 567 * cur_parts->pinfo_map[].dkl_cylno 568 * field for the current parition 569 */ 570 571 /* 572 * Determine the proper default end cylinder: 573 * Start Cyl Default Size End Cylinder 574 * 0 0 0 575 * >0 0 Start Cyl 576 * 0 >0 Default Size 577 * (Cyls) - 1 578 * >0 >0 (Start + 579 * Default Size 580 * (Cyls)) -1 581 */ 582 583 if (part_deflt->deflt_size == 0) { 584 cylno = part_deflt->start_cyl; 585 } else if (part_deflt->start_cyl == 0) { 586 cylno = bn2c(part_deflt->deflt_size) - 1; 587 } else { 588 cylno = (bn2c(part_deflt->deflt_size) + 589 part_deflt->start_cyl) - 1; 590 } 591 592 fmt_print("[%ub, %uc, %de, %1.2fmb, %1.2fgb]", 593 part_deflt->deflt_size, 594 bn2c(part_deflt->deflt_size), 595 cylno, 596 bn2mb(part_deflt->deflt_size), 597 bn2gb(part_deflt->deflt_size)); 598 599 break; 600 case FIO_EFI: 601 #if defined(lint) 602 /* caller is longlong aligned when specifying FIO_EFI */ 603 efi_deflt = NULL; 604 #else 605 efi_deflt = (efi_deflt_t *)deflt; 606 #endif 607 608 fmt_print("[%llub, %llue, %llumb, %llugb, %llutb]", 609 efi_deflt->end_sector, 610 efi_deflt->start_sector + efi_deflt->end_sector - 1, 611 (efi_deflt->end_sector * cur_blksz) / 612 (1024 * 1024), 613 (efi_deflt->end_sector * cur_blksz) / 614 (1024 * 1024 * 1024), 615 (efi_deflt->end_sector * cur_blksz) / 616 ((uint64_t)1024 * 1024 * 1024 * 1024)); 617 break; 618 case FIO_OPINT: 619 /* no default value for optional input type */ 620 fmt_print("[default]"); 621 break; 622 default: 623 err_print("Error: unknown input type.\n"); 624 fullabort(); 625 } 626 } 627 /* 628 * Print the delimiter character. 629 */ 630 fmt_print("%c ", delim); 631 /* 632 * Get the token. If we hit eof, exit the program gracefully. 633 */ 634 if (gettoken(token) == NULL) 635 fullabort(); 636 637 /* 638 * check if the user has issued (!) , escape to shell 639 */ 640 if ((cmdflag == CMD_INPUT) && (token[0] == '!')) { 641 642 /* get the list of arguments to shell command */ 643 (void) memset(shell_argv, 0, sizeof (shell_argv)); 644 645 /* initialize to the first token... */ 646 arg = &token[1]; 647 648 /* 649 * ... and then collect all tokens until the end of 650 * the line as arguments 651 */ 652 do { 653 /* skip empty tokens. */ 654 if (*arg == '\0') 655 continue; 656 /* 657 * If either of the following two strlcat() 658 * operations overflows, report an error and 659 * exit gracefully. 660 */ 661 if ((strlcat(shell_argv, arg, sizeof (shell_argv)) >= 662 sizeof (shell_argv)) || 663 (strlcat(shell_argv, " ", sizeof (shell_argv)) >= 664 sizeof (shell_argv))) { 665 err_print("Error: Command line too long.\n"); 666 fullabort(); 667 } 668 } while (token_present && (arg = gettoken(token)) != NULL); 669 670 /* execute the shell command */ 671 (void) execute_shell(shell_argv, sizeof (shell_argv)); 672 redisplay_menu_list((char **)param->io_charlist); 673 if (interactive) { 674 goto reprompt; 675 } 676 } 677 678 /* 679 * Certain commands accept up to two tokens 680 * Unfortunately, this is kind of a hack. 681 */ 682 token2[0] = 0; 683 cleantoken2[0] = 0; 684 if (type == FIO_CYL || type == FIO_ECYL) { 685 if (token_present) { 686 if (gettoken(token2) == NULL) 687 fullabort(); 688 clean_token(cleantoken2, token2); 689 } 690 } 691 /* 692 * Echo the token back to the user if it was in the pipe or we 693 * are running out of a command file. 694 */ 695 if (!interactive || option_f) { 696 if (token2[0] == 0) { 697 fmt_print("%s\n", token); 698 } else { 699 fmt_print("%s %s\n", token, token2); 700 } 701 } 702 /* 703 * If we are logging, echo the token to the log file. The else 704 * is necessary here because the above printf will also put the 705 * token in the log file. 706 */ 707 else if (log_file) { 708 log_print("%s %s\n", token, token2); 709 } 710 /* 711 * If the token was not in the pipe and it wasn't a command, flush 712 * the rest of the line to keep things in sync. 713 */ 714 if (interactive && cmdflag != CMD_INPUT) 715 flushline(); 716 /* 717 * Scrub off the white-space. 718 */ 719 clean_token(cleantoken, token); 720 /* 721 * If the input was a blank line and we weren't prompting 722 * specifically for a blank line... 723 */ 724 if ((strcmp(cleantoken, "") == 0) && (type != FIO_BLNK)) { 725 /* 726 * If there's a default, return it. 727 */ 728 if (deflt != NULL) { 729 if (type == FIO_OSTR) { 730 /* 731 * Duplicate and return the default string 732 */ 733 return ((int)alloc_string((char *)deflt)); 734 } else if (type == FIO_SLIST) { 735 /* 736 * If we can find a match for the default 737 * value in the list, return the default 738 * value. If there's no match for the 739 * default value, it's an illegal 740 * choice. Return the first value in 741 * the list. 742 */ 743 s = find_string(param->io_slist, *deflt); 744 if ((cur_label == L_TYPE_EFI) && 745 (s == (char *)NULL)) { 746 return (*deflt); 747 } 748 if (s == (char *)NULL) { 749 return ((param->io_slist)->value); 750 } else { 751 return (*deflt); 752 } 753 } else if (type == FIO_OPINT) { 754 /* 755 * The user didn't enter anything 756 */ 757 return (0); 758 } else if (type == FIO_ECYL) { 759 return (part_deflt->deflt_size); 760 } else if (type == FIO_INT64) { 761 return (efi_deflt->start_sector); 762 } else if (type == FIO_EFI) { 763 return (efi_deflt->end_sector); 764 } else { 765 return (*deflt); 766 } 767 } 768 /* 769 * If the blank was not in the pipe, just reprompt. 770 */ 771 if (interactive) { 772 goto reprompt; 773 } 774 /* 775 * If the blank was in the pipe, it's an error. 776 */ 777 err_print("No default for this entry.\n"); 778 cmdabort(SIGINT); 779 } 780 /* 781 * If token is a '?' or a 'h', it is a request for help. 782 */ 783 if ((strcmp(cleantoken, "?") == 0) || 784 (strcmp(cleantoken, "h") == 0) || 785 (strcmp(cleantoken, "help") == 0)) { 786 help = 1; 787 } 788 /* 789 * Switch on the type of input expected. 790 */ 791 switch (type) { 792 /* 793 * Expecting a disk block number. 794 */ 795 case FIO_BN: 796 /* 797 * Parameter is the bounds of legal block numbers. 798 */ 799 bounds = (struct bounds *)¶m->io_bounds; 800 /* 801 * Print help message if required. 802 */ 803 if (help) { 804 fmt_print("Expecting a block number from %llu (", 805 bounds->lower); 806 pr_dblock(fmt_print, bounds->lower); 807 fmt_print(") to %llu (", bounds->upper); 808 pr_dblock(fmt_print, bounds->upper); 809 fmt_print(")\n"); 810 break; 811 } 812 /* 813 * Convert token to a disk block number. 814 */ 815 if (cur_label == L_TYPE_EFI) { 816 if (geti64(cleantoken, (uint64_t *)&bn64, 817 (uint64_t *)NULL)) 818 break; 819 } else { 820 if (getbn(cleantoken, &bn64)) 821 break; 822 } 823 /* 824 * Check to be sure it is within the legal bounds. 825 */ 826 if ((bn64 < bounds->lower) || (bn64 > bounds->upper)) { 827 err_print("`"); 828 pr_dblock(err_print, bn64); 829 err_print("' is out of range.\n"); 830 break; 831 } 832 /* 833 * It's ok, return it. 834 */ 835 return (bn64); 836 /* 837 * Expecting an integer. 838 */ 839 case FIO_INT: 840 /* 841 * Parameter is the bounds of legal integers. 842 */ 843 bounds = (struct bounds *)¶m->io_bounds; 844 /* 845 * Print help message if required. 846 */ 847 if (help) { 848 fmt_print("Expecting an integer from %llu", 849 bounds->lower); 850 fmt_print(" to %llu\n", bounds->upper); 851 break; 852 } 853 /* 854 * Convert the token into an integer. 855 */ 856 if (geti(cleantoken, (int *)&bn, (int *)NULL)) 857 break; 858 /* 859 * Check to be sure it is within the legal bounds. 860 */ 861 if ((bn < bounds->lower) || (bn > bounds->upper)) { 862 err_print("`%lu' is out of range.\n", bn); 863 break; 864 } 865 /* 866 * If it's ok, return it. 867 */ 868 return (bn); 869 case FIO_INT64: 870 /* 871 * Parameter is the bounds of legal integers. 872 */ 873 bounds = (struct bounds *)¶m->io_bounds; 874 /* 875 * Print help message if required. 876 */ 877 if (help) { 878 fmt_print("Expecting an integer from %llu", 879 bounds->lower); 880 fmt_print(" to %llu\n", bounds->upper); 881 break; 882 } 883 /* 884 * Convert the token into an integer. 885 */ 886 if (geti64(cleantoken, (uint64_t *)&bn64, (uint64_t *)NULL)) { 887 break; 888 } 889 /* 890 * Check to be sure it is within the legal bounds. 891 */ 892 if ((bn64 < bounds->lower) || (bn64 > bounds->upper)) { 893 err_print("`%llu' is out of range.\n", bn64); 894 break; 895 } 896 /* 897 * If it's ok, return it. 898 */ 899 return (bn64); 900 /* 901 * Expecting an integer, or no input. 902 */ 903 case FIO_OPINT: 904 /* 905 * Parameter is the bounds of legal integers. 906 */ 907 bounds = (struct bounds *)¶m->io_bounds; 908 /* 909 * Print help message if required. 910 */ 911 if (help) { 912 fmt_print("Expecting an integer from %llu", 913 bounds->lower); 914 fmt_print(" to %llu, or no input\n", bounds->upper); 915 break; 916 } 917 /* 918 * Convert the token into an integer. 919 */ 920 if (geti(cleantoken, (int *)&bn, (int *)NULL)) 921 break; 922 /* 923 * Check to be sure it is within the legal bounds. 924 */ 925 if ((bn < bounds->lower) || (bn > bounds->upper)) { 926 err_print("`%lu' is out of range.\n", bn); 927 break; 928 } 929 /* 930 * For optional case, return 1 indicating that 931 * the user actually did enter something. 932 */ 933 if (!deflt) 934 *deflt = bn; 935 return (1); 936 /* 937 * Expecting a closed string. This means that the input 938 * string must exactly match one of the strings passed in 939 * as the parameter. 940 */ 941 case FIO_CSTR: 942 /* 943 * The parameter is a null terminated array of character 944 * pointers, each one pointing to a legal input string. 945 */ 946 strings = (char **)param->io_charlist; 947 /* 948 * Walk through the legal strings, seeing if any of them 949 * match the token. If a match is made, return the index 950 * of the string that was matched. 951 */ 952 for (str = strings; *str != NULL; str++) 953 if (strcmp(cleantoken, *str) == 0) 954 return (str - strings); 955 /* 956 * Print help message if required. 957 */ 958 if (help) { 959 print_input_choices(type, param); 960 } else { 961 err_print("`%s' is not expected.\n", cleantoken); 962 } 963 break; 964 /* 965 * Expecting a matched string. This means that the input 966 * string must either match one of the strings passed in, 967 * or be a unique abbreviation of one of them. 968 */ 969 case FIO_MSTR: 970 /* 971 * The parameter is a null terminated array of character 972 * pointers, each one pointing to a legal input string. 973 */ 974 strings = (char **)param->io_charlist; 975 length = index = tied = 0; 976 /* 977 * Loop through the legal input strings. 978 */ 979 for (str = strings; *str != NULL; str++) { 980 /* 981 * See how many characters of the token match 982 * this legal string. 983 */ 984 i = strcnt(cleantoken, *str); 985 /* 986 * If it's not the whole token, then it's not a match. 987 */ 988 if ((uint_t)i < strlen(cleantoken)) 989 continue; 990 /* 991 * If it ties with another input, remember that. 992 */ 993 if (i == length) 994 tied = 1; 995 /* 996 * If it matches the most so far, record that. 997 */ 998 if (i > length) { 999 index = str - strings; 1000 tied = 0; 1001 length = i; 1002 } 1003 } 1004 /* 1005 * Print help message if required. 1006 */ 1007 if (length == 0) { 1008 if (help) { 1009 print_input_choices(type, param); 1010 } else { 1011 err_print("`%s' is not expected.\n", 1012 cleantoken); 1013 } 1014 break; 1015 } 1016 /* 1017 * If the abbreviation was non-unique, it's an error. 1018 */ 1019 if (tied) { 1020 err_print("`%s' is ambiguous.\n", cleantoken); 1021 break; 1022 } 1023 /* 1024 * We matched one. Return the index of the string we matched. 1025 */ 1026 return (index); 1027 /* 1028 * Expecting an open string. This means that any string is legal. 1029 */ 1030 case FIO_OSTR: 1031 /* 1032 * Print a help message if required. 1033 */ 1034 if (help) { 1035 fmt_print("Expecting a string\n"); 1036 break; 1037 } 1038 /* 1039 * alloc a copy of the string and return it 1040 */ 1041 return ((int)alloc_string(token)); 1042 1043 /* 1044 * Expecting a blank line. 1045 */ 1046 case FIO_BLNK: 1047 /* 1048 * We are always in non-echo mode when we are inputting 1049 * this type. We echo the newline as a carriage return 1050 * only so the prompt string will be covered over. 1051 */ 1052 nolog_print("\015"); 1053 /* 1054 * If we are logging, send a newline to the log file. 1055 */ 1056 if (log_file) 1057 log_print("\n"); 1058 /* 1059 * There is no value returned for this type. 1060 */ 1061 return (0); 1062 1063 /* 1064 * Expecting one of the entries in a string list. 1065 * Accept unique abbreviations. 1066 * Return the value associated with the matched string. 1067 */ 1068 case FIO_SLIST: 1069 i = find_value((slist_t *)param->io_slist, 1070 cleantoken, &value); 1071 if (i == 1) { 1072 return (value); 1073 } else { 1074 /* 1075 * Print help message if required. 1076 */ 1077 1078 if (help) { 1079 print_input_choices(type, param); 1080 } else { 1081 if (i == 0) 1082 err_print("`%s' not expected.\n", 1083 cleantoken); 1084 else 1085 err_print("`%s' is ambiguous.\n", 1086 cleantoken); 1087 } 1088 } 1089 break; 1090 1091 /* 1092 * Cylinder size input when modifying a complete partition map 1093 */ 1094 case FIO_CYL: 1095 /* 1096 * Parameter is the bounds of legal block numbers. 1097 */ 1098 bounds = (struct bounds *)¶m->io_bounds; 1099 assert(bounds->lower == 0); 1100 /* 1101 * Print help message if required. 1102 */ 1103 if (help) { 1104 fmt_print("Expecting up to %llu blocks,", 1105 bounds->upper); 1106 fmt_print(" %u cylinders, ", bn2c(bounds->upper)); 1107 fmt_print(" %1.2f megabytes, ", bn2mb(bounds->upper)); 1108 fmt_print("or %1.2f gigabytes\n", bn2gb(bounds->upper)); 1109 break; 1110 } 1111 /* 1112 * Parse the first token: try to find 'b', 'c' or 'm' 1113 */ 1114 s = cleantoken; 1115 while (*s && (isdigit(*s) || (*s == '.') || (*s == '$'))) { 1116 s++; 1117 } 1118 /* 1119 * If we found a conversion specifier, second token is unused 1120 * Otherwise, the second token should supply it. 1121 */ 1122 if (*s != 0) { 1123 value = *s; 1124 *s = 0; 1125 } else { 1126 value = cleantoken2[0]; 1127 } 1128 /* 1129 * If the token is the wild card, simply supply the max 1130 * This order allows the user to specify the maximum in 1131 * either blocks/cyls/megabytes - a convenient fiction. 1132 */ 1133 if (strcmp(cleantoken, WILD_STRING) == 0) { 1134 return (bounds->upper); 1135 } 1136 /* 1137 * Allow the user to specify zero with no units, 1138 * by just defaulting to cylinders. 1139 */ 1140 if (strcmp(cleantoken, "0") == 0) { 1141 value = 'c'; 1142 } 1143 /* 1144 * If there's a decimal point, but no unit specification, 1145 * let's assume megabytes. 1146 */ 1147 if ((value == 0) && (strchr(cleantoken, '.') != NULL)) { 1148 value = 'm'; 1149 } 1150 /* 1151 * Handle each unit type we support 1152 */ 1153 switch (value) { 1154 case 'b': 1155 /* 1156 * Convert token to a disk block number. 1157 */ 1158 if (geti64(cleantoken, &bn64, &bounds->upper)) 1159 break; 1160 /* 1161 * Check to be sure it is within the legal bounds. 1162 */ 1163 if ((bn64 < bounds->lower) || (bn64 > bounds->upper)) { 1164 err_print( 1165 "`%llub' is out of the range %llu " 1166 "to %llu\n", 1167 bn64, bounds->lower, bounds->upper); 1168 break; 1169 } 1170 /* 1171 * Verify the block lies on a cylinder boundary 1172 */ 1173 if ((bn64 % spc()) != 0) { 1174 err_print( 1175 "partition size must be a multiple of " 1176 "%u blocks to lie on a cylinder boundary\n", 1177 spc()); 1178 err_print( 1179 "%llu blocks is approximately %u cylinders," 1180 " %1.2f megabytes or %1.2f gigabytes\n", 1181 bn64, bn2c(bn64), bn2mb(bn64), bn2gb(bn64)); 1182 break; 1183 } 1184 return (bn64); 1185 case 'c': 1186 /* 1187 * Convert token from a number of cylinders to 1188 * a number of blocks. 1189 */ 1190 i = bn2c(bounds->upper); 1191 if (geti(cleantoken, &cyls, &i)) 1192 break; 1193 /* 1194 * Check the bounds - cyls is number of cylinders 1195 */ 1196 if (cyls > (bounds->upper/spc())) { 1197 err_print("`%dc' is out of range\n", cyls); 1198 break; 1199 } 1200 /* 1201 * Convert cylinders to blocks and return 1202 */ 1203 return (cyls * spc()); 1204 case 'm': 1205 /* 1206 * Convert token from megabytes to a block number. 1207 */ 1208 if (sscanf(cleantoken, "%f2", &nmegs) != 1) { 1209 err_print("`%s' is not recognized\n", 1210 cleantoken); 1211 break; 1212 } 1213 /* 1214 * Check the bounds 1215 */ 1216 if (nmegs > bn2mb(bounds->upper)) { 1217 err_print("`%1.2fmb' is out of range\n", nmegs); 1218 break; 1219 } 1220 /* 1221 * Convert to blocks 1222 */ 1223 bn64 = mb2bn(nmegs); 1224 /* 1225 * Round value up to nearest cylinder 1226 */ 1227 i = spc(); 1228 bn64 = ((bn64 + (i-1)) / i) * i; 1229 return (bn64); 1230 case 'g': 1231 /* 1232 * Convert token from gigabytes to a block number. 1233 */ 1234 if (sscanf(cleantoken, "%f2", &ngigs) != 1) { 1235 err_print("`%s' is not recognized\n", 1236 cleantoken); 1237 break; 1238 } 1239 /* 1240 * Check the bounds 1241 */ 1242 if (ngigs > bn2gb(bounds->upper)) { 1243 err_print("`%1.2fgb' is out of range\n", ngigs); 1244 break; 1245 } 1246 /* 1247 * Convert to blocks 1248 */ 1249 bn64 = gb2bn(ngigs); 1250 /* 1251 * Round value up to nearest cylinder 1252 */ 1253 i = spc(); 1254 bn64 = ((bn64 + (i-1)) / i) * i; 1255 return (bn64); 1256 default: 1257 err_print( 1258 "Please specify units in either b(blocks), c(cylinders), m(megabytes) \ 1259 or g(gigabytes)\n"); 1260 break; 1261 } 1262 break; 1263 1264 case FIO_ECYL: 1265 /* 1266 * Parameter is the bounds of legal block numbers. 1267 */ 1268 bounds = (struct bounds *)¶m->io_bounds; 1269 assert(bounds->lower == 0); 1270 1271 /* 1272 * Print help message if required. 1273 */ 1274 if (help) { 1275 fmt_print("Expecting up to %llu blocks,", 1276 bounds->upper); 1277 fmt_print(" %u cylinders, ", 1278 bn2c(bounds->upper)); 1279 fmt_print(" %u end cylinder, ", 1280 (uint_t)(bounds->upper / spc())); 1281 fmt_print(" %1.2f megabytes, ", 1282 bn2mb(bounds->upper)); 1283 fmt_print("or %1.2f gigabytes\n", 1284 bn2gb(bounds->upper)); 1285 break; 1286 } 1287 1288 /* 1289 * Parse the first token: try to find 'b', 'c', 'e' 1290 * or 'm' 1291 */ 1292 s = cleantoken; 1293 while (*s && (isdigit(*s) || (*s == '.') || (*s == '$'))) { 1294 s++; 1295 } 1296 1297 /* 1298 * If we found a conversion specifier, second token is 1299 * unused Otherwise, the second token should supply it. 1300 */ 1301 if (*s != 0) { 1302 value = *s; 1303 *s = 0; 1304 } else { 1305 value = cleantoken2[0]; 1306 } 1307 1308 /* 1309 * If the token is the wild card, simply supply the max 1310 * This order allows the user to specify the maximum in 1311 * either blocks/cyls/megabytes - a convenient fiction. 1312 */ 1313 if (strcmp(cleantoken, WILD_STRING) == 0) { 1314 return (bounds->upper); 1315 } 1316 1317 /* 1318 * Allow the user to specify zero with no units, 1319 * by just defaulting to cylinders. 1320 */ 1321 1322 if (value != 'e' && strcmp(cleantoken, "0") == 0) { 1323 value = 'c'; 1324 } 1325 1326 1327 /* 1328 * If there's a decimal point, but no unit 1329 * specification, let's assume megabytes. 1330 */ 1331 if ((value == 0) && (strchr(cleantoken, '.') != NULL)) { 1332 value = 'm'; 1333 } 1334 1335 /* 1336 * Handle each unit type we support 1337 */ 1338 switch (value) { 1339 case 'b': 1340 /* 1341 * Convert token to a disk block number. 1342 */ 1343 if (geti64(cleantoken, &bn64, &bounds->upper)) 1344 break; 1345 /* 1346 * Check to be sure it is within the 1347 * legal bounds. 1348 */ 1349 if ((bn64 < bounds->lower) || (bn64 > bounds->upper)) { 1350 err_print( 1351 "`%llub' is out of the range %llu to %llu\n", 1352 bn64, bounds->lower, bounds->upper); 1353 break; 1354 } 1355 1356 /* 1357 * Verify the block lies on a cylinder 1358 * boundary 1359 */ 1360 if ((bn64 % spc()) != 0) { 1361 err_print( 1362 "partition size must be a multiple of %u " 1363 "blocks to lie on a cylinder boundary\n", 1364 spc()); 1365 err_print( 1366 "%llu blocks is approximately %u cylinders," 1367 " %1.2f megabytes or %1.2f gigabytes\n", 1368 bn64, bn2c(bn64), bn2mb(bn64), bn2gb(bn64)); 1369 break; 1370 } 1371 1372 return (bn64); 1373 1374 case 'e': 1375 /* 1376 * Token is ending cylinder 1377 */ 1378 1379 /* convert token to integer */ 1380 if (geti(cleantoken, &cylno, (int *)NULL)) { 1381 break; 1382 } 1383 1384 /* 1385 * check that input cylno isn't before the current 1386 * starting cylinder number. Note that we are NOT 1387 * using the starting cylinder from 1388 * cur_parts->pinfo_map[].dkl_cylno! 1389 */ 1390 if (cylno < part_deflt->start_cyl) { 1391 err_print( 1392 "End cylinder must fall on or after start cylinder %u\n", 1393 part_deflt->start_cyl); 1394 break; 1395 } 1396 1397 /* 1398 * calculate cylinder number of upper boundary, and 1399 * verify that our input is within range 1400 */ 1401 i = (bn2c(bounds->upper) + part_deflt->start_cyl - 1); 1402 1403 if (cylno > i) { 1404 err_print( 1405 "End cylinder %d is beyond max cylinder %d\n", 1406 cylno, i); 1407 break; 1408 } 1409 1410 /* 1411 * calculate number of cylinders based on input 1412 */ 1413 cyls = ((cylno - part_deflt->start_cyl) + 1); 1414 1415 return (cyls * spc()); 1416 1417 case 'c': 1418 /* 1419 * Convert token from a number of 1420 * cylinders to a number of blocks. 1421 */ 1422 i = bn2c(bounds->upper); 1423 if (geti(cleantoken, &cyls, &i)) 1424 break; 1425 1426 /* 1427 * Check the bounds - cyls is number of 1428 * cylinders 1429 */ 1430 if (cyls > (bounds->upper/spc())) { 1431 err_print("`%dc' is out of range\n", cyls); 1432 break; 1433 } 1434 1435 /* 1436 * Convert cylinders to blocks and 1437 * return 1438 */ 1439 return (cyls * spc()); 1440 1441 case 'm': 1442 /* 1443 * Convert token from megabytes to a 1444 * block number. 1445 */ 1446 if (sscanf(cleantoken, "%f2", &nmegs) != 1) { 1447 err_print("`%s' is not recognized\n", 1448 cleantoken); 1449 break; 1450 } 1451 1452 /* 1453 * Check the bounds 1454 */ 1455 if (nmegs > bn2mb(bounds->upper)) { 1456 err_print("`%1.2fmb' is out of range\n", nmegs); 1457 break; 1458 } 1459 1460 /* 1461 * Convert to blocks 1462 */ 1463 bn64 = mb2bn(nmegs); 1464 1465 /* 1466 * Round value up to nearest cylinder 1467 */ 1468 i = spc(); 1469 bn64 = ((bn64 + (i-1)) / i) * i; 1470 return (bn64); 1471 1472 case 'g': 1473 /* 1474 * Convert token from gigabytes to a 1475 * block number. 1476 */ 1477 if (sscanf(cleantoken, "%f2", &ngigs) != 1) { 1478 err_print("`%s' is not recognized\n", 1479 cleantoken); 1480 break; 1481 } 1482 1483 /* 1484 * Check the bounds 1485 */ 1486 if (ngigs > bn2gb(bounds->upper)) { 1487 err_print("`%1.2fgb' is out of range\n", ngigs); 1488 break; 1489 } 1490 1491 /* 1492 * Convert to blocks 1493 */ 1494 bn64 = gb2bn(ngigs); 1495 1496 /* 1497 * Round value up to nearest cylinder 1498 */ 1499 i = spc(); 1500 bn64 = ((bn64 + (i-1)) / i) * i; 1501 return (bn64); 1502 1503 default: 1504 err_print( 1505 "Please specify units in either b(blocks), c(cylinders), e(end cylinder),\n"); 1506 err_print("m(megabytes) or g(gigabytes)\n"); 1507 break; 1508 } 1509 break; 1510 case FIO_EFI: 1511 /* 1512 * Parameter is the bounds of legal block numbers. 1513 */ 1514 bounds = (struct bounds *)¶m->io_bounds; 1515 1516 /* 1517 * Print help message if required. 1518 */ 1519 if (help) { 1520 fmt_print("Expecting up to %llu sectors,", 1521 cur_parts->etoc->efi_last_u_lba); 1522 fmt_print("or %llu megabytes,", 1523 (cur_parts->etoc->efi_last_u_lba * cur_blksz)/ 1524 (1024 * 1024)); 1525 fmt_print("or %llu gigabytes\n", 1526 (cur_parts->etoc->efi_last_u_lba * cur_blksz)/ 1527 (1024 * 1024 * 1024)); 1528 fmt_print("or %llu terabytes\n", 1529 (cur_parts->etoc->efi_last_u_lba * cur_blksz)/ 1530 ((uint64_t)1024 * 1024 * 1024 * 1024)); 1531 break; 1532 } 1533 1534 /* 1535 * Parse the first token: try to find 'b', 'c', 'e' 1536 * or 'm' 1537 */ 1538 s = cleantoken; 1539 while (*s && (isdigit(*s) || (*s == '.') || (*s == '$'))) { 1540 s++; 1541 } 1542 1543 /* 1544 * If we found a conversion specifier, second token is 1545 * unused Otherwise, the second token should supply it. 1546 */ 1547 if (*s != 0) { 1548 value = *s; 1549 *s = 0; 1550 } else { 1551 value = cleantoken2[0]; 1552 } 1553 1554 /* 1555 * If the token is the wild card, simply supply the max 1556 * This order allows the user to specify the maximum in 1557 * either blocks/cyls/megabytes - a convenient fiction. 1558 */ 1559 if (strcmp(cleantoken, WILD_STRING) == 0) { 1560 uint64_t reserved; 1561 1562 reserved = efi_reserved_sectors(cur_parts->etoc); 1563 return (bounds->upper - reserved - 1564 efi_deflt->start_sector + 1); 1565 } 1566 1567 /* 1568 * Allow the user to specify zero with no units, 1569 * by just defaulting to sectors. 1570 */ 1571 1572 if (value != 'e' && strcmp(cleantoken, "0") == 0) { 1573 value = 'm'; 1574 } 1575 1576 1577 /* 1578 * If there's a decimal point, but no unit 1579 * specification, let's assume megabytes. 1580 */ 1581 if ((value == 0) && (strchr(cleantoken, '.') != NULL)) { 1582 value = 'm'; 1583 } 1584 1585 /* 1586 * Handle each unit type we support 1587 */ 1588 switch (value) { 1589 case 'b': 1590 /* 1591 * Token is number of blocks 1592 */ 1593 if (geti64(cleantoken, &blokno, (uint64_t *)NULL)) { 1594 break; 1595 } 1596 if (blokno > bounds->upper) { 1597 err_print( 1598 "Number of blocks must be less that the total available blocks.\n"); 1599 break; 1600 } 1601 return (blokno); 1602 1603 case 'e': 1604 /* 1605 * Token is ending block number 1606 */ 1607 1608 /* convert token to integer */ 1609 if (geti64(cleantoken, &blokno, (uint64_t *)NULL)) { 1610 break; 1611 } 1612 1613 /* 1614 * Some sanity check 1615 */ 1616 if (blokno < efi_deflt->start_sector) { 1617 err_print( 1618 "End Sector must fall on or after start sector %llu\n", 1619 efi_deflt->start_sector); 1620 break; 1621 } 1622 1623 /* 1624 * verify that our input is within range 1625 */ 1626 if (blokno > cur_parts->etoc->efi_last_u_lba) { 1627 err_print( 1628 "End Sector %llu is beyond max Sector %llu\n", 1629 blokno, cur_parts->etoc->efi_last_u_lba); 1630 break; 1631 } 1632 1633 /* 1634 * calculate number of blocks based on input 1635 */ 1636 1637 return (blokno - efi_deflt->start_sector + 1); 1638 1639 case 'm': 1640 /* 1641 * Convert token from megabytes to a 1642 * block number. 1643 */ 1644 if (sscanf(cleantoken, "%f2", &nmegs) != 1) { 1645 err_print("`%s' is not recognized\n", 1646 cleantoken); 1647 break; 1648 } 1649 1650 /* 1651 * Check the bounds 1652 */ 1653 if (nmegs > bn2mb(bounds->upper - bounds->lower)) { 1654 err_print("`%1.2fmb' is out of range\n", nmegs); 1655 break; 1656 } 1657 1658 return (mb2bn(nmegs)); 1659 1660 case 'g': 1661 if (sscanf(cleantoken, "%f2", &nmegs) != 1) { 1662 err_print("`%s' is not recognized\n", 1663 cleantoken); 1664 break; 1665 } 1666 if (nmegs > bn2gb(bounds->upper - bounds->lower)) { 1667 err_print("`%1.2fgb' is out of range\n", nmegs); 1668 break; 1669 } 1670 1671 return (gb2bn(nmegs)); 1672 1673 case 't': 1674 if (sscanf(cleantoken, "%f2", &nmegs) != 1) { 1675 err_print("`%s' is not recognized\n", 1676 cleantoken); 1677 break; 1678 } 1679 if (nmegs > bn2tb(bounds->upper - bounds->lower)) { 1680 err_print("`%1.2ftb' is out of range\n", nmegs); 1681 break; 1682 } 1683 return (uint64_t)((float)nmegs * 1024.0 * 1684 1024.0 * 1024.0 * 1024.0 / cur_blksz); 1685 1686 default: 1687 err_print( 1688 "Please specify units in either b(number of blocks), e(end sector),\n"); 1689 err_print(" g(gigabytes), m(megabytes)"); 1690 err_print(" or t(terabytes)\n"); 1691 break; 1692 } 1693 break; 1694 1695 /* 1696 * If we don't recognize the input type, it's bad news. 1697 */ 1698 default: 1699 err_print("Error: unknown input type.\n"); 1700 fullabort(); 1701 } 1702 /* 1703 * If we get here, it's because some error kept us from accepting 1704 * the token. If we are running out of a command file, gracefully 1705 * leave the program. If we are interacting with the user, simply 1706 * reprompt. If the token was in the pipe, abort the current command. 1707 */ 1708 if (option_f) 1709 fullabort(); 1710 else if (interactive) 1711 goto reprompt; 1712 else 1713 cmdabort(SIGINT); 1714 /* 1715 * Never actually reached. 1716 */ 1717 return (-1); 1718 } 1719 1720 /* 1721 * Print input choices 1722 */ 1723 static void 1724 print_input_choices(type, param) 1725 int type; 1726 u_ioparam_t *param; 1727 { 1728 char **sp; 1729 slist_t *lp; 1730 int width; 1731 int col; 1732 int ncols; 1733 1734 switch (type) { 1735 case FIO_CSTR: 1736 fmt_print("Expecting one of the following:\n"); 1737 goto common; 1738 1739 case FIO_MSTR: 1740 fmt_print("Expecting one of the following: "); 1741 fmt_print("(abbreviations ok):\n"); 1742 common: 1743 for (sp = (char **)param->io_charlist; *sp != NULL; sp++) { 1744 fmt_print("\t%s\n", *sp); 1745 } 1746 break; 1747 1748 case FIO_SLIST: 1749 fmt_print("Expecting one of the following: "); 1750 fmt_print("(abbreviations ok):\n"); 1751 /* 1752 * Figure out the width of the widest string 1753 */ 1754 width = slist_widest_str((slist_t *)param->io_slist); 1755 width += 4; 1756 /* 1757 * If the help messages are empty, print the 1758 * possible choices in left-justified columns 1759 */ 1760 lp = (slist_t *)param->io_slist; 1761 if (*lp->help == 0) { 1762 col = 0; 1763 ncols = 60 / width; 1764 for (; lp->str != NULL; lp++) { 1765 if (col == 0) 1766 fmt_print("\t"); 1767 ljust_print(lp->str, 1768 (++col == ncols) ? 0 : width); 1769 if (col == ncols) { 1770 col = 0; 1771 fmt_print("\n"); 1772 } 1773 } 1774 if (col != 0) 1775 fmt_print("\n"); 1776 } else { 1777 /* 1778 * With help messages, print each choice, 1779 * and help message, on its own line. 1780 */ 1781 for (; lp->str != NULL; lp++) { 1782 fmt_print("\t"); 1783 ljust_print(lp->str, width); 1784 fmt_print("- %s\n", lp->help); 1785 } 1786 } 1787 break; 1788 1789 default: 1790 err_print("Error: unknown input type.\n"); 1791 fullabort(); 1792 } 1793 1794 fmt_print("\n"); 1795 } 1796 1797 1798 /* 1799 * Search a string list for a particular string. 1800 * Use minimum recognition, to accept unique abbreviations 1801 * Return the number of possible matches found. 1802 * If only one match was found, return the arbitrary value 1803 * associated with the matched string in match_value. 1804 */ 1805 int 1806 find_value(slist, match_str, match_value) 1807 slist_t *slist; 1808 char *match_str; 1809 int *match_value; 1810 { 1811 int i; 1812 int nmatches; 1813 int length; 1814 int match_length; 1815 1816 nmatches = 0; 1817 length = 0; 1818 1819 match_length = strlen(match_str); 1820 1821 for (; slist->str != NULL; slist++) { 1822 /* 1823 * See how many characters of the token match 1824 */ 1825 i = strcnt(match_str, slist->str); 1826 /* 1827 * If it's not the whole token, then it's not a match. 1828 */ 1829 if (i < match_length) 1830 continue; 1831 /* 1832 * If it ties with another input, remember that. 1833 */ 1834 if (i == length) 1835 nmatches++; 1836 /* 1837 * If it matches the most so far, record that. 1838 */ 1839 if (i > length) { 1840 *match_value = slist->value; 1841 nmatches = 1; 1842 length = i; 1843 } 1844 } 1845 1846 return (nmatches); 1847 } 1848 1849 /* 1850 * Search a string list for a particular value. 1851 * Return the string associated with that value. 1852 */ 1853 char * 1854 find_string(slist, match_value) 1855 slist_t *slist; 1856 int match_value; 1857 { 1858 for (; slist->str != NULL; slist++) { 1859 if (slist->value == match_value) { 1860 return (slist->str); 1861 } 1862 } 1863 1864 return ((char *)NULL); 1865 } 1866 1867 /* 1868 * Return the width of the widest string in an slist 1869 */ 1870 static int 1871 slist_widest_str(slist) 1872 slist_t *slist; 1873 { 1874 int i; 1875 int width; 1876 1877 width = 0; 1878 for (; slist->str != NULL; slist++) { 1879 if ((i = strlen(slist->str)) > width) 1880 width = i; 1881 } 1882 1883 return (width); 1884 } 1885 1886 /* 1887 * Print a string left-justified to a fixed width. 1888 */ 1889 static void 1890 ljust_print(str, width) 1891 char *str; 1892 int width; 1893 { 1894 int i; 1895 1896 fmt_print("%s", str); 1897 for (i = width - strlen(str); i > 0; i--) { 1898 fmt_print(" "); 1899 } 1900 } 1901 1902 /* 1903 * This routine is a modified version of printf. It handles the cases 1904 * of silent mode and logging; other than that it is identical to the 1905 * library version. 1906 */ 1907 /*PRINTFLIKE1*/ 1908 void 1909 fmt_print(char *format, ...) 1910 { 1911 va_list ap; 1912 1913 va_start(ap, format); 1914 1915 /* 1916 * If we are running silent, skip it. 1917 */ 1918 if (option_s == 0) { 1919 /* 1920 * Do the print to standard out. 1921 */ 1922 if (need_newline) { 1923 (void) printf("\n"); 1924 } 1925 (void) vprintf(format, ap); 1926 /* 1927 * If we are logging, also print to the log file. 1928 */ 1929 if (log_file) { 1930 if (need_newline) { 1931 (void) fprintf(log_file, "\n"); 1932 } 1933 (void) vfprintf(log_file, format, ap); 1934 (void) fflush(log_file); 1935 } 1936 } 1937 1938 need_newline = 0; 1939 1940 va_end(ap); 1941 } 1942 1943 /* 1944 * This routine is a modified version of printf. It handles the cases 1945 * of silent mode; other than that it is identical to the 1946 * library version. It differs from the above printf in that it does 1947 * not print the message to a log file. 1948 */ 1949 /*PRINTFLIKE1*/ 1950 void 1951 nolog_print(char *format, ...) 1952 { 1953 va_list ap; 1954 1955 va_start(ap, format); 1956 1957 /* 1958 * If we are running silent, skip it. 1959 */ 1960 if (option_s == 0) { 1961 /* 1962 * Do the print to standard out. 1963 */ 1964 if (need_newline) { 1965 (void) printf("\n"); 1966 } 1967 (void) vprintf(format, ap); 1968 } 1969 1970 va_end(ap); 1971 1972 need_newline = 0; 1973 } 1974 1975 /* 1976 * This routine is a modified version of printf. It handles the cases 1977 * of silent mode, and only prints the message to the log file, not 1978 * stdout. Other than that is identical to the library version. 1979 */ 1980 /*PRINTFLIKE1*/ 1981 void 1982 log_print(char *format, ...) 1983 { 1984 va_list ap; 1985 1986 va_start(ap, format); 1987 1988 /* 1989 * If we are running silent, skip it. 1990 */ 1991 if (option_s == 0) { 1992 /* 1993 * Do the print to the log file. 1994 */ 1995 if (need_newline) { 1996 (void) fprintf(log_file, "\n"); 1997 } 1998 (void) vfprintf(log_file, format, ap); 1999 (void) fflush(log_file); 2000 } 2001 2002 va_end(ap); 2003 2004 need_newline = 0; 2005 } 2006 2007 /* 2008 * This routine is a modified version of printf. It prints the message 2009 * to stderr, and to the log file is appropriate. 2010 * Other than that is identical to the library version. 2011 */ 2012 /*PRINTFLIKE1*/ 2013 void 2014 err_print(char *format, ...) 2015 { 2016 va_list ap; 2017 2018 va_start(ap, format); 2019 2020 /* 2021 * Flush anything pending to stdout 2022 */ 2023 if (need_newline) { 2024 (void) printf("\n"); 2025 } 2026 (void) fflush(stdout); 2027 /* 2028 * Do the print to stderr. 2029 */ 2030 (void) vfprintf(stderr, format, ap); 2031 /* 2032 * If we are logging, also print to the log file. 2033 */ 2034 if (log_file) { 2035 if (need_newline) { 2036 (void) fprintf(log_file, "\n"); 2037 } 2038 (void) vfprintf(log_file, format, ap); 2039 (void) fflush(log_file); 2040 } 2041 va_end(ap); 2042 2043 need_newline = 0; 2044 } 2045 2046 /* 2047 * Print a number of characters from a buffer. The buffer 2048 * does not need to be null-terminated. Since the data 2049 * may be coming from a device, we cannot be sure the 2050 * data is not crud, so be rather defensive. 2051 */ 2052 void 2053 print_buf(buf, nbytes) 2054 char *buf; 2055 int nbytes; 2056 { 2057 int c; 2058 2059 while (nbytes-- > 0) { 2060 c = *buf++; 2061 if (isascii(c) && isprint(c)) { 2062 fmt_print("%c", c); 2063 } else 2064 break; 2065 } 2066 } 2067 2068 #ifdef not 2069 /* 2070 * This routine prints out a message describing the given ctlr. 2071 * The message is identical to the one printed by the kernel during 2072 * booting. 2073 */ 2074 void 2075 pr_ctlrline(ctlr) 2076 register struct ctlr_info *ctlr; 2077 { 2078 2079 fmt_print(" %s%d at %s 0x%x ", 2080 ctlr->ctlr_cname, ctlr->ctlr_num, 2081 space2str(ctlr->ctlr_space), ctlr->ctlr_addr); 2082 if (ctlr->ctlr_vec != 0) 2083 fmt_print("vec 0x%x ", ctlr->ctlr_vec); 2084 else 2085 fmt_print("pri %d ", ctlr->ctlr_prio); 2086 fmt_print("\n"); 2087 } 2088 #endif /* not */ 2089 2090 /* 2091 * This routine prints out a message describing the given disk. 2092 * The message is identical to the one printed by the kernel during 2093 * booting. 2094 */ 2095 void 2096 pr_diskline(disk, num) 2097 register struct disk_info *disk; 2098 int num; 2099 { 2100 struct ctlr_info *ctlr = disk->disk_ctlr; 2101 struct disk_type *type = disk->disk_type; 2102 2103 fmt_print(" %4d. %s ", num, disk->disk_name); 2104 if ((type != NULL) && (disk->label_type == L_TYPE_SOLARIS)) { 2105 fmt_print("<%s cyl %u alt %u hd %u sec %u>", 2106 type->dtype_asciilabel, type->dtype_ncyl, 2107 type->dtype_acyl, type->dtype_nhead, 2108 type->dtype_nsect); 2109 } else if ((type != NULL) && (disk->label_type == L_TYPE_EFI)) { 2110 cur_blksz = disk->disk_lbasize; 2111 print_efi_string(type->vendor, type->product, 2112 type->revision, type->capacity); 2113 } else if (disk->disk_flags & DSK_RESERVED) { 2114 fmt_print("<drive not available: reserved>"); 2115 } else if (disk->disk_flags & DSK_UNAVAILABLE) { 2116 fmt_print("<drive not available>"); 2117 } else { 2118 fmt_print("<drive type unknown>"); 2119 } 2120 if (chk_volname(disk)) { 2121 fmt_print(" "); 2122 print_volname(disk); 2123 } 2124 fmt_print("\n"); 2125 2126 if (disk->devfs_name != NULL) { 2127 fmt_print(" %s\n", disk->devfs_name); 2128 } else { 2129 fmt_print(" %s%d at %s%d slave %d\n", 2130 ctlr->ctlr_dname, disk->disk_dkinfo.dki_unit, 2131 ctlr->ctlr_cname, ctlr->ctlr_num, 2132 disk->disk_dkinfo.dki_slave); 2133 } 2134 2135 #ifdef OLD 2136 fmt_print(" %4d. %s at %s%d slave %d", num, disk->disk_name, 2137 ctlr->ctlr_cname, ctlr->ctlr_num, disk->disk_dkinfo.dki_slave); 2138 if (chk_volname(disk)) { 2139 fmt_print(": "); 2140 print_volname(disk); 2141 } 2142 fmt_print("\n"); 2143 if (type != NULL) { 2144 fmt_print( 2145 " %s%d: <%s cyl %u alt %u hd %u sec %u>\n", 2146 ctlr->ctlr_dname, disk->disk_dkinfo.dki_unit, 2147 type->dtype_asciilabel, type->dtype_ncyl, 2148 type->dtype_acyl, type->dtype_nhead, 2149 type->dtype_nsect); 2150 } else { 2151 fmt_print(" %s%d: <drive type unknown>\n", 2152 ctlr->ctlr_dname, disk->disk_dkinfo.dki_unit); 2153 } 2154 #endif /* OLD */ 2155 } 2156 2157 /* 2158 * This routine prints out a given disk block number in cylinder/head/sector 2159 * format. It uses the printing routine passed in to do the actual output. 2160 */ 2161 void 2162 pr_dblock(void (*func)(char *, ...), diskaddr_t bn) 2163 { 2164 if (cur_label == L_TYPE_SOLARIS) { 2165 (*func)("%u/%u/%u", bn2c(bn), 2166 bn2h(bn), bn2s(bn)); 2167 } else { 2168 (*func)("%llu", bn); 2169 } 2170 } 2171 2172 /* 2173 * This routine inputs a character from the data file. It understands 2174 * the use of '\' to prevent interpretation of a newline. It also keeps 2175 * track of the current line in the data file via a global variable. 2176 */ 2177 static int 2178 sup_inputchar() 2179 { 2180 int c; 2181 2182 /* 2183 * Input the character. 2184 */ 2185 c = getc(data_file); 2186 /* 2187 * If it's not a backslash, return it. 2188 */ 2189 if (c != '\\') 2190 return (c); 2191 /* 2192 * It was a backslash. Get the next character. 2193 */ 2194 c = getc(data_file); 2195 /* 2196 * If it was a newline, update the line counter and get the next 2197 * character. 2198 */ 2199 if (c == '\n') { 2200 data_lineno++; 2201 c = getc(data_file); 2202 } 2203 /* 2204 * Return the character. 2205 */ 2206 return (c); 2207 } 2208 2209 /* 2210 * This routine pushes a character back onto the input pipe for the data file. 2211 */ 2212 static void 2213 sup_pushchar(c) 2214 int c; 2215 { 2216 (void) ungetc(c, data_file); 2217 } 2218 2219 /* 2220 * Variables to support pushing back tokens 2221 */ 2222 static int have_pushed_token = 0; 2223 static TOKEN pushed_buf; 2224 static int pushed_token; 2225 2226 /* 2227 * This routine inputs a token from the data file. A token is a series 2228 * of contiguous non-white characters or a recognized special delimiter 2229 * character. Use of the wrapper lets us always have the value of the 2230 * last token around, which is useful for error recovery. 2231 */ 2232 int 2233 sup_gettoken(buf) 2234 char *buf; 2235 { 2236 last_token_type = sup_get_token(buf); 2237 return (last_token_type); 2238 } 2239 2240 static int 2241 sup_get_token(buf) 2242 char *buf; 2243 { 2244 char *ptr = buf; 2245 int c, quoted = 0; 2246 2247 /* 2248 * First check for presence of push-backed token. 2249 * If so, return it. 2250 */ 2251 if (have_pushed_token) { 2252 have_pushed_token = 0; 2253 bcopy(pushed_buf, buf, TOKEN_SIZE+1); 2254 return (pushed_token); 2255 } 2256 /* 2257 * Zero out the returned token buffer 2258 */ 2259 bzero(buf, TOKEN_SIZE + 1); 2260 /* 2261 * Strip off leading white-space. 2262 */ 2263 while ((isspace(c = sup_inputchar())) && (c != '\n')) 2264 ; 2265 /* 2266 * Read in characters until we hit unquoted white-space. 2267 */ 2268 for (; !isspace(c) || quoted; c = sup_inputchar()) { 2269 /* 2270 * If we hit eof, that's a token. 2271 */ 2272 if (feof(data_file)) 2273 return (SUP_EOF); 2274 /* 2275 * If we hit a double quote, change the state of quoting. 2276 */ 2277 if (c == '"') { 2278 quoted = !quoted; 2279 continue; 2280 } 2281 /* 2282 * If we hit a newline, that delimits a token. 2283 */ 2284 if (c == '\n') 2285 break; 2286 /* 2287 * If we hit any nonquoted special delimiters, that delimits 2288 * a token. 2289 */ 2290 if (!quoted && (c == '=' || c == ',' || c == ':' || 2291 c == '#' || c == '|' || c == '&' || c == '~')) 2292 break; 2293 /* 2294 * Store the character if there's room left. 2295 */ 2296 if (ptr - buf < TOKEN_SIZE) 2297 *ptr++ = (char)c; 2298 } 2299 /* 2300 * If we stored characters in the buffer, then we inputted a string. 2301 * Push the delimiter back into the pipe and return the string. 2302 */ 2303 if (ptr - buf > 0) { 2304 sup_pushchar(c); 2305 return (SUP_STRING); 2306 } 2307 /* 2308 * We didn't input a string, so we must have inputted a known delimiter. 2309 * store the delimiter in the buffer, so it will get returned. 2310 */ 2311 buf[0] = c; 2312 /* 2313 * Switch on the delimiter. Return the appropriate value for each one. 2314 */ 2315 switch (c) { 2316 case '=': 2317 return (SUP_EQL); 2318 case ':': 2319 return (SUP_COLON); 2320 case ',': 2321 return (SUP_COMMA); 2322 case '\n': 2323 return (SUP_EOL); 2324 case '|': 2325 return (SUP_OR); 2326 case '&': 2327 return (SUP_AND); 2328 case '~': 2329 return (SUP_TILDE); 2330 case '#': 2331 /* 2332 * For comments, we flush out the rest of the line and return 2333 * an EOL. 2334 */ 2335 while ((c = sup_inputchar()) != '\n' && !feof(data_file)) 2336 ; 2337 if (feof(data_file)) 2338 return (SUP_EOF); 2339 else 2340 return (SUP_EOL); 2341 /* 2342 * Shouldn't ever get here. 2343 */ 2344 default: 2345 return (SUP_STRING); 2346 } 2347 } 2348 2349 /* 2350 * Push back a token 2351 */ 2352 void 2353 sup_pushtoken(token_buf, token_type) 2354 char *token_buf; 2355 int token_type; 2356 { 2357 /* 2358 * We can only push one token back at a time 2359 */ 2360 assert(have_pushed_token == 0); 2361 2362 have_pushed_token = 1; 2363 bcopy(token_buf, pushed_buf, TOKEN_SIZE+1); 2364 pushed_token = token_type; 2365 } 2366 2367 /* 2368 * Get an entire line of input. Handles logging, comments, 2369 * and EOF. 2370 */ 2371 void 2372 get_inputline(line, nbytes) 2373 char *line; 2374 int nbytes; 2375 { 2376 char *p = line; 2377 int c; 2378 2379 /* 2380 * Remove any leading white-space and comments 2381 */ 2382 do { 2383 while ((isspace(c = getchar())) && (c != '\n')) 2384 ; 2385 } while (c == COMMENT_CHAR); 2386 /* 2387 * Loop on each character until end of line 2388 */ 2389 while (c != '\n') { 2390 /* 2391 * If we hit eof, get out. 2392 */ 2393 if (checkeof()) { 2394 fullabort(); 2395 } 2396 /* 2397 * Add the character to the buffer. 2398 */ 2399 if (nbytes > 1) { 2400 *p++ = (char)c; 2401 nbytes --; 2402 } 2403 /* 2404 * Get the next character. 2405 */ 2406 c = getchar(); 2407 } 2408 /* 2409 * Null terminate the token. 2410 */ 2411 *p = 0; 2412 /* 2413 * Indicate that we've emptied the pipe 2414 */ 2415 token_present = 0; 2416 /* 2417 * If we're running out of a file, echo the line to 2418 * the user, otherwise if we're logging, copy the 2419 * input to the log file. 2420 */ 2421 if (option_f) { 2422 fmt_print("%s\n", line); 2423 } else if (log_file) { 2424 log_print("%s\n", line); 2425 } 2426 } 2427 2428 /* 2429 * execute the shell escape command 2430 */ 2431 int 2432 execute_shell(char *s, size_t buff_size) 2433 { 2434 struct termio termio; 2435 struct termios tty; 2436 int tty_flag, i, j; 2437 char *shell_name; 2438 static char *default_shell = "/bin/sh"; 2439 2440 tty_flag = -1; 2441 2442 if (*s == '\0') { 2443 shell_name = getenv("SHELL"); 2444 2445 if (shell_name == NULL) { 2446 shell_name = default_shell; 2447 } 2448 if (strlcpy(s, shell_name, buff_size) >= 2449 buff_size) { 2450 err_print("Error: Shell command ($SHELL) too long.\n"); 2451 fullabort(); 2452 } 2453 } 2454 2455 /* save tty information */ 2456 2457 if (isatty(0)) { 2458 if (ioctl(0, TCGETS, &tty) == 0) 2459 tty_flag = 1; 2460 else { 2461 if (ioctl(0, TCGETA, &termio) == 0) { 2462 tty_flag = 0; 2463 tty.c_iflag = termio.c_iflag; 2464 tty.c_oflag = termio.c_oflag; 2465 tty.c_cflag = termio.c_cflag; 2466 tty.c_lflag = termio.c_lflag; 2467 for (i = 0; i < NCC; i++) 2468 tty.c_cc[i] = termio.c_cc[i]; 2469 } 2470 } 2471 } 2472 2473 /* close the current file descriptor */ 2474 if (cur_disk != NULL) { 2475 (void) close(cur_file); 2476 } 2477 2478 /* execute the shell escape */ 2479 (void) system(s); 2480 2481 /* reopen file descriptor if one was open before */ 2482 if (cur_disk != NULL) { 2483 if ((cur_file = open_disk(cur_disk->disk_path, 2484 O_RDWR | O_NDELAY)) < 0) { 2485 err_print("Error: can't reopen selected disk '%s'. \n", 2486 cur_disk->disk_name); 2487 fullabort(); 2488 } 2489 } 2490 2491 /* Restore tty information */ 2492 2493 if (isatty(0)) { 2494 if (tty_flag > 0) 2495 (void) ioctl(0, TCSETSW, &tty); 2496 else if (tty_flag == 0) { 2497 termio.c_iflag = tty.c_iflag; 2498 termio.c_oflag = tty.c_oflag; 2499 termio.c_cflag = tty.c_cflag; 2500 termio.c_lflag = tty.c_lflag; 2501 for (j = 0; j < NCC; j++) 2502 termio.c_cc[j] = tty.c_cc[j]; 2503 (void) ioctl(0, TCSETAW, &termio); 2504 } 2505 2506 if (isatty(1)) { 2507 fmt_print("\n[Hit Return to continue] \n"); 2508 (void) fflush(stdin); 2509 if (getchar() == EOF) 2510 fullabort(); 2511 } 2512 } 2513 return (0); 2514 } 2515 2516 void 2517 print_efi_string(char *vendor, char *product, char *revision, 2518 uint64_t capacity) 2519 { 2520 char *new_vendor; 2521 char *new_product; 2522 char *new_revision; 2523 char capacity_string[10]; 2524 float scaled; 2525 int i; 2526 2527 /* Strip whitespace from the end of inquiry strings */ 2528 new_vendor = strdup(vendor); 2529 if (new_vendor == NULL) 2530 return; 2531 2532 for (i = (strlen(new_vendor) - 1); i >= 0; i--) { 2533 if (new_vendor[i] != 0x20) { 2534 new_vendor[i+1] = '\0'; 2535 break; 2536 } 2537 } 2538 2539 new_product = strdup(product); 2540 if (new_product == NULL) { 2541 free(new_vendor); 2542 return; 2543 } 2544 2545 for (i = (strlen(new_product) - 1); i >= 0; i--) { 2546 if (new_product[i] != 0x20) { 2547 new_product[i+1] = '\0'; 2548 break; 2549 } 2550 } 2551 2552 new_revision = strdup(revision); 2553 if (new_product == NULL) { 2554 free(new_vendor); 2555 free(new_product); 2556 return; 2557 } 2558 2559 for (i = (strlen(new_revision) - 1); i >= 0; i--) { 2560 if (new_revision[i] != 0x20) { 2561 new_revision[i+1] = '\0'; 2562 break; 2563 } 2564 } 2565 2566 /* Now build size string */ 2567 scaled = bn2mb(capacity); 2568 if (scaled >= (float)1024.0 * 1024) { 2569 (void) snprintf(capacity_string, sizeof (capacity_string), 2570 "%.2fTB", scaled/((float)1024.0 * 1024)); 2571 } else if (scaled >= (float)1024.0) { 2572 (void) snprintf(capacity_string, sizeof (capacity_string), 2573 "%.2fGB", scaled/(float)1024.0); 2574 } else { 2575 (void) snprintf(capacity_string, sizeof (capacity_string), 2576 "%.2fMB", scaled); 2577 } 2578 2579 fmt_print("<%s-%s-%s-%s>", 2580 new_vendor, new_product, new_revision, capacity_string); 2581 2582 free(new_revision); 2583 free(new_product); 2584 free(new_vendor); 2585 } 2586