17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5a727d47aSjwadams * Common Development and Distribution License (the "License"). 6a727d47aSjwadams * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22a727d47aSjwadams * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 26cab8de14SBryan Cantrill /* 27*c8a3ee0eSBryan Cantrill * Copyright (c) 2019, Joyent, Inc. All rights reserved. 28218912f6SSteve Gonczi * Copyright (c) 2016 by Delphix. All rights reserved. 29cab8de14SBryan Cantrill */ 307c478bd9Sstevel@tonic-gate 317c478bd9Sstevel@tonic-gate /* 327c478bd9Sstevel@tonic-gate * MDB uses its own enhanced standard i/o mechanism for all input and output. 337c478bd9Sstevel@tonic-gate * This file provides the underpinnings of this mechanism, including the 347c478bd9Sstevel@tonic-gate * printf-style formatting code, the output pager, and APIs for raw input 357c478bd9Sstevel@tonic-gate * and output. This mechanism is used throughout the debugger for everything 367c478bd9Sstevel@tonic-gate * from simple sprintf and printf-style formatting, to input to the lexer 377c478bd9Sstevel@tonic-gate * and parser, to raw file i/o for reading ELF files. In general, we divide 387c478bd9Sstevel@tonic-gate * our i/o implementation into two parts: 397c478bd9Sstevel@tonic-gate * 407c478bd9Sstevel@tonic-gate * (1) An i/o buffer (mdb_iob_t) provides buffered read or write capabilities, 417c478bd9Sstevel@tonic-gate * as well as access to formatting and the ability to invoke a pager. The 427c478bd9Sstevel@tonic-gate * buffer is constructed explicitly for use in either reading or writing; it 437c478bd9Sstevel@tonic-gate * may not be used for both simultaneously. 447c478bd9Sstevel@tonic-gate * 457c478bd9Sstevel@tonic-gate * (2) Each i/o buffer is associated with an underlying i/o backend (mdb_io_t). 467c478bd9Sstevel@tonic-gate * The backend provides, through an ops-vector, equivalents for the standard 477c478bd9Sstevel@tonic-gate * read, write, lseek, ioctl, and close operations. In addition, the backend 487c478bd9Sstevel@tonic-gate * can provide an IOP_NAME entry point for returning a name for the backend, 497c478bd9Sstevel@tonic-gate * IOP_LINK and IOP_UNLINK entry points that are called when the backend is 507c478bd9Sstevel@tonic-gate * connected or disconnected from an mdb_iob_t, and an IOP_SETATTR entry point 517c478bd9Sstevel@tonic-gate * for manipulating terminal attributes. 527c478bd9Sstevel@tonic-gate * 537c478bd9Sstevel@tonic-gate * The i/o objects themselves are reference counted so that more than one i/o 547c478bd9Sstevel@tonic-gate * buffer may make use of the same i/o backend. In addition, each buffer 557c478bd9Sstevel@tonic-gate * provides the ability to push or pop backends to interpose on input or output 567c478bd9Sstevel@tonic-gate * behavior. We make use of this, for example, to implement interactive 577c478bd9Sstevel@tonic-gate * session logging. Normally, the stdout iob has a backend that is either 587c478bd9Sstevel@tonic-gate * file descriptor 1, or a terminal i/o backend associated with the tty. 597c478bd9Sstevel@tonic-gate * However, we can push a log i/o backend on top that multiplexes stdout to 607c478bd9Sstevel@tonic-gate * the original back-end and another backend that writes to a log file. The 617c478bd9Sstevel@tonic-gate * use of i/o backends is also used for simplifying tasks such as making 627c478bd9Sstevel@tonic-gate * lex and yacc read from strings for mdb_eval(), and making our ELF file 637c478bd9Sstevel@tonic-gate * processing code read executable "files" from a crash dump via kvm_uread. 647c478bd9Sstevel@tonic-gate * 657c478bd9Sstevel@tonic-gate * Additionally, the formatting code provides auto-wrap and indent facilities 667c478bd9Sstevel@tonic-gate * that are necessary for compatibility with adb macro formatting. In auto- 677c478bd9Sstevel@tonic-gate * wrap mode, the formatting code examines each new chunk of output to determine 687c478bd9Sstevel@tonic-gate * if it will fit on the current line. If not, instead of having the chunk 697c478bd9Sstevel@tonic-gate * divided between the current line of output and the next, the auto-wrap 707c478bd9Sstevel@tonic-gate * code will automatically output a newline, auto-indent the next line, 717c478bd9Sstevel@tonic-gate * and then continue. Auto-indent is implemented by simply prepending a number 727c478bd9Sstevel@tonic-gate * of blanks equal to iob_margin to the start of each line. The margin is 737c478bd9Sstevel@tonic-gate * inserted when the iob is created, and following each flush of the buffer. 747c478bd9Sstevel@tonic-gate */ 757c478bd9Sstevel@tonic-gate 767c478bd9Sstevel@tonic-gate #include <sys/types.h> 777c478bd9Sstevel@tonic-gate #include <sys/termios.h> 787c478bd9Sstevel@tonic-gate #include <stdarg.h> 797c478bd9Sstevel@tonic-gate #include <arpa/inet.h> 807c478bd9Sstevel@tonic-gate #include <sys/socket.h> 817c478bd9Sstevel@tonic-gate 827c478bd9Sstevel@tonic-gate #include <mdb/mdb_types.h> 837c478bd9Sstevel@tonic-gate #include <mdb/mdb_argvec.h> 847c478bd9Sstevel@tonic-gate #include <mdb/mdb_stdlib.h> 857c478bd9Sstevel@tonic-gate #include <mdb/mdb_string.h> 867c478bd9Sstevel@tonic-gate #include <mdb/mdb_target.h> 877c478bd9Sstevel@tonic-gate #include <mdb/mdb_signal.h> 887c478bd9Sstevel@tonic-gate #include <mdb/mdb_debug.h> 897c478bd9Sstevel@tonic-gate #include <mdb/mdb_io_impl.h> 907c478bd9Sstevel@tonic-gate #include <mdb/mdb_modapi.h> 917c478bd9Sstevel@tonic-gate #include <mdb/mdb_demangle.h> 927c478bd9Sstevel@tonic-gate #include <mdb/mdb_err.h> 937c478bd9Sstevel@tonic-gate #include <mdb/mdb_nv.h> 947c478bd9Sstevel@tonic-gate #include <mdb/mdb_frame.h> 957c478bd9Sstevel@tonic-gate #include <mdb/mdb_lex.h> 967c478bd9Sstevel@tonic-gate #include <mdb/mdb.h> 977c478bd9Sstevel@tonic-gate 987c478bd9Sstevel@tonic-gate /* 997c478bd9Sstevel@tonic-gate * Define list of possible integer sizes for conversion routines: 1007c478bd9Sstevel@tonic-gate */ 1017c478bd9Sstevel@tonic-gate typedef enum { 1027c478bd9Sstevel@tonic-gate SZ_SHORT, /* format %h? */ 1037c478bd9Sstevel@tonic-gate SZ_INT, /* format %? */ 1047c478bd9Sstevel@tonic-gate SZ_LONG, /* format %l? */ 1057c478bd9Sstevel@tonic-gate SZ_LONGLONG /* format %ll? */ 1067c478bd9Sstevel@tonic-gate } intsize_t; 1077c478bd9Sstevel@tonic-gate 1087c478bd9Sstevel@tonic-gate /* 1097c478bd9Sstevel@tonic-gate * The iob snprintf family of functions makes use of a special "sprintf 1107c478bd9Sstevel@tonic-gate * buffer" i/o backend in order to provide the appropriate snprintf semantics. 1117c478bd9Sstevel@tonic-gate * This structure is maintained as the backend-specific private storage, 1127c478bd9Sstevel@tonic-gate * and its use is described in more detail below (see spbuf_write()). 1137c478bd9Sstevel@tonic-gate */ 1147c478bd9Sstevel@tonic-gate typedef struct { 1157c478bd9Sstevel@tonic-gate char *spb_buf; /* pointer to underlying buffer */ 1167c478bd9Sstevel@tonic-gate size_t spb_bufsiz; /* length of underlying buffer */ 1177c478bd9Sstevel@tonic-gate size_t spb_total; /* total of all bytes passed via IOP_WRITE */ 1187c478bd9Sstevel@tonic-gate } spbuf_t; 1197c478bd9Sstevel@tonic-gate 1207c478bd9Sstevel@tonic-gate /* 1217c478bd9Sstevel@tonic-gate * Define VA_ARG macro for grabbing the next datum to format for the printf 1227c478bd9Sstevel@tonic-gate * family of functions. We use VA_ARG so that we can support two kinds of 1237c478bd9Sstevel@tonic-gate * argument lists: the va_list type supplied by <stdarg.h> used for printf and 1247c478bd9Sstevel@tonic-gate * vprintf, and an array of mdb_arg_t structures, which we expect will be 1257c478bd9Sstevel@tonic-gate * either type STRING or IMMEDIATE. The vec_arg function takes care of 1267c478bd9Sstevel@tonic-gate * handling the mdb_arg_t case. 1277c478bd9Sstevel@tonic-gate */ 1287c478bd9Sstevel@tonic-gate 1297c478bd9Sstevel@tonic-gate typedef enum { 1307c478bd9Sstevel@tonic-gate VAT_VARARGS, /* va_list is a va_list */ 1317c478bd9Sstevel@tonic-gate VAT_ARGVEC /* va_list is a const mdb_arg_t[] in disguise */ 1327c478bd9Sstevel@tonic-gate } vatype_t; 1337c478bd9Sstevel@tonic-gate 1347c478bd9Sstevel@tonic-gate typedef struct { 1357c478bd9Sstevel@tonic-gate vatype_t val_type; 1367c478bd9Sstevel@tonic-gate union { 1377c478bd9Sstevel@tonic-gate va_list _val_valist; 1387c478bd9Sstevel@tonic-gate const mdb_arg_t *_val_argv; 1397c478bd9Sstevel@tonic-gate } _val_u; 1407c478bd9Sstevel@tonic-gate } varglist_t; 1417c478bd9Sstevel@tonic-gate 1427c478bd9Sstevel@tonic-gate #define val_valist _val_u._val_valist 1437c478bd9Sstevel@tonic-gate #define val_argv _val_u._val_argv 1447c478bd9Sstevel@tonic-gate 1457c478bd9Sstevel@tonic-gate #define VA_ARG(ap, type) ((ap->val_type == VAT_VARARGS) ? \ 1467c478bd9Sstevel@tonic-gate va_arg(ap->val_valist, type) : (type)vec_arg(&ap->val_argv)) 1477c478bd9Sstevel@tonic-gate #define VA_PTRARG(ap) ((ap->val_type == VAT_VARARGS) ? \ 1487c478bd9Sstevel@tonic-gate (void *)va_arg(ap->val_valist, uintptr_t) : \ 1497c478bd9Sstevel@tonic-gate (void *)(uintptr_t)vec_arg(&ap->val_argv)) 1507c478bd9Sstevel@tonic-gate 1517c478bd9Sstevel@tonic-gate /* 1527c478bd9Sstevel@tonic-gate * Define macro for converting char constant to Ctrl-char equivalent: 1537c478bd9Sstevel@tonic-gate */ 1547c478bd9Sstevel@tonic-gate #ifndef CTRL 1557c478bd9Sstevel@tonic-gate #define CTRL(c) ((c) & 0x01f) 1567c478bd9Sstevel@tonic-gate #endif 1577c478bd9Sstevel@tonic-gate 158f76ff24cSBryan Cantrill #define IOB_AUTOWRAP(iob) \ 159f76ff24cSBryan Cantrill ((mdb.m_flags & MDB_FL_AUTOWRAP) && \ 160f76ff24cSBryan Cantrill ((iob)->iob_flags & MDB_IOB_AUTOWRAP)) 161f76ff24cSBryan Cantrill 1627c478bd9Sstevel@tonic-gate /* 1637c478bd9Sstevel@tonic-gate * Define macro for determining if we should automatically wrap to the next 1647c478bd9Sstevel@tonic-gate * line of output, based on the amount of consumed buffer space and the 165*c8a3ee0eSBryan Cantrill * specified size of the next thing to be inserted (n) -- being careful to 166*c8a3ee0eSBryan Cantrill * not force a spurious wrap if we're autoindented and already at the margin. 1677c478bd9Sstevel@tonic-gate */ 1687c478bd9Sstevel@tonic-gate #define IOB_WRAPNOW(iob, n) \ 169f76ff24cSBryan Cantrill (IOB_AUTOWRAP(iob) && (iob)->iob_nbytes != 0 && \ 170*c8a3ee0eSBryan Cantrill ((n) + (iob)->iob_nbytes > (iob)->iob_cols) && \ 171*c8a3ee0eSBryan Cantrill !(((iob)->iob_flags & MDB_IOB_INDENT) && \ 172*c8a3ee0eSBryan Cantrill (iob)->iob_nbytes == (iob)->iob_margin)) 1737c478bd9Sstevel@tonic-gate 1747c478bd9Sstevel@tonic-gate /* 1757c478bd9Sstevel@tonic-gate * Define prompt string and string to erase prompt string for iob_pager 1767c478bd9Sstevel@tonic-gate * function, which is invoked if the pager is enabled on an i/o buffer 1777c478bd9Sstevel@tonic-gate * and we're about to print a line which would be the last on the screen. 1787c478bd9Sstevel@tonic-gate */ 1797c478bd9Sstevel@tonic-gate 1807c478bd9Sstevel@tonic-gate static const char io_prompt[] = ">> More [<space>, <cr>, q, n, c, a] ? "; 1817c478bd9Sstevel@tonic-gate static const char io_perase[] = " "; 1827c478bd9Sstevel@tonic-gate 1837c478bd9Sstevel@tonic-gate static const char io_pbcksp[] = 1847c478bd9Sstevel@tonic-gate /*CSTYLED*/ 1857c478bd9Sstevel@tonic-gate "\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"; 1867c478bd9Sstevel@tonic-gate 1877c478bd9Sstevel@tonic-gate static const size_t io_promptlen = sizeof (io_prompt) - 1; 1887c478bd9Sstevel@tonic-gate static const size_t io_peraselen = sizeof (io_perase) - 1; 1897c478bd9Sstevel@tonic-gate static const size_t io_pbcksplen = sizeof (io_pbcksp) - 1; 1907c478bd9Sstevel@tonic-gate 1917c478bd9Sstevel@tonic-gate static ssize_t 1927c478bd9Sstevel@tonic-gate iob_write(mdb_iob_t *iob, mdb_io_t *io, const void *buf, size_t n) 1937c478bd9Sstevel@tonic-gate { 1947c478bd9Sstevel@tonic-gate ssize_t resid = n; 1957c478bd9Sstevel@tonic-gate ssize_t len; 1967c478bd9Sstevel@tonic-gate 1977c478bd9Sstevel@tonic-gate while (resid != 0) { 1987c478bd9Sstevel@tonic-gate if ((len = IOP_WRITE(io, buf, resid)) <= 0) 1997c478bd9Sstevel@tonic-gate break; 2007c478bd9Sstevel@tonic-gate 2017c478bd9Sstevel@tonic-gate buf = (char *)buf + len; 2027c478bd9Sstevel@tonic-gate resid -= len; 2037c478bd9Sstevel@tonic-gate } 2047c478bd9Sstevel@tonic-gate 2057c478bd9Sstevel@tonic-gate /* 2067c478bd9Sstevel@tonic-gate * Note that if we had a partial write before an error, we still want 2077c478bd9Sstevel@tonic-gate * to return the fact something was written. The caller will get an 2087c478bd9Sstevel@tonic-gate * error next time it tries to write anything. 2097c478bd9Sstevel@tonic-gate */ 2107c478bd9Sstevel@tonic-gate if (resid == n && n != 0) { 2117c478bd9Sstevel@tonic-gate iob->iob_flags |= MDB_IOB_ERR; 2127c478bd9Sstevel@tonic-gate return (-1); 2137c478bd9Sstevel@tonic-gate } 2147c478bd9Sstevel@tonic-gate 2157c478bd9Sstevel@tonic-gate return (n - resid); 2167c478bd9Sstevel@tonic-gate } 2177c478bd9Sstevel@tonic-gate 2187c478bd9Sstevel@tonic-gate static ssize_t 2197c478bd9Sstevel@tonic-gate iob_read(mdb_iob_t *iob, mdb_io_t *io) 2207c478bd9Sstevel@tonic-gate { 2217c478bd9Sstevel@tonic-gate ssize_t len; 2227c478bd9Sstevel@tonic-gate 2237c478bd9Sstevel@tonic-gate ASSERT(iob->iob_nbytes == 0); 2247c478bd9Sstevel@tonic-gate len = IOP_READ(io, iob->iob_buf, iob->iob_bufsiz); 2257c478bd9Sstevel@tonic-gate iob->iob_bufp = &iob->iob_buf[0]; 2267c478bd9Sstevel@tonic-gate 2277c478bd9Sstevel@tonic-gate switch (len) { 2287c478bd9Sstevel@tonic-gate case -1: 2297c478bd9Sstevel@tonic-gate iob->iob_flags |= MDB_IOB_ERR; 2307c478bd9Sstevel@tonic-gate break; 2317c478bd9Sstevel@tonic-gate case 0: 2327c478bd9Sstevel@tonic-gate iob->iob_flags |= MDB_IOB_EOF; 2337c478bd9Sstevel@tonic-gate break; 2347c478bd9Sstevel@tonic-gate default: 2357c478bd9Sstevel@tonic-gate iob->iob_nbytes = len; 2367c478bd9Sstevel@tonic-gate } 2377c478bd9Sstevel@tonic-gate 2387c478bd9Sstevel@tonic-gate return (len); 2397c478bd9Sstevel@tonic-gate } 2407c478bd9Sstevel@tonic-gate 2417c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 2427c478bd9Sstevel@tonic-gate static void 2437c478bd9Sstevel@tonic-gate iob_winch(int sig, siginfo_t *sip, ucontext_t *ucp, void *data) 2447c478bd9Sstevel@tonic-gate { 2457c478bd9Sstevel@tonic-gate siglongjmp(*((sigjmp_buf *)data), sig); 2467c478bd9Sstevel@tonic-gate } 2477c478bd9Sstevel@tonic-gate 2487c478bd9Sstevel@tonic-gate static int 2497c478bd9Sstevel@tonic-gate iob_pager(mdb_iob_t *iob) 2507c478bd9Sstevel@tonic-gate { 2517c478bd9Sstevel@tonic-gate int status = 0; 2527c478bd9Sstevel@tonic-gate sigjmp_buf env; 2537c478bd9Sstevel@tonic-gate uchar_t c; 2547c478bd9Sstevel@tonic-gate 2557c478bd9Sstevel@tonic-gate mdb_signal_f *termio_winch; 2567c478bd9Sstevel@tonic-gate void *termio_data; 2577c478bd9Sstevel@tonic-gate size_t old_rows; 2587c478bd9Sstevel@tonic-gate 2597c478bd9Sstevel@tonic-gate if (iob->iob_pgp == NULL || (iob->iob_flags & MDB_IOB_PGCONT)) 2607c478bd9Sstevel@tonic-gate return (0); 2617c478bd9Sstevel@tonic-gate 2627c478bd9Sstevel@tonic-gate termio_winch = mdb_signal_gethandler(SIGWINCH, &termio_data); 2637c478bd9Sstevel@tonic-gate (void) mdb_signal_sethandler(SIGWINCH, iob_winch, &env); 2647c478bd9Sstevel@tonic-gate 2657c478bd9Sstevel@tonic-gate if (sigsetjmp(env, 1) != 0) { 2667c478bd9Sstevel@tonic-gate /* 2677c478bd9Sstevel@tonic-gate * Reset the cursor back to column zero before printing a new 2687c478bd9Sstevel@tonic-gate * prompt, since its position is unreliable after a SIGWINCH. 2697c478bd9Sstevel@tonic-gate */ 2707c478bd9Sstevel@tonic-gate (void) iob_write(iob, iob->iob_pgp, "\r", sizeof (char)); 2717c478bd9Sstevel@tonic-gate old_rows = iob->iob_rows; 2727c478bd9Sstevel@tonic-gate 2737c478bd9Sstevel@tonic-gate /* 2747c478bd9Sstevel@tonic-gate * If an existing SIGWINCH handler was present, call it. We 2757c478bd9Sstevel@tonic-gate * expect that this will be termio: the handler will read the 2767c478bd9Sstevel@tonic-gate * new window size, and then resize this iob appropriately. 2777c478bd9Sstevel@tonic-gate */ 2787c478bd9Sstevel@tonic-gate if (termio_winch != (mdb_signal_f *)NULL) 2797c478bd9Sstevel@tonic-gate termio_winch(SIGWINCH, NULL, NULL, termio_data); 2807c478bd9Sstevel@tonic-gate 2817c478bd9Sstevel@tonic-gate /* 2827c478bd9Sstevel@tonic-gate * If the window has increased in size, we treat this like a 2837c478bd9Sstevel@tonic-gate * request to fill out the new remainder of the page. 2847c478bd9Sstevel@tonic-gate */ 2857c478bd9Sstevel@tonic-gate if (iob->iob_rows > old_rows) { 2867c478bd9Sstevel@tonic-gate iob->iob_flags &= ~MDB_IOB_PGSINGLE; 2877c478bd9Sstevel@tonic-gate iob->iob_nlines = old_rows; 2887c478bd9Sstevel@tonic-gate status = 0; 2897c478bd9Sstevel@tonic-gate goto winch; 2907c478bd9Sstevel@tonic-gate } 2917c478bd9Sstevel@tonic-gate } 2927c478bd9Sstevel@tonic-gate 2937c478bd9Sstevel@tonic-gate (void) iob_write(iob, iob->iob_pgp, io_prompt, io_promptlen); 2947c478bd9Sstevel@tonic-gate 2957c478bd9Sstevel@tonic-gate for (;;) { 2967c478bd9Sstevel@tonic-gate if (IOP_READ(iob->iob_pgp, &c, sizeof (c)) != sizeof (c)) { 2977c478bd9Sstevel@tonic-gate status = MDB_ERR_PAGER; 2987c478bd9Sstevel@tonic-gate break; 2997c478bd9Sstevel@tonic-gate } 3007c478bd9Sstevel@tonic-gate 3017c478bd9Sstevel@tonic-gate switch (c) { 3027c478bd9Sstevel@tonic-gate case 'N': 3037c478bd9Sstevel@tonic-gate case 'n': 3047c478bd9Sstevel@tonic-gate case '\n': 3057c478bd9Sstevel@tonic-gate case '\r': 3067c478bd9Sstevel@tonic-gate iob->iob_flags |= MDB_IOB_PGSINGLE; 3077c478bd9Sstevel@tonic-gate goto done; 3087c478bd9Sstevel@tonic-gate 3097c478bd9Sstevel@tonic-gate case CTRL('c'): 3107c478bd9Sstevel@tonic-gate case CTRL('\\'): 3117c478bd9Sstevel@tonic-gate case 'Q': 3127c478bd9Sstevel@tonic-gate case 'q': 3137c478bd9Sstevel@tonic-gate mdb_iob_discard(iob); 3147c478bd9Sstevel@tonic-gate status = MDB_ERR_PAGER; 3157c478bd9Sstevel@tonic-gate goto done; 3167c478bd9Sstevel@tonic-gate 3177c478bd9Sstevel@tonic-gate case 'A': 3187c478bd9Sstevel@tonic-gate case 'a': 3197c478bd9Sstevel@tonic-gate mdb_iob_discard(iob); 3207c478bd9Sstevel@tonic-gate status = MDB_ERR_ABORT; 3217c478bd9Sstevel@tonic-gate goto done; 3227c478bd9Sstevel@tonic-gate 3237c478bd9Sstevel@tonic-gate case 'C': 3247c478bd9Sstevel@tonic-gate case 'c': 3257c478bd9Sstevel@tonic-gate iob->iob_flags |= MDB_IOB_PGCONT; 3267c478bd9Sstevel@tonic-gate /*FALLTHRU*/ 3277c478bd9Sstevel@tonic-gate 3287c478bd9Sstevel@tonic-gate case ' ': 3297c478bd9Sstevel@tonic-gate iob->iob_flags &= ~MDB_IOB_PGSINGLE; 3307c478bd9Sstevel@tonic-gate goto done; 3317c478bd9Sstevel@tonic-gate } 3327c478bd9Sstevel@tonic-gate } 3337c478bd9Sstevel@tonic-gate 3347c478bd9Sstevel@tonic-gate done: 3357c478bd9Sstevel@tonic-gate (void) iob_write(iob, iob->iob_pgp, io_pbcksp, io_pbcksplen); 3367c478bd9Sstevel@tonic-gate winch: 3377c478bd9Sstevel@tonic-gate (void) iob_write(iob, iob->iob_pgp, io_perase, io_peraselen); 3387c478bd9Sstevel@tonic-gate (void) iob_write(iob, iob->iob_pgp, io_pbcksp, io_pbcksplen); 3397c478bd9Sstevel@tonic-gate (void) mdb_signal_sethandler(SIGWINCH, termio_winch, termio_data); 3407c478bd9Sstevel@tonic-gate 3417c478bd9Sstevel@tonic-gate if ((iob->iob_flags & MDB_IOB_ERR) && status == 0) 3427c478bd9Sstevel@tonic-gate status = MDB_ERR_OUTPUT; 3437c478bd9Sstevel@tonic-gate 3447c478bd9Sstevel@tonic-gate return (status); 3457c478bd9Sstevel@tonic-gate } 3467c478bd9Sstevel@tonic-gate 3477c478bd9Sstevel@tonic-gate static void 3487c478bd9Sstevel@tonic-gate iob_indent(mdb_iob_t *iob) 3497c478bd9Sstevel@tonic-gate { 3507c478bd9Sstevel@tonic-gate if (iob->iob_nbytes == 0 && iob->iob_margin != 0 && 3517c478bd9Sstevel@tonic-gate (iob->iob_flags & MDB_IOB_INDENT)) { 3527c478bd9Sstevel@tonic-gate size_t i; 3537c478bd9Sstevel@tonic-gate 3547c478bd9Sstevel@tonic-gate ASSERT(iob->iob_margin < iob->iob_cols); 3557c478bd9Sstevel@tonic-gate ASSERT(iob->iob_bufp == iob->iob_buf); 3567c478bd9Sstevel@tonic-gate 3577c478bd9Sstevel@tonic-gate for (i = 0; i < iob->iob_margin; i++) 3587c478bd9Sstevel@tonic-gate *iob->iob_bufp++ = ' '; 3597c478bd9Sstevel@tonic-gate 3607c478bd9Sstevel@tonic-gate iob->iob_nbytes = iob->iob_margin; 3617c478bd9Sstevel@tonic-gate } 3627c478bd9Sstevel@tonic-gate } 3637c478bd9Sstevel@tonic-gate 3647c478bd9Sstevel@tonic-gate static void 3657c478bd9Sstevel@tonic-gate iob_unindent(mdb_iob_t *iob) 3667c478bd9Sstevel@tonic-gate { 3677c478bd9Sstevel@tonic-gate if (iob->iob_nbytes != 0 && iob->iob_nbytes == iob->iob_margin) { 3687c478bd9Sstevel@tonic-gate const char *p = iob->iob_buf; 3697c478bd9Sstevel@tonic-gate 3707c478bd9Sstevel@tonic-gate while (p < &iob->iob_buf[iob->iob_margin]) { 3717c478bd9Sstevel@tonic-gate if (*p++ != ' ') 3727c478bd9Sstevel@tonic-gate return; 3737c478bd9Sstevel@tonic-gate } 3747c478bd9Sstevel@tonic-gate 3757c478bd9Sstevel@tonic-gate iob->iob_bufp = &iob->iob_buf[0]; 3767c478bd9Sstevel@tonic-gate iob->iob_nbytes = 0; 3777c478bd9Sstevel@tonic-gate } 3787c478bd9Sstevel@tonic-gate } 3797c478bd9Sstevel@tonic-gate 3807c478bd9Sstevel@tonic-gate mdb_iob_t * 3817c478bd9Sstevel@tonic-gate mdb_iob_create(mdb_io_t *io, uint_t flags) 3827c478bd9Sstevel@tonic-gate { 3837c478bd9Sstevel@tonic-gate mdb_iob_t *iob = mdb_alloc(sizeof (mdb_iob_t), UM_SLEEP); 3847c478bd9Sstevel@tonic-gate 3857c478bd9Sstevel@tonic-gate iob->iob_buf = mdb_alloc(BUFSIZ, UM_SLEEP); 3867c478bd9Sstevel@tonic-gate iob->iob_bufsiz = BUFSIZ; 3877c478bd9Sstevel@tonic-gate iob->iob_bufp = &iob->iob_buf[0]; 3887c478bd9Sstevel@tonic-gate iob->iob_nbytes = 0; 3897c478bd9Sstevel@tonic-gate iob->iob_nlines = 0; 3907c478bd9Sstevel@tonic-gate iob->iob_lineno = 1; 3917c478bd9Sstevel@tonic-gate iob->iob_rows = MDB_IOB_DEFROWS; 3927c478bd9Sstevel@tonic-gate iob->iob_cols = MDB_IOB_DEFCOLS; 3937c478bd9Sstevel@tonic-gate iob->iob_tabstop = MDB_IOB_DEFTAB; 3947c478bd9Sstevel@tonic-gate iob->iob_margin = MDB_IOB_DEFMARGIN; 3957c478bd9Sstevel@tonic-gate iob->iob_flags = flags & ~(MDB_IOB_EOF|MDB_IOB_ERR) | MDB_IOB_AUTOWRAP; 3967c478bd9Sstevel@tonic-gate iob->iob_iop = mdb_io_hold(io); 3977c478bd9Sstevel@tonic-gate iob->iob_pgp = NULL; 3987c478bd9Sstevel@tonic-gate iob->iob_next = NULL; 3997c478bd9Sstevel@tonic-gate 4007c478bd9Sstevel@tonic-gate IOP_LINK(io, iob); 4017c478bd9Sstevel@tonic-gate iob_indent(iob); 4027c478bd9Sstevel@tonic-gate return (iob); 4037c478bd9Sstevel@tonic-gate } 4047c478bd9Sstevel@tonic-gate 4057c478bd9Sstevel@tonic-gate void 4067c478bd9Sstevel@tonic-gate mdb_iob_pipe(mdb_iob_t **iobs, mdb_iobsvc_f *rdsvc, mdb_iobsvc_f *wrsvc) 4077c478bd9Sstevel@tonic-gate { 4087c478bd9Sstevel@tonic-gate mdb_io_t *pio = mdb_pipeio_create(rdsvc, wrsvc); 4097c478bd9Sstevel@tonic-gate int i; 4107c478bd9Sstevel@tonic-gate 4117c478bd9Sstevel@tonic-gate iobs[0] = mdb_iob_create(pio, MDB_IOB_RDONLY); 4127c478bd9Sstevel@tonic-gate iobs[1] = mdb_iob_create(pio, MDB_IOB_WRONLY); 4137c478bd9Sstevel@tonic-gate 4147c478bd9Sstevel@tonic-gate for (i = 0; i < 2; i++) { 4157c478bd9Sstevel@tonic-gate iobs[i]->iob_flags &= ~MDB_IOB_AUTOWRAP; 4167c478bd9Sstevel@tonic-gate iobs[i]->iob_cols = iobs[i]->iob_bufsiz; 4177c478bd9Sstevel@tonic-gate } 4187c478bd9Sstevel@tonic-gate } 4197c478bd9Sstevel@tonic-gate 4207c478bd9Sstevel@tonic-gate void 4217c478bd9Sstevel@tonic-gate mdb_iob_destroy(mdb_iob_t *iob) 4227c478bd9Sstevel@tonic-gate { 4237c478bd9Sstevel@tonic-gate /* 424f76ff24cSBryan Cantrill * Don't flush a pipe, since it may cause a context switch when the 4257c478bd9Sstevel@tonic-gate * other side has already been destroyed. 4267c478bd9Sstevel@tonic-gate */ 4277c478bd9Sstevel@tonic-gate if (!mdb_iob_isapipe(iob)) 4287c478bd9Sstevel@tonic-gate mdb_iob_flush(iob); 4297c478bd9Sstevel@tonic-gate 4307c478bd9Sstevel@tonic-gate if (iob->iob_pgp != NULL) 4317c478bd9Sstevel@tonic-gate mdb_io_rele(iob->iob_pgp); 4327c478bd9Sstevel@tonic-gate 4337c478bd9Sstevel@tonic-gate while (iob->iob_iop != NULL) { 4347c478bd9Sstevel@tonic-gate IOP_UNLINK(iob->iob_iop, iob); 4357c478bd9Sstevel@tonic-gate (void) mdb_iob_pop_io(iob); 4367c478bd9Sstevel@tonic-gate } 4377c478bd9Sstevel@tonic-gate 4387c478bd9Sstevel@tonic-gate mdb_free(iob->iob_buf, iob->iob_bufsiz); 4397c478bd9Sstevel@tonic-gate mdb_free(iob, sizeof (mdb_iob_t)); 4407c478bd9Sstevel@tonic-gate } 4417c478bd9Sstevel@tonic-gate 4427c478bd9Sstevel@tonic-gate void 4437c478bd9Sstevel@tonic-gate mdb_iob_discard(mdb_iob_t *iob) 4447c478bd9Sstevel@tonic-gate { 4457c478bd9Sstevel@tonic-gate iob->iob_bufp = &iob->iob_buf[0]; 4467c478bd9Sstevel@tonic-gate iob->iob_nbytes = 0; 4477c478bd9Sstevel@tonic-gate } 4487c478bd9Sstevel@tonic-gate 4497c478bd9Sstevel@tonic-gate void 4507c478bd9Sstevel@tonic-gate mdb_iob_flush(mdb_iob_t *iob) 4517c478bd9Sstevel@tonic-gate { 4527c478bd9Sstevel@tonic-gate int pgerr = 0; 4537c478bd9Sstevel@tonic-gate 4547c478bd9Sstevel@tonic-gate if (iob->iob_nbytes == 0) 4557c478bd9Sstevel@tonic-gate return; /* Nothing to do if buffer is empty */ 4567c478bd9Sstevel@tonic-gate 4577c478bd9Sstevel@tonic-gate if (iob->iob_flags & MDB_IOB_WRONLY) { 4587c478bd9Sstevel@tonic-gate if (iob->iob_flags & MDB_IOB_PGSINGLE) { 4597c478bd9Sstevel@tonic-gate iob->iob_flags &= ~MDB_IOB_PGSINGLE; 4607c478bd9Sstevel@tonic-gate iob->iob_nlines = 0; 4617c478bd9Sstevel@tonic-gate pgerr = iob_pager(iob); 4627c478bd9Sstevel@tonic-gate 4637c478bd9Sstevel@tonic-gate } else if (iob->iob_nlines >= iob->iob_rows - 1) { 4647c478bd9Sstevel@tonic-gate iob->iob_nlines = 0; 4657c478bd9Sstevel@tonic-gate if (iob->iob_flags & MDB_IOB_PGENABLE) 4667c478bd9Sstevel@tonic-gate pgerr = iob_pager(iob); 4677c478bd9Sstevel@tonic-gate } 4687c478bd9Sstevel@tonic-gate 4697c478bd9Sstevel@tonic-gate if (pgerr == 0) { 4707c478bd9Sstevel@tonic-gate /* 4717c478bd9Sstevel@tonic-gate * We only jump out of the dcmd on error if the iob is 4727c478bd9Sstevel@tonic-gate * m_out. Presumably, if a dcmd has opened a special 4737c478bd9Sstevel@tonic-gate * file and is writing to it, it will handle errors 4747c478bd9Sstevel@tonic-gate * properly. 4757c478bd9Sstevel@tonic-gate */ 4767c478bd9Sstevel@tonic-gate if (iob_write(iob, iob->iob_iop, iob->iob_buf, 4777c478bd9Sstevel@tonic-gate iob->iob_nbytes) < 0 && iob == mdb.m_out) 4787c478bd9Sstevel@tonic-gate pgerr = MDB_ERR_OUTPUT; 4797c478bd9Sstevel@tonic-gate iob->iob_nlines++; 4807c478bd9Sstevel@tonic-gate } 4817c478bd9Sstevel@tonic-gate } 4827c478bd9Sstevel@tonic-gate 4837c478bd9Sstevel@tonic-gate iob->iob_bufp = &iob->iob_buf[0]; 4847c478bd9Sstevel@tonic-gate iob->iob_nbytes = 0; 4857c478bd9Sstevel@tonic-gate iob_indent(iob); 4867c478bd9Sstevel@tonic-gate 4877c478bd9Sstevel@tonic-gate if (pgerr) 4887c478bd9Sstevel@tonic-gate longjmp(mdb.m_frame->f_pcb, pgerr); 4897c478bd9Sstevel@tonic-gate } 4907c478bd9Sstevel@tonic-gate 4917c478bd9Sstevel@tonic-gate void 4927c478bd9Sstevel@tonic-gate mdb_iob_nlflush(mdb_iob_t *iob) 4937c478bd9Sstevel@tonic-gate { 4947c478bd9Sstevel@tonic-gate iob_unindent(iob); 4957c478bd9Sstevel@tonic-gate 4967c478bd9Sstevel@tonic-gate if (iob->iob_nbytes != 0) 4977c478bd9Sstevel@tonic-gate mdb_iob_nl(iob); 4987c478bd9Sstevel@tonic-gate else 4997c478bd9Sstevel@tonic-gate iob_indent(iob); 5007c478bd9Sstevel@tonic-gate } 5017c478bd9Sstevel@tonic-gate 5027c478bd9Sstevel@tonic-gate void 5037c478bd9Sstevel@tonic-gate mdb_iob_push_io(mdb_iob_t *iob, mdb_io_t *io) 5047c478bd9Sstevel@tonic-gate { 5057c478bd9Sstevel@tonic-gate ASSERT(io->io_next == NULL); 5067c478bd9Sstevel@tonic-gate 5077c478bd9Sstevel@tonic-gate io->io_next = iob->iob_iop; 5087c478bd9Sstevel@tonic-gate iob->iob_iop = mdb_io_hold(io); 5097c478bd9Sstevel@tonic-gate } 5107c478bd9Sstevel@tonic-gate 5117c478bd9Sstevel@tonic-gate mdb_io_t * 5127c478bd9Sstevel@tonic-gate mdb_iob_pop_io(mdb_iob_t *iob) 5137c478bd9Sstevel@tonic-gate { 5147c478bd9Sstevel@tonic-gate mdb_io_t *io = iob->iob_iop; 5157c478bd9Sstevel@tonic-gate 5167c478bd9Sstevel@tonic-gate if (io != NULL) { 5177c478bd9Sstevel@tonic-gate iob->iob_iop = io->io_next; 5187c478bd9Sstevel@tonic-gate io->io_next = NULL; 5197c478bd9Sstevel@tonic-gate mdb_io_rele(io); 5207c478bd9Sstevel@tonic-gate } 5217c478bd9Sstevel@tonic-gate 5227c478bd9Sstevel@tonic-gate return (io); 5237c478bd9Sstevel@tonic-gate } 5247c478bd9Sstevel@tonic-gate 5257c478bd9Sstevel@tonic-gate void 5267c478bd9Sstevel@tonic-gate mdb_iob_resize(mdb_iob_t *iob, size_t rows, size_t cols) 5277c478bd9Sstevel@tonic-gate { 5287c478bd9Sstevel@tonic-gate if (cols > iob->iob_bufsiz) 5297c478bd9Sstevel@tonic-gate iob->iob_cols = iob->iob_bufsiz; 5307c478bd9Sstevel@tonic-gate else 5317c478bd9Sstevel@tonic-gate iob->iob_cols = cols != 0 ? cols : MDB_IOB_DEFCOLS; 5327c478bd9Sstevel@tonic-gate 5337c478bd9Sstevel@tonic-gate iob->iob_rows = rows != 0 ? rows : MDB_IOB_DEFROWS; 5347c478bd9Sstevel@tonic-gate } 5357c478bd9Sstevel@tonic-gate 5367c478bd9Sstevel@tonic-gate void 5377c478bd9Sstevel@tonic-gate mdb_iob_setpager(mdb_iob_t *iob, mdb_io_t *pgio) 5387c478bd9Sstevel@tonic-gate { 5397c478bd9Sstevel@tonic-gate struct winsize winsz; 5407c478bd9Sstevel@tonic-gate 5417c478bd9Sstevel@tonic-gate if (iob->iob_pgp != NULL) { 5427c478bd9Sstevel@tonic-gate IOP_UNLINK(iob->iob_pgp, iob); 5437c478bd9Sstevel@tonic-gate mdb_io_rele(iob->iob_pgp); 5447c478bd9Sstevel@tonic-gate } 5457c478bd9Sstevel@tonic-gate 5467c478bd9Sstevel@tonic-gate iob->iob_flags |= MDB_IOB_PGENABLE; 5477c478bd9Sstevel@tonic-gate iob->iob_flags &= ~(MDB_IOB_PGSINGLE | MDB_IOB_PGCONT); 5487c478bd9Sstevel@tonic-gate iob->iob_pgp = mdb_io_hold(pgio); 5497c478bd9Sstevel@tonic-gate 5507c478bd9Sstevel@tonic-gate IOP_LINK(iob->iob_pgp, iob); 5517c478bd9Sstevel@tonic-gate 5527c478bd9Sstevel@tonic-gate if (IOP_CTL(pgio, TIOCGWINSZ, &winsz) == 0) 5537c478bd9Sstevel@tonic-gate mdb_iob_resize(iob, (size_t)winsz.ws_row, (size_t)winsz.ws_col); 5547c478bd9Sstevel@tonic-gate } 5557c478bd9Sstevel@tonic-gate 5567c478bd9Sstevel@tonic-gate void 5577c478bd9Sstevel@tonic-gate mdb_iob_tabstop(mdb_iob_t *iob, size_t tabstop) 5587c478bd9Sstevel@tonic-gate { 5597c478bd9Sstevel@tonic-gate iob->iob_tabstop = MIN(tabstop, iob->iob_cols - 1); 5607c478bd9Sstevel@tonic-gate } 5617c478bd9Sstevel@tonic-gate 5627c478bd9Sstevel@tonic-gate void 5637c478bd9Sstevel@tonic-gate mdb_iob_margin(mdb_iob_t *iob, size_t margin) 5647c478bd9Sstevel@tonic-gate { 5657c478bd9Sstevel@tonic-gate iob_unindent(iob); 5667c478bd9Sstevel@tonic-gate iob->iob_margin = MIN(margin, iob->iob_cols - 1); 5677c478bd9Sstevel@tonic-gate iob_indent(iob); 5687c478bd9Sstevel@tonic-gate } 5697c478bd9Sstevel@tonic-gate 5707c478bd9Sstevel@tonic-gate void 5717c478bd9Sstevel@tonic-gate mdb_iob_setbuf(mdb_iob_t *iob, void *buf, size_t bufsiz) 5727c478bd9Sstevel@tonic-gate { 5737c478bd9Sstevel@tonic-gate ASSERT(buf != NULL && bufsiz != 0); 5747c478bd9Sstevel@tonic-gate 5757c478bd9Sstevel@tonic-gate mdb_free(iob->iob_buf, iob->iob_bufsiz); 5767c478bd9Sstevel@tonic-gate iob->iob_buf = buf; 5777c478bd9Sstevel@tonic-gate iob->iob_bufsiz = bufsiz; 5787c478bd9Sstevel@tonic-gate 5797c478bd9Sstevel@tonic-gate if (iob->iob_flags & MDB_IOB_WRONLY) 5807c478bd9Sstevel@tonic-gate iob->iob_cols = MIN(iob->iob_cols, iob->iob_bufsiz); 5817c478bd9Sstevel@tonic-gate } 5827c478bd9Sstevel@tonic-gate 5837c478bd9Sstevel@tonic-gate void 5847c478bd9Sstevel@tonic-gate mdb_iob_clearlines(mdb_iob_t *iob) 5857c478bd9Sstevel@tonic-gate { 5867c478bd9Sstevel@tonic-gate iob->iob_flags &= ~(MDB_IOB_PGSINGLE | MDB_IOB_PGCONT); 5877c478bd9Sstevel@tonic-gate iob->iob_nlines = 0; 5887c478bd9Sstevel@tonic-gate } 5897c478bd9Sstevel@tonic-gate 5907c478bd9Sstevel@tonic-gate void 5917c478bd9Sstevel@tonic-gate mdb_iob_setflags(mdb_iob_t *iob, uint_t flags) 5927c478bd9Sstevel@tonic-gate { 5937c478bd9Sstevel@tonic-gate iob->iob_flags |= flags; 5947c478bd9Sstevel@tonic-gate if (flags & MDB_IOB_INDENT) 5957c478bd9Sstevel@tonic-gate iob_indent(iob); 5967c478bd9Sstevel@tonic-gate } 5977c478bd9Sstevel@tonic-gate 5987c478bd9Sstevel@tonic-gate void 5997c478bd9Sstevel@tonic-gate mdb_iob_clrflags(mdb_iob_t *iob, uint_t flags) 6007c478bd9Sstevel@tonic-gate { 6017c478bd9Sstevel@tonic-gate iob->iob_flags &= ~flags; 6027c478bd9Sstevel@tonic-gate if (flags & MDB_IOB_INDENT) 6037c478bd9Sstevel@tonic-gate iob_unindent(iob); 6047c478bd9Sstevel@tonic-gate } 6057c478bd9Sstevel@tonic-gate 6067c478bd9Sstevel@tonic-gate uint_t 6077c478bd9Sstevel@tonic-gate mdb_iob_getflags(mdb_iob_t *iob) 6087c478bd9Sstevel@tonic-gate { 6097c478bd9Sstevel@tonic-gate return (iob->iob_flags); 6107c478bd9Sstevel@tonic-gate } 6117c478bd9Sstevel@tonic-gate 6127c478bd9Sstevel@tonic-gate static uintmax_t 6137c478bd9Sstevel@tonic-gate vec_arg(const mdb_arg_t **app) 6147c478bd9Sstevel@tonic-gate { 6157c478bd9Sstevel@tonic-gate uintmax_t value; 6167c478bd9Sstevel@tonic-gate 6177c478bd9Sstevel@tonic-gate if ((*app)->a_type == MDB_TYPE_STRING) 6187c478bd9Sstevel@tonic-gate value = (uintmax_t)(uintptr_t)(*app)->a_un.a_str; 6197c478bd9Sstevel@tonic-gate else 6207c478bd9Sstevel@tonic-gate value = (*app)->a_un.a_val; 6217c478bd9Sstevel@tonic-gate 6227c478bd9Sstevel@tonic-gate (*app)++; 6237c478bd9Sstevel@tonic-gate return (value); 6247c478bd9Sstevel@tonic-gate } 6257c478bd9Sstevel@tonic-gate 6267c478bd9Sstevel@tonic-gate static const char * 6277c478bd9Sstevel@tonic-gate iob_size2str(intsize_t size) 6287c478bd9Sstevel@tonic-gate { 6297c478bd9Sstevel@tonic-gate switch (size) { 6307c478bd9Sstevel@tonic-gate case SZ_SHORT: 6317c478bd9Sstevel@tonic-gate return ("short"); 6327c478bd9Sstevel@tonic-gate case SZ_INT: 6337c478bd9Sstevel@tonic-gate return ("int"); 6347c478bd9Sstevel@tonic-gate case SZ_LONG: 6357c478bd9Sstevel@tonic-gate return ("long"); 6367c478bd9Sstevel@tonic-gate case SZ_LONGLONG: 6377c478bd9Sstevel@tonic-gate return ("long long"); 6387c478bd9Sstevel@tonic-gate } 6397c478bd9Sstevel@tonic-gate return (""); 6407c478bd9Sstevel@tonic-gate } 6417c478bd9Sstevel@tonic-gate 6427c478bd9Sstevel@tonic-gate /* 6437c478bd9Sstevel@tonic-gate * In order to simplify maintenance of the ::formats display, we provide an 6447c478bd9Sstevel@tonic-gate * unparser for mdb_printf format strings that converts a simple format 6457c478bd9Sstevel@tonic-gate * string with one specifier into a descriptive representation, e.g. 6467c478bd9Sstevel@tonic-gate * mdb_iob_format2str("%llx") returns "hexadecimal long long". 6477c478bd9Sstevel@tonic-gate */ 6487c478bd9Sstevel@tonic-gate const char * 6497c478bd9Sstevel@tonic-gate mdb_iob_format2str(const char *format) 6507c478bd9Sstevel@tonic-gate { 6517c478bd9Sstevel@tonic-gate intsize_t size = SZ_INT; 6527c478bd9Sstevel@tonic-gate const char *p; 6537c478bd9Sstevel@tonic-gate 6547c478bd9Sstevel@tonic-gate static char buf[64]; 6557c478bd9Sstevel@tonic-gate 6567c478bd9Sstevel@tonic-gate buf[0] = '\0'; 6577c478bd9Sstevel@tonic-gate 6587c478bd9Sstevel@tonic-gate if ((p = strchr(format, '%')) == NULL) 6597c478bd9Sstevel@tonic-gate goto done; 6607c478bd9Sstevel@tonic-gate 6617c478bd9Sstevel@tonic-gate fmt_switch: 6627c478bd9Sstevel@tonic-gate switch (*++p) { 6637c478bd9Sstevel@tonic-gate case '0': case '1': case '2': case '3': case '4': 6647c478bd9Sstevel@tonic-gate case '5': case '6': case '7': case '8': case '9': 6657c478bd9Sstevel@tonic-gate while (*p >= '0' && *p <= '9') 6667c478bd9Sstevel@tonic-gate p++; 6677c478bd9Sstevel@tonic-gate p--; 6687c478bd9Sstevel@tonic-gate goto fmt_switch; 6697c478bd9Sstevel@tonic-gate 6707c478bd9Sstevel@tonic-gate case 'a': 6717c478bd9Sstevel@tonic-gate case 'A': 6727c478bd9Sstevel@tonic-gate return ("symbol"); 6737c478bd9Sstevel@tonic-gate 6747c478bd9Sstevel@tonic-gate case 'b': 6757c478bd9Sstevel@tonic-gate (void) strcpy(buf, "unsigned "); 6767c478bd9Sstevel@tonic-gate (void) strcat(buf, iob_size2str(size)); 6777c478bd9Sstevel@tonic-gate (void) strcat(buf, " bitfield"); 6787c478bd9Sstevel@tonic-gate break; 6797c478bd9Sstevel@tonic-gate 6807c478bd9Sstevel@tonic-gate case 'c': 6817c478bd9Sstevel@tonic-gate return ("character"); 6827c478bd9Sstevel@tonic-gate 6837c478bd9Sstevel@tonic-gate case 'd': 6847c478bd9Sstevel@tonic-gate case 'i': 6857c478bd9Sstevel@tonic-gate (void) strcpy(buf, "decimal signed "); 6867c478bd9Sstevel@tonic-gate (void) strcat(buf, iob_size2str(size)); 6877c478bd9Sstevel@tonic-gate break; 6887c478bd9Sstevel@tonic-gate 6897c478bd9Sstevel@tonic-gate case 'e': 6907c478bd9Sstevel@tonic-gate case 'E': 6917c478bd9Sstevel@tonic-gate case 'g': 6927c478bd9Sstevel@tonic-gate case 'G': 6937c478bd9Sstevel@tonic-gate return ("double"); 6947c478bd9Sstevel@tonic-gate 6957c478bd9Sstevel@tonic-gate case 'h': 6967c478bd9Sstevel@tonic-gate size = SZ_SHORT; 6977c478bd9Sstevel@tonic-gate goto fmt_switch; 6987c478bd9Sstevel@tonic-gate 699cab8de14SBryan Cantrill case 'H': 700cab8de14SBryan Cantrill return ("human-readable size"); 701cab8de14SBryan Cantrill 7027c478bd9Sstevel@tonic-gate case 'I': 7037c478bd9Sstevel@tonic-gate return ("IPv4 address"); 7047c478bd9Sstevel@tonic-gate 7057c478bd9Sstevel@tonic-gate case 'l': 7067c478bd9Sstevel@tonic-gate if (size >= SZ_LONG) 7077c478bd9Sstevel@tonic-gate size = SZ_LONGLONG; 7087c478bd9Sstevel@tonic-gate else 7097c478bd9Sstevel@tonic-gate size = SZ_LONG; 7107c478bd9Sstevel@tonic-gate goto fmt_switch; 7117c478bd9Sstevel@tonic-gate 7127c478bd9Sstevel@tonic-gate case 'm': 7137c478bd9Sstevel@tonic-gate return ("margin"); 7147c478bd9Sstevel@tonic-gate 7157c478bd9Sstevel@tonic-gate case 'N': 7167c478bd9Sstevel@tonic-gate return ("IPv6 address"); 7177c478bd9Sstevel@tonic-gate 7187c478bd9Sstevel@tonic-gate case 'o': 7197c478bd9Sstevel@tonic-gate (void) strcpy(buf, "octal unsigned "); 7207c478bd9Sstevel@tonic-gate (void) strcat(buf, iob_size2str(size)); 7217c478bd9Sstevel@tonic-gate break; 7227c478bd9Sstevel@tonic-gate 7237c478bd9Sstevel@tonic-gate case 'p': 7247c478bd9Sstevel@tonic-gate return ("pointer"); 7257c478bd9Sstevel@tonic-gate 7267c478bd9Sstevel@tonic-gate case 'q': 7277c478bd9Sstevel@tonic-gate (void) strcpy(buf, "octal signed "); 7287c478bd9Sstevel@tonic-gate (void) strcat(buf, iob_size2str(size)); 7297c478bd9Sstevel@tonic-gate break; 7307c478bd9Sstevel@tonic-gate 7317c478bd9Sstevel@tonic-gate case 'r': 7327c478bd9Sstevel@tonic-gate (void) strcpy(buf, "default radix unsigned "); 7337c478bd9Sstevel@tonic-gate (void) strcat(buf, iob_size2str(size)); 7347c478bd9Sstevel@tonic-gate break; 7357c478bd9Sstevel@tonic-gate 7367c478bd9Sstevel@tonic-gate case 'R': 7377c478bd9Sstevel@tonic-gate (void) strcpy(buf, "default radix signed "); 7387c478bd9Sstevel@tonic-gate (void) strcat(buf, iob_size2str(size)); 7397c478bd9Sstevel@tonic-gate break; 7407c478bd9Sstevel@tonic-gate 7417c478bd9Sstevel@tonic-gate case 's': 7427c478bd9Sstevel@tonic-gate return ("string"); 7437c478bd9Sstevel@tonic-gate 7447c478bd9Sstevel@tonic-gate case 't': 7457c478bd9Sstevel@tonic-gate case 'T': 7467c478bd9Sstevel@tonic-gate return ("tab"); 7477c478bd9Sstevel@tonic-gate 7487c478bd9Sstevel@tonic-gate case 'u': 7497c478bd9Sstevel@tonic-gate (void) strcpy(buf, "decimal unsigned "); 7507c478bd9Sstevel@tonic-gate (void) strcat(buf, iob_size2str(size)); 7517c478bd9Sstevel@tonic-gate break; 7527c478bd9Sstevel@tonic-gate 7537c478bd9Sstevel@tonic-gate case 'x': 7547c478bd9Sstevel@tonic-gate case 'X': 7557c478bd9Sstevel@tonic-gate (void) strcat(buf, "hexadecimal "); 7567c478bd9Sstevel@tonic-gate (void) strcat(buf, iob_size2str(size)); 7577c478bd9Sstevel@tonic-gate break; 7587c478bd9Sstevel@tonic-gate 7597c478bd9Sstevel@tonic-gate case 'Y': 7607c478bd9Sstevel@tonic-gate return ("time_t"); 7617c478bd9Sstevel@tonic-gate 7627c478bd9Sstevel@tonic-gate case '<': 7637c478bd9Sstevel@tonic-gate return ("terminal attribute"); 7647c478bd9Sstevel@tonic-gate 7657c478bd9Sstevel@tonic-gate case '?': 7667c478bd9Sstevel@tonic-gate case '#': 7677c478bd9Sstevel@tonic-gate case '+': 7687c478bd9Sstevel@tonic-gate case '-': 7697c478bd9Sstevel@tonic-gate goto fmt_switch; 7707c478bd9Sstevel@tonic-gate } 7717c478bd9Sstevel@tonic-gate 7727c478bd9Sstevel@tonic-gate done: 7737c478bd9Sstevel@tonic-gate if (buf[0] == '\0') 7747c478bd9Sstevel@tonic-gate (void) strcpy(buf, "text"); 7757c478bd9Sstevel@tonic-gate 7767c478bd9Sstevel@tonic-gate return ((const char *)buf); 7777c478bd9Sstevel@tonic-gate } 7787c478bd9Sstevel@tonic-gate 7797c478bd9Sstevel@tonic-gate static const char * 7807c478bd9Sstevel@tonic-gate iob_int2str(varglist_t *ap, intsize_t size, int base, uint_t flags, int *zero, 7817c478bd9Sstevel@tonic-gate u_longlong_t *value) 7827c478bd9Sstevel@tonic-gate { 7837c478bd9Sstevel@tonic-gate uintmax_t i; 7847c478bd9Sstevel@tonic-gate 7857c478bd9Sstevel@tonic-gate switch (size) { 7867c478bd9Sstevel@tonic-gate case SZ_LONGLONG: 7877c478bd9Sstevel@tonic-gate if (flags & NTOS_UNSIGNED) 7887c478bd9Sstevel@tonic-gate i = (u_longlong_t)VA_ARG(ap, u_longlong_t); 7897c478bd9Sstevel@tonic-gate else 7907c478bd9Sstevel@tonic-gate i = (longlong_t)VA_ARG(ap, longlong_t); 7917c478bd9Sstevel@tonic-gate break; 7927c478bd9Sstevel@tonic-gate 7937c478bd9Sstevel@tonic-gate case SZ_LONG: 7947c478bd9Sstevel@tonic-gate if (flags & NTOS_UNSIGNED) 7957c478bd9Sstevel@tonic-gate i = (ulong_t)VA_ARG(ap, ulong_t); 7967c478bd9Sstevel@tonic-gate else 7977c478bd9Sstevel@tonic-gate i = (long)VA_ARG(ap, long); 7987c478bd9Sstevel@tonic-gate break; 7997c478bd9Sstevel@tonic-gate 8007c478bd9Sstevel@tonic-gate case SZ_SHORT: 8017c478bd9Sstevel@tonic-gate if (flags & NTOS_UNSIGNED) 8027c478bd9Sstevel@tonic-gate i = (ushort_t)VA_ARG(ap, uint_t); 8037c478bd9Sstevel@tonic-gate else 8047c478bd9Sstevel@tonic-gate i = (short)VA_ARG(ap, int); 8057c478bd9Sstevel@tonic-gate break; 8067c478bd9Sstevel@tonic-gate 8077c478bd9Sstevel@tonic-gate default: 8087c478bd9Sstevel@tonic-gate if (flags & NTOS_UNSIGNED) 8097c478bd9Sstevel@tonic-gate i = (uint_t)VA_ARG(ap, uint_t); 8107c478bd9Sstevel@tonic-gate else 8117c478bd9Sstevel@tonic-gate i = (int)VA_ARG(ap, int); 8127c478bd9Sstevel@tonic-gate } 8137c478bd9Sstevel@tonic-gate 8147c478bd9Sstevel@tonic-gate *zero = i == 0; /* Return flag indicating if result was zero */ 8157c478bd9Sstevel@tonic-gate *value = i; /* Return value retrieved from va_list */ 8167c478bd9Sstevel@tonic-gate 8177c478bd9Sstevel@tonic-gate return (numtostr(i, base, flags)); 8187c478bd9Sstevel@tonic-gate } 8197c478bd9Sstevel@tonic-gate 8207c478bd9Sstevel@tonic-gate static const char * 8217c478bd9Sstevel@tonic-gate iob_time2str(time_t *tmp) 8227c478bd9Sstevel@tonic-gate { 8237c478bd9Sstevel@tonic-gate /* 8247c478bd9Sstevel@tonic-gate * ctime(3c) returns a string of the form 8257c478bd9Sstevel@tonic-gate * "Fri Sep 13 00:00:00 1986\n\0". We turn this into the canonical 8267c478bd9Sstevel@tonic-gate * adb /y format "1986 Sep 13 00:00:00" below. 8277c478bd9Sstevel@tonic-gate */ 8287c478bd9Sstevel@tonic-gate const char *src = ctime(tmp); 8297c478bd9Sstevel@tonic-gate static char buf[32]; 8307c478bd9Sstevel@tonic-gate char *dst = buf; 8317c478bd9Sstevel@tonic-gate int i; 8327c478bd9Sstevel@tonic-gate 8337c478bd9Sstevel@tonic-gate if (src == NULL) 834a727d47aSjwadams return (numtostr((uintmax_t)*tmp, mdb.m_radix, 0)); 8357c478bd9Sstevel@tonic-gate 8367c478bd9Sstevel@tonic-gate for (i = 20; i < 24; i++) 8377c478bd9Sstevel@tonic-gate *dst++ = src[i]; /* Copy the 4-digit year */ 8387c478bd9Sstevel@tonic-gate 8397c478bd9Sstevel@tonic-gate for (i = 3; i < 19; i++) 8407c478bd9Sstevel@tonic-gate *dst++ = src[i]; /* Copy month, day, and h:m:s */ 8417c478bd9Sstevel@tonic-gate 8427c478bd9Sstevel@tonic-gate *dst = '\0'; 8437c478bd9Sstevel@tonic-gate return (buf); 8447c478bd9Sstevel@tonic-gate } 8457c478bd9Sstevel@tonic-gate 8467c478bd9Sstevel@tonic-gate static const char * 8477c478bd9Sstevel@tonic-gate iob_addr2str(uintptr_t addr) 8487c478bd9Sstevel@tonic-gate { 8497c478bd9Sstevel@tonic-gate static char buf[MDB_TGT_SYM_NAMLEN]; 8507c478bd9Sstevel@tonic-gate char *name = buf; 8517c478bd9Sstevel@tonic-gate longlong_t offset; 8527c478bd9Sstevel@tonic-gate GElf_Sym sym; 8537c478bd9Sstevel@tonic-gate 8547c478bd9Sstevel@tonic-gate if (mdb_tgt_lookup_by_addr(mdb.m_target, addr, 8557c478bd9Sstevel@tonic-gate MDB_TGT_SYM_FUZZY, buf, sizeof (buf), &sym, NULL) == -1) 8567c478bd9Sstevel@tonic-gate return (NULL); 8577c478bd9Sstevel@tonic-gate 8587c478bd9Sstevel@tonic-gate if (mdb.m_demangler != NULL && (mdb.m_flags & MDB_FL_DEMANGLE)) 8597c478bd9Sstevel@tonic-gate name = (char *)mdb_dem_convert(mdb.m_demangler, buf); 8607c478bd9Sstevel@tonic-gate 8617c478bd9Sstevel@tonic-gate /* 8627c478bd9Sstevel@tonic-gate * Here we provide a little cooperation between the %a formatting code 8637c478bd9Sstevel@tonic-gate * and the proc target: if the initial address passed to %a is in fact 8647c478bd9Sstevel@tonic-gate * a PLT address, the proc target's lookup_by_addr code will convert 8657c478bd9Sstevel@tonic-gate * this to the PLT destination (a different address). We do not want 8667c478bd9Sstevel@tonic-gate * to append a "+/-offset" suffix based on comparison with the query 8677c478bd9Sstevel@tonic-gate * symbol in this case because the proc target has really done a hidden 8687c478bd9Sstevel@tonic-gate * query for us with a different address. We detect this case by 8697c478bd9Sstevel@tonic-gate * comparing the initial characters of buf to the special PLT= string. 8707c478bd9Sstevel@tonic-gate */ 8717c478bd9Sstevel@tonic-gate if (sym.st_value != addr && strncmp(name, "PLT=", 4) != 0) { 8727c478bd9Sstevel@tonic-gate if (sym.st_value > addr) 8737c478bd9Sstevel@tonic-gate offset = -(longlong_t)(sym.st_value - addr); 8747c478bd9Sstevel@tonic-gate else 8757c478bd9Sstevel@tonic-gate offset = (longlong_t)(addr - sym.st_value); 8767c478bd9Sstevel@tonic-gate 8777c478bd9Sstevel@tonic-gate (void) strcat(name, numtostr(offset, mdb.m_radix, 8787c478bd9Sstevel@tonic-gate NTOS_SIGNPOS | NTOS_SHOWBASE)); 8797c478bd9Sstevel@tonic-gate } 8807c478bd9Sstevel@tonic-gate 8817c478bd9Sstevel@tonic-gate return (name); 8827c478bd9Sstevel@tonic-gate } 8837c478bd9Sstevel@tonic-gate 884cab8de14SBryan Cantrill /* 885cab8de14SBryan Cantrill * Produce human-readable size, similar in spirit (and identical in output) 886cab8de14SBryan Cantrill * to libzfs's zfs_nicenum() -- but made significantly more complicated by 887cab8de14SBryan Cantrill * the constraint that we cannot use snprintf() as an implementation detail. 88813e9820cSBryan Cantrill * Recall, floating point is verboten in kmdb. 889cab8de14SBryan Cantrill */ 890cab8de14SBryan Cantrill static const char * 891cab8de14SBryan Cantrill iob_bytes2str(varglist_t *ap, intsize_t size) 892cab8de14SBryan Cantrill { 89313e9820cSBryan Cantrill #ifndef _KMDB 894cab8de14SBryan Cantrill const int sigfig = 3; 89513e9820cSBryan Cantrill uint64_t orig; 89613e9820cSBryan Cantrill #endif 89713e9820cSBryan Cantrill uint64_t n; 89813e9820cSBryan Cantrill 899cab8de14SBryan Cantrill static char buf[68], *c; 900cab8de14SBryan Cantrill int index = 0; 901cab8de14SBryan Cantrill char u; 902cab8de14SBryan Cantrill 903cab8de14SBryan Cantrill switch (size) { 904cab8de14SBryan Cantrill case SZ_LONGLONG: 905cab8de14SBryan Cantrill n = (u_longlong_t)VA_ARG(ap, u_longlong_t); 906cab8de14SBryan Cantrill break; 907cab8de14SBryan Cantrill 908cab8de14SBryan Cantrill case SZ_LONG: 909cab8de14SBryan Cantrill n = (ulong_t)VA_ARG(ap, ulong_t); 910cab8de14SBryan Cantrill break; 911cab8de14SBryan Cantrill 912cab8de14SBryan Cantrill case SZ_SHORT: 913cab8de14SBryan Cantrill n = (ushort_t)VA_ARG(ap, uint_t); 914b1da084bSToomas Soome break; 915cab8de14SBryan Cantrill 916cab8de14SBryan Cantrill default: 917cab8de14SBryan Cantrill n = (uint_t)VA_ARG(ap, uint_t); 918cab8de14SBryan Cantrill } 919cab8de14SBryan Cantrill 92013e9820cSBryan Cantrill #ifndef _KMDB 921cab8de14SBryan Cantrill orig = n; 92213e9820cSBryan Cantrill #endif 923cab8de14SBryan Cantrill 924cab8de14SBryan Cantrill while (n >= 1024) { 925cab8de14SBryan Cantrill n /= 1024; 926cab8de14SBryan Cantrill index++; 927cab8de14SBryan Cantrill } 928cab8de14SBryan Cantrill 929cab8de14SBryan Cantrill u = " KMGTPE"[index]; 930cab8de14SBryan Cantrill buf[0] = '\0'; 931cab8de14SBryan Cantrill 932cab8de14SBryan Cantrill if (index == 0) { 933cab8de14SBryan Cantrill return (numtostr(n, 10, 0)); 93413e9820cSBryan Cantrill #ifndef _KMDB 935cab8de14SBryan Cantrill } else if ((orig & ((1ULL << 10 * index) - 1)) == 0) { 93613e9820cSBryan Cantrill #else 93713e9820cSBryan Cantrill } else { 93813e9820cSBryan Cantrill #endif 939cab8de14SBryan Cantrill /* 94013e9820cSBryan Cantrill * If this is an even multiple of the base or we are in an 94113e9820cSBryan Cantrill * environment where floating point is verboten (i.e., kmdb), 94213e9820cSBryan Cantrill * always display without any decimal precision. 943cab8de14SBryan Cantrill */ 944cab8de14SBryan Cantrill (void) strcat(buf, numtostr(n, 10, 0)); 94513e9820cSBryan Cantrill #ifndef _KMDB 946cab8de14SBryan Cantrill } else { 947cab8de14SBryan Cantrill /* 948cab8de14SBryan Cantrill * We want to choose a precision that results in the specified 949cab8de14SBryan Cantrill * number of significant figures (by default, 3). This is 950cab8de14SBryan Cantrill * similar to the output that one would get specifying the %.*g 951cab8de14SBryan Cantrill * format specifier (where the asterisk denotes the number of 952cab8de14SBryan Cantrill * significant digits), but (1) we include trailing zeros if 953cab8de14SBryan Cantrill * the there are non-zero digits beyond the number of 954cab8de14SBryan Cantrill * significant digits (that is, 10241 is '10.0K', not the 955cab8de14SBryan Cantrill * '10K' that it would be with %.3g) and (2) we never resort 956cab8de14SBryan Cantrill * to %e notation when the number of digits exceeds the 957cab8de14SBryan Cantrill * number of significant figures (that is, 1043968 is '1020K', 958cab8de14SBryan Cantrill * not '1.02e+03K'). This is also made somewhat complicated 959cab8de14SBryan Cantrill * by the fact that we need to deal with rounding (10239 is 960cab8de14SBryan Cantrill * '10.0K', not '9.99K'), for which we perform nearest-even 961cab8de14SBryan Cantrill * rounding. 962cab8de14SBryan Cantrill */ 963cab8de14SBryan Cantrill double val = (double)orig / (1ULL << 10 * index); 964cab8de14SBryan Cantrill int i, mag = 1, thresh; 965cab8de14SBryan Cantrill 966cab8de14SBryan Cantrill for (i = 0; i < sigfig - 1; i++) 967cab8de14SBryan Cantrill mag *= 10; 968cab8de14SBryan Cantrill 969cab8de14SBryan Cantrill for (thresh = mag * 10; mag >= 1; mag /= 10, i--) { 970cab8de14SBryan Cantrill double mult = val * (double)mag; 971cab8de14SBryan Cantrill uint32_t v; 972cab8de14SBryan Cantrill 973cab8de14SBryan Cantrill /* 974cab8de14SBryan Cantrill * Note that we cast mult to a 32-bit value. We know 975cab8de14SBryan Cantrill * that val is less than 1024 due to the logic above, 976cab8de14SBryan Cantrill * and that mag is at most 10^(sigfig - 1). This means 977cab8de14SBryan Cantrill * that as long as sigfig is 9 or lower, this will not 978cab8de14SBryan Cantrill * overflow. (We perform this cast because it assures 979cab8de14SBryan Cantrill * that we are never converting a double to a uint64_t, 980cab8de14SBryan Cantrill * which for some compilers requires a call to a 981cab8de14SBryan Cantrill * function not guaranteed to be in libstand.) 982cab8de14SBryan Cantrill */ 983cab8de14SBryan Cantrill if (mult - (double)(uint32_t)mult != 0.5) { 984cab8de14SBryan Cantrill v = (uint32_t)(mult + 0.5); 985cab8de14SBryan Cantrill } else { 986cab8de14SBryan Cantrill /* 987cab8de14SBryan Cantrill * We are exactly between integer multiples 988cab8de14SBryan Cantrill * of units; perform nearest-even rounding 989cab8de14SBryan Cantrill * to be consistent with the behavior of 990cab8de14SBryan Cantrill * printf(). 991cab8de14SBryan Cantrill */ 992cab8de14SBryan Cantrill if ((v = (uint32_t)mult) & 1) 993cab8de14SBryan Cantrill v++; 994cab8de14SBryan Cantrill } 995cab8de14SBryan Cantrill 996cab8de14SBryan Cantrill if (mag == 1) { 997cab8de14SBryan Cantrill (void) strcat(buf, numtostr(v, 10, 0)); 998cab8de14SBryan Cantrill break; 999cab8de14SBryan Cantrill } 1000cab8de14SBryan Cantrill 1001cab8de14SBryan Cantrill if (v < thresh) { 1002cab8de14SBryan Cantrill (void) strcat(buf, numtostr(v / mag, 10, 0)); 1003cab8de14SBryan Cantrill (void) strcat(buf, "."); 1004cab8de14SBryan Cantrill 1005cab8de14SBryan Cantrill c = (char *)numtostr(v % mag, 10, 0); 1006cab8de14SBryan Cantrill i -= strlen(c); 1007cab8de14SBryan Cantrill 1008cab8de14SBryan Cantrill /* 1009cab8de14SBryan Cantrill * We need to zero-fill from the right of the 1010cab8de14SBryan Cantrill * decimal point to the first significant digit 1011cab8de14SBryan Cantrill * of the fractional component. 1012cab8de14SBryan Cantrill */ 1013cab8de14SBryan Cantrill while (i--) 1014cab8de14SBryan Cantrill (void) strcat(buf, "0"); 1015cab8de14SBryan Cantrill 1016cab8de14SBryan Cantrill (void) strcat(buf, c); 1017cab8de14SBryan Cantrill break; 1018cab8de14SBryan Cantrill } 1019cab8de14SBryan Cantrill } 102013e9820cSBryan Cantrill #endif 1021cab8de14SBryan Cantrill } 1022cab8de14SBryan Cantrill 1023cab8de14SBryan Cantrill c = &buf[strlen(buf)]; 1024cab8de14SBryan Cantrill *c++ = u; 1025cab8de14SBryan Cantrill *c++ = '\0'; 1026cab8de14SBryan Cantrill 1027cab8de14SBryan Cantrill return (buf); 1028cab8de14SBryan Cantrill } 1029cab8de14SBryan Cantrill 10307c478bd9Sstevel@tonic-gate static int 10317c478bd9Sstevel@tonic-gate iob_setattr(mdb_iob_t *iob, const char *s, size_t nbytes) 10327c478bd9Sstevel@tonic-gate { 10337c478bd9Sstevel@tonic-gate uint_t attr; 10347c478bd9Sstevel@tonic-gate int req; 10357c478bd9Sstevel@tonic-gate 10367c478bd9Sstevel@tonic-gate if (iob->iob_pgp == NULL) 10377c478bd9Sstevel@tonic-gate return (set_errno(ENOTTY)); 10387c478bd9Sstevel@tonic-gate 10397c478bd9Sstevel@tonic-gate if (nbytes != 0 && *s == '/') { 10407c478bd9Sstevel@tonic-gate req = ATT_OFF; 10417c478bd9Sstevel@tonic-gate nbytes--; 10427c478bd9Sstevel@tonic-gate s++; 10437c478bd9Sstevel@tonic-gate } else 10447c478bd9Sstevel@tonic-gate req = ATT_ON; 10457c478bd9Sstevel@tonic-gate 10467c478bd9Sstevel@tonic-gate if (nbytes != 1) 10477c478bd9Sstevel@tonic-gate return (set_errno(EINVAL)); 10487c478bd9Sstevel@tonic-gate 10497c478bd9Sstevel@tonic-gate switch (*s) { 10507c478bd9Sstevel@tonic-gate case 's': 10517c478bd9Sstevel@tonic-gate attr = ATT_STANDOUT; 10527c478bd9Sstevel@tonic-gate break; 10537c478bd9Sstevel@tonic-gate case 'u': 10547c478bd9Sstevel@tonic-gate attr = ATT_UNDERLINE; 10557c478bd9Sstevel@tonic-gate break; 10567c478bd9Sstevel@tonic-gate case 'r': 10577c478bd9Sstevel@tonic-gate attr = ATT_REVERSE; 10587c478bd9Sstevel@tonic-gate break; 10597c478bd9Sstevel@tonic-gate case 'b': 10607c478bd9Sstevel@tonic-gate attr = ATT_BOLD; 10617c478bd9Sstevel@tonic-gate break; 10627c478bd9Sstevel@tonic-gate case 'd': 10637c478bd9Sstevel@tonic-gate attr = ATT_DIM; 10647c478bd9Sstevel@tonic-gate break; 10657c478bd9Sstevel@tonic-gate case 'a': 10667c478bd9Sstevel@tonic-gate attr = ATT_ALTCHARSET; 10677c478bd9Sstevel@tonic-gate break; 10687c478bd9Sstevel@tonic-gate default: 10697c478bd9Sstevel@tonic-gate return (set_errno(EINVAL)); 10707c478bd9Sstevel@tonic-gate } 10717c478bd9Sstevel@tonic-gate 10727c478bd9Sstevel@tonic-gate /* 10737c478bd9Sstevel@tonic-gate * We need to flush the current buffer contents before calling 10747c478bd9Sstevel@tonic-gate * IOP_SETATTR because IOP_SETATTR may need to synchronously output 10757c478bd9Sstevel@tonic-gate * terminal escape sequences directly to the underlying device. 10767c478bd9Sstevel@tonic-gate */ 10777c478bd9Sstevel@tonic-gate (void) iob_write(iob, iob->iob_iop, iob->iob_buf, iob->iob_nbytes); 10787c478bd9Sstevel@tonic-gate iob->iob_bufp = &iob->iob_buf[0]; 10797c478bd9Sstevel@tonic-gate iob->iob_nbytes = 0; 10807c478bd9Sstevel@tonic-gate 10817c478bd9Sstevel@tonic-gate return (IOP_SETATTR(iob->iob_pgp, req, attr)); 10827c478bd9Sstevel@tonic-gate } 10837c478bd9Sstevel@tonic-gate 10847c478bd9Sstevel@tonic-gate static void 10857c478bd9Sstevel@tonic-gate iob_bits2str(mdb_iob_t *iob, u_longlong_t value, const mdb_bitmask_t *bmp, 10867c478bd9Sstevel@tonic-gate mdb_bool_t altflag) 10877c478bd9Sstevel@tonic-gate { 10887c478bd9Sstevel@tonic-gate mdb_bool_t delim = FALSE; 10897c478bd9Sstevel@tonic-gate const char *str; 10907c478bd9Sstevel@tonic-gate size_t width; 10917c478bd9Sstevel@tonic-gate 10927c478bd9Sstevel@tonic-gate if (bmp == NULL) 10937c478bd9Sstevel@tonic-gate goto out; 10947c478bd9Sstevel@tonic-gate 10957c478bd9Sstevel@tonic-gate for (; bmp->bm_name != NULL; bmp++) { 10967c478bd9Sstevel@tonic-gate if ((value & bmp->bm_mask) == bmp->bm_bits) { 10977c478bd9Sstevel@tonic-gate width = strlen(bmp->bm_name) + delim; 10987c478bd9Sstevel@tonic-gate 10997c478bd9Sstevel@tonic-gate if (IOB_WRAPNOW(iob, width)) 11007c478bd9Sstevel@tonic-gate mdb_iob_nl(iob); 11017c478bd9Sstevel@tonic-gate 11027c478bd9Sstevel@tonic-gate if (delim) 11037c478bd9Sstevel@tonic-gate mdb_iob_putc(iob, ','); 11047c478bd9Sstevel@tonic-gate else 11057c478bd9Sstevel@tonic-gate delim = TRUE; 11067c478bd9Sstevel@tonic-gate 11077c478bd9Sstevel@tonic-gate mdb_iob_puts(iob, bmp->bm_name); 11087c478bd9Sstevel@tonic-gate value &= ~bmp->bm_bits; 11097c478bd9Sstevel@tonic-gate } 11107c478bd9Sstevel@tonic-gate } 11117c478bd9Sstevel@tonic-gate 11127c478bd9Sstevel@tonic-gate out: 11137c478bd9Sstevel@tonic-gate if (altflag == TRUE && (delim == FALSE || value != 0)) { 11147c478bd9Sstevel@tonic-gate str = numtostr(value, 16, NTOS_UNSIGNED | NTOS_SHOWBASE); 11157c478bd9Sstevel@tonic-gate width = strlen(str) + delim; 11167c478bd9Sstevel@tonic-gate 11177c478bd9Sstevel@tonic-gate if (IOB_WRAPNOW(iob, width)) 11187c478bd9Sstevel@tonic-gate mdb_iob_nl(iob); 11197c478bd9Sstevel@tonic-gate if (delim) 11207c478bd9Sstevel@tonic-gate mdb_iob_putc(iob, ','); 11217c478bd9Sstevel@tonic-gate mdb_iob_puts(iob, str); 11227c478bd9Sstevel@tonic-gate } 11237c478bd9Sstevel@tonic-gate } 11247c478bd9Sstevel@tonic-gate 11257c478bd9Sstevel@tonic-gate static const char * 11267c478bd9Sstevel@tonic-gate iob_inaddr2str(uint32_t addr) 11277c478bd9Sstevel@tonic-gate { 11287c478bd9Sstevel@tonic-gate static char buf[INET_ADDRSTRLEN]; 11297c478bd9Sstevel@tonic-gate 11307c478bd9Sstevel@tonic-gate (void) mdb_inet_ntop(AF_INET, &addr, buf, sizeof (buf)); 11317c478bd9Sstevel@tonic-gate 11327c478bd9Sstevel@tonic-gate return (buf); 11337c478bd9Sstevel@tonic-gate } 11347c478bd9Sstevel@tonic-gate 11357c478bd9Sstevel@tonic-gate static const char * 11367c478bd9Sstevel@tonic-gate iob_ipv6addr2str(void *addr) 11377c478bd9Sstevel@tonic-gate { 11387c478bd9Sstevel@tonic-gate static char buf[INET6_ADDRSTRLEN]; 11397c478bd9Sstevel@tonic-gate 11407c478bd9Sstevel@tonic-gate (void) mdb_inet_ntop(AF_INET6, addr, buf, sizeof (buf)); 11417c478bd9Sstevel@tonic-gate 11427c478bd9Sstevel@tonic-gate return (buf); 11437c478bd9Sstevel@tonic-gate } 11447c478bd9Sstevel@tonic-gate 11457c478bd9Sstevel@tonic-gate static const char * 11467c478bd9Sstevel@tonic-gate iob_getvar(const char *s, size_t len) 11477c478bd9Sstevel@tonic-gate { 11487c478bd9Sstevel@tonic-gate mdb_var_t *val; 11497c478bd9Sstevel@tonic-gate char *var; 11507c478bd9Sstevel@tonic-gate 11517c478bd9Sstevel@tonic-gate if (len == 0) { 11527c478bd9Sstevel@tonic-gate (void) set_errno(EINVAL); 11537c478bd9Sstevel@tonic-gate return (NULL); 11547c478bd9Sstevel@tonic-gate } 11557c478bd9Sstevel@tonic-gate 11567c478bd9Sstevel@tonic-gate var = strndup(s, len); 11577c478bd9Sstevel@tonic-gate val = mdb_nv_lookup(&mdb.m_nv, var); 11587c478bd9Sstevel@tonic-gate strfree(var); 11597c478bd9Sstevel@tonic-gate 11607c478bd9Sstevel@tonic-gate if (val == NULL) { 11617c478bd9Sstevel@tonic-gate (void) set_errno(EINVAL); 11627c478bd9Sstevel@tonic-gate return (NULL); 11637c478bd9Sstevel@tonic-gate } 11647c478bd9Sstevel@tonic-gate 11657c478bd9Sstevel@tonic-gate return (numtostr(mdb_nv_get_value(val), 10, 0)); 11667c478bd9Sstevel@tonic-gate } 11677c478bd9Sstevel@tonic-gate 11687c478bd9Sstevel@tonic-gate /* 11697c478bd9Sstevel@tonic-gate * The iob_doprnt function forms the main engine of the debugger's output 11707c478bd9Sstevel@tonic-gate * formatting capabilities. Note that this is NOT exactly compatible with 11717c478bd9Sstevel@tonic-gate * the printf(3S) family, nor is it intended to be so. We support some 11727c478bd9Sstevel@tonic-gate * extensions and format characters not supported by printf(3S), and we 11737c478bd9Sstevel@tonic-gate * explicitly do NOT provide support for %C, %S, %ws (wide-character strings), 11747c478bd9Sstevel@tonic-gate * do NOT provide for the complete functionality of %f, %e, %E, %g, %G 11757c478bd9Sstevel@tonic-gate * (alternate double formats), and do NOT support %.x (precision specification). 11767c478bd9Sstevel@tonic-gate * Note that iob_doprnt consumes varargs off the original va_list. 11777c478bd9Sstevel@tonic-gate */ 11787c478bd9Sstevel@tonic-gate static void 11797c478bd9Sstevel@tonic-gate iob_doprnt(mdb_iob_t *iob, const char *format, varglist_t *ap) 11807c478bd9Sstevel@tonic-gate { 11817c478bd9Sstevel@tonic-gate char c[2] = { 0, 0 }; /* Buffer for single character output */ 11827c478bd9Sstevel@tonic-gate const char *p; /* Current position in format string */ 11837c478bd9Sstevel@tonic-gate size_t len; /* Length of format string to copy verbatim */ 11847c478bd9Sstevel@tonic-gate size_t altlen; /* Length of alternate print format prefix */ 11857c478bd9Sstevel@tonic-gate const char *altstr; /* Alternate print format prefix */ 11867c478bd9Sstevel@tonic-gate const char *symstr; /* Symbol + offset string */ 11877c478bd9Sstevel@tonic-gate 11887c478bd9Sstevel@tonic-gate u_longlong_t val; /* Current integer value */ 11897c478bd9Sstevel@tonic-gate intsize_t size; /* Current integer value size */ 11907c478bd9Sstevel@tonic-gate uint_t flags; /* Current flags to pass to iob_int2str */ 11917c478bd9Sstevel@tonic-gate size_t width; /* Current field width */ 11927c478bd9Sstevel@tonic-gate int zero; /* If != 0, then integer value == 0 */ 11937c478bd9Sstevel@tonic-gate 11947c478bd9Sstevel@tonic-gate mdb_bool_t f_alt; /* Use alternate print format (%#) */ 11957c478bd9Sstevel@tonic-gate mdb_bool_t f_altsuff; /* Alternate print format is a suffix */ 11967c478bd9Sstevel@tonic-gate mdb_bool_t f_zfill; /* Zero-fill field (%0) */ 11977c478bd9Sstevel@tonic-gate mdb_bool_t f_left; /* Left-adjust field (%-) */ 11987c478bd9Sstevel@tonic-gate mdb_bool_t f_digits; /* Explicit digits used to set field width */ 11997c478bd9Sstevel@tonic-gate 12007c478bd9Sstevel@tonic-gate union { 12017c478bd9Sstevel@tonic-gate const char *str; 12027c478bd9Sstevel@tonic-gate uint32_t ui32; 12037c478bd9Sstevel@tonic-gate void *ptr; 12047c478bd9Sstevel@tonic-gate time_t tm; 12057c478bd9Sstevel@tonic-gate char c; 12067c478bd9Sstevel@tonic-gate double d; 12077c478bd9Sstevel@tonic-gate long double ld; 12087c478bd9Sstevel@tonic-gate } u; 12097c478bd9Sstevel@tonic-gate 12107c478bd9Sstevel@tonic-gate ASSERT(iob->iob_flags & MDB_IOB_WRONLY); 12117c478bd9Sstevel@tonic-gate 12127c478bd9Sstevel@tonic-gate while ((p = strchr(format, '%')) != NULL) { 12137c478bd9Sstevel@tonic-gate /* 12147c478bd9Sstevel@tonic-gate * Output the format string verbatim up to the next '%' char 12157c478bd9Sstevel@tonic-gate */ 12167c478bd9Sstevel@tonic-gate if (p != format) { 12177c478bd9Sstevel@tonic-gate len = p - format; 12187c478bd9Sstevel@tonic-gate if (IOB_WRAPNOW(iob, len) && *format != '\n') 12197c478bd9Sstevel@tonic-gate mdb_iob_nl(iob); 12207c478bd9Sstevel@tonic-gate mdb_iob_nputs(iob, format, len); 12217c478bd9Sstevel@tonic-gate } 12227c478bd9Sstevel@tonic-gate 12237c478bd9Sstevel@tonic-gate /* 12247c478bd9Sstevel@tonic-gate * Now we need to parse the sequence of format characters 12257c478bd9Sstevel@tonic-gate * following the % marker and do the appropriate thing. 12267c478bd9Sstevel@tonic-gate */ 12277c478bd9Sstevel@tonic-gate size = SZ_INT; /* Use normal-sized int by default */ 12287c478bd9Sstevel@tonic-gate flags = 0; /* Clear numtostr() format flags */ 12297c478bd9Sstevel@tonic-gate width = 0; /* No field width limit by default */ 12307c478bd9Sstevel@tonic-gate altlen = 0; /* No alternate format string yet */ 12317c478bd9Sstevel@tonic-gate altstr = NULL; /* No alternate format string yet */ 12327c478bd9Sstevel@tonic-gate 12337c478bd9Sstevel@tonic-gate f_alt = FALSE; /* Alternate format off by default */ 12347c478bd9Sstevel@tonic-gate f_altsuff = FALSE; /* Alternate format is a prefix */ 12357c478bd9Sstevel@tonic-gate f_zfill = FALSE; /* Zero-fill off by default */ 12367c478bd9Sstevel@tonic-gate f_left = FALSE; /* Left-adjust off by default */ 12377c478bd9Sstevel@tonic-gate f_digits = FALSE; /* No digits for width specified yet */ 12387c478bd9Sstevel@tonic-gate 12397c478bd9Sstevel@tonic-gate fmt_switch: 12407c478bd9Sstevel@tonic-gate switch (*++p) { 12417c478bd9Sstevel@tonic-gate case '0': case '1': case '2': case '3': case '4': 12427c478bd9Sstevel@tonic-gate case '5': case '6': case '7': case '8': case '9': 12437c478bd9Sstevel@tonic-gate if (f_digits == FALSE && *p == '0') { 12447c478bd9Sstevel@tonic-gate f_zfill = TRUE; 12457c478bd9Sstevel@tonic-gate goto fmt_switch; 12467c478bd9Sstevel@tonic-gate } 12477c478bd9Sstevel@tonic-gate 12487c478bd9Sstevel@tonic-gate if (f_digits == FALSE) 12497c478bd9Sstevel@tonic-gate width = 0; /* clear any other width specifier */ 12507c478bd9Sstevel@tonic-gate 12517c478bd9Sstevel@tonic-gate for (u.c = *p; u.c >= '0' && u.c <= '9'; u.c = *++p) 12527c478bd9Sstevel@tonic-gate width = width * 10 + u.c - '0'; 12537c478bd9Sstevel@tonic-gate 12547c478bd9Sstevel@tonic-gate p--; 12557c478bd9Sstevel@tonic-gate f_digits = TRUE; 12567c478bd9Sstevel@tonic-gate goto fmt_switch; 12577c478bd9Sstevel@tonic-gate 12587c478bd9Sstevel@tonic-gate case 'a': 12597c478bd9Sstevel@tonic-gate if (size < SZ_LONG) 12607c478bd9Sstevel@tonic-gate size = SZ_LONG; /* Bump to size of uintptr_t */ 12617c478bd9Sstevel@tonic-gate 12627c478bd9Sstevel@tonic-gate u.str = iob_int2str(ap, size, 16, 12637c478bd9Sstevel@tonic-gate NTOS_UNSIGNED | NTOS_SHOWBASE, &zero, &val); 12647c478bd9Sstevel@tonic-gate 12657c478bd9Sstevel@tonic-gate if ((symstr = iob_addr2str(val)) != NULL) 12667c478bd9Sstevel@tonic-gate u.str = symstr; 12677c478bd9Sstevel@tonic-gate 12687c478bd9Sstevel@tonic-gate if (f_alt == TRUE) { 12697c478bd9Sstevel@tonic-gate f_altsuff = TRUE; 12707c478bd9Sstevel@tonic-gate altstr = ":"; 12717c478bd9Sstevel@tonic-gate altlen = 1; 12727c478bd9Sstevel@tonic-gate } 12737c478bd9Sstevel@tonic-gate break; 12747c478bd9Sstevel@tonic-gate 12757c478bd9Sstevel@tonic-gate case 'A': 12767c478bd9Sstevel@tonic-gate if (size < SZ_LONG) 12777c478bd9Sstevel@tonic-gate size = SZ_LONG; /* Bump to size of uintptr_t */ 12787c478bd9Sstevel@tonic-gate 12797c478bd9Sstevel@tonic-gate (void) iob_int2str(ap, size, 16, 12807c478bd9Sstevel@tonic-gate NTOS_UNSIGNED, &zero, &val); 12817c478bd9Sstevel@tonic-gate 12827c478bd9Sstevel@tonic-gate u.str = iob_addr2str(val); 12837c478bd9Sstevel@tonic-gate 12847c478bd9Sstevel@tonic-gate if (f_alt == TRUE && u.str == NULL) 12857c478bd9Sstevel@tonic-gate u.str = "?"; 12867c478bd9Sstevel@tonic-gate break; 12877c478bd9Sstevel@tonic-gate 12887c478bd9Sstevel@tonic-gate case 'b': 12897c478bd9Sstevel@tonic-gate u.str = iob_int2str(ap, size, 16, 12907c478bd9Sstevel@tonic-gate NTOS_UNSIGNED | NTOS_SHOWBASE, &zero, &val); 12917c478bd9Sstevel@tonic-gate 12927c478bd9Sstevel@tonic-gate iob_bits2str(iob, val, VA_PTRARG(ap), f_alt); 12937c478bd9Sstevel@tonic-gate 12947c478bd9Sstevel@tonic-gate format = ++p; 12957c478bd9Sstevel@tonic-gate continue; 12967c478bd9Sstevel@tonic-gate 12977c478bd9Sstevel@tonic-gate case 'c': 12987c478bd9Sstevel@tonic-gate c[0] = (char)VA_ARG(ap, int); 12997c478bd9Sstevel@tonic-gate u.str = c; 13007c478bd9Sstevel@tonic-gate break; 13017c478bd9Sstevel@tonic-gate 13027c478bd9Sstevel@tonic-gate case 'd': 13037c478bd9Sstevel@tonic-gate case 'i': 13047c478bd9Sstevel@tonic-gate if (f_alt) 13057c478bd9Sstevel@tonic-gate flags |= NTOS_SHOWBASE; 13067c478bd9Sstevel@tonic-gate u.str = iob_int2str(ap, size, 10, flags, &zero, &val); 13077c478bd9Sstevel@tonic-gate break; 13087c478bd9Sstevel@tonic-gate 13097c478bd9Sstevel@tonic-gate /* No floating point in kmdb */ 13107c478bd9Sstevel@tonic-gate #ifndef _KMDB 13117c478bd9Sstevel@tonic-gate case 'e': 13127c478bd9Sstevel@tonic-gate case 'E': 13137c478bd9Sstevel@tonic-gate u.d = VA_ARG(ap, double); 13147c478bd9Sstevel@tonic-gate u.str = doubletos(u.d, 7, *p); 13157c478bd9Sstevel@tonic-gate break; 13167c478bd9Sstevel@tonic-gate 13177c478bd9Sstevel@tonic-gate case 'g': 13187c478bd9Sstevel@tonic-gate case 'G': 13197c478bd9Sstevel@tonic-gate if (size >= SZ_LONG) { 13207c478bd9Sstevel@tonic-gate u.ld = VA_ARG(ap, long double); 13217c478bd9Sstevel@tonic-gate u.str = longdoubletos(&u.ld, 16, 13227c478bd9Sstevel@tonic-gate (*p == 'g') ? 'e' : 'E'); 13237c478bd9Sstevel@tonic-gate } else { 13247c478bd9Sstevel@tonic-gate u.d = VA_ARG(ap, double); 13257c478bd9Sstevel@tonic-gate u.str = doubletos(u.d, 16, 13267c478bd9Sstevel@tonic-gate (*p == 'g') ? 'e' : 'E'); 13277c478bd9Sstevel@tonic-gate } 13287c478bd9Sstevel@tonic-gate break; 13297c478bd9Sstevel@tonic-gate #endif 13307c478bd9Sstevel@tonic-gate 13317c478bd9Sstevel@tonic-gate case 'h': 13327c478bd9Sstevel@tonic-gate size = SZ_SHORT; 13337c478bd9Sstevel@tonic-gate goto fmt_switch; 13347c478bd9Sstevel@tonic-gate 1335cab8de14SBryan Cantrill case 'H': 1336cab8de14SBryan Cantrill u.str = iob_bytes2str(ap, size); 1337cab8de14SBryan Cantrill break; 1338cab8de14SBryan Cantrill 13397c478bd9Sstevel@tonic-gate case 'I': 13407c478bd9Sstevel@tonic-gate u.ui32 = VA_ARG(ap, uint32_t); 13417c478bd9Sstevel@tonic-gate u.str = iob_inaddr2str(u.ui32); 13427c478bd9Sstevel@tonic-gate break; 13437c478bd9Sstevel@tonic-gate 13447c478bd9Sstevel@tonic-gate case 'l': 13457c478bd9Sstevel@tonic-gate if (size >= SZ_LONG) 13467c478bd9Sstevel@tonic-gate size = SZ_LONGLONG; 13477c478bd9Sstevel@tonic-gate else 13487c478bd9Sstevel@tonic-gate size = SZ_LONG; 13497c478bd9Sstevel@tonic-gate goto fmt_switch; 13507c478bd9Sstevel@tonic-gate 13517c478bd9Sstevel@tonic-gate case 'm': 13527c478bd9Sstevel@tonic-gate if (iob->iob_nbytes == 0) { 13537c478bd9Sstevel@tonic-gate mdb_iob_ws(iob, (width != 0) ? width : 13547c478bd9Sstevel@tonic-gate iob->iob_margin); 13557c478bd9Sstevel@tonic-gate } 13567c478bd9Sstevel@tonic-gate format = ++p; 13577c478bd9Sstevel@tonic-gate continue; 13587c478bd9Sstevel@tonic-gate 13597c478bd9Sstevel@tonic-gate case 'N': 13607c478bd9Sstevel@tonic-gate u.ptr = VA_PTRARG(ap); 13617c478bd9Sstevel@tonic-gate u.str = iob_ipv6addr2str(u.ptr); 13627c478bd9Sstevel@tonic-gate break; 13637c478bd9Sstevel@tonic-gate 13647c478bd9Sstevel@tonic-gate case 'o': 13657c478bd9Sstevel@tonic-gate u.str = iob_int2str(ap, size, 8, NTOS_UNSIGNED, 13667c478bd9Sstevel@tonic-gate &zero, &val); 13677c478bd9Sstevel@tonic-gate 13687c478bd9Sstevel@tonic-gate if (f_alt && !zero) { 13697c478bd9Sstevel@tonic-gate altstr = "0"; 13707c478bd9Sstevel@tonic-gate altlen = 1; 13717c478bd9Sstevel@tonic-gate } 13727c478bd9Sstevel@tonic-gate break; 13737c478bd9Sstevel@tonic-gate 13747c478bd9Sstevel@tonic-gate case 'p': 13757c478bd9Sstevel@tonic-gate u.ptr = VA_PTRARG(ap); 13767c478bd9Sstevel@tonic-gate u.str = numtostr((uintptr_t)u.ptr, 16, NTOS_UNSIGNED); 13777c478bd9Sstevel@tonic-gate break; 13787c478bd9Sstevel@tonic-gate 13797c478bd9Sstevel@tonic-gate case 'q': 13807c478bd9Sstevel@tonic-gate u.str = iob_int2str(ap, size, 8, flags, &zero, &val); 13817c478bd9Sstevel@tonic-gate 13827c478bd9Sstevel@tonic-gate if (f_alt && !zero) { 13837c478bd9Sstevel@tonic-gate altstr = "0"; 13847c478bd9Sstevel@tonic-gate altlen = 1; 13857c478bd9Sstevel@tonic-gate } 13867c478bd9Sstevel@tonic-gate break; 13877c478bd9Sstevel@tonic-gate 13887c478bd9Sstevel@tonic-gate case 'r': 13897c478bd9Sstevel@tonic-gate if (f_alt) 13907c478bd9Sstevel@tonic-gate flags |= NTOS_SHOWBASE; 13917c478bd9Sstevel@tonic-gate u.str = iob_int2str(ap, size, mdb.m_radix, 13927c478bd9Sstevel@tonic-gate NTOS_UNSIGNED | flags, &zero, &val); 13937c478bd9Sstevel@tonic-gate break; 13947c478bd9Sstevel@tonic-gate 13957c478bd9Sstevel@tonic-gate case 'R': 13967c478bd9Sstevel@tonic-gate if (f_alt) 13977c478bd9Sstevel@tonic-gate flags |= NTOS_SHOWBASE; 13987c478bd9Sstevel@tonic-gate u.str = iob_int2str(ap, size, mdb.m_radix, flags, 13997c478bd9Sstevel@tonic-gate &zero, &val); 14007c478bd9Sstevel@tonic-gate break; 14017c478bd9Sstevel@tonic-gate 14027c478bd9Sstevel@tonic-gate case 's': 14037c478bd9Sstevel@tonic-gate u.str = VA_PTRARG(ap); 14047c478bd9Sstevel@tonic-gate if (u.str == NULL) 14057c478bd9Sstevel@tonic-gate u.str = "<NULL>"; /* Be forgiving of NULL */ 14067c478bd9Sstevel@tonic-gate break; 14077c478bd9Sstevel@tonic-gate 14087c478bd9Sstevel@tonic-gate case 't': 14097c478bd9Sstevel@tonic-gate if (width != 0) { 14107c478bd9Sstevel@tonic-gate while (width-- > 0) 14117c478bd9Sstevel@tonic-gate mdb_iob_tab(iob); 14127c478bd9Sstevel@tonic-gate } else 14137c478bd9Sstevel@tonic-gate mdb_iob_tab(iob); 14147c478bd9Sstevel@tonic-gate 14157c478bd9Sstevel@tonic-gate format = ++p; 14167c478bd9Sstevel@tonic-gate continue; 14177c478bd9Sstevel@tonic-gate 14187c478bd9Sstevel@tonic-gate case 'T': 14197c478bd9Sstevel@tonic-gate if (width != 0 && (iob->iob_nbytes % width) != 0) { 14207c478bd9Sstevel@tonic-gate size_t ots = iob->iob_tabstop; 14217c478bd9Sstevel@tonic-gate iob->iob_tabstop = width; 14227c478bd9Sstevel@tonic-gate mdb_iob_tab(iob); 14237c478bd9Sstevel@tonic-gate iob->iob_tabstop = ots; 14247c478bd9Sstevel@tonic-gate } 14257c478bd9Sstevel@tonic-gate format = ++p; 14267c478bd9Sstevel@tonic-gate continue; 14277c478bd9Sstevel@tonic-gate 14287c478bd9Sstevel@tonic-gate case 'u': 14297c478bd9Sstevel@tonic-gate if (f_alt) 14307c478bd9Sstevel@tonic-gate flags |= NTOS_SHOWBASE; 14317c478bd9Sstevel@tonic-gate u.str = iob_int2str(ap, size, 10, 14327c478bd9Sstevel@tonic-gate flags | NTOS_UNSIGNED, &zero, &val); 14337c478bd9Sstevel@tonic-gate break; 14347c478bd9Sstevel@tonic-gate 14357c478bd9Sstevel@tonic-gate case 'x': 14367c478bd9Sstevel@tonic-gate u.str = iob_int2str(ap, size, 16, NTOS_UNSIGNED, 14377c478bd9Sstevel@tonic-gate &zero, &val); 14387c478bd9Sstevel@tonic-gate 14397c478bd9Sstevel@tonic-gate if (f_alt && !zero) { 14407c478bd9Sstevel@tonic-gate altstr = "0x"; 14417c478bd9Sstevel@tonic-gate altlen = 2; 14427c478bd9Sstevel@tonic-gate } 14437c478bd9Sstevel@tonic-gate break; 14447c478bd9Sstevel@tonic-gate 14457c478bd9Sstevel@tonic-gate case 'X': 14467c478bd9Sstevel@tonic-gate u.str = iob_int2str(ap, size, 16, 14477c478bd9Sstevel@tonic-gate NTOS_UNSIGNED | NTOS_UPCASE, &zero, &val); 14487c478bd9Sstevel@tonic-gate 14497c478bd9Sstevel@tonic-gate if (f_alt && !zero) { 14507c478bd9Sstevel@tonic-gate altstr = "0X"; 14517c478bd9Sstevel@tonic-gate altlen = 2; 14527c478bd9Sstevel@tonic-gate } 14537c478bd9Sstevel@tonic-gate break; 14547c478bd9Sstevel@tonic-gate 14557c478bd9Sstevel@tonic-gate case 'Y': 14567c478bd9Sstevel@tonic-gate u.tm = VA_ARG(ap, time_t); 14577c478bd9Sstevel@tonic-gate u.str = iob_time2str(&u.tm); 14587c478bd9Sstevel@tonic-gate break; 14597c478bd9Sstevel@tonic-gate 14607c478bd9Sstevel@tonic-gate case '<': 14617c478bd9Sstevel@tonic-gate /* 14627c478bd9Sstevel@tonic-gate * Used to turn attributes on (<b>), to turn them 14637c478bd9Sstevel@tonic-gate * off (</b>), or to print variables (<_var>). 14647c478bd9Sstevel@tonic-gate */ 14657c478bd9Sstevel@tonic-gate for (u.str = ++p; *p != '\0' && *p != '>'; p++) 14667c478bd9Sstevel@tonic-gate continue; 14677c478bd9Sstevel@tonic-gate 14687c478bd9Sstevel@tonic-gate if (*p == '>') { 14697c478bd9Sstevel@tonic-gate size_t paramlen = p - u.str; 14707c478bd9Sstevel@tonic-gate 14717c478bd9Sstevel@tonic-gate if (paramlen > 0) { 14727c478bd9Sstevel@tonic-gate if (*u.str == '_') { 14737c478bd9Sstevel@tonic-gate u.str = iob_getvar(u.str + 1, 14747c478bd9Sstevel@tonic-gate paramlen - 1); 14757c478bd9Sstevel@tonic-gate break; 14767c478bd9Sstevel@tonic-gate } else { 14777c478bd9Sstevel@tonic-gate (void) iob_setattr(iob, u.str, 14787c478bd9Sstevel@tonic-gate paramlen); 14797c478bd9Sstevel@tonic-gate } 14807c478bd9Sstevel@tonic-gate } 14817c478bd9Sstevel@tonic-gate 14827c478bd9Sstevel@tonic-gate p++; 14837c478bd9Sstevel@tonic-gate } 14847c478bd9Sstevel@tonic-gate 14857c478bd9Sstevel@tonic-gate format = p; 14867c478bd9Sstevel@tonic-gate continue; 14877c478bd9Sstevel@tonic-gate 14887c478bd9Sstevel@tonic-gate case '*': 14897c478bd9Sstevel@tonic-gate width = (size_t)(uint_t)VA_ARG(ap, int); 14907c478bd9Sstevel@tonic-gate goto fmt_switch; 14917c478bd9Sstevel@tonic-gate 14927c478bd9Sstevel@tonic-gate case '%': 14937c478bd9Sstevel@tonic-gate u.str = "%"; 14947c478bd9Sstevel@tonic-gate break; 14957c478bd9Sstevel@tonic-gate 14967c478bd9Sstevel@tonic-gate case '?': 14977c478bd9Sstevel@tonic-gate width = sizeof (uintptr_t) * 2; 14987c478bd9Sstevel@tonic-gate goto fmt_switch; 14997c478bd9Sstevel@tonic-gate 15007c478bd9Sstevel@tonic-gate case '#': 15017c478bd9Sstevel@tonic-gate f_alt = TRUE; 15027c478bd9Sstevel@tonic-gate goto fmt_switch; 15037c478bd9Sstevel@tonic-gate 15047c478bd9Sstevel@tonic-gate case '+': 15057c478bd9Sstevel@tonic-gate flags |= NTOS_SIGNPOS; 15067c478bd9Sstevel@tonic-gate goto fmt_switch; 15077c478bd9Sstevel@tonic-gate 15087c478bd9Sstevel@tonic-gate case '-': 15097c478bd9Sstevel@tonic-gate f_left = TRUE; 15107c478bd9Sstevel@tonic-gate goto fmt_switch; 15117c478bd9Sstevel@tonic-gate 15127c478bd9Sstevel@tonic-gate default: 15137c478bd9Sstevel@tonic-gate c[0] = p[0]; 15147c478bd9Sstevel@tonic-gate u.str = c; 15157c478bd9Sstevel@tonic-gate } 15167c478bd9Sstevel@tonic-gate 15177c478bd9Sstevel@tonic-gate len = u.str != NULL ? strlen(u.str) : 0; 15187c478bd9Sstevel@tonic-gate 15197c478bd9Sstevel@tonic-gate if (len + altlen > width) 15207c478bd9Sstevel@tonic-gate width = len + altlen; 15217c478bd9Sstevel@tonic-gate 15227c478bd9Sstevel@tonic-gate /* 15237c478bd9Sstevel@tonic-gate * If the string and the option altstr won't fit on this line 15247c478bd9Sstevel@tonic-gate * and auto-wrap is set (default), skip to the next line. 1525218912f6SSteve Gonczi * If the string contains \n, and the \n terminated substring 1526218912f6SSteve Gonczi * + altstr is shorter than the above, use the shorter lf_len. 15277c478bd9Sstevel@tonic-gate */ 1528218912f6SSteve Gonczi if (u.str != NULL) { 1529218912f6SSteve Gonczi char *np = strchr(u.str, '\n'); 1530218912f6SSteve Gonczi if (np != NULL) { 1531218912f6SSteve Gonczi int lf_len = (np - u.str) + altlen; 1532218912f6SSteve Gonczi if (lf_len < width) 1533218912f6SSteve Gonczi width = lf_len; 1534218912f6SSteve Gonczi } 1535218912f6SSteve Gonczi } 15367c478bd9Sstevel@tonic-gate if (IOB_WRAPNOW(iob, width)) 15377c478bd9Sstevel@tonic-gate mdb_iob_nl(iob); 15387c478bd9Sstevel@tonic-gate 15397c478bd9Sstevel@tonic-gate /* 15407c478bd9Sstevel@tonic-gate * Optionally add whitespace or zeroes prefixing the value if 15417c478bd9Sstevel@tonic-gate * we haven't filled the minimum width and we're right-aligned. 15427c478bd9Sstevel@tonic-gate */ 15437c478bd9Sstevel@tonic-gate if (len < (width - altlen) && f_left == FALSE) { 15447c478bd9Sstevel@tonic-gate mdb_iob_fill(iob, f_zfill ? '0' : ' ', 15457c478bd9Sstevel@tonic-gate width - altlen - len); 15467c478bd9Sstevel@tonic-gate } 15477c478bd9Sstevel@tonic-gate 15487c478bd9Sstevel@tonic-gate /* 15497c478bd9Sstevel@tonic-gate * Print the alternate string if it's a prefix, and then 15507c478bd9Sstevel@tonic-gate * print the value string itself. 15517c478bd9Sstevel@tonic-gate */ 15527c478bd9Sstevel@tonic-gate if (altstr != NULL && f_altsuff == FALSE) 15537c478bd9Sstevel@tonic-gate mdb_iob_nputs(iob, altstr, altlen); 15547c478bd9Sstevel@tonic-gate if (len != 0) 15557c478bd9Sstevel@tonic-gate mdb_iob_nputs(iob, u.str, len); 15567c478bd9Sstevel@tonic-gate 15577c478bd9Sstevel@tonic-gate /* 15587c478bd9Sstevel@tonic-gate * If we have an alternate string and it's a suffix, print it. 15597c478bd9Sstevel@tonic-gate */ 15607c478bd9Sstevel@tonic-gate if (altstr != NULL && f_altsuff == TRUE) 15617c478bd9Sstevel@tonic-gate mdb_iob_nputs(iob, altstr, altlen); 15627c478bd9Sstevel@tonic-gate 15637c478bd9Sstevel@tonic-gate /* 15647c478bd9Sstevel@tonic-gate * Finally, if we haven't filled the field width and we're 15657c478bd9Sstevel@tonic-gate * left-aligned, pad out the rest with whitespace. 15667c478bd9Sstevel@tonic-gate */ 15677c478bd9Sstevel@tonic-gate if ((len + altlen) < width && f_left == TRUE) 15687c478bd9Sstevel@tonic-gate mdb_iob_ws(iob, width - altlen - len); 15697c478bd9Sstevel@tonic-gate 15707c478bd9Sstevel@tonic-gate format = (*p != '\0') ? ++p : p; 15717c478bd9Sstevel@tonic-gate } 15727c478bd9Sstevel@tonic-gate 15737c478bd9Sstevel@tonic-gate /* 15747c478bd9Sstevel@tonic-gate * If there's anything left in the format string, output it now 15757c478bd9Sstevel@tonic-gate */ 15767c478bd9Sstevel@tonic-gate if (*format != '\0') { 15777c478bd9Sstevel@tonic-gate len = strlen(format); 15787c478bd9Sstevel@tonic-gate if (IOB_WRAPNOW(iob, len) && *format != '\n') 15797c478bd9Sstevel@tonic-gate mdb_iob_nl(iob); 15807c478bd9Sstevel@tonic-gate mdb_iob_nputs(iob, format, len); 15817c478bd9Sstevel@tonic-gate } 15827c478bd9Sstevel@tonic-gate } 15837c478bd9Sstevel@tonic-gate 15847c478bd9Sstevel@tonic-gate void 15857c478bd9Sstevel@tonic-gate mdb_iob_vprintf(mdb_iob_t *iob, const char *format, va_list alist) 15867c478bd9Sstevel@tonic-gate { 15877c478bd9Sstevel@tonic-gate varglist_t ap = { VAT_VARARGS }; 15887c478bd9Sstevel@tonic-gate va_copy(ap.val_valist, alist); 15897c478bd9Sstevel@tonic-gate iob_doprnt(iob, format, &ap); 15907c478bd9Sstevel@tonic-gate } 15917c478bd9Sstevel@tonic-gate 15927c478bd9Sstevel@tonic-gate void 15937c478bd9Sstevel@tonic-gate mdb_iob_aprintf(mdb_iob_t *iob, const char *format, const mdb_arg_t *argv) 15947c478bd9Sstevel@tonic-gate { 15957c478bd9Sstevel@tonic-gate varglist_t ap = { VAT_ARGVEC }; 15967c478bd9Sstevel@tonic-gate ap.val_argv = argv; 15977c478bd9Sstevel@tonic-gate iob_doprnt(iob, format, &ap); 15987c478bd9Sstevel@tonic-gate } 15997c478bd9Sstevel@tonic-gate 16007c478bd9Sstevel@tonic-gate void 16017c478bd9Sstevel@tonic-gate mdb_iob_printf(mdb_iob_t *iob, const char *format, ...) 16027c478bd9Sstevel@tonic-gate { 16037c478bd9Sstevel@tonic-gate va_list alist; 16047c478bd9Sstevel@tonic-gate 16057c478bd9Sstevel@tonic-gate va_start(alist, format); 16067c478bd9Sstevel@tonic-gate mdb_iob_vprintf(iob, format, alist); 16077c478bd9Sstevel@tonic-gate va_end(alist); 16087c478bd9Sstevel@tonic-gate } 16097c478bd9Sstevel@tonic-gate 16107c478bd9Sstevel@tonic-gate /* 16117c478bd9Sstevel@tonic-gate * In order to handle the sprintf family of functions, we define a special 16127c478bd9Sstevel@tonic-gate * i/o backend known as a "sprintf buf" (or spbuf for short). This back end 16137c478bd9Sstevel@tonic-gate * provides an IOP_WRITE entry point that concatenates each buffer sent from 16147c478bd9Sstevel@tonic-gate * mdb_iob_flush() onto the caller's buffer until the caller's buffer is 16157c478bd9Sstevel@tonic-gate * exhausted. We also keep an absolute count of how many bytes were sent to 16167c478bd9Sstevel@tonic-gate * this function during the lifetime of the snprintf call. This allows us 16177c478bd9Sstevel@tonic-gate * to provide the ability to (1) return the total size required for the given 16187c478bd9Sstevel@tonic-gate * format string and argument list, and (2) support a call to snprintf with a 16197c478bd9Sstevel@tonic-gate * NULL buffer argument with no special case code elsewhere. 16207c478bd9Sstevel@tonic-gate */ 16217c478bd9Sstevel@tonic-gate static ssize_t 16227c478bd9Sstevel@tonic-gate spbuf_write(mdb_io_t *io, const void *buf, size_t buflen) 16237c478bd9Sstevel@tonic-gate { 16247c478bd9Sstevel@tonic-gate spbuf_t *spb = io->io_data; 16257c478bd9Sstevel@tonic-gate 16267c478bd9Sstevel@tonic-gate if (spb->spb_bufsiz != 0) { 16277c478bd9Sstevel@tonic-gate size_t n = MIN(spb->spb_bufsiz, buflen); 16287c478bd9Sstevel@tonic-gate bcopy(buf, spb->spb_buf, n); 16297c478bd9Sstevel@tonic-gate spb->spb_buf += n; 16307c478bd9Sstevel@tonic-gate spb->spb_bufsiz -= n; 16317c478bd9Sstevel@tonic-gate } 16327c478bd9Sstevel@tonic-gate 16337c478bd9Sstevel@tonic-gate spb->spb_total += buflen; 16347c478bd9Sstevel@tonic-gate return (buflen); 16357c478bd9Sstevel@tonic-gate } 16367c478bd9Sstevel@tonic-gate 16377c478bd9Sstevel@tonic-gate static const mdb_io_ops_t spbuf_ops = { 16387c478bd9Sstevel@tonic-gate no_io_read, 16397c478bd9Sstevel@tonic-gate spbuf_write, 16407c478bd9Sstevel@tonic-gate no_io_seek, 16417c478bd9Sstevel@tonic-gate no_io_ctl, 16427c478bd9Sstevel@tonic-gate no_io_close, 16437c478bd9Sstevel@tonic-gate no_io_name, 16447c478bd9Sstevel@tonic-gate no_io_link, 16457c478bd9Sstevel@tonic-gate no_io_unlink, 16467c478bd9Sstevel@tonic-gate no_io_setattr, 16477c478bd9Sstevel@tonic-gate no_io_suspend, 16487c478bd9Sstevel@tonic-gate no_io_resume 16497c478bd9Sstevel@tonic-gate }; 16507c478bd9Sstevel@tonic-gate 16517c478bd9Sstevel@tonic-gate /* 16527c478bd9Sstevel@tonic-gate * The iob_spb_create function initializes an iob suitable for snprintf calls, 16537c478bd9Sstevel@tonic-gate * a spbuf i/o backend, and the spbuf private data, and then glues these 16547c478bd9Sstevel@tonic-gate * objects together. The caller (either vsnprintf or asnprintf below) is 16557c478bd9Sstevel@tonic-gate * expected to have allocated the various structures on their stack. 16567c478bd9Sstevel@tonic-gate */ 16577c478bd9Sstevel@tonic-gate static void 16587c478bd9Sstevel@tonic-gate iob_spb_create(mdb_iob_t *iob, char *iob_buf, size_t iob_len, 16597c478bd9Sstevel@tonic-gate mdb_io_t *io, spbuf_t *spb, char *spb_buf, size_t spb_len) 16607c478bd9Sstevel@tonic-gate { 16617c478bd9Sstevel@tonic-gate spb->spb_buf = spb_buf; 16627c478bd9Sstevel@tonic-gate spb->spb_bufsiz = spb_len; 16637c478bd9Sstevel@tonic-gate spb->spb_total = 0; 16647c478bd9Sstevel@tonic-gate 16657c478bd9Sstevel@tonic-gate io->io_ops = &spbuf_ops; 16667c478bd9Sstevel@tonic-gate io->io_data = spb; 16677c478bd9Sstevel@tonic-gate io->io_next = NULL; 16687c478bd9Sstevel@tonic-gate io->io_refcnt = 1; 16697c478bd9Sstevel@tonic-gate 16707c478bd9Sstevel@tonic-gate iob->iob_buf = iob_buf; 16717c478bd9Sstevel@tonic-gate iob->iob_bufsiz = iob_len; 16727c478bd9Sstevel@tonic-gate iob->iob_bufp = iob_buf; 16737c478bd9Sstevel@tonic-gate iob->iob_nbytes = 0; 16747c478bd9Sstevel@tonic-gate iob->iob_nlines = 0; 16757c478bd9Sstevel@tonic-gate iob->iob_lineno = 1; 16767c478bd9Sstevel@tonic-gate iob->iob_rows = MDB_IOB_DEFROWS; 16777c478bd9Sstevel@tonic-gate iob->iob_cols = iob_len; 16787c478bd9Sstevel@tonic-gate iob->iob_tabstop = MDB_IOB_DEFTAB; 16797c478bd9Sstevel@tonic-gate iob->iob_margin = MDB_IOB_DEFMARGIN; 16807c478bd9Sstevel@tonic-gate iob->iob_flags = MDB_IOB_WRONLY; 16817c478bd9Sstevel@tonic-gate iob->iob_iop = io; 16827c478bd9Sstevel@tonic-gate iob->iob_pgp = NULL; 16837c478bd9Sstevel@tonic-gate iob->iob_next = NULL; 16847c478bd9Sstevel@tonic-gate } 16857c478bd9Sstevel@tonic-gate 16867c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 16877c478bd9Sstevel@tonic-gate ssize_t 16887c478bd9Sstevel@tonic-gate null_io_write(mdb_io_t *io, const void *buf, size_t nbytes) 16897c478bd9Sstevel@tonic-gate { 16907c478bd9Sstevel@tonic-gate return (nbytes); 16917c478bd9Sstevel@tonic-gate } 16927c478bd9Sstevel@tonic-gate 16937c478bd9Sstevel@tonic-gate static const mdb_io_ops_t null_ops = { 16947c478bd9Sstevel@tonic-gate no_io_read, 16957c478bd9Sstevel@tonic-gate null_io_write, 16967c478bd9Sstevel@tonic-gate no_io_seek, 16977c478bd9Sstevel@tonic-gate no_io_ctl, 16987c478bd9Sstevel@tonic-gate no_io_close, 16997c478bd9Sstevel@tonic-gate no_io_name, 17007c478bd9Sstevel@tonic-gate no_io_link, 17017c478bd9Sstevel@tonic-gate no_io_unlink, 17027c478bd9Sstevel@tonic-gate no_io_setattr, 17037c478bd9Sstevel@tonic-gate no_io_suspend, 17047c478bd9Sstevel@tonic-gate no_io_resume 17057c478bd9Sstevel@tonic-gate }; 17067c478bd9Sstevel@tonic-gate 17077c478bd9Sstevel@tonic-gate mdb_io_t * 17087c478bd9Sstevel@tonic-gate mdb_nullio_create(void) 17097c478bd9Sstevel@tonic-gate { 17107c478bd9Sstevel@tonic-gate static mdb_io_t null_io = { 17117c478bd9Sstevel@tonic-gate &null_ops, 17127c478bd9Sstevel@tonic-gate NULL, 17137c478bd9Sstevel@tonic-gate NULL, 17147c478bd9Sstevel@tonic-gate 1 17157c478bd9Sstevel@tonic-gate }; 17167c478bd9Sstevel@tonic-gate 17177c478bd9Sstevel@tonic-gate return (&null_io); 17187c478bd9Sstevel@tonic-gate } 17197c478bd9Sstevel@tonic-gate 17207c478bd9Sstevel@tonic-gate size_t 17217c478bd9Sstevel@tonic-gate mdb_iob_vsnprintf(char *buf, size_t nbytes, const char *format, va_list alist) 17227c478bd9Sstevel@tonic-gate { 17237c478bd9Sstevel@tonic-gate varglist_t ap = { VAT_VARARGS }; 17247c478bd9Sstevel@tonic-gate char iob_buf[64]; 17257c478bd9Sstevel@tonic-gate mdb_iob_t iob; 17267c478bd9Sstevel@tonic-gate mdb_io_t io; 17277c478bd9Sstevel@tonic-gate spbuf_t spb; 17287c478bd9Sstevel@tonic-gate 17297c478bd9Sstevel@tonic-gate ASSERT(buf != NULL || nbytes == 0); 17307c478bd9Sstevel@tonic-gate iob_spb_create(&iob, iob_buf, sizeof (iob_buf), &io, &spb, buf, nbytes); 17317c478bd9Sstevel@tonic-gate va_copy(ap.val_valist, alist); 17327c478bd9Sstevel@tonic-gate iob_doprnt(&iob, format, &ap); 17337c478bd9Sstevel@tonic-gate mdb_iob_flush(&iob); 17347c478bd9Sstevel@tonic-gate 17357c478bd9Sstevel@tonic-gate if (spb.spb_bufsiz != 0) 17367c478bd9Sstevel@tonic-gate *spb.spb_buf = '\0'; 17377c478bd9Sstevel@tonic-gate else if (buf != NULL && nbytes > 0) 17387c478bd9Sstevel@tonic-gate *--spb.spb_buf = '\0'; 17397c478bd9Sstevel@tonic-gate 17407c478bd9Sstevel@tonic-gate return (spb.spb_total); 17417c478bd9Sstevel@tonic-gate } 17427c478bd9Sstevel@tonic-gate 17437c478bd9Sstevel@tonic-gate size_t 17447c478bd9Sstevel@tonic-gate mdb_iob_asnprintf(char *buf, size_t nbytes, const char *format, 17457c478bd9Sstevel@tonic-gate const mdb_arg_t *argv) 17467c478bd9Sstevel@tonic-gate { 17477c478bd9Sstevel@tonic-gate varglist_t ap = { VAT_ARGVEC }; 17487c478bd9Sstevel@tonic-gate char iob_buf[64]; 17497c478bd9Sstevel@tonic-gate mdb_iob_t iob; 17507c478bd9Sstevel@tonic-gate mdb_io_t io; 17517c478bd9Sstevel@tonic-gate spbuf_t spb; 17527c478bd9Sstevel@tonic-gate 17537c478bd9Sstevel@tonic-gate ASSERT(buf != NULL || nbytes == 0); 17547c478bd9Sstevel@tonic-gate iob_spb_create(&iob, iob_buf, sizeof (iob_buf), &io, &spb, buf, nbytes); 17557c478bd9Sstevel@tonic-gate ap.val_argv = argv; 17567c478bd9Sstevel@tonic-gate iob_doprnt(&iob, format, &ap); 17577c478bd9Sstevel@tonic-gate mdb_iob_flush(&iob); 17587c478bd9Sstevel@tonic-gate 17597c478bd9Sstevel@tonic-gate if (spb.spb_bufsiz != 0) 17607c478bd9Sstevel@tonic-gate *spb.spb_buf = '\0'; 17617c478bd9Sstevel@tonic-gate else if (buf != NULL && nbytes > 0) 17627c478bd9Sstevel@tonic-gate *--spb.spb_buf = '\0'; 17637c478bd9Sstevel@tonic-gate 17647c478bd9Sstevel@tonic-gate return (spb.spb_total); 17657c478bd9Sstevel@tonic-gate } 17667c478bd9Sstevel@tonic-gate 17677c478bd9Sstevel@tonic-gate /*PRINTFLIKE3*/ 17687c478bd9Sstevel@tonic-gate size_t 17697c478bd9Sstevel@tonic-gate mdb_iob_snprintf(char *buf, size_t nbytes, const char *format, ...) 17707c478bd9Sstevel@tonic-gate { 17717c478bd9Sstevel@tonic-gate va_list alist; 17727c478bd9Sstevel@tonic-gate 17737c478bd9Sstevel@tonic-gate va_start(alist, format); 17747c478bd9Sstevel@tonic-gate nbytes = mdb_iob_vsnprintf(buf, nbytes, format, alist); 17757c478bd9Sstevel@tonic-gate va_end(alist); 17767c478bd9Sstevel@tonic-gate 17777c478bd9Sstevel@tonic-gate return (nbytes); 17787c478bd9Sstevel@tonic-gate } 17797c478bd9Sstevel@tonic-gate 17807c478bd9Sstevel@tonic-gate void 17817c478bd9Sstevel@tonic-gate mdb_iob_nputs(mdb_iob_t *iob, const char *s, size_t nbytes) 17827c478bd9Sstevel@tonic-gate { 17837c478bd9Sstevel@tonic-gate size_t m, n, nleft = nbytes; 17847c478bd9Sstevel@tonic-gate const char *p, *q = s; 17857c478bd9Sstevel@tonic-gate 17867c478bd9Sstevel@tonic-gate ASSERT(iob->iob_flags & MDB_IOB_WRONLY); 17877c478bd9Sstevel@tonic-gate 17887c478bd9Sstevel@tonic-gate if (nbytes == 0) 17897c478bd9Sstevel@tonic-gate return; /* Return immediately if there is no work to do */ 17907c478bd9Sstevel@tonic-gate 17917c478bd9Sstevel@tonic-gate /* 17927c478bd9Sstevel@tonic-gate * If the string contains embedded newlines or tabs, invoke ourself 17937c478bd9Sstevel@tonic-gate * recursively for each string component, followed by a call to the 17947c478bd9Sstevel@tonic-gate * newline or tab routine. This insures that strings with these 17957c478bd9Sstevel@tonic-gate * characters obey our wrapping and indenting rules, and that strings 17967c478bd9Sstevel@tonic-gate * with embedded newlines are flushed after each newline, allowing 17977c478bd9Sstevel@tonic-gate * the output pager to take over if it is enabled. 17987c478bd9Sstevel@tonic-gate */ 17997c478bd9Sstevel@tonic-gate while ((p = strnpbrk(q, "\t\n", nleft)) != NULL) { 18007c478bd9Sstevel@tonic-gate if (p > q) 18017c478bd9Sstevel@tonic-gate mdb_iob_nputs(iob, q, (size_t)(p - q)); 18027c478bd9Sstevel@tonic-gate 18037c478bd9Sstevel@tonic-gate if (*p == '\t') 18047c478bd9Sstevel@tonic-gate mdb_iob_tab(iob); 18057c478bd9Sstevel@tonic-gate else 18067c478bd9Sstevel@tonic-gate mdb_iob_nl(iob); 18077c478bd9Sstevel@tonic-gate 18087c478bd9Sstevel@tonic-gate nleft -= (size_t)(p - q) + 1; /* Update byte count */ 18097c478bd9Sstevel@tonic-gate q = p + 1; /* Advance past delimiter */ 18107c478bd9Sstevel@tonic-gate } 18117c478bd9Sstevel@tonic-gate 18127c478bd9Sstevel@tonic-gate /* 18137c478bd9Sstevel@tonic-gate * For a given string component, we determine how many bytes (n) we can 18147c478bd9Sstevel@tonic-gate * copy into our buffer (limited by either cols or bufsiz depending 18157c478bd9Sstevel@tonic-gate * on whether AUTOWRAP is on), copy a chunk into the buffer, and 18167c478bd9Sstevel@tonic-gate * flush the buffer if we reach the end of a line. 18177c478bd9Sstevel@tonic-gate */ 18187c478bd9Sstevel@tonic-gate while (nleft != 0) { 1819f76ff24cSBryan Cantrill if (IOB_AUTOWRAP(iob)) { 18207c478bd9Sstevel@tonic-gate ASSERT(iob->iob_cols >= iob->iob_nbytes); 18217c478bd9Sstevel@tonic-gate n = iob->iob_cols - iob->iob_nbytes; 18227c478bd9Sstevel@tonic-gate } else { 18237c478bd9Sstevel@tonic-gate ASSERT(iob->iob_bufsiz >= iob->iob_nbytes); 18247c478bd9Sstevel@tonic-gate n = iob->iob_bufsiz - iob->iob_nbytes; 18257c478bd9Sstevel@tonic-gate } 18267c478bd9Sstevel@tonic-gate 18277c478bd9Sstevel@tonic-gate m = MIN(nleft, n); /* copy at most n bytes in this pass */ 18287c478bd9Sstevel@tonic-gate 18297c478bd9Sstevel@tonic-gate bcopy(q, iob->iob_bufp, m); 18307c478bd9Sstevel@tonic-gate nleft -= m; 18317c478bd9Sstevel@tonic-gate q += m; 18327c478bd9Sstevel@tonic-gate 18337c478bd9Sstevel@tonic-gate iob->iob_bufp += m; 18347c478bd9Sstevel@tonic-gate iob->iob_nbytes += m; 18357c478bd9Sstevel@tonic-gate 18367c478bd9Sstevel@tonic-gate if (m == n && nleft != 0) { 1837f76ff24cSBryan Cantrill if (IOB_AUTOWRAP(iob)) { 18387c478bd9Sstevel@tonic-gate mdb_iob_nl(iob); 1839f76ff24cSBryan Cantrill } else { 18407c478bd9Sstevel@tonic-gate mdb_iob_flush(iob); 1841f76ff24cSBryan Cantrill } 18427c478bd9Sstevel@tonic-gate } 18437c478bd9Sstevel@tonic-gate } 18447c478bd9Sstevel@tonic-gate } 18457c478bd9Sstevel@tonic-gate 18467c478bd9Sstevel@tonic-gate void 18477c478bd9Sstevel@tonic-gate mdb_iob_puts(mdb_iob_t *iob, const char *s) 18487c478bd9Sstevel@tonic-gate { 18497c478bd9Sstevel@tonic-gate mdb_iob_nputs(iob, s, strlen(s)); 18507c478bd9Sstevel@tonic-gate } 18517c478bd9Sstevel@tonic-gate 18527c478bd9Sstevel@tonic-gate void 18537c478bd9Sstevel@tonic-gate mdb_iob_putc(mdb_iob_t *iob, int c) 18547c478bd9Sstevel@tonic-gate { 18557c478bd9Sstevel@tonic-gate mdb_iob_fill(iob, c, 1); 18567c478bd9Sstevel@tonic-gate } 18577c478bd9Sstevel@tonic-gate 18587c478bd9Sstevel@tonic-gate void 18597c478bd9Sstevel@tonic-gate mdb_iob_tab(mdb_iob_t *iob) 18607c478bd9Sstevel@tonic-gate { 18617c478bd9Sstevel@tonic-gate ASSERT(iob->iob_flags & MDB_IOB_WRONLY); 18627c478bd9Sstevel@tonic-gate 18637c478bd9Sstevel@tonic-gate if (iob->iob_tabstop != 0) { 18647c478bd9Sstevel@tonic-gate /* 18657c478bd9Sstevel@tonic-gate * Round up to the next multiple of the tabstop. If this puts 18667c478bd9Sstevel@tonic-gate * us off the end of the line, just insert a newline; otherwise 18677c478bd9Sstevel@tonic-gate * insert sufficient whitespace to reach position n. 18687c478bd9Sstevel@tonic-gate */ 18697c478bd9Sstevel@tonic-gate size_t n = (iob->iob_nbytes + iob->iob_tabstop) / 18707c478bd9Sstevel@tonic-gate iob->iob_tabstop * iob->iob_tabstop; 18717c478bd9Sstevel@tonic-gate 18727c478bd9Sstevel@tonic-gate if (n < iob->iob_cols) 18737c478bd9Sstevel@tonic-gate mdb_iob_fill(iob, ' ', n - iob->iob_nbytes); 18747c478bd9Sstevel@tonic-gate else 18757c478bd9Sstevel@tonic-gate mdb_iob_nl(iob); 18767c478bd9Sstevel@tonic-gate } 18777c478bd9Sstevel@tonic-gate } 18787c478bd9Sstevel@tonic-gate 18797c478bd9Sstevel@tonic-gate void 18807c478bd9Sstevel@tonic-gate mdb_iob_fill(mdb_iob_t *iob, int c, size_t nfill) 18817c478bd9Sstevel@tonic-gate { 18827c478bd9Sstevel@tonic-gate size_t i, m, n; 18837c478bd9Sstevel@tonic-gate 18847c478bd9Sstevel@tonic-gate ASSERT(iob->iob_flags & MDB_IOB_WRONLY); 18857c478bd9Sstevel@tonic-gate 18867c478bd9Sstevel@tonic-gate while (nfill != 0) { 1887f76ff24cSBryan Cantrill if (IOB_AUTOWRAP(iob)) { 18887c478bd9Sstevel@tonic-gate ASSERT(iob->iob_cols >= iob->iob_nbytes); 18897c478bd9Sstevel@tonic-gate n = iob->iob_cols - iob->iob_nbytes; 18907c478bd9Sstevel@tonic-gate } else { 18917c478bd9Sstevel@tonic-gate ASSERT(iob->iob_bufsiz >= iob->iob_nbytes); 18927c478bd9Sstevel@tonic-gate n = iob->iob_bufsiz - iob->iob_nbytes; 18937c478bd9Sstevel@tonic-gate } 18947c478bd9Sstevel@tonic-gate 18957c478bd9Sstevel@tonic-gate m = MIN(nfill, n); /* fill at most n bytes in this pass */ 18967c478bd9Sstevel@tonic-gate 18977c478bd9Sstevel@tonic-gate for (i = 0; i < m; i++) 18987c478bd9Sstevel@tonic-gate *iob->iob_bufp++ = (char)c; 18997c478bd9Sstevel@tonic-gate 19007c478bd9Sstevel@tonic-gate iob->iob_nbytes += m; 19017c478bd9Sstevel@tonic-gate nfill -= m; 19027c478bd9Sstevel@tonic-gate 19037c478bd9Sstevel@tonic-gate if (m == n && nfill != 0) { 1904f76ff24cSBryan Cantrill if (IOB_AUTOWRAP(iob)) { 19057c478bd9Sstevel@tonic-gate mdb_iob_nl(iob); 1906f76ff24cSBryan Cantrill } else { 19077c478bd9Sstevel@tonic-gate mdb_iob_flush(iob); 1908f76ff24cSBryan Cantrill } 19097c478bd9Sstevel@tonic-gate } 19107c478bd9Sstevel@tonic-gate } 19117c478bd9Sstevel@tonic-gate } 19127c478bd9Sstevel@tonic-gate 19137c478bd9Sstevel@tonic-gate void 19147c478bd9Sstevel@tonic-gate mdb_iob_ws(mdb_iob_t *iob, size_t n) 19157c478bd9Sstevel@tonic-gate { 1916*c8a3ee0eSBryan Cantrill if (!IOB_AUTOWRAP(iob) || iob->iob_nbytes + n < iob->iob_cols) 19177c478bd9Sstevel@tonic-gate mdb_iob_fill(iob, ' ', n); 19187c478bd9Sstevel@tonic-gate else 19197c478bd9Sstevel@tonic-gate mdb_iob_nl(iob); 19207c478bd9Sstevel@tonic-gate } 19217c478bd9Sstevel@tonic-gate 19227c478bd9Sstevel@tonic-gate void 19237c478bd9Sstevel@tonic-gate mdb_iob_nl(mdb_iob_t *iob) 19247c478bd9Sstevel@tonic-gate { 19257c478bd9Sstevel@tonic-gate ASSERT(iob->iob_flags & MDB_IOB_WRONLY); 19267c478bd9Sstevel@tonic-gate 19277c478bd9Sstevel@tonic-gate if (iob->iob_nbytes == iob->iob_bufsiz) 19287c478bd9Sstevel@tonic-gate mdb_iob_flush(iob); 19297c478bd9Sstevel@tonic-gate 19307c478bd9Sstevel@tonic-gate *iob->iob_bufp++ = '\n'; 19317c478bd9Sstevel@tonic-gate iob->iob_nbytes++; 19327c478bd9Sstevel@tonic-gate 19337c478bd9Sstevel@tonic-gate mdb_iob_flush(iob); 19347c478bd9Sstevel@tonic-gate } 19357c478bd9Sstevel@tonic-gate 19367c478bd9Sstevel@tonic-gate ssize_t 19377c478bd9Sstevel@tonic-gate mdb_iob_ngets(mdb_iob_t *iob, char *buf, size_t n) 19387c478bd9Sstevel@tonic-gate { 19397c478bd9Sstevel@tonic-gate ssize_t resid = n - 1; 19407c478bd9Sstevel@tonic-gate ssize_t len; 19417c478bd9Sstevel@tonic-gate int c; 19427c478bd9Sstevel@tonic-gate 19437c478bd9Sstevel@tonic-gate if (iob->iob_flags & (MDB_IOB_WRONLY | MDB_IOB_EOF)) 19447c478bd9Sstevel@tonic-gate return (EOF); /* can't gets a write buf or a read buf at EOF */ 19457c478bd9Sstevel@tonic-gate 19467c478bd9Sstevel@tonic-gate if (n == 0) 19477c478bd9Sstevel@tonic-gate return (0); /* we need room for a terminating \0 */ 19487c478bd9Sstevel@tonic-gate 19497c478bd9Sstevel@tonic-gate while (resid != 0) { 19507c478bd9Sstevel@tonic-gate if (iob->iob_nbytes == 0 && iob_read(iob, iob->iob_iop) <= 0) 19517c478bd9Sstevel@tonic-gate goto done; /* failed to refill buffer */ 19527c478bd9Sstevel@tonic-gate 19537c478bd9Sstevel@tonic-gate for (len = MIN(iob->iob_nbytes, resid); len != 0; len--) { 19547c478bd9Sstevel@tonic-gate c = *iob->iob_bufp++; 19557c478bd9Sstevel@tonic-gate iob->iob_nbytes--; 19567c478bd9Sstevel@tonic-gate 19577c478bd9Sstevel@tonic-gate if (c == EOF || c == '\n') 19587c478bd9Sstevel@tonic-gate goto done; 19597c478bd9Sstevel@tonic-gate 19607c478bd9Sstevel@tonic-gate *buf++ = (char)c; 19617c478bd9Sstevel@tonic-gate resid--; 19627c478bd9Sstevel@tonic-gate } 19637c478bd9Sstevel@tonic-gate } 19647c478bd9Sstevel@tonic-gate done: 19657c478bd9Sstevel@tonic-gate *buf = '\0'; 19667c478bd9Sstevel@tonic-gate return (n - resid - 1); 19677c478bd9Sstevel@tonic-gate } 19687c478bd9Sstevel@tonic-gate 19697c478bd9Sstevel@tonic-gate int 19707c478bd9Sstevel@tonic-gate mdb_iob_getc(mdb_iob_t *iob) 19717c478bd9Sstevel@tonic-gate { 19727c478bd9Sstevel@tonic-gate int c; 19737c478bd9Sstevel@tonic-gate 19747c478bd9Sstevel@tonic-gate if (iob->iob_flags & (MDB_IOB_WRONLY | MDB_IOB_EOF | MDB_IOB_ERR)) 19757c478bd9Sstevel@tonic-gate return (EOF); /* can't getc if write-only, EOF, or error bit */ 19767c478bd9Sstevel@tonic-gate 19777c478bd9Sstevel@tonic-gate if (iob->iob_nbytes == 0 && iob_read(iob, iob->iob_iop) <= 0) 19787c478bd9Sstevel@tonic-gate return (EOF); /* failed to refill buffer */ 19797c478bd9Sstevel@tonic-gate 19807c478bd9Sstevel@tonic-gate c = (uchar_t)*iob->iob_bufp++; 19817c478bd9Sstevel@tonic-gate iob->iob_nbytes--; 19827c478bd9Sstevel@tonic-gate 19837c478bd9Sstevel@tonic-gate return (c); 19847c478bd9Sstevel@tonic-gate } 19857c478bd9Sstevel@tonic-gate 19867c478bd9Sstevel@tonic-gate int 19877c478bd9Sstevel@tonic-gate mdb_iob_ungetc(mdb_iob_t *iob, int c) 19887c478bd9Sstevel@tonic-gate { 19897c478bd9Sstevel@tonic-gate if (iob->iob_flags & (MDB_IOB_WRONLY | MDB_IOB_ERR)) 19907c478bd9Sstevel@tonic-gate return (EOF); /* can't ungetc if write-only or error bit set */ 19917c478bd9Sstevel@tonic-gate 19927c478bd9Sstevel@tonic-gate if (c == EOF || iob->iob_nbytes == iob->iob_bufsiz) 19937c478bd9Sstevel@tonic-gate return (EOF); /* can't ungetc EOF, or ungetc if buffer full */ 19947c478bd9Sstevel@tonic-gate 19957c478bd9Sstevel@tonic-gate *--iob->iob_bufp = (char)c; 19967c478bd9Sstevel@tonic-gate iob->iob_nbytes++; 19977c478bd9Sstevel@tonic-gate iob->iob_flags &= ~MDB_IOB_EOF; 19987c478bd9Sstevel@tonic-gate 19997c478bd9Sstevel@tonic-gate return (c); 20007c478bd9Sstevel@tonic-gate } 20017c478bd9Sstevel@tonic-gate 20027c478bd9Sstevel@tonic-gate int 20037c478bd9Sstevel@tonic-gate mdb_iob_eof(mdb_iob_t *iob) 20047c478bd9Sstevel@tonic-gate { 20057c478bd9Sstevel@tonic-gate return ((iob->iob_flags & (MDB_IOB_RDONLY | MDB_IOB_EOF)) == 20067c478bd9Sstevel@tonic-gate (MDB_IOB_RDONLY | MDB_IOB_EOF)); 20077c478bd9Sstevel@tonic-gate } 20087c478bd9Sstevel@tonic-gate 20097c478bd9Sstevel@tonic-gate int 20107c478bd9Sstevel@tonic-gate mdb_iob_err(mdb_iob_t *iob) 20117c478bd9Sstevel@tonic-gate { 20127c478bd9Sstevel@tonic-gate return ((iob->iob_flags & MDB_IOB_ERR) == MDB_IOB_ERR); 20137c478bd9Sstevel@tonic-gate } 20147c478bd9Sstevel@tonic-gate 20157c478bd9Sstevel@tonic-gate ssize_t 20167c478bd9Sstevel@tonic-gate mdb_iob_read(mdb_iob_t *iob, void *buf, size_t n) 20177c478bd9Sstevel@tonic-gate { 20187c478bd9Sstevel@tonic-gate ssize_t resid = n; 20197c478bd9Sstevel@tonic-gate ssize_t len; 20207c478bd9Sstevel@tonic-gate 20217c478bd9Sstevel@tonic-gate if (iob->iob_flags & (MDB_IOB_WRONLY | MDB_IOB_EOF | MDB_IOB_ERR)) 20227c478bd9Sstevel@tonic-gate return (0); /* can't read if write-only, eof, or error */ 20237c478bd9Sstevel@tonic-gate 20247c478bd9Sstevel@tonic-gate while (resid != 0) { 20257c478bd9Sstevel@tonic-gate if (iob->iob_nbytes == 0 && iob_read(iob, iob->iob_iop) <= 0) 20267c478bd9Sstevel@tonic-gate break; /* failed to refill buffer */ 20277c478bd9Sstevel@tonic-gate 20287c478bd9Sstevel@tonic-gate len = MIN(resid, iob->iob_nbytes); 20297c478bd9Sstevel@tonic-gate bcopy(iob->iob_bufp, buf, len); 20307c478bd9Sstevel@tonic-gate 20317c478bd9Sstevel@tonic-gate iob->iob_bufp += len; 20327c478bd9Sstevel@tonic-gate iob->iob_nbytes -= len; 20337c478bd9Sstevel@tonic-gate 20347c478bd9Sstevel@tonic-gate buf = (char *)buf + len; 20357c478bd9Sstevel@tonic-gate resid -= len; 20367c478bd9Sstevel@tonic-gate } 20377c478bd9Sstevel@tonic-gate 20387c478bd9Sstevel@tonic-gate return (n - resid); 20397c478bd9Sstevel@tonic-gate } 20407c478bd9Sstevel@tonic-gate 20417c478bd9Sstevel@tonic-gate /* 20427c478bd9Sstevel@tonic-gate * For now, all binary writes are performed unbuffered. This has the 20437c478bd9Sstevel@tonic-gate * side effect that the pager will not be triggered by mdb_iob_write. 20447c478bd9Sstevel@tonic-gate */ 20457c478bd9Sstevel@tonic-gate ssize_t 20467c478bd9Sstevel@tonic-gate mdb_iob_write(mdb_iob_t *iob, const void *buf, size_t n) 20477c478bd9Sstevel@tonic-gate { 20487c478bd9Sstevel@tonic-gate ssize_t ret; 20497c478bd9Sstevel@tonic-gate 20507c478bd9Sstevel@tonic-gate if (iob->iob_flags & MDB_IOB_ERR) 20517c478bd9Sstevel@tonic-gate return (set_errno(EIO)); 20527c478bd9Sstevel@tonic-gate if (iob->iob_flags & MDB_IOB_RDONLY) 20537c478bd9Sstevel@tonic-gate return (set_errno(EMDB_IORO)); 20547c478bd9Sstevel@tonic-gate 20557c478bd9Sstevel@tonic-gate mdb_iob_flush(iob); 20567c478bd9Sstevel@tonic-gate ret = iob_write(iob, iob->iob_iop, buf, n); 20577c478bd9Sstevel@tonic-gate 20587c478bd9Sstevel@tonic-gate if (ret < 0 && iob == mdb.m_out) 20597c478bd9Sstevel@tonic-gate longjmp(mdb.m_frame->f_pcb, MDB_ERR_OUTPUT); 20607c478bd9Sstevel@tonic-gate 20617c478bd9Sstevel@tonic-gate return (ret); 20627c478bd9Sstevel@tonic-gate } 20637c478bd9Sstevel@tonic-gate 20647c478bd9Sstevel@tonic-gate int 20657c478bd9Sstevel@tonic-gate mdb_iob_ctl(mdb_iob_t *iob, int req, void *arg) 20667c478bd9Sstevel@tonic-gate { 20677c478bd9Sstevel@tonic-gate return (IOP_CTL(iob->iob_iop, req, arg)); 20687c478bd9Sstevel@tonic-gate } 20697c478bd9Sstevel@tonic-gate 20707c478bd9Sstevel@tonic-gate const char * 20717c478bd9Sstevel@tonic-gate mdb_iob_name(mdb_iob_t *iob) 20727c478bd9Sstevel@tonic-gate { 20737c478bd9Sstevel@tonic-gate if (iob == NULL) 20747c478bd9Sstevel@tonic-gate return ("<NULL>"); 20757c478bd9Sstevel@tonic-gate 20767c478bd9Sstevel@tonic-gate return (IOP_NAME(iob->iob_iop)); 20777c478bd9Sstevel@tonic-gate } 20787c478bd9Sstevel@tonic-gate 20797c478bd9Sstevel@tonic-gate size_t 20807c478bd9Sstevel@tonic-gate mdb_iob_lineno(mdb_iob_t *iob) 20817c478bd9Sstevel@tonic-gate { 20827c478bd9Sstevel@tonic-gate return (iob->iob_lineno); 20837c478bd9Sstevel@tonic-gate } 20847c478bd9Sstevel@tonic-gate 20857c478bd9Sstevel@tonic-gate size_t 20867c478bd9Sstevel@tonic-gate mdb_iob_gettabstop(mdb_iob_t *iob) 20877c478bd9Sstevel@tonic-gate { 20887c478bd9Sstevel@tonic-gate return (iob->iob_tabstop); 20897c478bd9Sstevel@tonic-gate } 20907c478bd9Sstevel@tonic-gate 20917c478bd9Sstevel@tonic-gate size_t 20927c478bd9Sstevel@tonic-gate mdb_iob_getmargin(mdb_iob_t *iob) 20937c478bd9Sstevel@tonic-gate { 20947c478bd9Sstevel@tonic-gate return (iob->iob_margin); 20957c478bd9Sstevel@tonic-gate } 20967c478bd9Sstevel@tonic-gate 20977c478bd9Sstevel@tonic-gate mdb_io_t * 20987c478bd9Sstevel@tonic-gate mdb_io_hold(mdb_io_t *io) 20997c478bd9Sstevel@tonic-gate { 21007c478bd9Sstevel@tonic-gate io->io_refcnt++; 21017c478bd9Sstevel@tonic-gate return (io); 21027c478bd9Sstevel@tonic-gate } 21037c478bd9Sstevel@tonic-gate 21047c478bd9Sstevel@tonic-gate void 21057c478bd9Sstevel@tonic-gate mdb_io_rele(mdb_io_t *io) 21067c478bd9Sstevel@tonic-gate { 21077c478bd9Sstevel@tonic-gate ASSERT(io->io_refcnt != 0); 21087c478bd9Sstevel@tonic-gate 21097c478bd9Sstevel@tonic-gate if (--io->io_refcnt == 0) { 21107c478bd9Sstevel@tonic-gate IOP_CLOSE(io); 21117c478bd9Sstevel@tonic-gate mdb_free(io, sizeof (mdb_io_t)); 21127c478bd9Sstevel@tonic-gate } 21137c478bd9Sstevel@tonic-gate } 21147c478bd9Sstevel@tonic-gate 21157c478bd9Sstevel@tonic-gate void 21167c478bd9Sstevel@tonic-gate mdb_io_destroy(mdb_io_t *io) 21177c478bd9Sstevel@tonic-gate { 21187c478bd9Sstevel@tonic-gate ASSERT(io->io_refcnt == 0); 21197c478bd9Sstevel@tonic-gate IOP_CLOSE(io); 21207c478bd9Sstevel@tonic-gate mdb_free(io, sizeof (mdb_io_t)); 21217c478bd9Sstevel@tonic-gate } 21227c478bd9Sstevel@tonic-gate 21237c478bd9Sstevel@tonic-gate void 21247c478bd9Sstevel@tonic-gate mdb_iob_stack_create(mdb_iob_stack_t *stk) 21257c478bd9Sstevel@tonic-gate { 21267c478bd9Sstevel@tonic-gate stk->stk_top = NULL; 21277c478bd9Sstevel@tonic-gate stk->stk_size = 0; 21287c478bd9Sstevel@tonic-gate } 21297c478bd9Sstevel@tonic-gate 21307c478bd9Sstevel@tonic-gate void 21317c478bd9Sstevel@tonic-gate mdb_iob_stack_destroy(mdb_iob_stack_t *stk) 21327c478bd9Sstevel@tonic-gate { 21337c478bd9Sstevel@tonic-gate mdb_iob_t *top, *ntop; 21347c478bd9Sstevel@tonic-gate 21357c478bd9Sstevel@tonic-gate for (top = stk->stk_top; top != NULL; top = ntop) { 21367c478bd9Sstevel@tonic-gate ntop = top->iob_next; 21377c478bd9Sstevel@tonic-gate mdb_iob_destroy(top); 21387c478bd9Sstevel@tonic-gate } 21397c478bd9Sstevel@tonic-gate } 21407c478bd9Sstevel@tonic-gate 21417c478bd9Sstevel@tonic-gate void 21427c478bd9Sstevel@tonic-gate mdb_iob_stack_push(mdb_iob_stack_t *stk, mdb_iob_t *iob, size_t lineno) 21437c478bd9Sstevel@tonic-gate { 21447c478bd9Sstevel@tonic-gate iob->iob_lineno = lineno; 21457c478bd9Sstevel@tonic-gate iob->iob_next = stk->stk_top; 21467c478bd9Sstevel@tonic-gate stk->stk_top = iob; 21477c478bd9Sstevel@tonic-gate stk->stk_size++; 21487c478bd9Sstevel@tonic-gate yylineno = 1; 21497c478bd9Sstevel@tonic-gate } 21507c478bd9Sstevel@tonic-gate 21517c478bd9Sstevel@tonic-gate mdb_iob_t * 21527c478bd9Sstevel@tonic-gate mdb_iob_stack_pop(mdb_iob_stack_t *stk) 21537c478bd9Sstevel@tonic-gate { 21547c478bd9Sstevel@tonic-gate mdb_iob_t *top = stk->stk_top; 21557c478bd9Sstevel@tonic-gate 21567c478bd9Sstevel@tonic-gate ASSERT(top != NULL); 21577c478bd9Sstevel@tonic-gate 21587c478bd9Sstevel@tonic-gate stk->stk_top = top->iob_next; 21597c478bd9Sstevel@tonic-gate top->iob_next = NULL; 21607c478bd9Sstevel@tonic-gate stk->stk_size--; 21617c478bd9Sstevel@tonic-gate 21627c478bd9Sstevel@tonic-gate return (top); 21637c478bd9Sstevel@tonic-gate } 21647c478bd9Sstevel@tonic-gate 21657c478bd9Sstevel@tonic-gate size_t 21667c478bd9Sstevel@tonic-gate mdb_iob_stack_size(mdb_iob_stack_t *stk) 21677c478bd9Sstevel@tonic-gate { 21687c478bd9Sstevel@tonic-gate return (stk->stk_size); 21697c478bd9Sstevel@tonic-gate } 21707c478bd9Sstevel@tonic-gate 21717c478bd9Sstevel@tonic-gate /* 21727c478bd9Sstevel@tonic-gate * Stub functions for i/o backend implementors: these stubs either act as 21737c478bd9Sstevel@tonic-gate * pass-through no-ops or return ENOTSUP as appropriate. 21747c478bd9Sstevel@tonic-gate */ 21757c478bd9Sstevel@tonic-gate ssize_t 21767c478bd9Sstevel@tonic-gate no_io_read(mdb_io_t *io, void *buf, size_t nbytes) 21777c478bd9Sstevel@tonic-gate { 21787c478bd9Sstevel@tonic-gate if (io->io_next != NULL) 21797c478bd9Sstevel@tonic-gate return (IOP_READ(io->io_next, buf, nbytes)); 21807c478bd9Sstevel@tonic-gate 21817c478bd9Sstevel@tonic-gate return (set_errno(EMDB_IOWO)); 21827c478bd9Sstevel@tonic-gate } 21837c478bd9Sstevel@tonic-gate 21847c478bd9Sstevel@tonic-gate ssize_t 21857c478bd9Sstevel@tonic-gate no_io_write(mdb_io_t *io, const void *buf, size_t nbytes) 21867c478bd9Sstevel@tonic-gate { 21877c478bd9Sstevel@tonic-gate if (io->io_next != NULL) 21887c478bd9Sstevel@tonic-gate return (IOP_WRITE(io->io_next, buf, nbytes)); 21897c478bd9Sstevel@tonic-gate 21907c478bd9Sstevel@tonic-gate return (set_errno(EMDB_IORO)); 21917c478bd9Sstevel@tonic-gate } 21927c478bd9Sstevel@tonic-gate 21937c478bd9Sstevel@tonic-gate off64_t 21947c478bd9Sstevel@tonic-gate no_io_seek(mdb_io_t *io, off64_t offset, int whence) 21957c478bd9Sstevel@tonic-gate { 21967c478bd9Sstevel@tonic-gate if (io->io_next != NULL) 21977c478bd9Sstevel@tonic-gate return (IOP_SEEK(io->io_next, offset, whence)); 21987c478bd9Sstevel@tonic-gate 21997c478bd9Sstevel@tonic-gate return (set_errno(ENOTSUP)); 22007c478bd9Sstevel@tonic-gate } 22017c478bd9Sstevel@tonic-gate 22027c478bd9Sstevel@tonic-gate int 22037c478bd9Sstevel@tonic-gate no_io_ctl(mdb_io_t *io, int req, void *arg) 22047c478bd9Sstevel@tonic-gate { 22057c478bd9Sstevel@tonic-gate if (io->io_next != NULL) 22067c478bd9Sstevel@tonic-gate return (IOP_CTL(io->io_next, req, arg)); 22077c478bd9Sstevel@tonic-gate 22087c478bd9Sstevel@tonic-gate return (set_errno(ENOTSUP)); 22097c478bd9Sstevel@tonic-gate } 22107c478bd9Sstevel@tonic-gate 22117c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 22127c478bd9Sstevel@tonic-gate void 22137c478bd9Sstevel@tonic-gate no_io_close(mdb_io_t *io) 22147c478bd9Sstevel@tonic-gate { 22157c478bd9Sstevel@tonic-gate /* 22167c478bd9Sstevel@tonic-gate * Note that we do not propagate IOP_CLOSE down the io stack. IOP_CLOSE should 22177c478bd9Sstevel@tonic-gate * only be called by mdb_io_rele when an io's reference count has gone to zero. 22187c478bd9Sstevel@tonic-gate */ 22197c478bd9Sstevel@tonic-gate } 22207c478bd9Sstevel@tonic-gate 22217c478bd9Sstevel@tonic-gate const char * 22227c478bd9Sstevel@tonic-gate no_io_name(mdb_io_t *io) 22237c478bd9Sstevel@tonic-gate { 22247c478bd9Sstevel@tonic-gate if (io->io_next != NULL) 22257c478bd9Sstevel@tonic-gate return (IOP_NAME(io->io_next)); 22267c478bd9Sstevel@tonic-gate 22277c478bd9Sstevel@tonic-gate return ("(anonymous)"); 22287c478bd9Sstevel@tonic-gate } 22297c478bd9Sstevel@tonic-gate 22307c478bd9Sstevel@tonic-gate void 22317c478bd9Sstevel@tonic-gate no_io_link(mdb_io_t *io, mdb_iob_t *iob) 22327c478bd9Sstevel@tonic-gate { 22337c478bd9Sstevel@tonic-gate if (io->io_next != NULL) 22347c478bd9Sstevel@tonic-gate IOP_LINK(io->io_next, iob); 22357c478bd9Sstevel@tonic-gate } 22367c478bd9Sstevel@tonic-gate 22377c478bd9Sstevel@tonic-gate void 22387c478bd9Sstevel@tonic-gate no_io_unlink(mdb_io_t *io, mdb_iob_t *iob) 22397c478bd9Sstevel@tonic-gate { 22407c478bd9Sstevel@tonic-gate if (io->io_next != NULL) 22417c478bd9Sstevel@tonic-gate IOP_UNLINK(io->io_next, iob); 22427c478bd9Sstevel@tonic-gate } 22437c478bd9Sstevel@tonic-gate 22447c478bd9Sstevel@tonic-gate int 22457c478bd9Sstevel@tonic-gate no_io_setattr(mdb_io_t *io, int req, uint_t attrs) 22467c478bd9Sstevel@tonic-gate { 22477c478bd9Sstevel@tonic-gate if (io->io_next != NULL) 22487c478bd9Sstevel@tonic-gate return (IOP_SETATTR(io->io_next, req, attrs)); 22497c478bd9Sstevel@tonic-gate 22507c478bd9Sstevel@tonic-gate return (set_errno(ENOTSUP)); 22517c478bd9Sstevel@tonic-gate } 22527c478bd9Sstevel@tonic-gate 22537c478bd9Sstevel@tonic-gate void 22547c478bd9Sstevel@tonic-gate no_io_suspend(mdb_io_t *io) 22557c478bd9Sstevel@tonic-gate { 22567c478bd9Sstevel@tonic-gate if (io->io_next != NULL) 22577c478bd9Sstevel@tonic-gate IOP_SUSPEND(io->io_next); 22587c478bd9Sstevel@tonic-gate } 22597c478bd9Sstevel@tonic-gate 22607c478bd9Sstevel@tonic-gate void 22617c478bd9Sstevel@tonic-gate no_io_resume(mdb_io_t *io) 22627c478bd9Sstevel@tonic-gate { 22637c478bd9Sstevel@tonic-gate if (io->io_next != NULL) 22647c478bd9Sstevel@tonic-gate IOP_RESUME(io->io_next); 22657c478bd9Sstevel@tonic-gate } 22667c478bd9Sstevel@tonic-gate 22677c478bd9Sstevel@tonic-gate /* 22687c478bd9Sstevel@tonic-gate * Iterate over the varargs. The first item indicates the mode: 22697c478bd9Sstevel@tonic-gate * MDB_TBL_PRNT 22707c478bd9Sstevel@tonic-gate * pull out the next vararg as a const char * and pass it and the 22717c478bd9Sstevel@tonic-gate * remaining varargs to iob_doprnt; if we want to print the column, 22727c478bd9Sstevel@tonic-gate * direct the output to mdb.m_out otherwise direct it to mdb.m_null 22737c478bd9Sstevel@tonic-gate * 22747c478bd9Sstevel@tonic-gate * MDB_TBL_FUNC 22757c478bd9Sstevel@tonic-gate * pull out the next vararg as type mdb_table_print_f and the 22767c478bd9Sstevel@tonic-gate * following one as a void * argument to the function; call the 22777c478bd9Sstevel@tonic-gate * function with the given argument if we want to print the column 22787c478bd9Sstevel@tonic-gate * 22797c478bd9Sstevel@tonic-gate * The second item indicates the flag; if the flag is set in the flags 22807c478bd9Sstevel@tonic-gate * argument, then the column is printed. A flag value of 0 indicates 22817c478bd9Sstevel@tonic-gate * that the column should always be printed. 22827c478bd9Sstevel@tonic-gate */ 22837c478bd9Sstevel@tonic-gate void 22847c478bd9Sstevel@tonic-gate mdb_table_print(uint_t flags, const char *delimeter, ...) 22857c478bd9Sstevel@tonic-gate { 22867c478bd9Sstevel@tonic-gate va_list alist; 22877c478bd9Sstevel@tonic-gate uint_t flg; 22887c478bd9Sstevel@tonic-gate uint_t type; 22897c478bd9Sstevel@tonic-gate const char *fmt; 22907c478bd9Sstevel@tonic-gate mdb_table_print_f *func; 22917c478bd9Sstevel@tonic-gate void *arg; 22927c478bd9Sstevel@tonic-gate mdb_iob_t *out; 22937c478bd9Sstevel@tonic-gate mdb_bool_t first = TRUE; 22947c478bd9Sstevel@tonic-gate mdb_bool_t print; 22957c478bd9Sstevel@tonic-gate 22967c478bd9Sstevel@tonic-gate va_start(alist, delimeter); 22977c478bd9Sstevel@tonic-gate 22987c478bd9Sstevel@tonic-gate while ((type = va_arg(alist, uint_t)) != MDB_TBL_DONE) { 22997c478bd9Sstevel@tonic-gate flg = va_arg(alist, uint_t); 23007c478bd9Sstevel@tonic-gate 23017c478bd9Sstevel@tonic-gate print = flg == 0 || (flg & flags) != 0; 23027c478bd9Sstevel@tonic-gate 23037c478bd9Sstevel@tonic-gate if (print) { 23047c478bd9Sstevel@tonic-gate if (first) 23057c478bd9Sstevel@tonic-gate first = FALSE; 23067c478bd9Sstevel@tonic-gate else 23077c478bd9Sstevel@tonic-gate mdb_printf("%s", delimeter); 23087c478bd9Sstevel@tonic-gate } 23097c478bd9Sstevel@tonic-gate 23107c478bd9Sstevel@tonic-gate switch (type) { 23117c478bd9Sstevel@tonic-gate case MDB_TBL_PRNT: { 23127c478bd9Sstevel@tonic-gate varglist_t ap = { VAT_VARARGS }; 23137c478bd9Sstevel@tonic-gate fmt = va_arg(alist, const char *); 23147c478bd9Sstevel@tonic-gate out = print ? mdb.m_out : mdb.m_null; 23157c478bd9Sstevel@tonic-gate va_copy(ap.val_valist, alist); 23167c478bd9Sstevel@tonic-gate iob_doprnt(out, fmt, &ap); 23177c478bd9Sstevel@tonic-gate va_end(alist); 23187c478bd9Sstevel@tonic-gate va_copy(alist, ap.val_valist); 23197c478bd9Sstevel@tonic-gate break; 23207c478bd9Sstevel@tonic-gate } 23217c478bd9Sstevel@tonic-gate 23227c478bd9Sstevel@tonic-gate case MDB_TBL_FUNC: 23237c478bd9Sstevel@tonic-gate func = va_arg(alist, mdb_table_print_f *); 23247c478bd9Sstevel@tonic-gate arg = va_arg(alist, void *); 23257c478bd9Sstevel@tonic-gate 23267c478bd9Sstevel@tonic-gate if (print) 23277c478bd9Sstevel@tonic-gate func(arg); 23287c478bd9Sstevel@tonic-gate 23297c478bd9Sstevel@tonic-gate break; 23307c478bd9Sstevel@tonic-gate 23317c478bd9Sstevel@tonic-gate default: 23327c478bd9Sstevel@tonic-gate warn("bad format type %x\n", type); 23337c478bd9Sstevel@tonic-gate break; 23347c478bd9Sstevel@tonic-gate } 23357c478bd9Sstevel@tonic-gate } 23367c478bd9Sstevel@tonic-gate 23377c478bd9Sstevel@tonic-gate va_end(alist); 23387c478bd9Sstevel@tonic-gate } 2339