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