1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * This file contains I/O related functions. 31 */ 32 #include "global.h" 33 34 #include <unistd.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <signal.h> 38 #include <ctype.h> 39 #include <stdarg.h> 40 #include <sys/tty.h> 41 #include <sys/termio.h> 42 #include <sys/termios.h> 43 44 #include "startup.h" 45 #include "misc.h" 46 #include "menu_partition.h" 47 #include "param.h" 48 #include "menu.h" 49 50 51 extern int data_lineno; 52 extern char *space2str(); 53 extern long strtol(); 54 55 /* 56 * This variable is used to determine whether a token is present in the pipe 57 * already. 58 */ 59 static char token_present = 0; 60 61 /* 62 * This variable always gives us access to the most recent token type 63 */ 64 int last_token_type = 0; 65 66 #ifdef __STDC__ 67 /* 68 * Prototypes for ANSI C compilers 69 */ 70 static int sup_get_token(char *); 71 static void pushchar(int c); 72 static int checkeof(void); 73 static void flushline(void); 74 static int strcnt(char *s1, char *s2); 75 static int getbn(char *str, daddr_t *iptr); 76 static void print_input_choices(int type, u_ioparam_t *param); 77 static int slist_widest_str(slist_t *slist); 78 static void ljust_print(char *str, int width); 79 static int sup_inputchar(void); 80 static void sup_pushchar(int c); 81 static int geti64(char *str, uint64_t *iptr, uint64_t *wild); 82 83 #else /* __STDC__ */ 84 /* 85 * Prototypes for non-ANSI C compilers 86 */ 87 88 static int sup_get_token(); 89 static void pushchar(int c); 90 static int checkeof(void); 91 static void flushline(void); 92 static int strcnt(char *s1, char *s2); 93 static int getbn(char *str, daddr_t *iptr); 94 static void print_input_choices(int type, u_ioparam_t *param); 95 static int slist_widest_str(slist_t *slist); 96 static void ljust_print(char *str, int width); 97 static int sup_inputchar(void); 98 static void sup_pushchar(int c); 99 static int geti64(char *str, uint64_t *iptr, uint64_t *wild); 100 101 #endif /* __STDC__ */ 102 103 104 /* 105 * This routine pushes the given character back onto the input stream. 106 */ 107 static void 108 pushchar(c) 109 int c; 110 { 111 (void) ungetc(c, stdin); 112 } 113 114 /* 115 * This routine checks the input stream for an eof condition. 116 */ 117 static int 118 checkeof() 119 { 120 return (feof(stdin)); 121 } 122 123 /* 124 * This routine gets the next token off the input stream. A token is 125 * basically any consecutive non-white characters. 126 */ 127 char * 128 gettoken(inbuf) 129 char *inbuf; 130 { 131 char *ptr = inbuf; 132 int c, quoted = 0; 133 134 retoke: 135 /* 136 * Remove any leading white-space. 137 */ 138 while ((isspace(c = getchar())) && (c != '\n')) 139 ; 140 /* 141 * If we are at the beginning of a line and hit the comment character, 142 * flush the line and start again. 143 */ 144 if (!token_present && c == COMMENT_CHAR) { 145 token_present = 1; 146 flushline(); 147 goto retoke; 148 } 149 /* 150 * Loop on each character until we hit unquoted white-space. 151 */ 152 while (!isspace(c) || quoted && (c != '\n')) { 153 /* 154 * If we hit eof, get out. 155 */ 156 if (checkeof()) 157 return (NULL); 158 /* 159 * If we hit a double quote, change the state of quotedness. 160 */ 161 if (c == '"') 162 quoted = !quoted; 163 /* 164 * If there's room in the buffer, add the character to the end. 165 */ 166 else if (ptr - inbuf < TOKEN_SIZE) 167 *ptr++ = (char)c; 168 /* 169 * Get the next character. 170 */ 171 c = getchar(); 172 } 173 /* 174 * Null terminate the token. 175 */ 176 *ptr = '\0'; 177 /* 178 * Peel off white-space still in the pipe. 179 */ 180 while (isspace(c) && (c != '\n')) 181 c = getchar(); 182 /* 183 * If we hit another token, push it back and set state. 184 */ 185 if (c != '\n') { 186 pushchar(c); 187 token_present = 1; 188 } else 189 token_present = 0; 190 /* 191 * Return the token. 192 */ 193 return (inbuf); 194 } 195 196 /* 197 * This routine removes the leading and trailing spaces from a token. 198 */ 199 void 200 clean_token(cleantoken, token) 201 char *cleantoken, *token; 202 { 203 char *ptr; 204 205 /* 206 * Strip off leading white-space. 207 */ 208 for (ptr = token; isspace(*ptr); ptr++) 209 ; 210 /* 211 * Copy it into the clean buffer. 212 */ 213 (void) strcpy(cleantoken, ptr); 214 /* 215 * Strip off trailing white-space. 216 */ 217 for (ptr = cleantoken + strlen(cleantoken) - 1; 218 isspace(*ptr) && (ptr >= cleantoken); ptr--) { 219 *ptr = '\0'; 220 } 221 } 222 223 /* 224 * This routine checks if a token is already present on the input line 225 */ 226 int 227 istokenpresent() 228 { 229 return (token_present); 230 } 231 232 /* 233 * This routine flushes the rest of an input line if there is known 234 * to be data in it. The flush has to be qualified because the newline 235 * may have already been swallowed by the last gettoken. 236 */ 237 static void 238 flushline() 239 { 240 if (token_present) { 241 /* 242 * Flush the pipe to eol or eof. 243 */ 244 while ((getchar() != '\n') && !checkeof()) 245 ; 246 /* 247 * Mark the pipe empty. 248 */ 249 token_present = 0; 250 } 251 } 252 253 /* 254 * This routine returns the number of characters that are identical 255 * between s1 and s2, stopping as soon as a mismatch is found. 256 */ 257 static int 258 strcnt(s1, s2) 259 char *s1, *s2; 260 { 261 int i = 0; 262 263 while ((*s1 != '\0') && (*s1++ == *s2++)) 264 i++; 265 return (i); 266 } 267 268 /* 269 * This routine converts the given token into an integer. The token 270 * must convert cleanly into an integer with no unknown characters. 271 * If the token is the wildcard string, and the wildcard parameter 272 * is present, the wildcard value will be returned. 273 */ 274 int 275 geti(str, iptr, wild) 276 char *str; 277 int *iptr, *wild; 278 { 279 char *str2; 280 281 /* 282 * If there's a wildcard value and the string is wild, return the 283 * wildcard value. 284 */ 285 if (wild != NULL && strcmp(str, WILD_STRING) == 0) 286 *iptr = *wild; 287 else { 288 /* 289 * Conver the string to an integer. 290 */ 291 *iptr = (int)strtol(str, &str2, 0); 292 /* 293 * If any characters didn't convert, it's an error. 294 */ 295 if (*str2 != '\0') { 296 err_print("`%s' is not an integer.\n", str); 297 return (-1); 298 } 299 } 300 return (0); 301 } 302 303 /* 304 * This routine converts the given token into a long long. The token 305 * must convert cleanly into a 64-bit integer with no unknown characters. 306 * If the token is the wildcard string, and the wildcard parameter 307 * is present, the wildcard value will be returned. 308 */ 309 static int 310 geti64(str, iptr, wild) 311 char *str; 312 uint64_t *iptr, *wild; 313 { 314 char *str2; 315 316 /* 317 * If there's a wildcard value and the string is wild, return the 318 * wildcard value. 319 */ 320 if ((wild != NULL) && (strcmp(str, WILD_STRING)) == 0) { 321 *iptr = *wild; 322 } else { 323 /* 324 * Conver the string to an integer. 325 */ 326 *iptr = (uint64_t)strtoll(str, &str2, 0); 327 /* 328 * If any characters didn't convert, it's an error. 329 */ 330 if (*str2 != '\0') { 331 err_print("`%s' is not an integer.\n", str); 332 return (-1); 333 } 334 } 335 return (0); 336 } 337 338 /* 339 * This routine converts the given string into a block number on the 340 * current disk. The format of a block number is either a self-based 341 * number, or a series of self-based numbers separated by slashes. 342 * Any number preceeding the first slash is considered a cylinder value. 343 * Any number succeeding the first slash but preceeding the second is 344 * considered a head value. Any number succeeding the second slash is 345 * considered a sector value. Any of these numbers can be wildcarded 346 * to the highest possible legal value. 347 */ 348 static int 349 getbn(str, iptr) 350 char *str; 351 daddr_t *iptr; 352 { 353 char *cptr, *hptr, *sptr; 354 int cyl, head, sect, wild; 355 TOKEN buf; 356 357 /* 358 * Set cylinder pointer to beginning of string. 359 */ 360 cptr = str; 361 /* 362 * Look for the first slash. 363 */ 364 while ((*str != '\0') && (*str != '/')) 365 str++; 366 /* 367 * If there wasn't one, convert string to an integer and return it. 368 */ 369 if (*str == '\0') { 370 wild = physsects() - 1; 371 if (geti(cptr, (int *)iptr, &wild)) 372 return (-1); 373 return (0); 374 } 375 /* 376 * Null out the slash and set head pointer just beyond it. 377 */ 378 *str++ = '\0'; 379 hptr = str; 380 /* 381 * Look for the second slash. 382 */ 383 while ((*str != '\0') && (*str != '/')) 384 str++; 385 /* 386 * If there wasn't one, sector pointer points to a . 387 */ 388 if (*str == '\0') 389 sptr = str; 390 /* 391 * If there was, null it out and set sector point just beyond it. 392 */ 393 else { 394 *str++ = '\0'; 395 sptr = str; 396 } 397 /* 398 * Convert the cylinder part to an integer and store it. 399 */ 400 clean_token(buf, cptr); 401 wild = ncyl + acyl - 1; 402 if (geti(buf, &cyl, &wild)) 403 return (-1); 404 if ((cyl < 0) || (cyl >= (ncyl + acyl))) { 405 err_print("`%d' is out of range.\n", cyl); 406 return (-1); 407 } 408 /* 409 * Convert the head part to an integer and store it. 410 */ 411 clean_token(buf, hptr); 412 wild = nhead - 1; 413 if (geti(buf, &head, &wild)) 414 return (-1); 415 if ((head < 0) || (head >= nhead)) { 416 err_print("`%d' is out of range.\n", head); 417 return (-1); 418 } 419 /* 420 * Convert the sector part to an integer and store it. 421 */ 422 clean_token(buf, sptr); 423 wild = sectors(head) - 1; 424 if (geti(buf, §, &wild)) 425 return (-1); 426 if ((sect < 0) || (sect >= sectors(head))) { 427 err_print("`%d' is out of range.\n", sect); 428 return (-1); 429 } 430 /* 431 * Combine the pieces into a block number and return it. 432 */ 433 *iptr = chs2bn(cyl, head, sect); 434 return (0); 435 } 436 437 /* 438 * This routine is the basis for all input into the program. It 439 * understands the semantics of a set of input types, and provides 440 * consistent error messages for all input. It allows for default 441 * values and prompt strings. 442 */ 443 uint64_t 444 input(type, promptstr, delim, param, deflt, cmdflag) 445 int type; 446 char *promptstr; 447 int delim; 448 u_ioparam_t *param; 449 int *deflt; 450 int cmdflag; 451 { 452 int interactive, help, i, length, index, tied; 453 daddr_t bn; 454 diskaddr_t bn64; 455 char **str, **strings; 456 TOKEN token, cleantoken; 457 TOKEN token2, cleantoken2; 458 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, MAXPATHLEN); 640 if (strlen(token) > 1) { 641 if (strlcpy(shell_argv, &token[1], MAXPATHLEN) >= 642 MAXPATHLEN) { 643 err_print("Error: token length exceeds " 644 "MAXPATHLEN\n"); 645 fullabort(); 646 } 647 } 648 649 /* 650 * collect all tokens till the end of line as arguments 651 */ 652 while (token_present && (gettoken(token) != NULL)) { 653 (void) strcat(shell_argv, " "); 654 (void) strcat(shell_argv, token); 655 } 656 657 /* execute the shell command */ 658 (void) execute_shell(shell_argv); 659 redisplay_menu_list((char **)param->io_charlist); 660 if (interactive) { 661 goto reprompt; 662 } 663 } 664 665 /* 666 * Certain commands accept up to two tokens 667 * Unfortunately, this is kind of a hack. 668 */ 669 token2[0] = 0; 670 cleantoken2[0] = 0; 671 if (type == FIO_CYL || type == FIO_ECYL) { 672 if (token_present) { 673 if (gettoken(token2) == NULL) 674 fullabort(); 675 clean_token(cleantoken2, token2); 676 } 677 } 678 /* 679 * Echo the token back to the user if it was in the pipe or we 680 * are running out of a command file. 681 */ 682 if (!interactive || option_f) { 683 if (token2[0] == 0) { 684 fmt_print("%s\n", token); 685 } else { 686 fmt_print("%s %s\n", token, token2); 687 } 688 } 689 /* 690 * If we are logging, echo the token to the log file. The else 691 * is necessary here because the above printf will also put the 692 * token in the log file. 693 */ 694 else if (log_file) { 695 log_print("%s %s\n", token, token2); 696 } 697 /* 698 * If the token was not in the pipe and it wasn't a command, flush 699 * the rest of the line to keep things in sync. 700 */ 701 if (interactive && cmdflag != CMD_INPUT) 702 flushline(); 703 /* 704 * Scrub off the white-space. 705 */ 706 clean_token(cleantoken, token); 707 /* 708 * If the input was a blank line and we weren't prompting 709 * specifically for a blank line... 710 */ 711 if ((strcmp(cleantoken, "") == 0) && (type != FIO_BLNK)) { 712 /* 713 * If there's a default, return it. 714 */ 715 if (deflt != NULL) { 716 if (type == FIO_OSTR) { 717 /* 718 * Duplicate and return the default string 719 */ 720 return ((int)alloc_string((char *)deflt)); 721 } else if (type == FIO_SLIST) { 722 /* 723 * If we can find a match for the default 724 * value in the list, return the default 725 * value. If there's no match for the 726 * default value, it's an illegal 727 * choice. Return the first value in 728 * the list. 729 */ 730 s = find_string(param->io_slist, *deflt); 731 if ((cur_label == L_TYPE_EFI) && 732 (s == (char *)NULL)) { 733 return (*deflt); 734 } 735 if (s == (char *)NULL) { 736 return ((param->io_slist)->value); 737 } else { 738 return (*deflt); 739 } 740 } else if (type == FIO_OPINT) { 741 /* 742 * The user didn't enter anything 743 */ 744 return (0); 745 } else if (type == FIO_ECYL) { 746 return (part_deflt->deflt_size); 747 } else if (type == FIO_INT64) { 748 return (efi_deflt->start_sector); 749 } else if (type == FIO_EFI) { 750 return (efi_deflt->end_sector); 751 } else { 752 return (*deflt); 753 } 754 } 755 /* 756 * If the blank was not in the pipe, just reprompt. 757 */ 758 if (interactive) { 759 goto reprompt; 760 } 761 /* 762 * If the blank was in the pipe, it's an error. 763 */ 764 err_print("No default for this entry.\n"); 765 cmdabort(SIGINT); 766 } 767 /* 768 * If token is a '?' or a 'h', it is a request for help. 769 */ 770 if ((strcmp(cleantoken, "?") == 0) || 771 (strcmp(cleantoken, "h") == 0) || 772 (strcmp(cleantoken, "help") == 0)) { 773 help = 1; 774 } 775 /* 776 * Switch on the type of input expected. 777 */ 778 switch (type) { 779 /* 780 * Expecting a disk block number. 781 */ 782 case FIO_BN: 783 /* 784 * Parameter is the bounds of legal block numbers. 785 */ 786 bounds = (struct bounds *)¶m->io_bounds; 787 /* 788 * Print help message if required. 789 */ 790 if (help) { 791 fmt_print("Expecting a block number from %llu (", 792 bounds->lower); 793 pr_dblock(fmt_print, bounds->lower); 794 fmt_print(") to %llu (", bounds->upper); 795 pr_dblock(fmt_print, bounds->upper); 796 fmt_print(")\n"); 797 break; 798 } 799 /* 800 * Convert token to a disk block number. 801 */ 802 if (cur_label == L_TYPE_EFI) { 803 if (geti64(cleantoken, (uint64_t *)&bn64, 804 (uint64_t *)NULL)) 805 break; 806 } else { 807 if (getbn(cleantoken, &bn)) 808 break; 809 } 810 if (cur_label == L_TYPE_EFI) { 811 if ((bn64 < bounds->lower) || (bn64 > bounds->upper)) { 812 err_print("`"); 813 pr_dblock(err_print, bn64); 814 err_print("' is out of range.\n"); 815 break; 816 } 817 return (bn64); 818 } 819 /* 820 * Check to be sure it is within the legal bounds. 821 */ 822 if ((bn < bounds->lower) || (bn > bounds->upper)) { 823 err_print("`"); 824 pr_dblock(err_print, bn); 825 err_print("' is out of range.\n"); 826 break; 827 } 828 /* 829 * If it's ok, return it. 830 */ 831 return (bn); 832 /* 833 * Expecting an integer. 834 */ 835 case FIO_INT: 836 /* 837 * Parameter is the bounds of legal integers. 838 */ 839 bounds = (struct bounds *)¶m->io_bounds; 840 /* 841 * Print help message if required. 842 */ 843 if (help) { 844 fmt_print("Expecting an integer from %llu", 845 bounds->lower); 846 fmt_print(" to %llu\n", bounds->upper); 847 break; 848 } 849 /* 850 * Convert the token into an integer. 851 */ 852 if (geti(cleantoken, (int *)&bn, (int *)NULL)) 853 break; 854 /* 855 * Check to be sure it is within the legal bounds. 856 */ 857 if ((bn < bounds->lower) || (bn > bounds->upper)) { 858 err_print("`%ld' is out of range.\n", bn); 859 break; 860 } 861 /* 862 * If it's ok, return it. 863 */ 864 return (bn); 865 case FIO_INT64: 866 /* 867 * Parameter is the bounds of legal integers. 868 */ 869 bounds = (struct bounds *)¶m->io_bounds; 870 /* 871 * Print help message if required. 872 */ 873 if (help) { 874 fmt_print("Expecting an integer from %llu", 875 bounds->lower); 876 fmt_print(" to %llu\n", bounds->upper); 877 break; 878 } 879 /* 880 * Convert the token into an integer. 881 */ 882 if (geti64(cleantoken, (uint64_t *)&bn64, (uint64_t *)NULL)) { 883 break; 884 } 885 /* 886 * Check to be sure it is within the legal bounds. 887 */ 888 if ((bn64 < bounds->lower) || (bn64 > bounds->upper)) { 889 err_print("`%llu' is out of range.\n", bn64); 890 break; 891 } 892 /* 893 * If it's ok, return it. 894 */ 895 return (bn64); 896 /* 897 * Expecting an integer, or no input. 898 */ 899 case FIO_OPINT: 900 /* 901 * Parameter is the bounds of legal integers. 902 */ 903 bounds = (struct bounds *)¶m->io_bounds; 904 /* 905 * Print help message if required. 906 */ 907 if (help) { 908 fmt_print("Expecting an integer from %llu", 909 bounds->lower); 910 fmt_print(" to %llu, or no input\n", bounds->upper); 911 break; 912 } 913 /* 914 * Convert the token into an integer. 915 */ 916 if (geti(cleantoken, (int *)&bn, (int *)NULL)) 917 break; 918 /* 919 * Check to be sure it is within the legal bounds. 920 */ 921 if ((bn < bounds->lower) || (bn > bounds->upper)) { 922 err_print("`%ld' is out of range.\n", bn); 923 break; 924 } 925 /* 926 * For optional case, return 1 indicating that 927 * the user actually did enter something. 928 */ 929 *deflt = bn; 930 return (1); 931 /* 932 * Expecting a closed string. This means that the input 933 * string must exactly match one of the strings passed in 934 * as the parameter. 935 */ 936 case FIO_CSTR: 937 /* 938 * The parameter is a null terminated array of character 939 * pointers, each one pointing to a legal input string. 940 */ 941 strings = (char **)param->io_charlist; 942 /* 943 * Walk through the legal strings, seeing if any of them 944 * match the token. If a match is made, return the index 945 * of the string that was matched. 946 */ 947 for (str = strings; *str != NULL; str++) 948 if (strcmp(cleantoken, *str) == 0) 949 return (str - strings); 950 /* 951 * Print help message if required. 952 */ 953 if (help) { 954 print_input_choices(type, param); 955 } else { 956 err_print("`%s' is not expected.\n", cleantoken); 957 } 958 break; 959 /* 960 * Expecting a matched string. This means that the input 961 * string must either match one of the strings passed in, 962 * or be a unique abbreviation of one of them. 963 */ 964 case FIO_MSTR: 965 /* 966 * The parameter is a null terminated array of character 967 * pointers, each one pointing to a legal input string. 968 */ 969 strings = (char **)param->io_charlist; 970 length = index = tied = 0; 971 /* 972 * Loop through the legal input strings. 973 */ 974 for (str = strings; *str != NULL; str++) { 975 /* 976 * See how many characters of the token match 977 * this legal string. 978 */ 979 i = strcnt(cleantoken, *str); 980 /* 981 * If it's not the whole token, then it's not a match. 982 */ 983 if ((uint_t)i < strlen(cleantoken)) 984 continue; 985 /* 986 * If it ties with another input, remember that. 987 */ 988 if (i == length) 989 tied = 1; 990 /* 991 * If it matches the most so far, record that. 992 */ 993 if (i > length) { 994 index = str - strings; 995 tied = 0; 996 length = i; 997 } 998 } 999 /* 1000 * Print help message if required. 1001 */ 1002 if (length == 0) { 1003 if (help) { 1004 print_input_choices(type, param); 1005 } else { 1006 err_print("`%s' is not expected.\n", 1007 cleantoken); 1008 } 1009 break; 1010 } 1011 /* 1012 * If the abbreviation was non-unique, it's an error. 1013 */ 1014 if (tied) { 1015 err_print("`%s' is ambiguous.\n", cleantoken); 1016 break; 1017 } 1018 /* 1019 * We matched one. Return the index of the string we matched. 1020 */ 1021 return (index); 1022 /* 1023 * Expecting an open string. This means that any string is legal. 1024 */ 1025 case FIO_OSTR: 1026 /* 1027 * Print a help message if required. 1028 */ 1029 if (help) { 1030 fmt_print("Expecting a string\n"); 1031 break; 1032 } 1033 /* 1034 * alloc a copy of the string and return it 1035 */ 1036 return ((int)alloc_string(token)); 1037 1038 /* 1039 * Expecting a blank line. 1040 */ 1041 case FIO_BLNK: 1042 /* 1043 * We are always in non-echo mode when we are inputting 1044 * this type. We echo the newline as a carriage return 1045 * only so the prompt string will be covered over. 1046 */ 1047 nolog_print("\015"); 1048 /* 1049 * If we are logging, send a newline to the log file. 1050 */ 1051 if (log_file) 1052 log_print("\n"); 1053 /* 1054 * There is no value returned for this type. 1055 */ 1056 return (0); 1057 1058 /* 1059 * Expecting one of the entries in a string list. 1060 * Accept unique abbreviations. 1061 * Return the value associated with the matched string. 1062 */ 1063 case FIO_SLIST: 1064 i = find_value((slist_t *)param->io_slist, 1065 cleantoken, &value); 1066 if (i == 1) { 1067 return (value); 1068 } else { 1069 /* 1070 * Print help message if required. 1071 */ 1072 1073 if (help) { 1074 print_input_choices(type, param); 1075 } else { 1076 if (i == 0) 1077 err_print("`%s' not expected.\n", 1078 cleantoken); 1079 else 1080 err_print("`%s' is ambiguous.\n", 1081 cleantoken); 1082 } 1083 } 1084 break; 1085 1086 /* 1087 * Cylinder size input when modifying a complete partition map 1088 */ 1089 case FIO_CYL: 1090 /* 1091 * Parameter is the bounds of legal block numbers. 1092 */ 1093 bounds = (struct bounds *)¶m->io_bounds; 1094 assert(bounds->lower == 0); 1095 /* 1096 * Print help message if required. 1097 */ 1098 if (help) { 1099 fmt_print("Expecting up to %llu blocks,", 1100 bounds->upper); 1101 fmt_print(" %llu cylinders, ", bn2c(bounds->upper)); 1102 fmt_print(" %1.2f megabytes, ", bn2mb(bounds->upper)); 1103 fmt_print("or %1.2f gigabytes\n", bn2gb(bounds->upper)); 1104 break; 1105 } 1106 /* 1107 * Parse the first token: try to find 'b', 'c' or 'm' 1108 */ 1109 s = cleantoken; 1110 while (*s && (isdigit(*s) || (*s == '.') || (*s == '$'))) { 1111 s++; 1112 } 1113 /* 1114 * If we found a conversion specifier, second token is unused 1115 * Otherwise, the second token should supply it. 1116 */ 1117 if (*s != 0) { 1118 value = *s; 1119 *s = 0; 1120 } else { 1121 value = cleantoken2[0]; 1122 } 1123 /* 1124 * If the token is the wild card, simply supply the max 1125 * This order allows the user to specify the maximum in 1126 * either blocks/cyls/megabytes - a convenient fiction. 1127 */ 1128 if (strcmp(cleantoken, WILD_STRING) == 0) { 1129 return (bounds->upper); 1130 } 1131 /* 1132 * Allow the user to specify zero with no units, 1133 * by just defaulting to cylinders. 1134 */ 1135 if (strcmp(cleantoken, "0") == 0) { 1136 value = 'c'; 1137 } 1138 /* 1139 * If there's a decimal point, but no unit specification, 1140 * let's assume megabytes. 1141 */ 1142 if ((value == 0) && (strchr(cleantoken, '.') != NULL)) { 1143 value = 'm'; 1144 } 1145 /* 1146 * Handle each unit type we support 1147 */ 1148 switch (value) { 1149 case 'b': 1150 /* 1151 * Convert token to a disk block number. 1152 */ 1153 i = bounds->upper; 1154 if (geti(cleantoken, &value, &i)) 1155 break; 1156 bn = value; 1157 /* 1158 * Check to be sure it is within the legal bounds. 1159 */ 1160 if ((bn < bounds->lower) || (bn > bounds->upper)) { 1161 err_print( 1162 "`%ldb' is out of the range %llu to %llu\n", 1163 bn, bounds->lower, bounds->upper); 1164 break; 1165 } 1166 /* 1167 * Verify the block lies on a cylinder boundary 1168 */ 1169 if ((bn % spc()) != 0) { 1170 err_print( 1171 "partition size must be a multiple of %d blocks to lie on a cylinder \ 1172 boundary\n", 1173 spc()); 1174 err_print( 1175 "%ld blocks is approximately %ld cylinders, %1.2f megabytes or %1.2f\ 1176 gigabytes\n", 1177 bn, bn2c(bn), bn2mb(bn), bn2gb(bn)); 1178 break; 1179 } 1180 return (bn); 1181 case 'c': 1182 /* 1183 * Convert token from a number of cylinders to 1184 * a number of blocks. 1185 */ 1186 i = bn2c(bounds->upper); 1187 if (geti(cleantoken, &cyls, &i)) 1188 break; 1189 /* 1190 * Check the bounds - cyls is number of cylinders 1191 */ 1192 if (cyls > (bounds->upper/spc())) { 1193 err_print("`%dc' is out of range\n", cyls); 1194 break; 1195 } 1196 /* 1197 * Convert cylinders to blocks and return 1198 */ 1199 return (cyls * spc()); 1200 case 'm': 1201 /* 1202 * Convert token from megabytes to a block number. 1203 */ 1204 if (sscanf(cleantoken, "%f2", &nmegs) == 0) { 1205 err_print("`%s' is not recognized\n", 1206 cleantoken); 1207 break; 1208 } 1209 /* 1210 * Check the bounds 1211 */ 1212 if (nmegs > bn2mb(bounds->upper)) { 1213 err_print("`%1.2fmb' is out of range\n", nmegs); 1214 break; 1215 } 1216 /* 1217 * Convert to blocks 1218 */ 1219 bn = mb2bn(nmegs); 1220 /* 1221 * Round value up to nearest cylinder 1222 */ 1223 i = spc(); 1224 bn = ((bn + (i-1)) / i) * i; 1225 return (bn); 1226 case 'g': 1227 /* 1228 * Convert token from gigabytes to a block number. 1229 */ 1230 if (sscanf(cleantoken, "%f2", &ngigs) == 0) { 1231 err_print("`%s' is not recognized\n", 1232 cleantoken); 1233 break; 1234 } 1235 /* 1236 * Check the bounds 1237 */ 1238 if (ngigs > bn2gb(bounds->upper)) { 1239 err_print("`%1.2fgb' is out of range\n", ngigs); 1240 break; 1241 } 1242 /* 1243 * Convert to blocks 1244 */ 1245 bn = gb2bn(ngigs); 1246 /* 1247 * Round value up to nearest cylinder 1248 */ 1249 i = spc(); 1250 bn = ((bn + (i-1)) / i) * i; 1251 return (bn); 1252 default: 1253 err_print( 1254 "Please specify units in either b(blocks), c(cylinders), m(megabytes) \ 1255 or g(gigabytes)\n"); 1256 break; 1257 } 1258 break; 1259 1260 case FIO_ECYL: 1261 /* 1262 * Parameter is the bounds of legal block numbers. 1263 */ 1264 bounds = (struct bounds *)¶m->io_bounds; 1265 assert(bounds->lower == 0); 1266 1267 /* 1268 * Print help message if required. 1269 */ 1270 if (help) { 1271 fmt_print("Expecting up to %llu blocks,", 1272 bounds->upper); 1273 fmt_print(" %llu cylinders, ", 1274 bn2c(bounds->upper)); 1275 fmt_print(" %llu end cylinder, ", 1276 (bounds->upper / spc())); 1277 fmt_print(" %1.2f megabytes, ", 1278 bn2mb(bounds->upper)); 1279 fmt_print("or %1.2f gigabytes\n", 1280 bn2gb(bounds->upper)); 1281 break; 1282 } 1283 1284 /* 1285 * Parse the first token: try to find 'b', 'c', 'e' 1286 * or 'm' 1287 */ 1288 s = cleantoken; 1289 while (*s && (isdigit(*s) || (*s == '.') || (*s == '$'))) { 1290 s++; 1291 } 1292 1293 /* 1294 * If we found a conversion specifier, second token is 1295 * unused Otherwise, the second token should supply it. 1296 */ 1297 if (*s != 0) { 1298 value = *s; 1299 *s = 0; 1300 } else { 1301 value = cleantoken2[0]; 1302 } 1303 1304 /* 1305 * If the token is the wild card, simply supply the max 1306 * This order allows the user to specify the maximum in 1307 * either blocks/cyls/megabytes - a convenient fiction. 1308 */ 1309 if (strcmp(cleantoken, WILD_STRING) == 0) { 1310 return (bounds->upper); 1311 } 1312 1313 /* 1314 * Allow the user to specify zero with no units, 1315 * by just defaulting to cylinders. 1316 */ 1317 1318 if (value != 'e' && strcmp(cleantoken, "0") == 0) { 1319 value = 'c'; 1320 } 1321 1322 1323 /* 1324 * If there's a decimal point, but no unit 1325 * specification, let's assume megabytes. 1326 */ 1327 if ((value == 0) && (strchr(cleantoken, '.') != NULL)) { 1328 value = 'm'; 1329 } 1330 1331 /* 1332 * Handle each unit type we support 1333 */ 1334 switch (value) { 1335 case 'b': 1336 /* 1337 * Convert token to a disk block number. 1338 */ 1339 i = bounds->upper; 1340 if (geti(cleantoken, &value, &i)) 1341 break; 1342 bn = value; 1343 /* 1344 * Check to be sure it is within the 1345 * legal bounds. 1346 */ 1347 if ((bn < bounds->lower) || (bn > bounds->upper)) { 1348 err_print( 1349 "`%ldb' is out of the range %llu to %llu\n", 1350 bn, bounds->lower, bounds->upper); 1351 break; 1352 } 1353 1354 /* 1355 * Verify the block lies on a cylinder 1356 * boundary 1357 */ 1358 if ((bn % spc()) != 0) { 1359 err_print( 1360 "partition size must be a multiple of %d blocks to lie on a cylinder \ 1361 boundary\n", 1362 spc()); 1363 err_print( 1364 "%ld blocks is approximately %ld cylinders, %1.2f \ 1365 megabytes or %1.2f gigabytes\n", 1366 bn, bn2c(bn), bn2mb(bn), bn2gb(bn)); 1367 break; 1368 } 1369 1370 return (bn); 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 %d\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) == 0) { 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 bn = mb2bn(nmegs); 1462 1463 /* 1464 * Round value up to nearest cylinder 1465 */ 1466 i = spc(); 1467 bn = ((bn + (i-1)) / i) * i; 1468 return (bn); 1469 1470 case 'g': 1471 /* 1472 * Convert token from gigabytes to a 1473 * block number. 1474 */ 1475 if (sscanf(cleantoken, "%f2", &ngigs) == 0) { 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 bn = gb2bn(ngigs); 1493 1494 /* 1495 * Round value up to nearest cylinder 1496 */ 1497 i = spc(); 1498 bn = ((bn + (i-1)) / i) * i; 1499 return (bn); 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 * DEV_BSIZE)/ 1522 (1024 * 1024)); 1523 fmt_print("or %llu gigabytes\n", 1524 (cur_parts->etoc->efi_last_u_lba * DEV_BSIZE)/ 1525 (1024 * 1024 * 1024)); 1526 fmt_print("or %llu terabytes\n", 1527 (cur_parts->etoc->efi_last_u_lba * DEV_BSIZE)/ 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) == 0) { 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) == 0) { 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) == 0) { 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 / DEV_BSIZE); 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 %d alt %d hd %d sec %d>", 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 print_efi_string(type->vendor, type->product, 2106 type->revision, type->capacity); 2107 } else if (disk->disk_flags & DSK_RESERVED) { 2108 fmt_print("<drive not available: reserved>"); 2109 } else if (disk->disk_flags & DSK_UNAVAILABLE) { 2110 fmt_print("<drive not available>"); 2111 } else { 2112 fmt_print("<drive type unknown>"); 2113 } 2114 if (chk_volname(disk)) { 2115 fmt_print(" "); 2116 print_volname(disk); 2117 } 2118 fmt_print("\n"); 2119 2120 if (disk->devfs_name != NULL) { 2121 fmt_print(" %s\n", disk->devfs_name); 2122 } else { 2123 fmt_print(" %s%d at %s%d slave %d\n", 2124 ctlr->ctlr_dname, disk->disk_dkinfo.dki_unit, 2125 ctlr->ctlr_cname, ctlr->ctlr_num, 2126 disk->disk_dkinfo.dki_slave); 2127 } 2128 2129 #ifdef OLD 2130 fmt_print(" %4d. %s at %s%d slave %d", num, disk->disk_name, 2131 ctlr->ctlr_cname, ctlr->ctlr_num, disk->disk_dkinfo.dki_slave); 2132 if (chk_volname(disk)) { 2133 fmt_print(": "); 2134 print_volname(disk); 2135 } 2136 fmt_print("\n"); 2137 if (type != NULL) { 2138 fmt_print( 2139 " %s%d: <%s cyl %d alt %d hd %d sec %d>\n", 2140 ctlr->ctlr_dname, disk->disk_dkinfo.dki_unit, 2141 type->dtype_asciilabel, type->dtype_ncyl, 2142 type->dtype_acyl, type->dtype_nhead, 2143 type->dtype_nsect); 2144 } else { 2145 fmt_print(" %s%d: <drive type unknown>\n", 2146 ctlr->ctlr_dname, disk->disk_dkinfo.dki_unit); 2147 } 2148 #endif /* OLD */ 2149 } 2150 2151 /* 2152 * This routine prints out a given disk block number in cylinder/head/sector 2153 * format. It uses the printing routine passed in to do the actual output. 2154 * Downcasting bn is okay for L_TYPE_SOLARIS because the number of blocks 2155 * on a Solaris (VTOC) label will never exceed 4 bytes (daddr_t). 2156 */ 2157 void 2158 pr_dblock(void (*func)(char *, ...), diskaddr_t bn) 2159 { 2160 if (cur_label == L_TYPE_SOLARIS) { 2161 (*func)("%d/%d/%d", bn2c((daddr_t)bn), 2162 bn2h((daddr_t)bn), bn2s((daddr_t)bn)); 2163 } else { 2164 (*func)("%llu", bn); 2165 } 2166 } 2167 2168 /* 2169 * This routine inputs a character from the data file. It understands 2170 * the use of '\' to prevent interpretation of a newline. It also keeps 2171 * track of the current line in the data file via a global variable. 2172 */ 2173 static int 2174 sup_inputchar() 2175 { 2176 int c; 2177 2178 /* 2179 * Input the character. 2180 */ 2181 c = getc(data_file); 2182 /* 2183 * If it's not a backslash, return it. 2184 */ 2185 if (c != '\\') 2186 return (c); 2187 /* 2188 * It was a backslash. Get the next character. 2189 */ 2190 c = getc(data_file); 2191 /* 2192 * If it was a newline, update the line counter and get the next 2193 * character. 2194 */ 2195 if (c == '\n') { 2196 data_lineno++; 2197 c = getc(data_file); 2198 } 2199 /* 2200 * Return the character. 2201 */ 2202 return (c); 2203 } 2204 2205 /* 2206 * This routine pushes a character back onto the input pipe for the data file. 2207 */ 2208 static void 2209 sup_pushchar(c) 2210 int c; 2211 { 2212 (void) ungetc(c, data_file); 2213 } 2214 2215 /* 2216 * Variables to support pushing back tokens 2217 */ 2218 static int have_pushed_token = 0; 2219 static TOKEN pushed_buf; 2220 static int pushed_token; 2221 2222 /* 2223 * This routine inputs a token from the data file. A token is a series 2224 * of contiguous non-white characters or a recognized special delimiter 2225 * character. Use of the wrapper lets us always have the value of the 2226 * last token around, which is useful for error recovery. 2227 */ 2228 int 2229 sup_gettoken(buf) 2230 char *buf; 2231 { 2232 last_token_type = sup_get_token(buf); 2233 return (last_token_type); 2234 } 2235 2236 static int 2237 sup_get_token(buf) 2238 char *buf; 2239 { 2240 char *ptr = buf; 2241 int c, quoted = 0; 2242 2243 /* 2244 * First check for presence of push-backed token. 2245 * If so, return it. 2246 */ 2247 if (have_pushed_token) { 2248 have_pushed_token = 0; 2249 bcopy(pushed_buf, buf, TOKEN_SIZE+1); 2250 return (pushed_token); 2251 } 2252 /* 2253 * Zero out the returned token buffer 2254 */ 2255 bzero(buf, TOKEN_SIZE + 1); 2256 /* 2257 * Strip off leading white-space. 2258 */ 2259 while ((isspace(c = sup_inputchar())) && (c != '\n')) 2260 ; 2261 /* 2262 * Read in characters until we hit unquoted white-space. 2263 */ 2264 for (; !isspace(c) || quoted; c = sup_inputchar()) { 2265 /* 2266 * If we hit eof, that's a token. 2267 */ 2268 if (feof(data_file)) 2269 return (SUP_EOF); 2270 /* 2271 * If we hit a double quote, change the state of quoting. 2272 */ 2273 if (c == '"') { 2274 quoted = !quoted; 2275 continue; 2276 } 2277 /* 2278 * If we hit a newline, that delimits a token. 2279 */ 2280 if (c == '\n') 2281 break; 2282 /* 2283 * If we hit any nonquoted special delimiters, that delimits 2284 * a token. 2285 */ 2286 if (!quoted && (c == '=' || c == ',' || c == ':' || 2287 c == '#' || c == '|' || c == '&' || c == '~')) 2288 break; 2289 /* 2290 * Store the character if there's room left. 2291 */ 2292 if (ptr - buf < TOKEN_SIZE) 2293 *ptr++ = (char)c; 2294 } 2295 /* 2296 * If we stored characters in the buffer, then we inputted a string. 2297 * Push the delimiter back into the pipe and return the string. 2298 */ 2299 if (ptr - buf > 0) { 2300 sup_pushchar(c); 2301 return (SUP_STRING); 2302 } 2303 /* 2304 * We didn't input a string, so we must have inputted a known delimiter. 2305 * store the delimiter in the buffer, so it will get returned. 2306 */ 2307 buf[0] = c; 2308 /* 2309 * Switch on the delimiter. Return the appropriate value for each one. 2310 */ 2311 switch (c) { 2312 case '=': 2313 return (SUP_EQL); 2314 case ':': 2315 return (SUP_COLON); 2316 case ',': 2317 return (SUP_COMMA); 2318 case '\n': 2319 return (SUP_EOL); 2320 case '|': 2321 return (SUP_OR); 2322 case '&': 2323 return (SUP_AND); 2324 case '~': 2325 return (SUP_TILDE); 2326 case '#': 2327 /* 2328 * For comments, we flush out the rest of the line and return 2329 * an EOL. 2330 */ 2331 while ((c = sup_inputchar()) != '\n' && !feof(data_file)) 2332 ; 2333 if (feof(data_file)) 2334 return (SUP_EOF); 2335 else 2336 return (SUP_EOL); 2337 /* 2338 * Shouldn't ever get here. 2339 */ 2340 default: 2341 return (SUP_STRING); 2342 } 2343 } 2344 2345 /* 2346 * Push back a token 2347 */ 2348 void 2349 sup_pushtoken(token_buf, token_type) 2350 char *token_buf; 2351 int token_type; 2352 { 2353 /* 2354 * We can only push one token back at a time 2355 */ 2356 assert(have_pushed_token == 0); 2357 2358 have_pushed_token = 1; 2359 bcopy(token_buf, pushed_buf, TOKEN_SIZE+1); 2360 pushed_token = token_type; 2361 } 2362 2363 /* 2364 * Get an entire line of input. Handles logging, comments, 2365 * and EOF. 2366 */ 2367 void 2368 get_inputline(line, nbytes) 2369 char *line; 2370 int nbytes; 2371 { 2372 char *p = line; 2373 int c; 2374 2375 /* 2376 * Remove any leading white-space and comments 2377 */ 2378 do { 2379 while ((isspace(c = getchar())) && (c != '\n')) 2380 ; 2381 } while (c == COMMENT_CHAR); 2382 /* 2383 * Loop on each character until end of line 2384 */ 2385 while (c != '\n') { 2386 /* 2387 * If we hit eof, get out. 2388 */ 2389 if (checkeof()) { 2390 fullabort(); 2391 } 2392 /* 2393 * Add the character to the buffer. 2394 */ 2395 if (nbytes > 1) { 2396 *p++ = (char)c; 2397 nbytes --; 2398 } 2399 /* 2400 * Get the next character. 2401 */ 2402 c = getchar(); 2403 } 2404 /* 2405 * Null terminate the token. 2406 */ 2407 *p = 0; 2408 /* 2409 * Indicate that we've emptied the pipe 2410 */ 2411 token_present = 0; 2412 /* 2413 * If we're running out of a file, echo the line to 2414 * the user, otherwise if we're logging, copy the 2415 * input to the log file. 2416 */ 2417 if (option_f) { 2418 fmt_print("%s\n", line); 2419 } else if (log_file) { 2420 log_print("%s\n", line); 2421 } 2422 } 2423 2424 /* 2425 * execute the shell escape command 2426 */ 2427 int 2428 execute_shell(s) 2429 char *s; 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 2446 if (strlcpy(s, shell_name, MAXPATHLEN) >= 2447 MAXPATHLEN) { 2448 err_print("Error: length of shell command" 2449 " ($SHELL) exceeds MAXPATHLEN\n"); 2450 fullabort(); 2451 } 2452 } 2453 2454 /* save tty information */ 2455 2456 if (isatty(0)) { 2457 if (ioctl(0, TCGETS, &tty) == 0) 2458 tty_flag = 1; 2459 else { 2460 if (ioctl(0, TCGETA, &termio) == 0) { 2461 tty_flag = 0; 2462 tty.c_iflag = termio.c_iflag; 2463 tty.c_oflag = termio.c_oflag; 2464 tty.c_cflag = termio.c_cflag; 2465 tty.c_lflag = termio.c_lflag; 2466 for (i = 0; i < NCC; i++) 2467 tty.c_cc[i] = termio.c_cc[i]; 2468 } 2469 } 2470 } 2471 2472 /* close the current file descriptor */ 2473 if (cur_disk != NULL) { 2474 (void) close(cur_file); 2475 } 2476 2477 /* execute the shell escape */ 2478 (void) system(s); 2479 2480 /* reopen file descriptor if one was open before */ 2481 if (cur_disk != NULL) { 2482 if ((cur_file = open_disk(cur_disk->disk_path, 2483 O_RDWR | O_NDELAY)) < 0) { 2484 err_print("Error: can't reopen selected disk '%s'. \n", 2485 cur_disk->disk_name); 2486 fullabort(); 2487 } 2488 } 2489 2490 /* Restore tty information */ 2491 2492 if (isatty(0)) { 2493 if (tty_flag > 0) 2494 (void) ioctl(0, TCSETSW, &tty); 2495 else if (tty_flag == 0) { 2496 termio.c_iflag = tty.c_iflag; 2497 termio.c_oflag = tty.c_oflag; 2498 termio.c_cflag = tty.c_cflag; 2499 termio.c_lflag = tty.c_lflag; 2500 for (j = 0; j < NCC; j++) 2501 termio.c_cc[j] = tty.c_cc[j]; 2502 (void) ioctl(0, TCSETAW, &termio); 2503 } 2504 2505 if (isatty(1)) { 2506 fmt_print("\n[Hit Return to continue] \n"); 2507 (void) fflush(stdin); 2508 if (getchar() == EOF) 2509 fullabort(); 2510 } 2511 } 2512 return (0); 2513 } 2514 2515 void 2516 print_efi_string(char *vendor, char *product, char *revision, 2517 uint64_t capacity) 2518 { 2519 char new_vendor[9]; 2520 char new_product[17]; 2521 char new_revision[5]; 2522 char capacity_string[10]; 2523 float scaled; 2524 int i; 2525 2526 /* Strip whitespace from the end of inquiry strings */ 2527 (void) strlcpy(new_vendor, vendor, sizeof (new_vendor)); 2528 for (i = (strlen(new_vendor) - 1); i >= 0; i--) { 2529 if (new_vendor[i] != 0x20) { 2530 new_vendor[i+1] = '\0'; 2531 break; 2532 } 2533 } 2534 2535 (void) strlcpy(new_product, product, sizeof (new_product)); 2536 for (i = (strlen(new_product) - 1); i >= 0; i--) { 2537 if (new_product[i] != 0x20) { 2538 new_product[i+1] = '\0'; 2539 break; 2540 } 2541 } 2542 2543 (void) strlcpy(new_revision, revision, sizeof (new_revision)); 2544 for (i = (strlen(new_revision) - 1); i >= 0; i--) { 2545 if (new_revision[i] != 0x20) { 2546 new_revision[i+1] = '\0'; 2547 break; 2548 } 2549 } 2550 2551 /* Now build size string */ 2552 scaled = bn2mb(capacity); 2553 if (scaled >= (float)1024.0 * 1024) { 2554 (void) snprintf(capacity_string, sizeof (capacity_string), 2555 "%.2fTB", scaled/((float)1024.0 * 1024)); 2556 } else if (scaled >= (float)1024.0) { 2557 (void) snprintf(capacity_string, sizeof (capacity_string), 2558 "%.2fGB", scaled/(float)1024.0); 2559 } else { 2560 (void) snprintf(capacity_string, sizeof (capacity_string), 2561 "%.2fMB", scaled); 2562 } 2563 2564 fmt_print("<%s-%s-%s-%s>", 2565 new_vendor, new_product, new_revision, capacity_string); 2566 } 2567