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