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
pushchar(c)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
checkeof()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 *
gettoken(inbuf)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
clean_token(cleantoken,token)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
istokenpresent()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
flushline()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
strcnt(s1,s2)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
geti(str,iptr,wild)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
geti64(str,iptr,wild)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
getbn(str,iptr)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
input(type,promptstr,delim,param,deflt,cmdflag)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
print_input_choices(type,param)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
find_value(slist,match_str,match_value)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 *
find_string(slist,match_value)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
slist_widest_str(slist)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
ljust_print(str,width)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
fmt_print(char * format,...)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
nolog_print(char * format,...)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
log_print(char * format,...)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
err_print(char * format,...)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
print_buf(buf,nbytes)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
pr_ctlrline(ctlr)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
pr_diskline(disk,num)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
pr_dblock(void (* func)(char *,...),diskaddr_t bn)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
sup_inputchar()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
sup_pushchar(c)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
sup_gettoken(buf)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
sup_get_token(buf)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
sup_pushtoken(token_buf,token_type)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
get_inputline(line,nbytes)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
execute_shell(s,buff_size)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
print_efi_string(char * vendor,char * product,char * revision,uint64_t capacity)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