Home | History | Annotate | Download | only in mdb
      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 2008 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     27 
     28 /*
     29  * MDB uses its own enhanced standard i/o mechanism for all input and output.
     30  * This file provides the underpinnings of this mechanism, including the
     31  * printf-style formatting code, the output pager, and APIs for raw input
     32  * and output.  This mechanism is used throughout the debugger for everything
     33  * from simple sprintf and printf-style formatting, to input to the lexer
     34  * and parser, to raw file i/o for reading ELF files.  In general, we divide
     35  * our i/o implementation into two parts:
     36  *
     37  * (1) An i/o buffer (mdb_iob_t) provides buffered read or write capabilities,
     38  * as well as access to formatting and the ability to invoke a pager.  The
     39  * buffer is constructed explicitly for use in either reading or writing; it
     40  * may not be used for both simultaneously.
     41  *
     42  * (2) Each i/o buffer is associated with an underlying i/o backend (mdb_io_t).
     43  * The backend provides, through an ops-vector, equivalents for the standard
     44  * read, write, lseek, ioctl, and close operations.  In addition, the backend
     45  * can provide an IOP_NAME entry point for returning a name for the backend,
     46  * IOP_LINK and IOP_UNLINK entry points that are called when the backend is
     47  * connected or disconnected from an mdb_iob_t, and an IOP_SETATTR entry point
     48  * for manipulating terminal attributes.
     49  *
     50  * The i/o objects themselves are reference counted so that more than one i/o
     51  * buffer may make use of the same i/o backend.  In addition, each buffer
     52  * provides the ability to push or pop backends to interpose on input or output
     53  * behavior.  We make use of this, for example, to implement interactive
     54  * session logging.  Normally, the stdout iob has a backend that is either
     55  * file descriptor 1, or a terminal i/o backend associated with the tty.
     56  * However, we can push a log i/o backend on top that multiplexes stdout to
     57  * the original back-end and another backend that writes to a log file.  The
     58  * use of i/o backends is also used for simplifying tasks such as making
     59  * lex and yacc read from strings for mdb_eval(), and making our ELF file
     60  * processing code read executable "files" from a crash dump via kvm_uread.
     61  *
     62  * Additionally, the formatting code provides auto-wrap and indent facilities
     63  * that are necessary for compatibility with adb macro formatting.  In auto-
     64  * wrap mode, the formatting code examines each new chunk of output to determine
     65  * if it will fit on the current line.  If not, instead of having the chunk
     66  * divided between the current line of output and the next, the auto-wrap
     67  * code will automatically output a newline, auto-indent the next line,
     68  * and then continue.  Auto-indent is implemented by simply prepending a number
     69  * of blanks equal to iob_margin to the start of each line.  The margin is
     70  * inserted when the iob is created, and following each flush of the buffer.
     71  */
     72 
     73 #include <sys/types.h>
     74 #include <sys/termios.h>
     75 #include <stdarg.h>
     76 #include <arpa/inet.h>
     77 #include <sys/socket.h>
     78 
     79 #include <mdb/mdb_types.h>
     80 #include <mdb/mdb_argvec.h>
     81 #include <mdb/mdb_stdlib.h>
     82 #include <mdb/mdb_string.h>
     83 #include <mdb/mdb_target.h>
     84 #include <mdb/mdb_signal.h>
     85 #include <mdb/mdb_debug.h>
     86 #include <mdb/mdb_io_impl.h>
     87 #include <mdb/mdb_modapi.h>
     88 #include <mdb/mdb_demangle.h>
     89 #include <mdb/mdb_err.h>
     90 #include <mdb/mdb_nv.h>
     91 #include <mdb/mdb_frame.h>
     92 #include <mdb/mdb_lex.h>
     93 #include <mdb/mdb.h>
     94 
     95 /*
     96  * Define list of possible integer sizes for conversion routines:
     97  */
     98 typedef enum {
     99 	SZ_SHORT,		/* format %h? */
    100 	SZ_INT,			/* format %? */
    101 	SZ_LONG,		/* format %l? */
    102 	SZ_LONGLONG		/* format %ll? */
    103 } intsize_t;
    104 
    105 /*
    106  * The iob snprintf family of functions makes use of a special "sprintf
    107  * buffer" i/o backend in order to provide the appropriate snprintf semantics.
    108  * This structure is maintained as the backend-specific private storage,
    109  * and its use is described in more detail below (see spbuf_write()).
    110  */
    111 typedef struct {
    112 	char *spb_buf;		/* pointer to underlying buffer */
    113 	size_t spb_bufsiz;	/* length of underlying buffer */
    114 	size_t spb_total;	/* total of all bytes passed via IOP_WRITE */
    115 } spbuf_t;
    116 
    117 /*
    118  * Define VA_ARG macro for grabbing the next datum to format for the printf
    119  * family of functions.  We use VA_ARG so that we can support two kinds of
    120  * argument lists: the va_list type supplied by <stdarg.h> used for printf and
    121  * vprintf, and an array of mdb_arg_t structures, which we expect will be
    122  * either type STRING or IMMEDIATE.  The vec_arg function takes care of
    123  * handling the mdb_arg_t case.
    124  */
    125 
    126 typedef enum {
    127 	VAT_VARARGS,		/* va_list is a va_list */
    128 	VAT_ARGVEC		/* va_list is a const mdb_arg_t[] in disguise */
    129 } vatype_t;
    130 
    131 typedef struct {
    132 	vatype_t val_type;
    133 	union {
    134 		va_list	_val_valist;
    135 		const mdb_arg_t *_val_argv;
    136 	} _val_u;
    137 } varglist_t;
    138 
    139 #define	val_valist	_val_u._val_valist
    140 #define	val_argv	_val_u._val_argv
    141 
    142 #define	VA_ARG(ap, type) ((ap->val_type == VAT_VARARGS) ? \
    143 	va_arg(ap->val_valist, type) : (type)vec_arg(&ap->val_argv))
    144 #define	VA_PTRARG(ap) ((ap->val_type == VAT_VARARGS) ? \
    145 	(void *)va_arg(ap->val_valist, uintptr_t) : \
    146 	(void *)(uintptr_t)vec_arg(&ap->val_argv))
    147 
    148 /*
    149  * Define macro for converting char constant to Ctrl-char equivalent:
    150  */
    151 #ifndef CTRL
    152 #define	CTRL(c)	((c) & 0x01f)
    153 #endif
    154 
    155 /*
    156  * Define macro for determining if we should automatically wrap to the next
    157  * line of output, based on the amount of consumed buffer space and the
    158  * specified size of the next thing to be inserted (n).
    159  */
    160 #define	IOB_WRAPNOW(iob, n)	\
    161 	(((iob)->iob_flags & MDB_IOB_AUTOWRAP) && ((iob)->iob_nbytes != 0) && \
    162 	((n) + (iob)->iob_nbytes > (iob)->iob_cols))
    163 
    164 /*
    165  * Define prompt string and string to erase prompt string for iob_pager
    166  * function, which is invoked if the pager is enabled on an i/o buffer
    167  * and we're about to print a line which would be the last on the screen.
    168  */
    169 
    170 static const char io_prompt[] = ">> More [<space>, <cr>, q, n, c, a] ? ";
    171 static const char io_perase[] = "                                      ";
    172 
    173 static const char io_pbcksp[] =
    174 /*CSTYLED*/
    175 "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
    176 
    177 static const size_t io_promptlen = sizeof (io_prompt) - 1;
    178 static const size_t io_peraselen = sizeof (io_perase) - 1;
    179 static const size_t io_pbcksplen = sizeof (io_pbcksp) - 1;
    180 
    181 static ssize_t
    182 iob_write(mdb_iob_t *iob, mdb_io_t *io, const void *buf, size_t n)
    183 {
    184 	ssize_t resid = n;
    185 	ssize_t len;
    186 
    187 	while (resid != 0) {
    188 		if ((len = IOP_WRITE(io, buf, resid)) <= 0)
    189 			break;
    190 
    191 		buf = (char *)buf + len;
    192 		resid -= len;
    193 	}
    194 
    195 	/*
    196 	 * Note that if we had a partial write before an error, we still want
    197 	 * to return the fact something was written.  The caller will get an
    198 	 * error next time it tries to write anything.
    199 	 */
    200 	if (resid == n && n != 0) {
    201 		iob->iob_flags |= MDB_IOB_ERR;
    202 		return (-1);
    203 	}
    204 
    205 	return (n - resid);
    206 }
    207 
    208 static ssize_t
    209 iob_read(mdb_iob_t *iob, mdb_io_t *io)
    210 {
    211 	ssize_t len;
    212 
    213 	ASSERT(iob->iob_nbytes == 0);
    214 	len = IOP_READ(io, iob->iob_buf, iob->iob_bufsiz);
    215 	iob->iob_bufp = &iob->iob_buf[0];
    216 
    217 	switch (len) {
    218 	case -1:
    219 		iob->iob_flags |= MDB_IOB_ERR;
    220 		break;
    221 	case 0:
    222 		iob->iob_flags |= MDB_IOB_EOF;
    223 		break;
    224 	default:
    225 		iob->iob_nbytes = len;
    226 	}
    227 
    228 	return (len);
    229 }
    230 
    231 /*ARGSUSED*/
    232 static void
    233 iob_winch(int sig, siginfo_t *sip, ucontext_t *ucp, void *data)
    234 {
    235 	siglongjmp(*((sigjmp_buf *)data), sig);
    236 }
    237 
    238 static int
    239 iob_pager(mdb_iob_t *iob)
    240 {
    241 	int status = 0;
    242 	sigjmp_buf env;
    243 	uchar_t c;
    244 
    245 	mdb_signal_f *termio_winch;
    246 	void *termio_data;
    247 	size_t old_rows;
    248 
    249 	if (iob->iob_pgp == NULL || (iob->iob_flags & MDB_IOB_PGCONT))
    250 		return (0);
    251 
    252 	termio_winch = mdb_signal_gethandler(SIGWINCH, &termio_data);
    253 	(void) mdb_signal_sethandler(SIGWINCH, iob_winch, &env);
    254 
    255 	if (sigsetjmp(env, 1) != 0) {
    256 		/*
    257 		 * Reset the cursor back to column zero before printing a new
    258 		 * prompt, since its position is unreliable after a SIGWINCH.
    259 		 */
    260 		(void) iob_write(iob, iob->iob_pgp, "\r", sizeof (char));
    261 		old_rows = iob->iob_rows;
    262 
    263 		/*
    264 		 * If an existing SIGWINCH handler was present, call it.  We
    265 		 * expect that this will be termio: the handler will read the
    266 		 * new window size, and then resize this iob appropriately.
    267 		 */
    268 		if (termio_winch != (mdb_signal_f *)NULL)
    269 			termio_winch(SIGWINCH, NULL, NULL, termio_data);
    270 
    271 		/*
    272 		 * If the window has increased in size, we treat this like a
    273 		 * request to fill out the new remainder of the page.
    274 		 */
    275 		if (iob->iob_rows > old_rows) {
    276 			iob->iob_flags &= ~MDB_IOB_PGSINGLE;
    277 			iob->iob_nlines = old_rows;
    278 			status = 0;
    279 			goto winch;
    280 		}
    281 	}
    282 
    283 	(void) iob_write(iob, iob->iob_pgp, io_prompt, io_promptlen);
    284 
    285 	for (;;) {
    286 		if (IOP_READ(iob->iob_pgp, &c, sizeof (c)) != sizeof (c)) {
    287 			status = MDB_ERR_PAGER;
    288 			break;
    289 		}
    290 
    291 		switch (c) {
    292 		case 'N':
    293 		case 'n':
    294 		case '\n':
    295 		case '\r':
    296 			iob->iob_flags |= MDB_IOB_PGSINGLE;
    297 			goto done;
    298 
    299 		case CTRL('c'):
    300 		case CTRL('\\'):
    301 		case 'Q':
    302 		case 'q':
    303 			mdb_iob_discard(iob);
    304 			status = MDB_ERR_PAGER;
    305 			goto done;
    306 
    307 		case 'A':
    308 		case 'a':
    309 			mdb_iob_discard(iob);
    310 			status = MDB_ERR_ABORT;
    311 			goto done;
    312 
    313 		case 'C':
    314 		case 'c':
    315 			iob->iob_flags |= MDB_IOB_PGCONT;
    316 			/*FALLTHRU*/
    317 
    318 		case ' ':
    319 			iob->iob_flags &= ~MDB_IOB_PGSINGLE;
    320 			goto done;
    321 		}
    322 	}
    323 
    324 done:
    325 	(void) iob_write(iob, iob->iob_pgp, io_pbcksp, io_pbcksplen);
    326 winch:
    327 	(void) iob_write(iob, iob->iob_pgp, io_perase, io_peraselen);
    328 	(void) iob_write(iob, iob->iob_pgp, io_pbcksp, io_pbcksplen);
    329 	(void) mdb_signal_sethandler(SIGWINCH, termio_winch, termio_data);
    330 
    331 	if ((iob->iob_flags & MDB_IOB_ERR) && status == 0)
    332 		status = MDB_ERR_OUTPUT;
    333 
    334 	return (status);
    335 }
    336 
    337 static void
    338 iob_indent(mdb_iob_t *iob)
    339 {
    340 	if (iob->iob_nbytes == 0 && iob->iob_margin != 0 &&
    341 	    (iob->iob_flags & MDB_IOB_INDENT)) {
    342 		size_t i;
    343 
    344 		ASSERT(iob->iob_margin < iob->iob_cols);
    345 		ASSERT(iob->iob_bufp == iob->iob_buf);
    346 
    347 		for (i = 0; i < iob->iob_margin; i++)
    348 			*iob->iob_bufp++ = ' ';
    349 
    350 		iob->iob_nbytes = iob->iob_margin;
    351 	}
    352 }
    353 
    354 static void
    355 iob_unindent(mdb_iob_t *iob)
    356 {
    357 	if (iob->iob_nbytes != 0 && iob->iob_nbytes == iob->iob_margin) {
    358 		const char *p = iob->iob_buf;
    359 
    360 		while (p < &iob->iob_buf[iob->iob_margin]) {
    361 			if (*p++ != ' ')
    362 				return;
    363 		}
    364 
    365 		iob->iob_bufp = &iob->iob_buf[0];
    366 		iob->iob_nbytes = 0;
    367 	}
    368 }
    369 
    370 mdb_iob_t *
    371 mdb_iob_create(mdb_io_t *io, uint_t flags)
    372 {
    373 	mdb_iob_t *iob = mdb_alloc(sizeof (mdb_iob_t), UM_SLEEP);
    374 
    375 	iob->iob_buf = mdb_alloc(BUFSIZ, UM_SLEEP);
    376 	iob->iob_bufsiz = BUFSIZ;
    377 	iob->iob_bufp = &iob->iob_buf[0];
    378 	iob->iob_nbytes = 0;
    379 	iob->iob_nlines = 0;
    380 	iob->iob_lineno = 1;
    381 	iob->iob_rows = MDB_IOB_DEFROWS;
    382 	iob->iob_cols = MDB_IOB_DEFCOLS;
    383 	iob->iob_tabstop = MDB_IOB_DEFTAB;
    384 	iob->iob_margin = MDB_IOB_DEFMARGIN;
    385 	iob->iob_flags = flags & ~(MDB_IOB_EOF|MDB_IOB_ERR) | MDB_IOB_AUTOWRAP;
    386 	iob->iob_iop = mdb_io_hold(io);
    387 	iob->iob_pgp = NULL;
    388 	iob->iob_next = NULL;
    389 
    390 	IOP_LINK(io, iob);
    391 	iob_indent(iob);
    392 	return (iob);
    393 }
    394 
    395 void
    396 mdb_iob_pipe(mdb_iob_t **iobs, mdb_iobsvc_f *rdsvc, mdb_iobsvc_f *wrsvc)
    397 {
    398 	mdb_io_t *pio = mdb_pipeio_create(rdsvc, wrsvc);
    399 	int i;
    400 
    401 	iobs[0] = mdb_iob_create(pio, MDB_IOB_RDONLY);
    402 	iobs[1] = mdb_iob_create(pio, MDB_IOB_WRONLY);
    403 
    404 	for (i = 0; i < 2; i++) {
    405 		iobs[i]->iob_flags &= ~MDB_IOB_AUTOWRAP;
    406 		iobs[i]->iob_cols = iobs[i]->iob_bufsiz;
    407 	}
    408 }
    409 
    410 void
    411 mdb_iob_destroy(mdb_iob_t *iob)
    412 {
    413 	/*
    414 	 * Don't flush a pipe, since it may cause a context swith when the
    415 	 * other side has already been destroyed.
    416 	 */
    417 	if (!mdb_iob_isapipe(iob))
    418 		mdb_iob_flush(iob);
    419 
    420 	if (iob->iob_pgp != NULL)
    421 		mdb_io_rele(iob->iob_pgp);
    422 
    423 	while (iob->iob_iop != NULL) {
    424 		IOP_UNLINK(iob->iob_iop, iob);
    425 		(void) mdb_iob_pop_io(iob);
    426 	}
    427 
    428 	mdb_free(iob->iob_buf, iob->iob_bufsiz);
    429 	mdb_free(iob, sizeof (mdb_iob_t));
    430 }
    431 
    432 void
    433 mdb_iob_discard(mdb_iob_t *iob)
    434 {
    435 	iob->iob_bufp = &iob->iob_buf[0];
    436 	iob->iob_nbytes = 0;
    437 }
    438 
    439 void
    440 mdb_iob_flush(mdb_iob_t *iob)
    441 {
    442 	int pgerr = 0;
    443 
    444 	if (iob->iob_nbytes == 0)
    445 		return; /* Nothing to do if buffer is empty */
    446 
    447 	if (iob->iob_flags & MDB_IOB_WRONLY) {
    448 		if (iob->iob_flags & MDB_IOB_PGSINGLE) {
    449 			iob->iob_flags &= ~MDB_IOB_PGSINGLE;
    450 			iob->iob_nlines = 0;
    451 			pgerr = iob_pager(iob);
    452 
    453 		} else if (iob->iob_nlines >= iob->iob_rows - 1) {
    454 			iob->iob_nlines = 0;
    455 			if (iob->iob_flags & MDB_IOB_PGENABLE)
    456 				pgerr = iob_pager(iob);
    457 		}
    458 
    459 		if (pgerr == 0) {
    460 			/*
    461 			 * We only jump out of the dcmd on error if the iob is
    462 			 * m_out. Presumably, if a dcmd has opened a special
    463 			 * file and is writing to it, it will handle errors
    464 			 * properly.
    465 			 */
    466 			if (iob_write(iob, iob->iob_iop, iob->iob_buf,
    467 			    iob->iob_nbytes) < 0 && iob == mdb.m_out)
    468 				pgerr = MDB_ERR_OUTPUT;
    469 			iob->iob_nlines++;
    470 		}
    471 	}
    472 
    473 	iob->iob_bufp = &iob->iob_buf[0];
    474 	iob->iob_nbytes = 0;
    475 	iob_indent(iob);
    476 
    477 	if (pgerr)
    478 		longjmp(mdb.m_frame->f_pcb, pgerr);
    479 }
    480 
    481 void
    482 mdb_iob_nlflush(mdb_iob_t *iob)
    483 {
    484 	iob_unindent(iob);
    485 
    486 	if (iob->iob_nbytes != 0)
    487 		mdb_iob_nl(iob);
    488 	else
    489 		iob_indent(iob);
    490 }
    491 
    492 void
    493 mdb_iob_push_io(mdb_iob_t *iob, mdb_io_t *io)
    494 {
    495 	ASSERT(io->io_next == NULL);
    496 
    497 	io->io_next = iob->iob_iop;
    498 	iob->iob_iop = mdb_io_hold(io);
    499 }
    500 
    501 mdb_io_t *
    502 mdb_iob_pop_io(mdb_iob_t *iob)
    503 {
    504 	mdb_io_t *io = iob->iob_iop;
    505 
    506 	if (io != NULL) {
    507 		iob->iob_iop = io->io_next;
    508 		io->io_next = NULL;
    509 		mdb_io_rele(io);
    510 	}
    511 
    512 	return (io);
    513 }
    514 
    515 void
    516 mdb_iob_resize(mdb_iob_t *iob, size_t rows, size_t cols)
    517 {
    518 	if (cols > iob->iob_bufsiz)
    519 		iob->iob_cols = iob->iob_bufsiz;
    520 	else
    521 		iob->iob_cols = cols != 0 ? cols : MDB_IOB_DEFCOLS;
    522 
    523 	iob->iob_rows = rows != 0 ? rows : MDB_IOB_DEFROWS;
    524 }
    525 
    526 void
    527 mdb_iob_setpager(mdb_iob_t *iob, mdb_io_t *pgio)
    528 {
    529 	struct winsize winsz;
    530 
    531 	if (iob->iob_pgp != NULL) {
    532 		IOP_UNLINK(iob->iob_pgp, iob);
    533 		mdb_io_rele(iob->iob_pgp);
    534 	}
    535 
    536 	iob->iob_flags |= MDB_IOB_PGENABLE;
    537 	iob->iob_flags &= ~(MDB_IOB_PGSINGLE | MDB_IOB_PGCONT);
    538 	iob->iob_pgp = mdb_io_hold(pgio);
    539 
    540 	IOP_LINK(iob->iob_pgp, iob);
    541 
    542 	if (IOP_CTL(pgio, TIOCGWINSZ, &winsz) == 0)
    543 		mdb_iob_resize(iob, (size_t)winsz.ws_row, (size_t)winsz.ws_col);
    544 }
    545 
    546 void
    547 mdb_iob_tabstop(mdb_iob_t *iob, size_t tabstop)
    548 {
    549 	iob->iob_tabstop = MIN(tabstop, iob->iob_cols - 1);
    550 }
    551 
    552 void
    553 mdb_iob_margin(mdb_iob_t *iob, size_t margin)
    554 {
    555 	iob_unindent(iob);
    556 	iob->iob_margin = MIN(margin, iob->iob_cols - 1);
    557 	iob_indent(iob);
    558 }
    559 
    560 void
    561 mdb_iob_setbuf(mdb_iob_t *iob, void *buf, size_t bufsiz)
    562 {
    563 	ASSERT(buf != NULL && bufsiz != 0);
    564 
    565 	mdb_free(iob->iob_buf, iob->iob_bufsiz);
    566 	iob->iob_buf = buf;
    567 	iob->iob_bufsiz = bufsiz;
    568 
    569 	if (iob->iob_flags & MDB_IOB_WRONLY)
    570 		iob->iob_cols = MIN(iob->iob_cols, iob->iob_bufsiz);
    571 }
    572 
    573 void
    574 mdb_iob_clearlines(mdb_iob_t *iob)
    575 {
    576 	iob->iob_flags &= ~(MDB_IOB_PGSINGLE | MDB_IOB_PGCONT);
    577 	iob->iob_nlines = 0;
    578 }
    579 
    580 void
    581 mdb_iob_setflags(mdb_iob_t *iob, uint_t flags)
    582 {
    583 	iob->iob_flags |= flags;
    584 	if (flags & MDB_IOB_INDENT)
    585 		iob_indent(iob);
    586 }
    587 
    588 void
    589 mdb_iob_clrflags(mdb_iob_t *iob, uint_t flags)
    590 {
    591 	iob->iob_flags &= ~flags;
    592 	if (flags & MDB_IOB_INDENT)
    593 		iob_unindent(iob);
    594 }
    595 
    596 uint_t
    597 mdb_iob_getflags(mdb_iob_t *iob)
    598 {
    599 	return (iob->iob_flags);
    600 }
    601 
    602 static uintmax_t
    603 vec_arg(const mdb_arg_t **app)
    604 {
    605 	uintmax_t value;
    606 
    607 	if ((*app)->a_type == MDB_TYPE_STRING)
    608 		value = (uintmax_t)(uintptr_t)(*app)->a_un.a_str;
    609 	else
    610 		value = (*app)->a_un.a_val;
    611 
    612 	(*app)++;
    613 	return (value);
    614 }
    615 
    616 static const char *
    617 iob_size2str(intsize_t size)
    618 {
    619 	switch (size) {
    620 	case SZ_SHORT:
    621 		return ("short");
    622 	case SZ_INT:
    623 		return ("int");
    624 	case SZ_LONG:
    625 		return ("long");
    626 	case SZ_LONGLONG:
    627 		return ("long long");
    628 	}
    629 	return ("");
    630 }
    631 
    632 /*
    633  * In order to simplify maintenance of the ::formats display, we provide an
    634  * unparser for mdb_printf format strings that converts a simple format
    635  * string with one specifier into a descriptive representation, e.g.
    636  * mdb_iob_format2str("%llx") returns "hexadecimal long long".
    637  */
    638 const char *
    639 mdb_iob_format2str(const char *format)
    640 {
    641 	intsize_t size = SZ_INT;
    642 	const char *p;
    643 
    644 	static char buf[64];
    645 
    646 	buf[0] = '\0';
    647 
    648 	if ((p = strchr(format, '%')) == NULL)
    649 		goto done;
    650 
    651 fmt_switch:
    652 	switch (*++p) {
    653 	case '0': case '1': case '2': case '3': case '4':
    654 	case '5': case '6': case '7': case '8': case '9':
    655 		while (*p >= '0' && *p <= '9')
    656 			p++;
    657 		p--;
    658 		goto fmt_switch;
    659 
    660 	case 'a':
    661 	case 'A':
    662 		return ("symbol");
    663 
    664 	case 'b':
    665 		(void) strcpy(buf, "unsigned ");
    666 		(void) strcat(buf, iob_size2str(size));
    667 		(void) strcat(buf, " bitfield");
    668 		break;
    669 
    670 	case 'c':
    671 		return ("character");
    672 
    673 	case 'd':
    674 	case 'i':
    675 		(void) strcpy(buf, "decimal signed ");
    676 		(void) strcat(buf, iob_size2str(size));
    677 		break;
    678 
    679 	case 'e':
    680 	case 'E':
    681 	case 'g':
    682 	case 'G':
    683 		return ("double");
    684 
    685 	case 'h':
    686 		size = SZ_SHORT;
    687 		goto fmt_switch;
    688 
    689 	case 'I':
    690 		return ("IPv4 address");
    691 
    692 	case 'l':
    693 		if (size >= SZ_LONG)
    694 			size = SZ_LONGLONG;
    695 		else
    696 			size = SZ_LONG;
    697 		goto fmt_switch;
    698 
    699 	case 'm':
    700 		return ("margin");
    701 
    702 	case 'N':
    703 		return ("IPv6 address");
    704 
    705 	case 'o':
    706 		(void) strcpy(buf, "octal unsigned ");
    707 		(void) strcat(buf, iob_size2str(size));
    708 		break;
    709 
    710 	case 'p':
    711 		return ("pointer");
    712 
    713 	case 'q':
    714 		(void) strcpy(buf, "octal signed ");
    715 		(void) strcat(buf, iob_size2str(size));
    716 		break;
    717 
    718 	case 'r':
    719 		(void) strcpy(buf, "default radix unsigned ");
    720 		(void) strcat(buf, iob_size2str(size));
    721 		break;
    722 
    723 	case 'R':
    724 		(void) strcpy(buf, "default radix signed ");
    725 		(void) strcat(buf, iob_size2str(size));
    726 		break;
    727 
    728 	case 's':
    729 		return ("string");
    730 
    731 	case 't':
    732 	case 'T':
    733 		return ("tab");
    734 
    735 	case 'u':
    736 		(void) strcpy(buf, "decimal unsigned ");
    737 		(void) strcat(buf, iob_size2str(size));
    738 		break;
    739 
    740 	case 'x':
    741 	case 'X':
    742 		(void) strcat(buf, "hexadecimal ");
    743 		(void) strcat(buf, iob_size2str(size));
    744 		break;
    745 
    746 	case 'Y':
    747 		return ("time_t");
    748 
    749 	case '<':
    750 		return ("terminal attribute");
    751 
    752 	case '?':
    753 	case '#':
    754 	case '+':
    755 	case '-':
    756 		goto fmt_switch;
    757 	}
    758 
    759 done:
    760 	if (buf[0] == '\0')
    761 		(void) strcpy(buf, "text");
    762 
    763 	return ((const char *)buf);
    764 }
    765 
    766 static const char *
    767 iob_int2str(varglist_t *ap, intsize_t size, int base, uint_t flags, int *zero,
    768     u_longlong_t *value)
    769 {
    770 	uintmax_t i;
    771 
    772 	switch (size) {
    773 	case SZ_LONGLONG:
    774 		if (flags & NTOS_UNSIGNED)
    775 			i = (u_longlong_t)VA_ARG(ap, u_longlong_t);
    776 		else
    777 			i = (longlong_t)VA_ARG(ap, longlong_t);
    778 		break;
    779 
    780 	case SZ_LONG:
    781 		if (flags & NTOS_UNSIGNED)
    782 			i = (ulong_t)VA_ARG(ap, ulong_t);
    783 		else
    784 			i = (long)VA_ARG(ap, long);
    785 		break;
    786 
    787 	case SZ_SHORT:
    788 		if (flags & NTOS_UNSIGNED)
    789 			i = (ushort_t)VA_ARG(ap, uint_t);
    790 		else
    791 			i = (short)VA_ARG(ap, int);
    792 		break;
    793 
    794 	default:
    795 		if (flags & NTOS_UNSIGNED)
    796 			i = (uint_t)VA_ARG(ap, uint_t);
    797 		else
    798 			i = (int)VA_ARG(ap, int);
    799 	}
    800 
    801 	*zero = i == 0;	/* Return flag indicating if result was zero */
    802 	*value = i;	/* Return value retrieved from va_list */
    803 
    804 	return (numtostr(i, base, flags));
    805 }
    806 
    807 static const char *
    808 iob_time2str(time_t *tmp)
    809 {
    810 	/*
    811 	 * ctime(3c) returns a string of the form
    812 	 * "Fri Sep 13 00:00:00 1986\n\0".  We turn this into the canonical
    813 	 * adb /y format "1986 Sep 13 00:00:00" below.
    814 	 */
    815 	const char *src = ctime(tmp);
    816 	static char buf[32];
    817 	char *dst = buf;
    818 	int i;
    819 
    820 	if (src == NULL)
    821 		return (numtostr((uintmax_t)*tmp, mdb.m_radix, 0));
    822 
    823 	for (i = 20; i < 24; i++)
    824 		*dst++ = src[i]; /* Copy the 4-digit year */
    825 
    826 	for (i = 3; i < 19; i++)
    827 		*dst++ = src[i]; /* Copy month, day, and h:m:s */
    828 
    829 	*dst = '\0';
    830 	return (buf);
    831 }
    832 
    833 static const char *
    834 iob_addr2str(uintptr_t addr)
    835 {
    836 	static char buf[MDB_TGT_SYM_NAMLEN];
    837 	char *name = buf;
    838 	longlong_t offset;
    839 	GElf_Sym sym;
    840 
    841 	if (mdb_tgt_lookup_by_addr(mdb.m_target, addr,
    842 	    MDB_TGT_SYM_FUZZY, buf, sizeof (buf), &sym, NULL) == -1)
    843 		return (NULL);
    844 
    845 	if (mdb.m_demangler != NULL && (mdb.m_flags & MDB_FL_DEMANGLE))
    846 		name = (char *)mdb_dem_convert(mdb.m_demangler, buf);
    847 
    848 	/*
    849 	 * Here we provide a little cooperation between the %a formatting code
    850 	 * and the proc target: if the initial address passed to %a is in fact
    851 	 * a PLT address, the proc target's lookup_by_addr code will convert
    852 	 * this to the PLT destination (a different address).  We do not want
    853 	 * to append a "+/-offset" suffix based on comparison with the query
    854 	 * symbol in this case because the proc target has really done a hidden
    855 	 * query for us with a different address.  We detect this case by
    856 	 * comparing the initial characters of buf to the special PLT= string.
    857 	 */
    858 	if (sym.st_value != addr && strncmp(name, "PLT=", 4) != 0) {
    859 		if (sym.st_value > addr)
    860 			offset = -(longlong_t)(sym.st_value - addr);
    861 		else
    862 			offset = (longlong_t)(addr - sym.st_value);
    863 
    864 		(void) strcat(name, numtostr(offset, mdb.m_radix,
    865 		    NTOS_SIGNPOS | NTOS_SHOWBASE));
    866 	}
    867 
    868 	return (name);
    869 }
    870 
    871 static int
    872 iob_setattr(mdb_iob_t *iob, const char *s, size_t nbytes)
    873 {
    874 	uint_t attr;
    875 	int req;
    876 
    877 	if (iob->iob_pgp == NULL)
    878 		return (set_errno(ENOTTY));
    879 
    880 	if (nbytes != 0 && *s == '/') {
    881 		req = ATT_OFF;
    882 		nbytes--;
    883 		s++;
    884 	} else
    885 		req = ATT_ON;
    886 
    887 	if (nbytes != 1)
    888 		return (set_errno(EINVAL));
    889 
    890 	switch (*s) {
    891 	case 's':
    892 		attr = ATT_STANDOUT;
    893 		break;
    894 	case 'u':
    895 		attr = ATT_UNDERLINE;
    896 		break;
    897 	case 'r':
    898 		attr = ATT_REVERSE;
    899 		break;
    900 	case 'b':
    901 		attr = ATT_BOLD;
    902 		break;
    903 	case 'd':
    904 		attr = ATT_DIM;
    905 		break;
    906 	case 'a':
    907 		attr = ATT_ALTCHARSET;
    908 		break;
    909 	default:
    910 		return (set_errno(EINVAL));
    911 	}
    912 
    913 	/*
    914 	 * We need to flush the current buffer contents before calling
    915 	 * IOP_SETATTR because IOP_SETATTR may need to synchronously output
    916 	 * terminal escape sequences directly to the underlying device.
    917 	 */
    918 	(void) iob_write(iob, iob->iob_iop, iob->iob_buf, iob->iob_nbytes);
    919 	iob->iob_bufp = &iob->iob_buf[0];
    920 	iob->iob_nbytes = 0;
    921 
    922 	return (IOP_SETATTR(iob->iob_pgp, req, attr));
    923 }
    924 
    925 static void
    926 iob_bits2str(mdb_iob_t *iob, u_longlong_t value, const mdb_bitmask_t *bmp,
    927     mdb_bool_t altflag)
    928 {
    929 	mdb_bool_t delim = FALSE;
    930 	const char *str;
    931 	size_t width;
    932 
    933 	if (bmp == NULL)
    934 		goto out;
    935 
    936 	for (; bmp->bm_name != NULL; bmp++) {
    937 		if ((value & bmp->bm_mask) == bmp->bm_bits) {
    938 			width = strlen(bmp->bm_name) + delim;
    939 
    940 			if (IOB_WRAPNOW(iob, width))
    941 				mdb_iob_nl(iob);
    942 
    943 			if (delim)
    944 				mdb_iob_putc(iob, ',');
    945 			else
    946 				delim = TRUE;
    947 
    948 			mdb_iob_puts(iob, bmp->bm_name);
    949 			value &= ~bmp->bm_bits;
    950 		}
    951 	}
    952 
    953 out:
    954 	if (altflag == TRUE && (delim == FALSE || value != 0)) {
    955 		str = numtostr(value, 16, NTOS_UNSIGNED | NTOS_SHOWBASE);
    956 		width = strlen(str) + delim;
    957 
    958 		if (IOB_WRAPNOW(iob, width))
    959 			mdb_iob_nl(iob);
    960 		if (delim)
    961 			mdb_iob_putc(iob, ',');
    962 		mdb_iob_puts(iob, str);
    963 	}
    964 }
    965 
    966 static const char *
    967 iob_inaddr2str(uint32_t addr)
    968 {
    969 	static char buf[INET_ADDRSTRLEN];
    970 
    971 	(void) mdb_inet_ntop(AF_INET, &addr, buf, sizeof (buf));
    972 
    973 	return (buf);
    974 }
    975 
    976 static const char *
    977 iob_ipv6addr2str(void *addr)
    978 {
    979 	static char buf[INET6_ADDRSTRLEN];
    980 
    981 	(void) mdb_inet_ntop(AF_INET6, addr, buf, sizeof (buf));
    982 
    983 	return (buf);
    984 }
    985 
    986 static const char *
    987 iob_getvar(const char *s, size_t len)
    988 {
    989 	mdb_var_t *val;
    990 	char *var;
    991 
    992 	if (len == 0) {
    993 		(void) set_errno(EINVAL);
    994 		return (NULL);
    995 	}
    996 
    997 	var = strndup(s, len);
    998 	val = mdb_nv_lookup(&mdb.m_nv, var);
    999 	strfree(var);
   1000 
   1001 	if (val == NULL) {
   1002 		(void) set_errno(EINVAL);
   1003 		return (NULL);
   1004 	}
   1005 
   1006 	return (numtostr(mdb_nv_get_value(val), 10, 0));
   1007 }
   1008 
   1009 /*
   1010  * The iob_doprnt function forms the main engine of the debugger's output
   1011  * formatting capabilities.  Note that this is NOT exactly compatible with
   1012  * the printf(3S) family, nor is it intended to be so.  We support some
   1013  * extensions and format characters not supported by printf(3S), and we
   1014  * explicitly do NOT provide support for %C, %S, %ws (wide-character strings),
   1015  * do NOT provide for the complete functionality of %f, %e, %E, %g, %G
   1016  * (alternate double formats), and do NOT support %.x (precision specification).
   1017  * Note that iob_doprnt consumes varargs off the original va_list.
   1018  */
   1019 static void
   1020 iob_doprnt(mdb_iob_t *iob, const char *format, varglist_t *ap)
   1021 {
   1022 	char c[2] = { 0, 0 };	/* Buffer for single character output */
   1023 	const char *p;		/* Current position in format string */
   1024 	size_t len;		/* Length of format string to copy verbatim */
   1025 	size_t altlen;		/* Length of alternate print format prefix */
   1026 	const char *altstr;	/* Alternate print format prefix */
   1027 	const char *symstr;	/* Symbol + offset string */
   1028 
   1029 	u_longlong_t val;	/* Current integer value */
   1030 	intsize_t size;		/* Current integer value size */
   1031 	uint_t flags;		/* Current flags to pass to iob_int2str */
   1032 	size_t width;		/* Current field width */
   1033 	int zero;		/* If != 0, then integer value == 0 */
   1034 
   1035 	mdb_bool_t f_alt;	/* Use alternate print format (%#) */
   1036 	mdb_bool_t f_altsuff;	/* Alternate print format is a suffix */
   1037 	mdb_bool_t f_zfill;	/* Zero-fill field (%0) */
   1038 	mdb_bool_t f_left;	/* Left-adjust field (%-) */
   1039 	mdb_bool_t f_digits;	/* Explicit digits used to set field width */
   1040 
   1041 	union {
   1042 		const char *str;
   1043 		uint32_t ui32;
   1044 		void *ptr;
   1045 		time_t tm;
   1046 		char c;
   1047 		double d;
   1048 		long double ld;
   1049 	} u;
   1050 
   1051 	ASSERT(iob->iob_flags & MDB_IOB_WRONLY);
   1052 
   1053 	while ((p = strchr(format, '%')) != NULL) {
   1054 		/*
   1055 		 * Output the format string verbatim up to the next '%' char
   1056 		 */
   1057 		if (p != format) {
   1058 			len = p - format;
   1059 			if (IOB_WRAPNOW(iob, len) && *format != '\n')
   1060 				mdb_iob_nl(iob);
   1061 			mdb_iob_nputs(iob, format, len);
   1062 		}
   1063 
   1064 		/*
   1065 		 * Now we need to parse the sequence of format characters
   1066 		 * following the % marker and do the appropriate thing.
   1067 		 */
   1068 		size = SZ_INT;		/* Use normal-sized int by default */
   1069 		flags = 0;		/* Clear numtostr() format flags */
   1070 		width = 0;		/* No field width limit by default */
   1071 		altlen = 0;		/* No alternate format string yet */
   1072 		altstr = NULL;		/* No alternate format string yet */
   1073 
   1074 		f_alt = FALSE;		/* Alternate format off by default */
   1075 		f_altsuff = FALSE;	/* Alternate format is a prefix */
   1076 		f_zfill = FALSE;	/* Zero-fill off by default */
   1077 		f_left = FALSE;		/* Left-adjust off by default */
   1078 		f_digits = FALSE;	/* No digits for width specified yet */
   1079 
   1080 		fmt_switch:
   1081 		switch (*++p) {
   1082 		case '0': case '1': case '2': case '3': case '4':
   1083 		case '5': case '6': case '7': case '8': case '9':
   1084 			if (f_digits == FALSE && *p == '0') {
   1085 				f_zfill = TRUE;
   1086 				goto fmt_switch;
   1087 			}
   1088 
   1089 			if (f_digits == FALSE)
   1090 				width = 0; /* clear any other width specifier */
   1091 
   1092 			for (u.c = *p; u.c >= '0' && u.c <= '9'; u.c = *++p)
   1093 				width = width * 10 + u.c - '0';
   1094 
   1095 			p--;
   1096 			f_digits = TRUE;
   1097 			goto fmt_switch;
   1098 
   1099 		case 'a':
   1100 			if (size < SZ_LONG)
   1101 				size = SZ_LONG;	/* Bump to size of uintptr_t */
   1102 
   1103 			u.str = iob_int2str(ap, size, 16,
   1104 			    NTOS_UNSIGNED | NTOS_SHOWBASE, &zero, &val);
   1105 
   1106 			if ((symstr = iob_addr2str(val)) != NULL)
   1107 				u.str = symstr;
   1108 
   1109 			if (f_alt == TRUE) {
   1110 				f_altsuff = TRUE;
   1111 				altstr = ":";
   1112 				altlen = 1;
   1113 			}
   1114 			break;
   1115 
   1116 		case 'A':
   1117 			if (size < SZ_LONG)
   1118 				size = SZ_LONG;	/* Bump to size of uintptr_t */
   1119 
   1120 			(void) iob_int2str(ap, size, 16,
   1121 			    NTOS_UNSIGNED, &zero, &val);
   1122 
   1123 			u.str = iob_addr2str(val);
   1124 
   1125 			if (f_alt == TRUE && u.str == NULL)
   1126 				u.str = "?";
   1127 			break;
   1128 
   1129 		case 'b':
   1130 			u.str = iob_int2str(ap, size, 16,
   1131 			    NTOS_UNSIGNED | NTOS_SHOWBASE, &zero, &val);
   1132 
   1133 			iob_bits2str(iob, val, VA_PTRARG(ap), f_alt);
   1134 
   1135 			format = ++p;
   1136 			continue;
   1137 
   1138 		case 'c':
   1139 			c[0] = (char)VA_ARG(ap, int);
   1140 			u.str = c;
   1141 			break;
   1142 
   1143 		case 'd':
   1144 		case 'i':
   1145 			if (f_alt)
   1146 				flags |= NTOS_SHOWBASE;
   1147 			u.str = iob_int2str(ap, size, 10, flags, &zero, &val);
   1148 			break;
   1149 
   1150 		/* No floating point in kmdb */
   1151 #ifndef _KMDB
   1152 		case 'e':
   1153 		case 'E':
   1154 			u.d = VA_ARG(ap, double);
   1155 			u.str = doubletos(u.d, 7, *p);
   1156 			break;
   1157 
   1158 		case 'g':
   1159 		case 'G':
   1160 			if (size >= SZ_LONG) {
   1161 				u.ld = VA_ARG(ap, long double);
   1162 				u.str = longdoubletos(&u.ld, 16,
   1163 				    (*p == 'g') ? 'e' : 'E');
   1164 			} else {
   1165 				u.d = VA_ARG(ap, double);
   1166 				u.str = doubletos(u.d, 16,
   1167 				    (*p == 'g') ? 'e' : 'E');
   1168 			}
   1169 			break;
   1170 #endif
   1171 
   1172 		case 'h':
   1173 			size = SZ_SHORT;
   1174 			goto fmt_switch;
   1175 
   1176 		case 'I':
   1177 			u.ui32 = VA_ARG(ap, uint32_t);
   1178 			u.str = iob_inaddr2str(u.ui32);
   1179 			break;
   1180 
   1181 		case 'l':
   1182 			if (size >= SZ_LONG)
   1183 				size = SZ_LONGLONG;
   1184 			else
   1185 				size = SZ_LONG;
   1186 			goto fmt_switch;
   1187 
   1188 		case 'm':
   1189 			if (iob->iob_nbytes == 0) {
   1190 				mdb_iob_ws(iob, (width != 0) ? width :
   1191 				    iob->iob_margin);
   1192 			}
   1193 			format = ++p;
   1194 			continue;
   1195 
   1196 		case 'N':
   1197 			u.ptr = VA_PTRARG(ap);
   1198 			u.str = iob_ipv6addr2str(u.ptr);
   1199 			break;
   1200 
   1201 		case 'o':
   1202 			u.str = iob_int2str(ap, size, 8, NTOS_UNSIGNED,
   1203 			    &zero, &val);
   1204 
   1205 			if (f_alt && !zero) {
   1206 				altstr = "0";
   1207 				altlen = 1;
   1208 			}
   1209 			break;
   1210 
   1211 		case 'p':
   1212 			u.ptr = VA_PTRARG(ap);
   1213 			u.str = numtostr((uintptr_t)u.ptr, 16, NTOS_UNSIGNED);
   1214 			break;
   1215 
   1216 		case 'q':
   1217 			u.str = iob_int2str(ap, size, 8, flags, &zero, &val);
   1218 
   1219 			if (f_alt && !zero) {
   1220 				altstr = "0";
   1221 				altlen = 1;
   1222 			}
   1223 			break;
   1224 
   1225 		case 'r':
   1226 			if (f_alt)
   1227 				flags |= NTOS_SHOWBASE;
   1228 			u.str = iob_int2str(ap, size, mdb.m_radix,
   1229 			    NTOS_UNSIGNED | flags, &zero, &val);
   1230 			break;
   1231 
   1232 		case 'R':
   1233 			if (f_alt)
   1234 				flags |= NTOS_SHOWBASE;
   1235 			u.str = iob_int2str(ap, size, mdb.m_radix, flags,
   1236 			    &zero, &val);
   1237 			break;
   1238 
   1239 		case 's':
   1240 			u.str = VA_PTRARG(ap);
   1241 			if (u.str == NULL)
   1242 				u.str = "<NULL>"; /* Be forgiving of NULL */
   1243 			break;
   1244 
   1245 		case 't':
   1246 			if (width != 0) {
   1247 				while (width-- > 0)
   1248 					mdb_iob_tab(iob);
   1249 			} else
   1250 				mdb_iob_tab(iob);
   1251 
   1252 			format = ++p;
   1253 			continue;
   1254 
   1255 		case 'T':
   1256 			if (width != 0 && (iob->iob_nbytes % width) != 0) {
   1257 				size_t ots = iob->iob_tabstop;
   1258 				iob->iob_tabstop = width;
   1259 				mdb_iob_tab(iob);
   1260 				iob->iob_tabstop = ots;
   1261 			}
   1262 			format = ++p;
   1263 			continue;
   1264 
   1265 		case 'u':
   1266 			if (f_alt)
   1267 				flags |= NTOS_SHOWBASE;
   1268 			u.str = iob_int2str(ap, size, 10,
   1269 			    flags | NTOS_UNSIGNED, &zero, &val);
   1270 			break;
   1271 
   1272 		case 'x':
   1273 			u.str = iob_int2str(ap, size, 16, NTOS_UNSIGNED,
   1274 			    &zero, &val);
   1275 
   1276 			if (f_alt && !zero) {
   1277 				altstr = "0x";
   1278 				altlen = 2;
   1279 			}
   1280 			break;
   1281 
   1282 		case 'X':
   1283 			u.str = iob_int2str(ap, size, 16,
   1284 			    NTOS_UNSIGNED | NTOS_UPCASE, &zero, &val);
   1285 
   1286 			if (f_alt && !zero) {
   1287 				altstr = "0X";
   1288 				altlen = 2;
   1289 			}
   1290 			break;
   1291 
   1292 		case 'Y':
   1293 			u.tm = VA_ARG(ap, time_t);
   1294 			u.str = iob_time2str(&u.tm);
   1295 			break;
   1296 
   1297 		case '<':
   1298 			/*
   1299 			 * Used to turn attributes on (<b>), to turn them
   1300 			 * off (</b>), or to print variables (<_var>).
   1301 			 */
   1302 			for (u.str = ++p; *p != '\0' && *p != '>'; p++)
   1303 				continue;
   1304 
   1305 			if (*p == '>') {
   1306 				size_t paramlen = p - u.str;
   1307 
   1308 				if (paramlen > 0) {
   1309 					if (*u.str == '_') {
   1310 						u.str = iob_getvar(u.str + 1,
   1311 						    paramlen - 1);
   1312 						break;
   1313 					} else {
   1314 						(void) iob_setattr(iob, u.str,
   1315 						    paramlen);
   1316 					}
   1317 				}
   1318 
   1319 				p++;
   1320 			}
   1321 
   1322 			format = p;
   1323 			continue;
   1324 
   1325 		case '*':
   1326 			width = (size_t)(uint_t)VA_ARG(ap, int);
   1327 			goto fmt_switch;
   1328 
   1329 		case '%':
   1330 			u.str = "%";
   1331 			break;
   1332 
   1333 		case '?':
   1334 			width = sizeof (uintptr_t) * 2;
   1335 			goto fmt_switch;
   1336 
   1337 		case '#':
   1338 			f_alt = TRUE;
   1339 			goto fmt_switch;
   1340 
   1341 		case '+':
   1342 			flags |= NTOS_SIGNPOS;
   1343 			goto fmt_switch;
   1344 
   1345 		case '-':
   1346 			f_left = TRUE;
   1347 			goto fmt_switch;
   1348 
   1349 		default:
   1350 			c[0] = p[0];
   1351 			u.str = c;
   1352 		}
   1353 
   1354 		len = u.str != NULL ? strlen(u.str) : 0;
   1355 
   1356 		if (len + altlen > width)
   1357 			width = len + altlen;
   1358 
   1359 		/*
   1360 		 * If the string and the option altstr won't fit on this line
   1361 		 * and auto-wrap is set (default), skip to the next line.
   1362 		 */
   1363 		if (IOB_WRAPNOW(iob, width))
   1364 			mdb_iob_nl(iob);
   1365 
   1366 		/*
   1367 		 * Optionally add whitespace or zeroes prefixing the value if
   1368 		 * we haven't filled the minimum width and we're right-aligned.
   1369 		 */
   1370 		if (len < (width - altlen) && f_left == FALSE) {
   1371 			mdb_iob_fill(iob, f_zfill ? '0' : ' ',
   1372 			    width - altlen - len);
   1373 		}
   1374 
   1375 		/*
   1376 		 * Print the alternate string if it's a prefix, and then
   1377 		 * print the value string itself.
   1378 		 */
   1379 		if (altstr != NULL && f_altsuff == FALSE)
   1380 			mdb_iob_nputs(iob, altstr, altlen);
   1381 		if (len != 0)
   1382 			mdb_iob_nputs(iob, u.str, len);
   1383 
   1384 		/*
   1385 		 * If we have an alternate string and it's a suffix, print it.
   1386 		 */
   1387 		if (altstr != NULL && f_altsuff == TRUE)
   1388 			mdb_iob_nputs(iob, altstr, altlen);
   1389 
   1390 		/*
   1391 		 * Finally, if we haven't filled the field width and we're
   1392 		 * left-aligned, pad out the rest with whitespace.
   1393 		 */
   1394 		if ((len + altlen) < width && f_left == TRUE)
   1395 			mdb_iob_ws(iob, width - altlen - len);
   1396 
   1397 		format = (*p != '\0') ? ++p : p;
   1398 	}
   1399 
   1400 	/*
   1401 	 * If there's anything left in the format string, output it now
   1402 	 */
   1403 	if (*format != '\0') {
   1404 		len = strlen(format);
   1405 		if (IOB_WRAPNOW(iob, len) && *format != '\n')
   1406 			mdb_iob_nl(iob);
   1407 		mdb_iob_nputs(iob, format, len);
   1408 	}
   1409 }
   1410 
   1411 void
   1412 mdb_iob_vprintf(mdb_iob_t *iob, const char *format, va_list alist)
   1413 {
   1414 	varglist_t ap = { VAT_VARARGS };
   1415 	va_copy(ap.val_valist, alist);
   1416 	iob_doprnt(iob, format, &ap);
   1417 }
   1418 
   1419 void
   1420 mdb_iob_aprintf(mdb_iob_t *iob, const char *format, const mdb_arg_t *argv)
   1421 {
   1422 	varglist_t ap = { VAT_ARGVEC };
   1423 	ap.val_argv = argv;
   1424 	iob_doprnt(iob, format, &ap);
   1425 }
   1426 
   1427 void
   1428 mdb_iob_printf(mdb_iob_t *iob, const char *format, ...)
   1429 {
   1430 	va_list alist;
   1431 
   1432 	va_start(alist, format);
   1433 	mdb_iob_vprintf(iob, format, alist);
   1434 	va_end(alist);
   1435 }
   1436 
   1437 /*
   1438  * In order to handle the sprintf family of functions, we define a special
   1439  * i/o backend known as a "sprintf buf" (or spbuf for short).  This back end
   1440  * provides an IOP_WRITE entry point that concatenates each buffer sent from
   1441  * mdb_iob_flush() onto the caller's buffer until the caller's buffer is
   1442  * exhausted.  We also keep an absolute count of how many bytes were sent to
   1443  * this function during the lifetime of the snprintf call.  This allows us
   1444  * to provide the ability to (1) return the total size required for the given
   1445  * format string and argument list, and (2) support a call to snprintf with a
   1446  * NULL buffer argument with no special case code elsewhere.
   1447  */
   1448 static ssize_t
   1449 spbuf_write(mdb_io_t *io, const void *buf, size_t buflen)
   1450 {
   1451 	spbuf_t *spb = io->io_data;
   1452 
   1453 	if (spb->spb_bufsiz != 0) {
   1454 		size_t n = MIN(spb->spb_bufsiz, buflen);
   1455 		bcopy(buf, spb->spb_buf, n);
   1456 		spb->spb_buf += n;
   1457 		spb->spb_bufsiz -= n;
   1458 	}
   1459 
   1460 	spb->spb_total += buflen;
   1461 	return (buflen);
   1462 }
   1463 
   1464 static const mdb_io_ops_t spbuf_ops = {
   1465 	no_io_read,
   1466 	spbuf_write,
   1467 	no_io_seek,
   1468 	no_io_ctl,
   1469 	no_io_close,
   1470 	no_io_name,
   1471 	no_io_link,
   1472 	no_io_unlink,
   1473 	no_io_setattr,
   1474 	no_io_suspend,
   1475 	no_io_resume
   1476 };
   1477 
   1478 /*
   1479  * The iob_spb_create function initializes an iob suitable for snprintf calls,
   1480  * a spbuf i/o backend, and the spbuf private data, and then glues these
   1481  * objects together.  The caller (either vsnprintf or asnprintf below) is
   1482  * expected to have allocated the various structures on their stack.
   1483  */
   1484 static void
   1485 iob_spb_create(mdb_iob_t *iob, char *iob_buf, size_t iob_len,
   1486     mdb_io_t *io, spbuf_t *spb, char *spb_buf, size_t spb_len)
   1487 {
   1488 	spb->spb_buf = spb_buf;
   1489 	spb->spb_bufsiz = spb_len;
   1490 	spb->spb_total = 0;
   1491 
   1492 	io->io_ops = &spbuf_ops;
   1493 	io->io_data = spb;
   1494 	io->io_next = NULL;
   1495 	io->io_refcnt = 1;
   1496 
   1497 	iob->iob_buf = iob_buf;
   1498 	iob->iob_bufsiz = iob_len;
   1499 	iob->iob_bufp = iob_buf;
   1500 	iob->iob_nbytes = 0;
   1501 	iob->iob_nlines = 0;
   1502 	iob->iob_lineno = 1;
   1503 	iob->iob_rows = MDB_IOB_DEFROWS;
   1504 	iob->iob_cols = iob_len;
   1505 	iob->iob_tabstop = MDB_IOB_DEFTAB;
   1506 	iob->iob_margin = MDB_IOB_DEFMARGIN;
   1507 	iob->iob_flags = MDB_IOB_WRONLY;
   1508 	iob->iob_iop = io;
   1509 	iob->iob_pgp = NULL;
   1510 	iob->iob_next = NULL;
   1511 }
   1512 
   1513 /*ARGSUSED*/
   1514 ssize_t
   1515 null_io_write(mdb_io_t *io, const void *buf, size_t nbytes)
   1516 {
   1517 	return (nbytes);
   1518 }
   1519 
   1520 static const mdb_io_ops_t null_ops = {
   1521 	no_io_read,
   1522 	null_io_write,
   1523 	no_io_seek,
   1524 	no_io_ctl,
   1525 	no_io_close,
   1526 	no_io_name,
   1527 	no_io_link,
   1528 	no_io_unlink,
   1529 	no_io_setattr,
   1530 	no_io_suspend,
   1531 	no_io_resume
   1532 };
   1533 
   1534 mdb_io_t *
   1535 mdb_nullio_create(void)
   1536 {
   1537 	static mdb_io_t null_io = {
   1538 		&null_ops,
   1539 		NULL,
   1540 		NULL,
   1541 		1
   1542 	};
   1543 
   1544 	return (&null_io);
   1545 }
   1546 
   1547 size_t
   1548 mdb_iob_vsnprintf(char *buf, size_t nbytes, const char *format, va_list alist)
   1549 {
   1550 	varglist_t ap = { VAT_VARARGS };
   1551 	char iob_buf[64];
   1552 	mdb_iob_t iob;
   1553 	mdb_io_t io;
   1554 	spbuf_t spb;
   1555 
   1556 	ASSERT(buf != NULL || nbytes == 0);
   1557 	iob_spb_create(&iob, iob_buf, sizeof (iob_buf), &io, &spb, buf, nbytes);
   1558 	va_copy(ap.val_valist, alist);
   1559 	iob_doprnt(&iob, format, &ap);
   1560 	mdb_iob_flush(&iob);
   1561 
   1562 	if (spb.spb_bufsiz != 0)
   1563 		*spb.spb_buf = '\0';
   1564 	else if (buf != NULL && nbytes > 0)
   1565 		*--spb.spb_buf = '\0';
   1566 
   1567 	return (spb.spb_total);
   1568 }
   1569 
   1570 size_t
   1571 mdb_iob_asnprintf(char *buf, size_t nbytes, const char *format,
   1572     const mdb_arg_t *argv)
   1573 {
   1574 	varglist_t ap = { VAT_ARGVEC };
   1575 	char iob_buf[64];
   1576 	mdb_iob_t iob;
   1577 	mdb_io_t io;
   1578 	spbuf_t spb;
   1579 
   1580 	ASSERT(buf != NULL || nbytes == 0);
   1581 	iob_spb_create(&iob, iob_buf, sizeof (iob_buf), &io, &spb, buf, nbytes);
   1582 	ap.val_argv = argv;
   1583 	iob_doprnt(&iob, format, &ap);
   1584 	mdb_iob_flush(&iob);
   1585 
   1586 	if (spb.spb_bufsiz != 0)
   1587 		*spb.spb_buf = '\0';
   1588 	else if (buf != NULL && nbytes > 0)
   1589 		*--spb.spb_buf = '\0';
   1590 
   1591 	return (spb.spb_total);
   1592 }
   1593 
   1594 /*PRINTFLIKE3*/
   1595 size_t
   1596 mdb_iob_snprintf(char *buf, size_t nbytes, const char *format, ...)
   1597 {
   1598 	va_list alist;
   1599 
   1600 	va_start(alist, format);
   1601 	nbytes = mdb_iob_vsnprintf(buf, nbytes, format, alist);
   1602 	va_end(alist);
   1603 
   1604 	return (nbytes);
   1605 }
   1606 
   1607 void
   1608 mdb_iob_nputs(mdb_iob_t *iob, const char *s, size_t nbytes)
   1609 {
   1610 	size_t m, n, nleft = nbytes;
   1611 	const char *p, *q = s;
   1612 
   1613 	ASSERT(iob->iob_flags & MDB_IOB_WRONLY);
   1614 
   1615 	if (nbytes == 0)
   1616 		return; /* Return immediately if there is no work to do */
   1617 
   1618 	/*
   1619 	 * If the string contains embedded newlines or tabs, invoke ourself
   1620 	 * recursively for each string component, followed by a call to the
   1621 	 * newline or tab routine.  This insures that strings with these
   1622 	 * characters obey our wrapping and indenting rules, and that strings
   1623 	 * with embedded newlines are flushed after each newline, allowing
   1624 	 * the output pager to take over if it is enabled.
   1625 	 */
   1626 	while ((p = strnpbrk(q, "\t\n", nleft)) != NULL) {
   1627 		if (p > q)
   1628 			mdb_iob_nputs(iob, q, (size_t)(p - q));
   1629 
   1630 		if (*p == '\t')
   1631 			mdb_iob_tab(iob);
   1632 		else
   1633 			mdb_iob_nl(iob);
   1634 
   1635 		nleft -= (size_t)(p - q) + 1;	/* Update byte count */
   1636 		q = p + 1;			/* Advance past delimiter */
   1637 	}
   1638 
   1639 	/*
   1640 	 * For a given string component, we determine how many bytes (n) we can
   1641 	 * copy into our buffer (limited by either cols or bufsiz depending
   1642 	 * on whether AUTOWRAP is on), copy a chunk into the buffer, and
   1643 	 * flush the buffer if we reach the end of a line.
   1644 	 */
   1645 	while (nleft != 0) {
   1646 		if (iob->iob_flags & MDB_IOB_AUTOWRAP) {
   1647 			ASSERT(iob->iob_cols >= iob->iob_nbytes);
   1648 			n = iob->iob_cols - iob->iob_nbytes;
   1649 		} else {
   1650 			ASSERT(iob->iob_bufsiz >= iob->iob_nbytes);
   1651 			n = iob->iob_bufsiz - iob->iob_nbytes;
   1652 		}
   1653 
   1654 		m = MIN(nleft, n); /* copy at most n bytes in this pass */
   1655 
   1656 		bcopy(q, iob->iob_bufp, m);
   1657 		nleft -= m;
   1658 		q += m;
   1659 
   1660 		iob->iob_bufp += m;
   1661 		iob->iob_nbytes += m;
   1662 
   1663 		if (m == n && nleft != 0) {
   1664 			if (iob->iob_flags & MDB_IOB_AUTOWRAP)
   1665 				mdb_iob_nl(iob);
   1666 			else
   1667 				mdb_iob_flush(iob);
   1668 		}
   1669 	}
   1670 }
   1671 
   1672 void
   1673 mdb_iob_puts(mdb_iob_t *iob, const char *s)
   1674 {
   1675 	mdb_iob_nputs(iob, s, strlen(s));
   1676 }
   1677 
   1678 void
   1679 mdb_iob_putc(mdb_iob_t *iob, int c)
   1680 {
   1681 	mdb_iob_fill(iob, c, 1);
   1682 }
   1683 
   1684 void
   1685 mdb_iob_tab(mdb_iob_t *iob)
   1686 {
   1687 	ASSERT(iob->iob_flags & MDB_IOB_WRONLY);
   1688 
   1689 	if (iob->iob_tabstop != 0) {
   1690 		/*
   1691 		 * Round up to the next multiple of the tabstop.  If this puts
   1692 		 * us off the end of the line, just insert a newline; otherwise
   1693 		 * insert sufficient whitespace to reach position n.
   1694 		 */
   1695 		size_t n = (iob->iob_nbytes + iob->iob_tabstop) /
   1696 		    iob->iob_tabstop * iob->iob_tabstop;
   1697 
   1698 		if (n < iob->iob_cols)
   1699 			mdb_iob_fill(iob, ' ', n - iob->iob_nbytes);
   1700 		else
   1701 			mdb_iob_nl(iob);
   1702 	}
   1703 }
   1704 
   1705 void
   1706 mdb_iob_fill(mdb_iob_t *iob, int c, size_t nfill)
   1707 {
   1708 	size_t i, m, n;
   1709 
   1710 	ASSERT(iob->iob_flags & MDB_IOB_WRONLY);
   1711 
   1712 	while (nfill != 0) {
   1713 		if (iob->iob_flags & MDB_IOB_AUTOWRAP) {
   1714 			ASSERT(iob->iob_cols >= iob->iob_nbytes);
   1715 			n = iob->iob_cols - iob->iob_nbytes;
   1716 		} else {
   1717 			ASSERT(iob->iob_bufsiz >= iob->iob_nbytes);
   1718 			n = iob->iob_bufsiz - iob->iob_nbytes;
   1719 		}
   1720 
   1721 		m = MIN(nfill, n); /* fill at most n bytes in this pass */
   1722 
   1723 		for (i = 0; i < m; i++)
   1724 			*iob->iob_bufp++ = (char)c;
   1725 
   1726 		iob->iob_nbytes += m;
   1727 		nfill -= m;
   1728 
   1729 		if (m == n && nfill != 0) {
   1730 			if (iob->iob_flags & MDB_IOB_AUTOWRAP)
   1731 				mdb_iob_nl(iob);
   1732 			else
   1733 				mdb_iob_flush(iob);
   1734 		}
   1735 	}
   1736 }
   1737 
   1738 void
   1739 mdb_iob_ws(mdb_iob_t *iob, size_t n)
   1740 {
   1741 	if (iob->iob_nbytes + n < iob->iob_cols)
   1742 		mdb_iob_fill(iob, ' ', n);
   1743 	else
   1744 		mdb_iob_nl(iob);
   1745 }
   1746 
   1747 void
   1748 mdb_iob_nl(mdb_iob_t *iob)
   1749 {
   1750 	ASSERT(iob->iob_flags & MDB_IOB_WRONLY);
   1751 
   1752 	if (iob->iob_nbytes == iob->iob_bufsiz)
   1753 		mdb_iob_flush(iob);
   1754 
   1755 	*iob->iob_bufp++ = '\n';
   1756 	iob->iob_nbytes++;
   1757 
   1758 	mdb_iob_flush(iob);
   1759 }
   1760 
   1761 ssize_t
   1762 mdb_iob_ngets(mdb_iob_t *iob, char *buf, size_t n)
   1763 {
   1764 	ssize_t resid = n - 1;
   1765 	ssize_t len;
   1766 	int c;
   1767 
   1768 	if (iob->iob_flags & (MDB_IOB_WRONLY | MDB_IOB_EOF))
   1769 		return (EOF); /* can't gets a write buf or a read buf at EOF */
   1770 
   1771 	if (n == 0)
   1772 		return (0);   /* we need room for a terminating \0 */
   1773 
   1774 	while (resid != 0) {
   1775 		if (iob->iob_nbytes == 0 && iob_read(iob, iob->iob_iop) <= 0)
   1776 			goto done; /* failed to refill buffer */
   1777 
   1778 		for (len = MIN(iob->iob_nbytes, resid); len != 0; len--) {
   1779 			c = *iob->iob_bufp++;
   1780 			iob->iob_nbytes--;
   1781 
   1782 			if (c == EOF || c == '\n')
   1783 				goto done;
   1784 
   1785 			*buf++ = (char)c;
   1786 			resid--;
   1787 		}
   1788 	}
   1789 done:
   1790 	*buf = '\0';
   1791 	return (n - resid - 1);
   1792 }
   1793 
   1794 int
   1795 mdb_iob_getc(mdb_iob_t *iob)
   1796 {
   1797 	int c;
   1798 
   1799 	if (iob->iob_flags & (MDB_IOB_WRONLY | MDB_IOB_EOF | MDB_IOB_ERR))
   1800 		return (EOF); /* can't getc if write-only, EOF, or error bit */
   1801 
   1802 	if (iob->iob_nbytes == 0 && iob_read(iob, iob->iob_iop) <= 0)
   1803 		return (EOF); /* failed to refill buffer */
   1804 
   1805 	c = (uchar_t)*iob->iob_bufp++;
   1806 	iob->iob_nbytes--;
   1807 
   1808 	return (c);
   1809 }
   1810 
   1811 int
   1812 mdb_iob_ungetc(mdb_iob_t *iob, int c)
   1813 {
   1814 	if (iob->iob_flags & (MDB_IOB_WRONLY | MDB_IOB_ERR))
   1815 		return (EOF); /* can't ungetc if write-only or error bit set */
   1816 
   1817 	if (c == EOF || iob->iob_nbytes == iob->iob_bufsiz)
   1818 		return (EOF); /* can't ungetc EOF, or ungetc if buffer full */
   1819 
   1820 	*--iob->iob_bufp = (char)c;
   1821 	iob->iob_nbytes++;
   1822 	iob->iob_flags &= ~MDB_IOB_EOF;
   1823 
   1824 	return (c);
   1825 }
   1826 
   1827 int
   1828 mdb_iob_eof(mdb_iob_t *iob)
   1829 {
   1830 	return ((iob->iob_flags & (MDB_IOB_RDONLY | MDB_IOB_EOF)) ==
   1831 	    (MDB_IOB_RDONLY | MDB_IOB_EOF));
   1832 }
   1833 
   1834 int
   1835 mdb_iob_err(mdb_iob_t *iob)
   1836 {
   1837 	return ((iob->iob_flags & MDB_IOB_ERR) == MDB_IOB_ERR);
   1838 }
   1839 
   1840 ssize_t
   1841 mdb_iob_read(mdb_iob_t *iob, void *buf, size_t n)
   1842 {
   1843 	ssize_t resid = n;
   1844 	ssize_t len;
   1845 
   1846 	if (iob->iob_flags & (MDB_IOB_WRONLY | MDB_IOB_EOF | MDB_IOB_ERR))
   1847 		return (0); /* can't read if write-only, eof, or error */
   1848 
   1849 	while (resid != 0) {
   1850 		if (iob->iob_nbytes == 0 && iob_read(iob, iob->iob_iop) <= 0)
   1851 			break; /* failed to refill buffer */
   1852 
   1853 		len = MIN(resid, iob->iob_nbytes);
   1854 		bcopy(iob->iob_bufp, buf, len);
   1855 
   1856 		iob->iob_bufp += len;
   1857 		iob->iob_nbytes -= len;
   1858 
   1859 		buf = (char *)buf + len;
   1860 		resid -= len;
   1861 	}
   1862 
   1863 	return (n - resid);
   1864 }
   1865 
   1866 /*
   1867  * For now, all binary writes are performed unbuffered.  This has the
   1868  * side effect that the pager will not be triggered by mdb_iob_write.
   1869  */
   1870 ssize_t
   1871 mdb_iob_write(mdb_iob_t *iob, const void *buf, size_t n)
   1872 {
   1873 	ssize_t ret;
   1874 
   1875 	if (iob->iob_flags & MDB_IOB_ERR)
   1876 		return (set_errno(EIO));
   1877 	if (iob->iob_flags & MDB_IOB_RDONLY)
   1878 		return (set_errno(EMDB_IORO));
   1879 
   1880 	mdb_iob_flush(iob);
   1881 	ret = iob_write(iob, iob->iob_iop, buf, n);
   1882 
   1883 	if (ret < 0 && iob == mdb.m_out)
   1884 		longjmp(mdb.m_frame->f_pcb, MDB_ERR_OUTPUT);
   1885 
   1886 	return (ret);
   1887 }
   1888 
   1889 int
   1890 mdb_iob_ctl(mdb_iob_t *iob, int req, void *arg)
   1891 {
   1892 	return (IOP_CTL(iob->iob_iop, req, arg));
   1893 }
   1894 
   1895 const char *
   1896 mdb_iob_name(mdb_iob_t *iob)
   1897 {
   1898 	if (iob == NULL)
   1899 		return ("<NULL>");
   1900 
   1901 	return (IOP_NAME(iob->iob_iop));
   1902 }
   1903 
   1904 size_t
   1905 mdb_iob_lineno(mdb_iob_t *iob)
   1906 {
   1907 	return (iob->iob_lineno);
   1908 }
   1909 
   1910 size_t
   1911 mdb_iob_gettabstop(mdb_iob_t *iob)
   1912 {
   1913 	return (iob->iob_tabstop);
   1914 }
   1915 
   1916 size_t
   1917 mdb_iob_getmargin(mdb_iob_t *iob)
   1918 {
   1919 	return (iob->iob_margin);
   1920 }
   1921 
   1922 mdb_io_t *
   1923 mdb_io_hold(mdb_io_t *io)
   1924 {
   1925 	io->io_refcnt++;
   1926 	return (io);
   1927 }
   1928 
   1929 void
   1930 mdb_io_rele(mdb_io_t *io)
   1931 {
   1932 	ASSERT(io->io_refcnt != 0);
   1933 
   1934 	if (--io->io_refcnt == 0) {
   1935 		IOP_CLOSE(io);
   1936 		mdb_free(io, sizeof (mdb_io_t));
   1937 	}
   1938 }
   1939 
   1940 void
   1941 mdb_io_destroy(mdb_io_t *io)
   1942 {
   1943 	ASSERT(io->io_refcnt == 0);
   1944 	IOP_CLOSE(io);
   1945 	mdb_free(io, sizeof (mdb_io_t));
   1946 }
   1947 
   1948 void
   1949 mdb_iob_stack_create(mdb_iob_stack_t *stk)
   1950 {
   1951 	stk->stk_top = NULL;
   1952 	stk->stk_size = 0;
   1953 }
   1954 
   1955 void
   1956 mdb_iob_stack_destroy(mdb_iob_stack_t *stk)
   1957 {
   1958 	mdb_iob_t *top, *ntop;
   1959 
   1960 	for (top = stk->stk_top; top != NULL; top = ntop) {
   1961 		ntop = top->iob_next;
   1962 		mdb_iob_destroy(top);
   1963 	}
   1964 }
   1965 
   1966 void
   1967 mdb_iob_stack_push(mdb_iob_stack_t *stk, mdb_iob_t *iob, size_t lineno)
   1968 {
   1969 	iob->iob_lineno = lineno;
   1970 	iob->iob_next = stk->stk_top;
   1971 	stk->stk_top = iob;
   1972 	stk->stk_size++;
   1973 	yylineno = 1;
   1974 }
   1975 
   1976 mdb_iob_t *
   1977 mdb_iob_stack_pop(mdb_iob_stack_t *stk)
   1978 {
   1979 	mdb_iob_t *top = stk->stk_top;
   1980 
   1981 	ASSERT(top != NULL);
   1982 
   1983 	stk->stk_top = top->iob_next;
   1984 	top->iob_next = NULL;
   1985 	stk->stk_size--;
   1986 
   1987 	return (top);
   1988 }
   1989 
   1990 size_t
   1991 mdb_iob_stack_size(mdb_iob_stack_t *stk)
   1992 {
   1993 	return (stk->stk_size);
   1994 }
   1995 
   1996 /*
   1997  * Stub functions for i/o backend implementors: these stubs either act as
   1998  * pass-through no-ops or return ENOTSUP as appropriate.
   1999  */
   2000 ssize_t
   2001 no_io_read(mdb_io_t *io, void *buf, size_t nbytes)
   2002 {
   2003 	if (io->io_next != NULL)
   2004 		return (IOP_READ(io->io_next, buf, nbytes));
   2005 
   2006 	return (set_errno(EMDB_IOWO));
   2007 }
   2008 
   2009 ssize_t
   2010 no_io_write(mdb_io_t *io, const void *buf, size_t nbytes)
   2011 {
   2012 	if (io->io_next != NULL)
   2013 		return (IOP_WRITE(io->io_next, buf, nbytes));
   2014 
   2015 	return (set_errno(EMDB_IORO));
   2016 }
   2017 
   2018 off64_t
   2019 no_io_seek(mdb_io_t *io, off64_t offset, int whence)
   2020 {
   2021 	if (io->io_next != NULL)
   2022 		return (IOP_SEEK(io->io_next, offset, whence));
   2023 
   2024 	return (set_errno(ENOTSUP));
   2025 }
   2026 
   2027 int
   2028 no_io_ctl(mdb_io_t *io, int req, void *arg)
   2029 {
   2030 	if (io->io_next != NULL)
   2031 		return (IOP_CTL(io->io_next, req, arg));
   2032 
   2033 	return (set_errno(ENOTSUP));
   2034 }
   2035 
   2036 /*ARGSUSED*/
   2037 void
   2038 no_io_close(mdb_io_t *io)
   2039 {
   2040 /*
   2041  * Note that we do not propagate IOP_CLOSE down the io stack.  IOP_CLOSE should
   2042  * only be called by mdb_io_rele when an io's reference count has gone to zero.
   2043  */
   2044 }
   2045 
   2046 const char *
   2047 no_io_name(mdb_io_t *io)
   2048 {
   2049 	if (io->io_next != NULL)
   2050 		return (IOP_NAME(io->io_next));
   2051 
   2052 	return ("(anonymous)");
   2053 }
   2054 
   2055 void
   2056 no_io_link(mdb_io_t *io, mdb_iob_t *iob)
   2057 {
   2058 	if (io->io_next != NULL)
   2059 		IOP_LINK(io->io_next, iob);
   2060 }
   2061 
   2062 void
   2063 no_io_unlink(mdb_io_t *io, mdb_iob_t *iob)
   2064 {
   2065 	if (io->io_next != NULL)
   2066 		IOP_UNLINK(io->io_next, iob);
   2067 }
   2068 
   2069 int
   2070 no_io_setattr(mdb_io_t *io, int req, uint_t attrs)
   2071 {
   2072 	if (io->io_next != NULL)
   2073 		return (IOP_SETATTR(io->io_next, req, attrs));
   2074 
   2075 	return (set_errno(ENOTSUP));
   2076 }
   2077 
   2078 void
   2079 no_io_suspend(mdb_io_t *io)
   2080 {
   2081 	if (io->io_next != NULL)
   2082 		IOP_SUSPEND(io->io_next);
   2083 }
   2084 
   2085 void
   2086 no_io_resume(mdb_io_t *io)
   2087 {
   2088 	if (io->io_next != NULL)
   2089 		IOP_RESUME(io->io_next);
   2090 }
   2091 
   2092 /*
   2093  * Iterate over the varargs. The first item indicates the mode:
   2094  * MDB_TBL_PRNT
   2095  * 	pull out the next vararg as a const char * and pass it and the
   2096  * 	remaining varargs to iob_doprnt; if we want to print the column,
   2097  * 	direct the output to mdb.m_out otherwise direct it to mdb.m_null
   2098  *
   2099  * MDB_TBL_FUNC
   2100  * 	pull out the next vararg as type mdb_table_print_f and the
   2101  * 	following one as a void * argument to the function; call the
   2102  * 	function with the given argument if we want to print the column
   2103  *
   2104  * The second item indicates the flag; if the flag is set in the flags
   2105  * argument, then the column is printed. A flag value of 0 indicates
   2106  * that the column should always be printed.
   2107  */
   2108 void
   2109 mdb_table_print(uint_t flags, const char *delimeter, ...)
   2110 {
   2111 	va_list alist;
   2112 	uint_t flg;
   2113 	uint_t type;
   2114 	const char *fmt;
   2115 	mdb_table_print_f *func;
   2116 	void *arg;
   2117 	mdb_iob_t *out;
   2118 	mdb_bool_t first = TRUE;
   2119 	mdb_bool_t print;
   2120 
   2121 	va_start(alist, delimeter);
   2122 
   2123 	while ((type = va_arg(alist, uint_t)) != MDB_TBL_DONE) {
   2124 		flg = va_arg(alist, uint_t);
   2125 
   2126 		print = flg == 0 || (flg & flags) != 0;
   2127 
   2128 		if (print) {
   2129 			if (first)
   2130 				first = FALSE;
   2131 			else
   2132 				mdb_printf("%s", delimeter);
   2133 		}
   2134 
   2135 		switch (type) {
   2136 		case MDB_TBL_PRNT: {
   2137 			varglist_t ap = { VAT_VARARGS };
   2138 			fmt = va_arg(alist, const char *);
   2139 			out = print ? mdb.m_out : mdb.m_null;
   2140 			va_copy(ap.val_valist, alist);
   2141 			iob_doprnt(out, fmt, &ap);
   2142 			va_end(alist);
   2143 			va_copy(alist, ap.val_valist);
   2144 			break;
   2145 		}
   2146 
   2147 		case MDB_TBL_FUNC:
   2148 			func = va_arg(alist, mdb_table_print_f *);
   2149 			arg = va_arg(alist, void *);
   2150 
   2151 			if (print)
   2152 				func(arg);
   2153 
   2154 			break;
   2155 
   2156 		default:
   2157 			warn("bad format type %x\n", type);
   2158 			break;
   2159 		}
   2160 	}
   2161 
   2162 	va_end(alist);
   2163 }
   2164