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