xref: /illumos-gate/usr/src/cmd/format/misc.c (revision a24e89c4a1eec8361718d94a6275e6720643284e)
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 miscellaneous routines.
28  */
29 #include "global.h"
30 
31 #include <stdlib.h>
32 #include <signal.h>
33 #include <malloc.h>
34 #include <unistd.h>
35 #include <string.h>
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <sys/ioctl.h>
39 #include <sys/fcntl.h>
40 #include <sys/time.h>
41 #include <ctype.h>
42 #include <termio.h>
43 #include "misc.h"
44 #include "analyze.h"
45 #include "label.h"
46 #include "startup.h"
47 
48 #ifdef __STDC__
49 
50 /* Function prototypes for ANSI C Compilers */
51 static void	cleanup(int sig);
52 
53 #else	/* __STDC__ */
54 
55 /* Function prototypes for non-ANSI C Compilers */
56 static void	cleanup();
57 
58 #endif	/* __STDC__ */
59 
60 struct	env *current_env = NULL;	/* ptr to current environment */
61 static int	stop_pending = 0;	/* ctrl-Z is pending */
62 struct	ttystate ttystate;		/* tty info */
63 static int	aborting = 0;		/* in process of aborting */
64 
65 /*
66  * For 4.x, limit the choices of valid disk names to this set.
67  */
68 static char		*disk_4x_identifiers[] = { "sd", "id"};
69 #define	N_DISK_4X_IDS	(sizeof (disk_4x_identifiers)/sizeof (char *))
70 
71 
72 /*
73  * This is the list of legal inputs for all yes/no questions.
74  */
75 char	*confirm_list[] = {
76 	"yes",
77 	"no",
78 	NULL,
79 };
80 
81 /*
82  * This routine is a wrapper for malloc.  It allocates pre-zeroed space,
83  * and checks the return value so the caller doesn't have to.
84  */
85 void *
86 zalloc(count)
87 	int	count;
88 {
89 	void	*ptr;
90 
91 	if ((ptr = (void *) calloc(1, (unsigned)count)) == NULL) {
92 		err_print("Error: unable to calloc more space.\n");
93 		fullabort();
94 	}
95 	return (ptr);
96 }
97 
98 /*
99  * This routine is a wrapper for realloc.  It reallocates the given
100  * space, and checks the return value so the caller doesn't have to.
101  * Note that the any space added by this call is NOT necessarily
102  * zeroed.
103  */
104 void *
105 rezalloc(ptr, count)
106 	void	*ptr;
107 	int	count;
108 {
109 	void	*new_ptr;
110 
111 
112 	if ((new_ptr = (void *) realloc((char *)ptr,
113 				(unsigned)count)) == NULL) {
114 		err_print("Error: unable to realloc more space.\n");
115 		fullabort();
116 	}
117 	return (new_ptr);
118 }
119 
120 /*
121  * This routine is a wrapper for free.
122  */
123 void
124 destroy_data(data)
125 	char	*data;
126 {
127 	free((char *)data);
128 }
129 
130 #ifdef	not
131 /*
132  * This routine takes the space number returned by an ioctl call and
133  * returns a mnemonic name for that space.
134  */
135 char *
136 space2str(space)
137 	uint_t	space;
138 {
139 	char	*name;
140 
141 	switch (space&SP_BUSMASK) {
142 	    case SP_VIRTUAL:
143 		name = "virtual";
144 		break;
145 	    case SP_OBMEM:
146 		name = "obmem";
147 		break;
148 	    case SP_OBIO:
149 		name = "obio";
150 		break;
151 	    case SP_MBMEM:
152 		name = "mbmem";
153 		break;
154 	    case SP_MBIO:
155 		name = "mbio";
156 		break;
157 	    default:
158 		err_print("Error: unknown address space type encountered.\n");
159 		fullabort();
160 	}
161 	return (name);
162 }
163 #endif	/* not */
164 
165 /*
166  * This routine asks the user the given yes/no question and returns
167  * the response.
168  */
169 int
170 check(question)
171 	char	*question;
172 {
173 	int		answer;
174 	u_ioparam_t	ioparam;
175 
176 	/*
177 	 * If we are running out of a command file, assume a yes answer.
178 	 */
179 	if (option_f)
180 		return (0);
181 	/*
182 	 * Ask the user.
183 	 */
184 	ioparam.io_charlist = confirm_list;
185 	answer = input(FIO_MSTR, question, '?', &ioparam,
186 	    (int *)NULL, DATA_INPUT);
187 	return (answer);
188 }
189 
190 /*
191  * This routine aborts the current command.  It is called by a ctrl-C
192  * interrupt and also under certain error conditions.
193  */
194 /*ARGSUSED*/
195 void
196 cmdabort(sig)
197 	int	sig;
198 {
199 
200 	/*
201 	 * If there is no usable saved environment, gracefully exit.  This
202 	 * allows the user to interrupt the program even when input is from
203 	 * a file, or if there is no current menu, like at the "Select disk:"
204 	 * prompt.
205 	 */
206 	if (current_env == NULL || !(current_env->flags & ENV_USE))
207 		fullabort();
208 
209 	/*
210 	 * If we are in a critical zone, note the attempt and return.
211 	 */
212 	if (current_env->flags & ENV_CRITICAL) {
213 		current_env->flags |= ENV_ABORT;
214 		return;
215 	}
216 	/*
217 	 * All interruptions when we are running out of a command file
218 	 * cause the program to gracefully exit.
219 	 */
220 	if (option_f)
221 		fullabort();
222 	fmt_print("\n");
223 	/*
224 	 * Clean up any state left by the interrupted command.
225 	 */
226 	cleanup(sig);
227 	/*
228 	 * Jump to the saved environment.
229 	 */
230 	longjmp(current_env->env, 0);
231 }
232 
233 /*
234  * This routine implements the ctrl-Z suspend mechanism.  It is called
235  * when a suspend signal is received.
236  */
237 /*ARGSUSED*/
238 void
239 onsusp(sig)
240 	int	sig;
241 {
242 	int		fix_term;
243 #ifdef	NOT_DEF
244 	sigset_t	sigmask;
245 #endif	/* NOT_DEF */
246 
247 	/*
248 	 * If we are in a critical zone, note the attempt and return.
249 	 */
250 	if (current_env != NULL && current_env->flags & ENV_CRITICAL) {
251 		stop_pending = 1;
252 		return;
253 	}
254 	/*
255 	 * If the terminal is mucked up, note that we will need to
256 	 * re-muck it when we start up again.
257 	 */
258 	fix_term = ttystate.ttyflags;
259 	fmt_print("\n");
260 	/*
261 	 * Clean up any state left by the interrupted command.
262 	 */
263 	cleanup(sig);
264 #ifdef	NOT_DEF
265 	/* Investigate whether all this is necessary */
266 	/*
267 	 * Stop intercepting the suspend signal, then send ourselves one
268 	 * to cause us to stop.
269 	 */
270 	sigmask.sigbits[0] = (ulong_t)0xffffffff;
271 	if (sigprocmask(SIG_SETMASK, &sigmask, (sigset_t *)NULL) == -1)
272 		err_print("sigprocmask failed %d\n", errno);
273 #endif	/* NOT_DEF */
274 	(void) signal(SIGTSTP, SIG_DFL);
275 	(void) kill(0, SIGTSTP);
276 	/*
277 	 * PC stops here
278 	 */
279 	/*
280 	 * We are started again.  Set us up to intercept the suspend
281 	 * signal once again.
282 	 */
283 	(void) signal(SIGTSTP, onsusp);
284 	/*
285 	 * Re-muck the terminal if necessary.
286 	 */
287 	if (fix_term & TTY_ECHO_OFF)
288 		echo_off();
289 	if (fix_term & TTY_CBREAK_ON)
290 		charmode_on();
291 }
292 
293 /*
294  * This routine implements the timing function used during long-term
295  * disk operations (e.g. formatting).  It is called when an alarm signal
296  * is received.
297  */
298 /*ARGSUSED*/
299 void
300 onalarm(sig)
301 	int	sig;
302 {
303 }
304 
305 
306 /*
307  * This routine gracefully exits the program.
308  */
309 void
310 fullabort()
311 {
312 
313 	fmt_print("\n");
314 	/*
315 	 * Clean up any state left by an interrupted command.
316 	 * Avoid infinite loops caused by a clean-up
317 	 * routine failing again...
318 	 */
319 	if (!aborting) {
320 		aborting = 1;
321 		cleanup(SIGKILL);
322 	}
323 	exit(1);
324 	/*NOTREACHED*/
325 }
326 
327 /*
328  * This routine cleans up the state of the world.  It is a hodge-podge
329  * of kludges to allow us to interrupt commands whenever possible.
330  *
331  * Some cleanup actions may depend on the type of signal.
332  */
333 static void
334 cleanup(int sig)
335 {
336 
337 	/*
338 	 * Lock out interrupts to avoid recursion.
339 	 */
340 	enter_critical();
341 	/*
342 	 * Fix up the tty if necessary.
343 	 */
344 	if (ttystate.ttyflags & TTY_CBREAK_ON) {
345 		charmode_off();
346 	}
347 	if (ttystate.ttyflags & TTY_ECHO_OFF) {
348 		echo_on();
349 	}
350 
351 	/*
352 	 * If the defect list is dirty, write it out.
353 	 */
354 	if (cur_list.flags & LIST_DIRTY) {
355 		cur_list.flags = 0;
356 		if (!EMBEDDED_SCSI)
357 			write_deflist(&cur_list);
358 	}
359 	/*
360 	 * If the label is dirty, write it out.
361 	 */
362 	if (cur_flags & LABEL_DIRTY) {
363 		cur_flags &= ~LABEL_DIRTY;
364 		(void) write_label();
365 	}
366 	/*
367 	 * If we are logging and just interrupted a scan, print out
368 	 * some summary info to the log file.
369 	 */
370 	if (log_file && scan_cur_block >= 0) {
371 		pr_dblock(log_print, scan_cur_block);
372 		log_print("\n");
373 	}
374 	if (scan_blocks_fixed >= 0)
375 		fmt_print("Total of %lld defective blocks repaired.\n",
376 		    scan_blocks_fixed);
377 	if (sig != SIGSTOP) { /* Don't reset on suspend (converted to stop) */
378 		scan_cur_block = scan_blocks_fixed = -1;
379 	}
380 	exit_critical();
381 }
382 
383 /*
384  * This routine causes the program to enter a critical zone.  Within the
385  * critical zone, no interrupts are allowed.  Note that calls to this
386  * routine for the same environment do NOT nest, so there is not
387  * necessarily pairing between calls to enter_critical() and exit_critical().
388  */
389 void
390 enter_critical()
391 {
392 
393 	/*
394 	 * If there is no saved environment, interrupts will be ignored.
395 	 */
396 	if (current_env == NULL)
397 		return;
398 	/*
399 	 * Mark the environment to be in a critical zone.
400 	 */
401 	current_env->flags |= ENV_CRITICAL;
402 }
403 
404 /*
405  * This routine causes the program to exit a critical zone.  Note that
406  * calls to enter_critical() for the same environment do NOT nest, so
407  * one call to exit_critical() will erase any number of such calls.
408  */
409 void
410 exit_critical()
411 {
412 
413 	/*
414 	 * If there is a saved environment, mark it to be non-critical.
415 	 */
416 	if (current_env != NULL)
417 		current_env->flags &= ~ENV_CRITICAL;
418 	/*
419 	 * If there is a stop pending, execute the stop.
420 	 */
421 	if (stop_pending) {
422 		stop_pending = 0;
423 		onsusp(SIGSTOP);
424 	}
425 	/*
426 	 * If there is an abort pending, execute the abort.
427 	 */
428 	if (current_env == NULL)
429 		return;
430 	if (current_env->flags & ENV_ABORT) {
431 		current_env->flags &= ~ENV_ABORT;
432 		cmdabort(SIGINT);
433 	}
434 }
435 
436 /*
437  * This routine turns off echoing on the controlling tty for the program.
438  */
439 void
440 echo_off()
441 {
442 	/*
443 	 * Open the tty and store the file pointer for later.
444 	 */
445 	if (ttystate.ttyflags == 0) {
446 		if ((ttystate.ttyfile = open("/dev/tty",
447 		    O_RDWR | O_NDELAY)) < 0) {
448 			err_print("Unable to open /dev/tty.\n");
449 			fullabort();
450 		}
451 	}
452 	/*
453 	 * Get the parameters for the tty, turn off echoing and set them.
454 	 */
455 	if (tcgetattr(ttystate.ttyfile, &ttystate.ttystate) < 0) {
456 		err_print("Unable to get tty parameters.\n");
457 		fullabort();
458 	}
459 	ttystate.ttystate.c_lflag &= ~ECHO;
460 	if (tcsetattr(ttystate.ttyfile, TCSANOW, &ttystate.ttystate) < 0) {
461 		err_print("Unable to set tty to echo off state.\n");
462 		fullabort();
463 	}
464 
465 	/*
466 	 * Remember that we've successfully turned
467 	 * ECHO mode off, so we know to fix it later.
468 	 */
469 	ttystate.ttyflags |= TTY_ECHO_OFF;
470 }
471 
472 /*
473  * This routine turns on echoing on the controlling tty for the program.
474  */
475 void
476 echo_on()
477 {
478 
479 	/*
480 	 * Using the saved parameters, turn echoing on and set them.
481 	 */
482 	ttystate.ttystate.c_lflag |= ECHO;
483 	if (tcsetattr(ttystate.ttyfile, TCSANOW, &ttystate.ttystate) < 0) {
484 		err_print("Unable to set tty to echo on state.\n");
485 		fullabort();
486 	}
487 	/*
488 	 * Close the tty and mark it ok again.
489 	 */
490 	ttystate.ttyflags &= ~TTY_ECHO_OFF;
491 	if (ttystate.ttyflags == 0) {
492 		(void) close(ttystate.ttyfile);
493 	}
494 }
495 
496 /*
497  * This routine turns off single character entry mode for tty.
498  */
499 void
500 charmode_on()
501 {
502 
503 	/*
504 	 * If tty unopened, open the tty and store the file pointer for later.
505 	 */
506 	if (ttystate.ttyflags == 0) {
507 		if ((ttystate.ttyfile = open("/dev/tty",
508 		    O_RDWR | O_NDELAY)) < 0) {
509 			err_print("Unable to open /dev/tty.\n");
510 			fullabort();
511 		}
512 	}
513 	/*
514 	 * Get the parameters for the tty, turn on char mode.
515 	 */
516 	if (tcgetattr(ttystate.ttyfile, &ttystate.ttystate) < 0) {
517 		err_print("Unable to get tty parameters.\n");
518 		fullabort();
519 	}
520 	ttystate.vmin = ttystate.ttystate.c_cc[VMIN];
521 	ttystate.vtime = ttystate.ttystate.c_cc[VTIME];
522 
523 	ttystate.ttystate.c_lflag &= ~ICANON;
524 	ttystate.ttystate.c_cc[VMIN] = 1;
525 	ttystate.ttystate.c_cc[VTIME] = 0;
526 
527 	if (tcsetattr(ttystate.ttyfile, TCSANOW, &ttystate.ttystate) < 0) {
528 		err_print("Unable to set tty to cbreak on state.\n");
529 		fullabort();
530 	}
531 
532 	/*
533 	 * Remember that we've successfully turned
534 	 * CBREAK mode on, so we know to fix it later.
535 	 */
536 	ttystate.ttyflags |= TTY_CBREAK_ON;
537 }
538 
539 /*
540  * This routine turns on single character entry mode for tty.
541  * Note, this routine must be called before echo_on.
542  */
543 void
544 charmode_off()
545 {
546 
547 	/*
548 	 * Using the saved parameters, turn char mode on.
549 	 */
550 	ttystate.ttystate.c_lflag |= ICANON;
551 	ttystate.ttystate.c_cc[VMIN] = ttystate.vmin;
552 	ttystate.ttystate.c_cc[VTIME] = ttystate.vtime;
553 	if (tcsetattr(ttystate.ttyfile, TCSANOW, &ttystate.ttystate) < 0) {
554 		err_print("Unable to set tty to cbreak off state.\n");
555 		fullabort();
556 	}
557 	/*
558 	 * Close the tty and mark it ok again.
559 	 */
560 	ttystate.ttyflags &= ~TTY_CBREAK_ON;
561 	if (ttystate.ttyflags == 0) {
562 		(void) close(ttystate.ttyfile);
563 	}
564 }
565 
566 
567 /*
568  * Allocate space for and return a pointer to a string
569  * on the stack.  If the string is null, create
570  * an empty string.
571  * Use destroy_data() to free when no longer used.
572  */
573 char *
574 alloc_string(s)
575 	char	*s;
576 {
577 	char	*ns;
578 
579 	if (s == (char *)NULL) {
580 		ns = (char *)zalloc(1);
581 	} else {
582 		ns = (char *)zalloc(strlen(s) + 1);
583 		(void) strcpy(ns, s);
584 	}
585 	return (ns);
586 }
587 
588 
589 
590 /*
591  * This function can be used to build up an array of strings
592  * dynamically, with a trailing NULL to terminate the list.
593  *
594  * Parameters:
595  *	argvlist:  a pointer to the base of the current list.
596  *		   does not have to be initialized.
597  *	size:	   pointer to an integer, indicating the number
598  *		   of string installed in the list.  Must be
599  *		   initialized to zero.
600  *	alloc:	   pointer to an integer, indicating the amount
601  *		   of space allocated.  Must be initialized to
602  *		   zero.  For efficiency, we allocate the list
603  *		   in chunks and use it piece-by-piece.
604  *	str:	   the string to be inserted in the list.
605  *		   A copy of the string is malloc'ed, and
606  *		   appended at the end of the list.
607  * Returns:
608  *	a pointer to the possibly-moved argvlist.
609  *
610  * No attempt to made to free unused memory when the list is
611  * completed, although this would not be hard to do.  For
612  * reasonably small lists, this should suffice.
613  */
614 #define	INITIAL_LISTSIZE	32
615 #define	INCR_LISTSIZE		32
616 
617 char **
618 build_argvlist(argvlist, size, alloc, str)
619 	char	**argvlist;
620 	int	*size;
621 	int	*alloc;
622 	char	*str;
623 {
624 	if (*size + 2 > *alloc) {
625 		if (*alloc == 0) {
626 			*alloc = INITIAL_LISTSIZE;
627 			argvlist = (char **)
628 				zalloc(sizeof (char *) * (*alloc));
629 		} else {
630 			*alloc += INCR_LISTSIZE;
631 			argvlist = (char **)
632 				rezalloc((void *) argvlist,
633 				sizeof (char *) * (*alloc));
634 		}
635 	}
636 
637 	argvlist[*size] = alloc_string(str);
638 	*size += 1;
639 	argvlist[*size] = NULL;
640 
641 	return (argvlist);
642 }
643 
644 
645 /*
646  * Useful parsing macros
647  */
648 #define	must_be(s, c)		if (*s++ != c) return (0)
649 #define	skip_digits(s)		while (isdigit(*s)) s++
650 /* Parsing macro below is created to handle fabric devices which contains */
651 /* upper hex digits like c2t210000203708B8CEd0s0.			  */
652 /* To get the target id(tid) the digit and hex upper digit need to	  */
653 /* be processed.							  */
654 #define	skip_digit_or_hexupper(s)	while (isdigit(*s) || \
655 					(isxdigit(*s) && isupper(*s))) s++
656 
657 /*
658  * Return true if a device name matches the conventions
659  * for the particular system.
660  */
661 int
662 conventional_name(char *name)
663 {
664 	must_be(name, 'c');
665 	skip_digits(name);
666 	if (*name == 't') {
667 		name++;
668 		skip_digit_or_hexupper(name);
669 	}
670 	must_be(name, 'd');
671 	skip_digits(name);
672 	must_be(name, 's');
673 	skip_digits(name);
674 	return (*name == 0);
675 }
676 
677 /*
678  * Return true if a device name matches the intel physical name conventions
679  * for the particular system.
680  */
681 int
682 fdisk_physical_name(char *name)
683 {
684 	must_be(name, 'c');
685 	skip_digits(name);
686 	if (*name == 't') {
687 		name++;
688 		skip_digit_or_hexupper(name);
689 	}
690 	must_be(name, 'd');
691 	skip_digits(name);
692 	must_be(name, 'p');
693 	skip_digits(name);
694 	return (*name == 0);
695 }
696 
697 /*
698  * Return true if a device name matches the conventions
699  * for a "whole disk" name for the particular system.
700  * The name in this case must match exactly that which
701  * would appear in the device directory itself.
702  */
703 int
704 whole_disk_name(name)
705 	char	*name;
706 {
707 	must_be(name, 'c');
708 	skip_digits(name);
709 	if (*name == 't') {
710 		name++;
711 		skip_digit_or_hexupper(name);
712 	}
713 	must_be(name, 'd');
714 	skip_digits(name);
715 	must_be(name, 's');
716 	must_be(name, '2');
717 	return (*name == 0);
718 }
719 
720 
721 /*
722  * Return true if a name is in the internal canonical form
723  */
724 int
725 canonical_name(name)
726 	char	*name;
727 {
728 	must_be(name, 'c');
729 	skip_digits(name);
730 	if (*name == 't') {
731 		name++;
732 		skip_digit_or_hexupper(name);
733 	}
734 	must_be(name, 'd');
735 	skip_digits(name);
736 	return (*name == 0);
737 }
738 
739 
740 /*
741  * Return true if a name is in the internal canonical form for 4.x
742  * Used to support 4.x naming conventions under 5.0.
743  */
744 int
745 canonical4x_name(name)
746 	char	*name;
747 {
748 	char    **p;
749 	int	i;
750 
751 	p = disk_4x_identifiers;
752 	for (i = N_DISK_4X_IDS; i > 0; i--, p++) {
753 		if (match_substr(name, *p)) {
754 			name += strlen(*p);
755 			break;
756 		}
757 	}
758 	if (i == 0)
759 		return (0);
760 	skip_digits(name);
761 	return (*name == 0);
762 }
763 
764 
765 /*
766  * Map a conventional name into the internal canonical form:
767  *
768  *	/dev/rdsk/c0t0d0s0 -> c0t0d0
769  */
770 void
771 canonicalize_name(dst, src)
772 	char	*dst;
773 	char	*src;
774 {
775 	char	*s;
776 
777 	/*
778 	 * Copy from the 'c' to the end to the destination string...
779 	 */
780 	s = strchr(src, 'c');
781 	if (s != NULL) {
782 		(void) strcpy(dst, s);
783 		/*
784 		 * Remove the trailing slice (partition) reference
785 		 */
786 		s = dst + strlen(dst) - 2;
787 		if (*s == 's') {
788 			*s = 0;
789 		}
790 	} else {
791 		*dst = 0;	/* be tolerant of garbage input */
792 	}
793 }
794 
795 
796 /*
797  * Return true if we find an occurance of s2 at the
798  * beginning of s1.  We don't have to match all of
799  * s1, but we do have to match all of s2
800  */
801 int
802 match_substr(s1, s2)
803 	char    *s1;
804 	char    *s2;
805 {
806 	while (*s2 != 0) {
807 		if (*s1++ != *s2++)
808 		return (0);
809 	}
810 
811 	return (1);
812 }
813 
814 
815 /*
816  * Dump a structure in hexadecimal, for diagnostic purposes
817  */
818 #define	BYTES_PER_LINE		16
819 
820 void
821 dump(hdr, src, nbytes, format)
822 	char	*hdr;
823 	caddr_t	src;
824 	int	nbytes;
825 	int	format;
826 {
827 	int	i;
828 	int	n;
829 	char	*p;
830 	char	s[256];
831 
832 	assert(format == HEX_ONLY || format == HEX_ASCII);
833 
834 	(void) strcpy(s, hdr);
835 	for (p = s; *p; p++) {
836 		*p = ' ';
837 	}
838 
839 	p = hdr;
840 	while (nbytes > 0) {
841 		err_print("%s", p);
842 		p = s;
843 		n = min(nbytes, BYTES_PER_LINE);
844 		for (i = 0; i < n; i++) {
845 			err_print("%02x ", src[i] & 0xff);
846 		}
847 		if (format == HEX_ASCII) {
848 			for (i = BYTES_PER_LINE-n; i > 0; i--) {
849 				err_print("   ");
850 			}
851 			err_print("    ");
852 			for (i = 0; i < n; i++) {
853 				err_print("%c",
854 					isprint(src[i]) ? src[i] : '.');
855 			}
856 		}
857 		err_print("\n");
858 		nbytes -= n;
859 		src += n;
860 	}
861 }
862 
863 
864 float
865 bn2mb(uint64_t nblks)
866 {
867 	float	n;
868 
869 	n = (float)nblks / 1024.0;
870 	return ((n / 1024.0) * cur_blksz);
871 }
872 
873 
874 diskaddr_t
875 mb2bn(float mb)
876 {
877 	diskaddr_t	n;
878 
879 	n = (diskaddr_t)(mb * 1024.0 * (1024.0 / cur_blksz));
880 	return (n);
881 }
882 
883 float
884 bn2gb(uint64_t nblks)
885 {
886 	float	n;
887 
888 	n = (float)nblks / (1024.0 * 1024.0);
889 	return ((n/1024.0) * cur_blksz);
890 
891 }
892 
893 float
894 bn2tb(uint64_t nblks)
895 {
896 	float	n;
897 
898 	n = (float)nblks / (1024.0 * 1024.0 * 1024.0);
899 	return ((n/1024.0) * cur_blksz);
900 }
901 
902 diskaddr_t
903 gb2bn(float gb)
904 {
905 	diskaddr_t	n;
906 
907 	n = (diskaddr_t)(gb * 1024.0 * 1024.0 * (1024.0 / cur_blksz));
908 	return (n);
909 }
910 
911 /*
912  * This routine finds out the number of lines (rows) in a terminal
913  * window. The default value of TTY_LINES is returned on error.
914  */
915 int
916 get_tty_lines()
917 {
918 	int	tty_lines = TTY_LINES;
919 	struct	winsize	winsize;
920 
921 	if ((option_f == (char *)NULL) && isatty(0) == 1 && isatty(1) == 1) {
922 		/*
923 		 * We have a real terminal for std input and output
924 		 */
925 		winsize.ws_row = 0;
926 		if (ioctl(1, TIOCGWINSZ, &winsize) == 0) {
927 			if (winsize.ws_row > 2) {
928 				/*
929 				 * Should be atleast 2 lines, for division
930 				 * by (tty_lines - 1, tty_lines - 2) to work.
931 				 */
932 				tty_lines = winsize.ws_row;
933 			}
934 		}
935 	}
936 	return (tty_lines);
937 }
938