/*
 * |-----------------------------------------------------------|
 * | Copyright (c) 1991, 1990 MIPS Computer Systems, Inc.      |
 * | All Rights Reserved                                       |
 * |-----------------------------------------------------------|
 * |          Restricted Rights Legend                         |
 * | Use, duplication, or disclosure by the Government is      |
 * | subject to restrictions as set forth in                   |
 * | subparagraph (c)(1)(ii) of the Rights in Technical        |
 * | Data and Computer Software Clause of DFARS 252.227-7013.  |
 * |         MIPS Computer Systems, Inc.                       |
 * |         950 DeGuigne Avenue                               |
 * |         Sunnyvale, California 94088-3650, USA             |
 * |-----------------------------------------------------------|
 */
#ident	"$Header: stream.c,v 1.16.1.1 92/06/24 13:26:56 marker Exp $"

/*	Copyright (c) 1984 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

#include "crash.h"
#define KERNEL
#define _KERNEL_SVR4
#include "sys/fs/fifonode.h"
#undef KERNEL
#undef _KERNEL_SVR4

#include "sys/poll.h"
#include "sys/stropts.h"
#include "sys/strstat.h"
#include "sys/stream.h"
#include "sys/zone.h"
#include "bsd/sys/socket.h"
#include "bsd/sys/socketvar.h"
#include "bsd/sys/mbuf.h"

/*
 * Default and Maximum Streams buffer size for strbuf related commands
 */
#define DEFAULT_BUFSIZE 32
#define MAX_BUFSIZE     16384

struct zoneinfo zinfo_mblk, *zinfo_mblkp;
struct zoneinfo zinfo_dblk, *zinfo_dblkp;

struct zoneinfo zinfo_mbuf, *zinfo_mbufp;
struct zoneinfo zinfo_clustermbuf, *zinfo_clustermbufp;

struct syment *SYM_spec_vnodeops, *SYM_cdevcnt, *SYM_cdevsw, *SYM_MAJOR;

static struct syment *Qhead, *Strst, *Class_info;
static struct syment *Zoneinfo_mblk, *Zoneinfo_dblk;
static struct syment *Zoneinfo_mbuf, *Zoneinfo_clustermbuf;
static struct syment *Linkblk, *Nmuxlink;
static int linkblk_inited = 0;
int nmuxlink;

static struct syment *Nmblock, *Ndblock;

char	MAJOR[127];

struct cdevsw *cdevsw_copy;
int	cdevcnt;
static int cdev_inited = 0;

static int str_inited = 0;
static int mbuf_inited = 0;

int nmblock;	 /* Configured number of message blocks */
int ndblock;	 /* Configured number of data blocks */

struct dfam_arg {
	int	(*dfam_fun)();
	caddr_t	dfam_funarg;
	int	dfam_class;
	int	dfam_all;
	int	dfam_phys;
};
	
/*
 * Arguments for fifo stream function do_prfifo
 */
struct do_prfifo_arg {
	int all, full, phys;
	char *str_heading, *fifo_heading1, *fifo_heading2;
	int list;
};

	char *str_heading =
"STRADDR  WRQ      IOCB     VNODE    STRTAB   IOCID IOCWT WOFF ERR FLAG\n";
/*
*12345678 12345678 12345678 12345678 12345678 12345678 12345678 12345678 1234
*/

	char *queuelist_heading =
"QUEADDR  QLINK    QPOINTER QCNT QFIRST   QLAST    MINP MAXP HIWT LOWT FLAGS\n";
/*
*12345678 12345678 12345678 1234 12345678 12345678 1234 1234 1234 1234 123456
*/

#ifdef SVR3_FIFO
	char *fifo_svr3_heading0 = "FIFO DATA FOR STREAM";
	char *fifo_svr3_heading1 =
"FIFOADR  FIRSTBHD  LASTBHD RSELPROC WSELPROC XSELPROC SIZE\n";
/*
*12345678 12345678 12345678 12345678 12345678 12345678 12345678
*/
	char *fifo_svr3_heading2 =
"WCNT RCNT WPTR RPTR NBUF FLAGS\n";
/*
*1234 1234 1234 1234 1234 12345678
*/
#endif /* SVR3_FIFO */

	char *fifo_svr4_heading0 = "FIFO DATA FOR STREAM";
	char *fifo_svr4_heading1 =
"FIFODADR VN_MATE  VN_REAL  INO  WCNT RCNT OPEN VN_UNIQ\n";
/*
*12345678 12345678 12345678 1234 1234 1234 1234 12345678
*/
	char *fifo_svr4_heading2 =
"ATIME    MTIME    CTIME    NEXTVP   BACKVP   FLAGS\n";
/*
*12345678 12345678 12345678 12345678 12345678 12345678
*/

/*
 * Externally defined symbols
 */
extern struct syment *Fifo_vnodeops;

/*
 * Forward referenced procedures
 */
int do_queue_mblk(), do_stream_queue();
extern char *malloc(), *realloc();

/*
 * dblk function to apply with arguments in 'funarg' to each instance of
 * a stream data block (dblk).
 */
int
do_for_all_dblk_fun(funarg, mb_addr, mb)
	struct	dfam_arg *funarg;
	long	mb_addr;
	mblk_t	*mb;
{
	dblk_t	db;
	long	db_addr;

	db_addr = (long)(mb->b_datap);

	if (db_addr == (long) NULL)
		return(0);

	readbuf(db_addr,0,funarg->dfam_phys,-1,(char *)&db,sizeof(db),"dblk");

	(*funarg->dfam_fun)(funarg->dfam_funarg,db_addr,&db);
	return(1);
}

/*
 * General enumeration and apply procedure which calls the function 'fun'
 * with arguments in 'funarg' to EITHER
 * 1. all free stream data blocks (dblk) OR
 * 2. all dblk's linked to mblk's which are on streams queue's
 */
int	
do_for_all_dblk(fun, funarg, class, all, phys)
	int	(*fun)();
	caddr_t	funarg;
	int	class, all, phys;
{
	struct	dfam_arg dfunarg;
	dblk_t	db;
	long	dnext;

	if (all >= 0) {
		dfunarg.dfam_fun = fun;
		dfunarg.dfam_funarg = funarg;
		dfunarg.dfam_class = class;
		dfunarg.dfam_all = all;
		dfunarg.dfam_phys = phys;
		do_for_all_mblk(do_for_all_dblk_fun, &dfunarg, 0, phys);
	}
	if (all) {
		dnext = (long)zinfo_dblk.node_freelist;

		while (dnext != 0) {
			readbuf(dnext,0,phys,-1,(char *)&db,sizeof(db),"dblk");
			if (class == -1 || db.db_class == class) {
				(*fun)(funarg,dnext,&db);
			}
			dnext = (long) ((struct zonenode *) &db)->next;
		}	
	}
}

/*
 * General enumeration and apply procedure which calls the function 'fun'
 * with arguments in 'funarg' to each instance of a fifo stream.
 * NOTE: vnodes for both spec and fifo file systems are enumerated and handled.
 */
int
do_for_all_fifo(fun, funarg, phys)
	int	(*fun)();
	struct do_prfifo_arg *funarg;
	int	phys;
{
	struct file *fnext, fbuf;
	struct vnode vnode_buf;
	struct fifonode *fifo_bufp;
	int newheader;

	/*
	 * Enumerate the entire file table looking for fifo's
	 */
	readbuf(File->n_value, 0, phys, -1,
		(char *)&fnext, sizeof(fnext),"file table base");
	newheader = 0;

	while (fnext != NULL) {

		readbuf((long)fnext, 0, phys, -1,
			(char *) &fbuf, sizeof(fbuf),"file table entry");

		fnext = fbuf.f_next;
		if (fbuf.f_type == DTYPE_VNODE &&
		    (fbuf.f_vnode != (struct vnode *)NULL)) {
			/*
			 * This file node points to a vnode and the
			 * vnode address looks reasonable.
			 */
			readbuf((long)fbuf.f_vnode, 0, phys, -1,
				(char *) &vnode_buf, sizeof(vnode_buf),
				"file vnode entry");

			if (vnode_buf.v_type != VFIFO ||
			    (vnode_buf.v_op !=
				(struct vnodeops *)Fifo_vnodeops->n_value) ||
			    ((long)vnode_buf.v_stream == NULL))
				continue;

			fifo_bufp = VTOF(&vnode_buf);
			if (fifo_bufp) {
				/*
				 * vnode is a FIFO, major number is legal
				 * and it has valid stream pointer so call
				 * the function 'fun' with stream and fifo
				 * 'core' addresses.
				 */
				if (newheader) { /* print new stream header */
					fprintf(fp,"%s", funarg->str_heading);
				}

				newheader = (*fun)((caddr_t)funarg,
					(long)vnode_buf.v_stream, fifo_bufp);
			}
		}
	}
	return newheader;
}

struct do_queue_mblk_arg {
	int	(*fun)();
	caddr_t	funarg;
	int	phys;
};

/*
 * General enumeration and apply procedure which calls the function 'fun'
 * with arguments in 'funarg' for each stream message block (mblk) for EITHER 
 * 1. if 'all >= 0' then ALL mblk's which are on streams queue's OR
 * 2. all free stream message blocks (mblk) on the free list
 */
int
do_for_all_mblk(fun, funarg, all, phys)
	int	(*fun)();
	caddr_t	funarg;
	int	all, phys;
{
	struct	do_queue_mblk_arg mfunarg;
	long	mb_addr;
	mblk_t	mb;

	if (all >= 0) { /* dump those on stream queues */
		mfunarg.fun = fun;
		mfunarg.funarg = funarg;
		mfunarg.phys = phys;

		do_for_all_queue(do_queue_mblk,(caddr_t) &mfunarg,0,phys);
	}
	if (all) { /* dump those on free list */
		mb_addr = (long)zinfo_mblk.node_freelist;
		while (mb_addr != (long) NULL) {
			readbuf(mb_addr,0,phys,-1,
				    (char *)&mb,sizeof(mb),"mblk");
			(*fun)(funarg,mb_addr,&mb);
			mb_addr = (long) ((struct zonenode *) &mb)->next;
		}
	}
}

struct do_stream_queue_arg {
	int	(*fun)();
	caddr_t	funarg;
	int	all, phys;
};
	
/*
 * General enumeration and apply procedure which calls the function 'fun'
 * with arguments in 'funarg' to each instance of a stream queue EITHER
 * 1. all queue's associated with each stream
 * 2. all device multiplexer's
 */
int
do_for_all_queue(fun, funarg, all, phys)
	int	(*fun)();
	caddr_t	funarg;
	int	all, phys;
{
	struct do_stream_queue_arg sfunarg;
	struct linkblk linkbuf;
	struct queue que;
	int slot, rd_addr, wr_addr, next_addr;

	read_cdevsw();

	sfunarg.fun = fun;
	sfunarg.funarg = funarg;
	sfunarg.all = all;
	sfunarg.phys = phys;

	do_for_all_stream(do_stream_queue,(caddr_t) &sfunarg,phys);

	linkblk_init();

	for(slot = 0; slot < nmuxlink; slot++) {

		readbuf((long)(Linkblk->n_value+slot*sizeof linkbuf),0,phys,-1,
			(char *)&linkbuf,sizeof linkbuf,"linkblk table");
		if(!linkbuf.l_qtop || !linkbuf.l_qbot)
			continue;
		wr_addr = (long) linkbuf.l_qbot;
		while (wr_addr != (long) NULL) {
			rd_addr = (long) RD((struct queue *) wr_addr);
			readbuf(wr_addr,0,phys,-1,(char *)&que,
				sizeof que,"queue");
			next_addr = (long) que.q_next;
			(*fun)(funarg,wr_addr,&que);
			readbuf(rd_addr,0,phys,-1,(char *)&que,
				sizeof que,"queue");
			(*fun)(funarg,rd_addr,&que);
			wr_addr = next_addr;
		}
	}
}

/*
 * General enumeration and apply procedure which calls the function 'fun'
 * with arguments in 'funarg' to each instance of a socket.
 */
int
do_for_all_socket(fun, funarg, phys)
	int	(*fun)();
	caddr_t	funarg;
	int	phys;
{
	struct findvnodeargs fva;
	struct vnode vnode_buf;

	fva = null_findvnodeargs;
	fva.fva_type = VFS_TYPE_spec;
	fva.fva_flags |= FVA_SINGLE_TYPE;

	while (find_next_vnode(&fva, &vnode_buf)) {
		if ((vnode_buf.v_type == VSOCK) &&
		    ((long) vnode_buf.v_socket != NULL)) { /* socket */
			(*fun)(funarg, (long)vnode_buf.v_socket);
		}
	}
}

/*
 * General enumeration and apply procedure which calls the function 'fun'
 * with arguments in 'funarg' to each instance of a stream.
 * NOTE: vnodes for only spec file systems are enumerated and handled.
 */
int
do_for_all_stream(fun, funarg, phys)
	int	(*fun)();
	caddr_t	funarg;
	int	phys;
{
	struct findvnodeargs fva;
	struct vnode vnode_buf;
	int imajor, newheader;

	fva = null_findvnodeargs;
	fva.fva_type = VFS_TYPE_spec;
	fva.fva_flags |= FVA_SINGLE_TYPE;
	newheader = 0;

	while (find_next_vnode(&fva,&vnode_buf)) {
		imajor = MAJOR[major(vnode_buf.v_rdev)];

		if (vnode_buf.v_type != VCHR || (imajor >= cdevcnt) ||
		    (cdevsw_copy[imajor].d_str == NULL) ||
		    (long)vnode_buf.v_stream == NULL)
			continue;
		/*
		 * vnode is either a CHR, the major number is legal and
		 * it has a stream attached so call 'fun' with this stream.
		 */
		if (newheader) {
			fprintf(fp,"%s", str_heading);
		}
		newheader = (*fun)(funarg,(long)vnode_buf.v_stream);
	}
}

/*
 * Get arguments for dblock function
 */
struct do_prdblk_arg {
	int	all;
	int	phys;
	int	class;
};

/*
 * This procedure is called with the address of the data block header
 * and a valid address in 'db' if non-null. It's function is to call the
 * generalized printing procedure for dumping out the fields of the
 * streams data block.
 */
int
do_prdblk(funarg, db_addr, db)
	struct do_prdblk_arg *funarg;
	long	db_addr;
	dblk_t	*db;
{
	if (funarg->class != -1 && funarg->class != db->db_class)
		return(0);
	prdblk(funarg->all, funarg->phys, db_addr, funarg->class, db);
	return(1);
}

/*
 * This procedure is called with the address of the data block header
 * and a valid address in 'db' if non-null. It's function is to call the
 * printing procedure for dumping out the first 32 bytes of the streams
 * data buffer attached to this data block header.
 */
int
do_prdblk_str(funarg, db_addr, db)
	struct do_prdblk_arg *funarg;
	long	db_addr;
	dblk_t	*db;
{
	if (funarg->class != -1 && funarg->class != db->db_class) {
		return(0);
	}
	prdblk_str(funarg->all, funarg->phys, db_addr, funarg->class, db);
	return 1;
}

/*
 * Get arguments for print message block function
 */
struct do_prmess_arg {
	int	all;
	int	phys;
};

int
do_prmess(funarg, addr, mb)
	struct	do_prmess_arg *funarg;
	long	addr;
	mblk_t	*mb;
{
	prmess(funarg->all, funarg->phys, addr,mb);
	return(1);
}

/*
 * Get arguments for queue function
 */
struct do_prqueuelist_arg {
	int all, phys;
	queue_t	*que_p;
	int full, do_rd, do_wr, list;
};

int
do_prqueuelist(funarg, addr)
	struct do_prqueuelist_arg *funarg;
	long	addr;
{
	struct stdata strm;
	int newheader;

	if (addr == (long) NULL)
		return 0;

	readbuf((long)addr,0,funarg->phys,-1,
		(char *)&strm, sizeof strm, "streams data block");

	if (strm.sd_wrq == NULL)
		return 0;

	newheader = prqueuelist(funarg->all, funarg->phys,
			(long)strm.sd_wrq, funarg->que_p,
			funarg->full, funarg->do_rd,
			funarg->do_wr, funarg->list);
	return newheader;
}

int
do_prfifo(funarg, vstream, fifo)
	struct do_prfifo_arg *funarg;
	long	vstream, fifo;

{
	int newheader;
#ifdef SVR3_FIFO
	int svr3_fifo;
#endif

	newheader = prstream(funarg->all, funarg->full, funarg->phys, vstream,
		 funarg->str_heading, funarg->list);

#ifdef SVR3_FIFO
	svr3_fifo = 1;

	if (svr3_fifo) { /* svr3 fifo */
		if (fifo)
			fprintf(fp,"%s %x:\n", fifo_svr3_heading0, vstream);

		prsvr3fifo(funarg->all, funarg->full,
			funarg->phys, fifo,
			funarg->fifo_heading1, funarg->fifo_heading2,
			1 /* print FIFO headings */
			);
	} else { /* svr4 */
#else
		if (fifo)
			fprintf(fp,"%s %x:\n", fifo_svr4_heading0, vstream);

		newheader = prsvr4fifo(funarg->all, funarg->full,
			funarg->phys, fifo,
			funarg->fifo_heading1, funarg->fifo_heading2,
			1 /* print FIFO headings */
			);
#endif
#ifdef SVR3_FIFO
	}
#endif
	return newheader;
}

/*
 * Arguments for stream function do_prstream
 */
struct do_prstream_arg {
	int all, full, phys;
	char *heading;
	int list;
};

int
do_prstream(funarg, vstream)
	struct do_prstream_arg *funarg;
	long vstream;
{
	int newheader;

	newheader = prstream(funarg->all, funarg->full, funarg->phys,
		vstream, funarg->heading, funarg->list);
	return newheader;
}

/*
 * Apply procedure which calls the function 'fun' in arguments 'funarg'
 * to each entry message block on a stream queue.
 */
int
do_queue_mblk(funarg, addr, que_p)
	struct do_queue_mblk_arg *funarg;
	long	addr;
	struct queue *que_p;
{
	mblk_t	mb;
	long mb_addr, mb_cont_addr;

	mb_addr = (long) que_p->q_first;
	while (mb_addr != (long) NULL) {
		readbuf(mb_addr,0,funarg->phys,-1,
			(char *)&mb,sizeof(mb),
			"mblk");
		(*funarg->fun)(funarg->funarg,mb_addr,&mb);
		mb_addr = (long) mb.b_next;
		mb_cont_addr = (long) mb.b_cont;
		while (mb_cont_addr != (long) NULL) {
		    readbuf(mb_cont_addr,0,funarg->phys,-1,
			    (char *)&mb,sizeof(mb),"mblk");
		    (*funarg->fun)(funarg->funarg,mb_cont_addr,&mb);
		    mb_cont_addr = (long) mb.b_cont;
	        }
	}
}	

/*
 * Arguments passed to socket enumeration function for mbuf processing
 */
struct do_mbuf_arg {
	int	(*fun)();
	caddr_t	funarg;
	int	phys;
};

/*
 * General enumeration and apply procedure which calls the function 'fun'
 * with arguments in 'funarg' to each queue'd entry on a socket.
 * Assumes on entry that 'sock_addr' is the address of the socket
 * object in the kernel. The 'fun' is called first with the input queue
 * address and secondly with the output queue address in the socket object.
 */
int
do_socket_fun(funarg, sock_addr)
	struct	do_mbuf_arg *funarg;
	long	sock_addr;
{
	struct	socket sockm;
	struct	mbuf   qmbuf, *qmbufp, *next_qmbufp;
	int	phys;

	if (sock_addr == (long)NULL) {
		return;
	}

	phys = funarg->phys;
	readbuf((long)sock_addr, 0, 0, -1, (char *)&sockm,
		sizeof sockm, "socket object");

	qmbufp = sockm.so_rcv.sb_mb;
	if (qmbufp != NULL) {
		while (qmbufp != (struct mbuf *)NULL) {
			readbuf(qmbufp, 0, phys, -1, (char *)&qmbuf,
				sizeof(qmbuf), "mbuf");
			next_qmbufp = qmbuf.m_hdr.mh_nextpkt;
			(*funarg->fun)(funarg->funarg, qmbufp, &qmbuf);
			qmbufp = next_qmbufp;
		}
	}

	qmbufp = sockm.so_snd.sb_mb;
	if (qmbufp != NULL) {
		while (qmbufp != (struct mbuf *)NULL) {
			readbuf(qmbufp, 0, phys, -1, (char *)&qmbuf,
				sizeof(qmbuf), "mbuf");
			next_qmbufp = qmbuf.m_hdr.mh_nextpkt;
			(*funarg->fun)(funarg->funarg, qmbufp, &qmbuf);
			qmbufp = next_qmbufp;
		}
	}
}

/*
 * General enumeration and apply procedure which calls the function 'fun'
 * with arguments in 'funarg' to each entry on a stream queue.
 * Assumes on entry that the 'str_addr' is the address of the stdata
 * object in the kernel. The 'fun' is called first with the writequeue
 * address and secondly with the readqueue address in the stdata object.
 */
int
do_stream_queue(funarg, str_addr)
	struct	do_stream_queue_arg *funarg;
	long	str_addr;
{
	dblk_t	db;
	struct	stdata strm;
	struct	queue que;
	long	db_addr, wr_addr, rd_addr, next_addr;

	if (str_addr == (long) NULL)
		return;

	readbuf((long)str_addr,0,funarg->phys,-1,
		(char *)&strm, sizeof strm, "streams data block");

	if (strm.sd_wrq == NULL || strm.sd_flag & STPLEX)
		return;

	wr_addr = (long) strm.sd_wrq;

	while (wr_addr != (long) NULL) {
		rd_addr = (long) (RD((struct queue *) wr_addr));

		readbuf(wr_addr,0,funarg->phys,-1,
			(char *) &que, sizeof(que),"queue");
		next_addr = (long) que.q_next;

		(*funarg->fun)(funarg->funarg,wr_addr,&que);

		readbuf(rd_addr,0,funarg->phys,-1,
			(char *) &que, sizeof(que),"queue");

		(*funarg->fun)(funarg->funarg,rd_addr,&que);
		wr_addr = next_addr;
	}
	return 0;
}
			
/*
 * Print stream statistics arguments
 */
struct do_strstat_arg {
	int	*freecount;
	int	*usecount;
	int	*dfc;
	int	*duc;
};

int
do_strstat_dblk(funarg, db_addr, db)
	struct	do_strstat_arg *funarg;
	long	db_addr;
	dblk_t	*db;
{
	if (db->db_ref == 0) {
		if (db->db_class >= 0 &&
		     db->db_class < NCLASS)
			(funarg->dfc[db->db_class])++;
		(*(funarg->freecount))++;
	} else {
		if (db->db_class >= 0 &&
		     db->db_class < NCLASS)
			(funarg->duc[db->db_class])++;
		(*(funarg->usecount))++;
	}
}

int
do_strstat_mblk(funarg, mb_addr, mb)
	struct	do_strstat_arg *funarg;
	long	mb_addr;
	mblk_t	*mb;
{
	if (mb->b_datap != NULL)
		(*(funarg->usecount))++;
	else
		(*(funarg->freecount))++;
}

/*
 * Get arguments for dballoc function
 *
 * dballoc [-wfilename] [class(es)]
 */
int
getdballoc()
{
	int c,class;

	optind = 1;

	while((c = getopt(argcnt,args,"w:")) !=EOF) {
		switch(c) {
			case 'w' :	redirect();
					break;
			default  :	longjmp(syn,0);
		}
	}

	if(args[optind]) {
		do {
			if((class = strcon(args[optind++],'d')) == -1)
				continue;
			if((class >= 0) && (class < NCLASS))
				prdballoc(class);
			else {
				fprintf(fp,"%d is out of class range\n",
					class);
				continue;
			}
		} while(args[optind]);
	}
	else {
		for(class = 0; class < NCLASS; class++)
			prdballoc(class);
	}
}

/*
 * Controlling procedure which parses the command line parameters,
 * enumerates all allocated Streams data blocks and applies the supplied
 * function to each data block (dblk).
 *
 * dblock [-e] [-wfilename] [virtual_address[es]]
 */
int
getdblk()
{
	struct do_prdblk_arg funarg;
	int all, c, class, cswitch, err, phys;
	long addr, arg2, arg1;

	all = class = cswitch = phys = 0;
	addr = arg2 = arg1 = -1;
	optind = 1;

	while((c = getopt(argcnt,args,"cew:")) !=EOF) {
		switch(c) {
			case 'c' :	cswitch = 1;
					break;
			case 'e' :	all = 1;
					break;
			case 'w' :	redirect();
					break;
			default  :	longjmp(syn,0);
		}
	}
	fprintf(fp,"NUMBER ALLOCATED DATA BLOCKS= %d\n", ndblock);
	fprintf(fp,
	 "ADDR     CLASS SIZE  RCNT TYPE     BASE     LIMIT    FREEP\n");

	if (args[optind]) {
		if (cswitch) 
			do {
				if((class = (int)strcon(args[optind++],'d'))
					== -1)
					continue;
				prclass(class);
			}while(args[optind]);
		else {
			all = 1;
			do {
				getargs(GETARGS_NO_SLOT,&arg1,&arg2);
				if(arg1 == -1) 
					continue;
				if(arg2 == -1)
					arg2 = arg1;
				for (addr = arg1; addr <= arg2;
				     addr = (long) (((dblk_t *) addr) + 1)) {
					prdblk(all,phys,addr,-1,NULL);
				}
				addr = arg1 = arg2 = -1;
			} while(args[++optind]);
		}
	} else {
		funarg.all = all;
		funarg.phys = phys;
		funarg.class = -1; /* print all stream buffer classes */
		do_for_all_dblk(do_prdblk, (caddr_t)&funarg, class, all, phys);
	}
}

/*
 * Parse the command line arguments for dbfree function and print
 * descriptions for each stream data block on the free list. If the
 * optional stream class number is provided on the command then only that
 * size (ie class) of buffer is printed.
 *
 * dbfree [-wfilename] [class(es)]
 */
int
getdbfree()
{
	int c, class;

	optind = 1;
	class = 0;

	while((c = getopt(argcnt,args,"w:")) != EOF) {
		switch(c) {
			case 'w' :	redirect();
					break;
			default  :	longjmp(syn,0);
		}
	}
	fprintf(fp,
	 "ADDR     CLASS SIZE  RCNT TYPE     BASE     LIMIT    FREEP\n");

	if(args[optind]) {
		do {
			if((class = (int)strcon(args[optind++],'d')) == -1)
				continue;
			prdbfree(class);
		} while(args[optind]);
	}
	else { /* enumerate them all */
		for(class = 0; class < NCLASS; class++) {
			prdbfree(class);
		}
	}
}

/*
 * Enumerate all the regular mbufs on the free list. This is of questionable
 * value but since it trivially falls out of the general implementation
 * it was easy to support.
 *
 * mbufsfree [-wfilename] [virtual_address | virtual_address_pairs]
 */
int
getfree_mbufs()
{
	int c, err, phys;
	long addr, arg2, arg1;
	struct mbuf print_mbuf;

	phys = 0;
	optind = 1;
	addr = arg2 = arg1 = -1;

	while((c = getopt(argcnt,args,"w:")) != EOF) {
		switch(c) {
			case 'w' :	redirect();
					break;
			default  :	longjmp(syn,0);
		}
	}

	fprintf(fp,
	"ADDR     NEXT     NEXTPKT    LEN DATA     TYPE     FLAGS\n");

	if(args[optind]) {
		do {
			getargs(GETARGS_NO_SLOT,&arg1,&arg2);
			if(arg1 == -1) 
				continue;
			if(arg2 == -1)
				arg2 = arg1;

			for (addr = arg1; addr <= arg2;
			     addr = (long)(((char *) addr)
					   + zinfo_mbuf.node_size)) {

				err = readbuf(addr, 0, phys, -1, print_mbuf,
					sizeof print_mbuf, "mbuf");
				if (err) {
					fprintf(fp,
					 "invalid mbuf address %8x\n", addr);
					break;
				}
				prmbuf(0, addr, &print_mbuf);
			}
			addr = arg1 = arg2 = -1;
		} while(args[++optind]);
	} else {
		addr = (long)zinfo_mbuf.node_freelist;

		while (addr != (long) NULL) {
			err = readbuf(addr, 0, phys, -1,
				&print_mbuf, sizeof print_mbuf, "mbuf");
			if (err) {
				fprintf(fp, "invalid mbuf address %8x\n",addr);
				break;
			}
			prmbuf(0, addr, &print_mbuf);
			addr = (long)((struct zonenode *)&print_mbuf)->next;
		}
	}
}

/*
 * Enumerate all streams buffers on the free list for each size by it's
 * class number. This might be questionable value but since it trivially
 * falls out of the general implementation it's easy to support this function.
 *
 * strbufsfree [-e] [-wfilename]
 *            ([-c class[es] |
 *            ([-s size] | virtual_address | virtual_address_pairs]))
 */
int
getfree_strbufs()
{
	int all, c, cswitch, err, klass, phys, str_bufsize, str_class;
	long addr, arg2, arg1, mb_addr;
	unsigned char mb[32];
	struct zoneinfo zinfo, *z;

	str_bufsize = DEFAULT_BUFSIZE;
	all = phys = cswitch = 0;
	addr = arg2 = arg1 = -1;

	optind = 1;

	while((c = getopt(argcnt,args,"ecsw:")) != EOF) {
		switch(c) {
			case 'e' :	all = 1;
					break;
			case 'c' :	cswitch = 1;
					break;
			case 's' :	str_bufsize=(int)strcon(args[optind++],
						'd');
					if (str_bufsize == -1) continue;
					if (str_bufsize > MAX_BUFSIZE)
						str_bufsize = MAX_BUFSIZE;
					break;
			case 'w' :	redirect();
					break;
			default  :	longjmp(syn,0);
		}
	}

	if(args[optind]) {
		if (cswitch) {
			do {
				klass = (int)strcon(args[optind++],'d');
				if (klass == -1)
					continue;
				if ((klass < 0) || (klass > NCLASS-1)) {
					fprintf(fp,
					 "Out of range class= %d\n",klass);
				} else {
					prclass(klass);
				}
			} while(args[optind]);
		} else {
			all = 1;
			fprintf(fp, "ADDRESS  CLASS SIZE\n");
			do {
				getargs(GETARGS_NO_SLOT,&arg1,&arg2);
				if(arg1 == -1) 
					continue;
				if(arg2 == -1)
					arg2 = arg1;

				for (addr = arg1; addr <= arg2; addr =
				     (long)(((char *) addr) + str_bufsize)) {
					err = readbuf(addr, 0, phys, -1, mb,
					 sizeof mb, "strbuf");
					if (err) {
					  fprintf(fp,
					 "Invalid virtual address %8x\n",addr);
						break;
					}
					prstrbuf(addr, mb, sizeof mb, 1);
				}
				addr = arg1 = arg2 = -1;
			} while(args[++optind]);
		}
	} else {
		if (all) { /* dump those on free list */

			for (str_class=0; str_class < NCLASS; str_class++) {

				readmem((long)Class_info->n_value+
				 str_class*sizeof(class_info_t),1,-1,
				 (char *)&z, sizeof z,"zoneinfo_buffer");

				readmem((long)z,1,-1,(char *)&zinfo,
				 sizeof(zinfo),	"*zoneinfo_buffer");

				mb_addr = (long)zinfo.node_freelist;

				while (mb_addr != (long) NULL) {
					readbuf(mb_addr, 0,phys, -1, mb,
						sizeof mb,"strbuf");
					prstrbuf(mb_addr, mb, sizeof mb, 1);
					mb_addr = (long)
					  ((struct zonenode *)&mb_addr)->next;
				} /* end of entire free list */
			} /* end of class forloop */
		}
	}
}

/*
 * Find all mbufs currently in use. The algorithm used is to
 * enumerate all open sockets and for both the read and write queue's
 * of the socket enumerate all enqueued mbufs and note there buffers.
 * For each regular mbuf and any attached cluster mbuf the mbuf printing
 * procedure is called.
 *
 * mbufs [-wfilename] | [[virtual_address] | ]virtual_address_pair]}
 */
int
getinuse_mbufs()
{
	struct do_mbuf_arg funarg;
	int c, err, phys;
	long addr, arg2, arg1;
	struct mbuf print_mbuf;

	phys = 0;
	optind = 1;
	addr = arg2 = arg1 = -1;

	while((c = getopt(argcnt,args,"w:")) != EOF) {
		switch(c) {
			case 'w' :	redirect();
					break;
			default  :	longjmp(syn,0);
		}
	}

	fprintf(fp,
		"ADDR     NEXT     NEXTPKT    LEN DATA     TYPE     FLAGS\n");

	if(args[optind]) {
		do {
			getargs(GETARGS_NO_SLOT,&arg1,&arg2);
			if(arg1 == -1) 
				continue;
			if(arg2 == -1)
				arg2 = arg1;

			for (addr = arg1; addr <= arg2;
			     addr=(long)(((char *)addr)+zinfo_mbuf.node_size)){

				err = readbuf(addr, 0, phys, -1, print_mbuf,
					sizeof print_mbuf, "mbuf");
				if (err) {
					fprintf(fp,
					 "invalid mbuf address= %8x\n", addr);
					break;
				}
				prmbuf(0, addr, &print_mbuf);
			}
			addr = arg1 = arg2 = -1;
		} while(args[++optind]);
	} else {
		funarg.fun = prmbuf;
		funarg.funarg = (caddr_t)0;
		funarg.phys = 0;
		do_for_all_socket(do_socket_fun, (caddr_t)&funarg, 0);
	}
}

/*
 * Find all streams buffers currently in use. The algorithm used is to
 * enumerate all open streams and for both the read and write queue's
 * of the stream enumerate all enqueued messages and consequently all
 * the associated buffers. For each stream buffer the buffer printing
 * procedure is called.
 *
 * strbufs [-wfilename]
 *            ([-c class[es] |
 *            ([-s size] | virtual_address | virtual_address_pairs]))
 */
int
getinuse_strbufs()
{
	struct do_prdblk_arg funarg;
	int all, c, class, cswitch, err, klass, phys, str_bufsize;
	long addr, arg1, arg2;
	unsigned char mb[32];

	all = class = cswitch = klass = phys = 0;
	addr = arg1 = arg2 = -1;
	str_bufsize = DEFAULT_BUFSIZE;
	optind = 1;

	while((c = getopt(argcnt,args,"csw:")) != EOF) {
		switch(c) {
			case 'c' :	cswitch = 1;
					break;
			case 's' :	str_bufsize=(int)strcon(args[optind++],
						'd');
					if (str_bufsize == -1) continue;
					if (str_bufsize > MAX_BUFSIZE)
						str_bufsize = MAX_BUFSIZE;
					break;
			case 'w' :	redirect();
					break;
			default  :	longjmp(syn,0);
		}
	}
	fprintf(fp,"STREAMS BUFFERS IN USE\n");
	fprintf(fp,"ADDRESS  CONTENTS\n");

	if(args[optind]) {
		if (cswitch) {
			do {
				if((klass = (int)strcon(args[optind++],'d'))
					== -1)
					continue;
				if ((klass < 0) || (klass > NCLASS-1)) {
					fprintf(fp,
					 "Out of range class= %d\n",klass);
				} else {
					funarg.all = 0;
					funarg.phys = 0;
					funarg.class = klass;

					do_for_all_dblk(do_prdblk_str,
						(caddr_t)&funarg, klass,0, 0);
				}
			} while(args[optind]);
		} else {
			all = 1;
			do {
				getargs(GETARGS_NO_SLOT,&arg1,&arg2);
				if(arg1 == -1) 
					continue;
				if(arg2 == -1)
					arg2 = arg1;

				for (addr = arg1; addr <= arg2; addr =
				     (long)(((char *) addr)+str_bufsize)) {

					err = readbuf(addr, 0, phys, -1, mb,
					 sizeof mb,"strbuf");
					if (err) {
					  fprintf(fp,
					 "Invalid virtual adress= %X\n", addr);
						break;
					}
					prstrbuf(addr, mb, sizeof mb, 1);
				}
				addr = arg1 = arg2 = -1;
			} while(args[++optind]);
		}
	      } else { /* dump all stream buffers hung on stream queues */

		funarg.all = funarg.phys = 0;
		funarg.class = -1; /* all classes */
		do_for_all_dblk(do_prdblk_str, (caddr_t)&funarg, NCLASS, 0, 0);
	      }
}

/*
 * Get arguments for linkblk function
 *
 * linkblk [-e] [-wfilename] [[-p] tbl_entry[s]]
 */
int
getlinkblk()
{
	int all, c, phys, slot;
	long addr, arg2, arg1;

	all = c = slot = phys = 0;
	addr = arg2 = arg1 = slot = -1;

	linkblk_init();

	optind = 1;
	while((c = getopt(argcnt,args,"epw:")) !=EOF) {
		switch(c) {
			case 'e' :	all = 1;
					break;
			case 'p' :	phys = 1;
					break;
			case 'w' :	redirect();
					break;
			default  :	longjmp(syn,0);
		}
	}

	fprintf(fp,"LINKBLK TABLE SIZE = %d\n",nmuxlink);
	fprintf(fp,"SLOT     QTOP     QBOT   INDEX\n");

	if (args[optind]) {
		all = 1;
		do {
			getargs(- nmuxlink,&arg1,&arg2);
			if(arg1 == -1) 
				continue;
			if(arg2 != -1) {
				if (arg1 >= 0 &&
				    arg1 < nmuxlink) {
					for(slot = arg1; slot <= arg2; slot++)
						prlinkblk(all,slot,phys,addr,nmuxlink);
				} else {
					for (addr = arg1; addr <= arg2;
					     addr = (long) (((struct linkblk *) addr) + 1))
						prlinkblk(all,-1,phys,addr,nmuxlink);
				};
			} else {
				if(arg1 >= 0 &&
				   arg1 < nmuxlink)
					slot = arg1;
				else addr = arg1;
				prlinkblk(all,slot,phys,addr,nmuxlink);
			}
			slot = addr = arg1 = arg2 = -1;
		}while(args[++optind]);
	}
	else for(slot = 0; slot < nmuxlink; slot++)
		prlinkblk(all,slot,phys,addr,nmuxlink);	
}

/*
 * Controlling procedure for obtaining and evaluating the command line
 * arguments for dummping the information on all free streams messages.
 *
 * mbfree [-wfilename]
 */
int
getmbfree()
{
 	int c;

	optind = 1;
	while((c = getopt(argcnt,args,"w:")) !=EOF) {
		switch(c) {
			case 'w' :	redirect();
					break;
			default  :	longjmp(syn,0);
		}
	}
	fprintf(fp,"ADDR     NEXT     CONT     PREV       RPTR     WPTR   DATAB\n");
	if(args[optind]) 
		longjmp(syn,0);
	prmbfree();
}

/*
 * Enumerate allocated Streams message block headers
 *
 * mblock [-e] [-wfilename] [virtual_address[es]]
 */
int
getmess()
{
	struct do_prmess_arg funarg;
	int all, c, phys;
	long addr, arg1, arg2;

	all = phys = 0;
	addr = arg1 = arg2 = -1;

	optind = 1;

	while((c = getopt(argcnt,args,"ew:")) != EOF) {
		switch(c) {
			case 'e' :	all = 1;
					break;
			case 'w' :	redirect();
					break;
			default  :	longjmp(syn,0);
		}
	}

	fprintf(fp,"NUMBER FREE MESSAGE BLOCKS= %d\n",
		zinfo_mblk.node_freecount);
	fprintf(fp,"ADDR     NEXT     CONT     PREV       RPTR     WPTR   DATAB\n");
	if(args[optind]) {
		all = 1;
		do {
			getargs(GETARGS_NO_SLOT,&arg1,&arg2);
			if(arg1 == -1) 
				continue;
			if(arg2 == -1)
				arg2 = arg1;
			for (addr = arg1; addr <= arg2; 
			     addr = (long) (((mblk_t *) addr) + 1))
				prmess(all,phys,addr,NULL);
			addr = arg1 = arg2 = -1;
		}while(args[++optind]);
	}
	else {
		funarg.all = all;
		funarg.phys = phys;

		do_for_all_mblk(do_prmess,(caddr_t) &funarg,all,phys);
	}
}

/*
 * queue [-R] [-W] [-n] [-f] [-wfilename] [[-p] tbl_entry[s]]
 */
int
getqueue()
{
	struct do_prqueuelist_arg funarg;
	int all, c, do_rd, do_wr, do_list, full, phys;
	long addr, arg1, arg2;
	queue_t	que;

	all = do_rd = do_wr = do_list = full = phys = 0;
	addr = arg1 = arg2 = -1;

	read_cdevsw();
	optind = 1;
	while((c = getopt(argcnt,args,"fRWnpw:")) !=EOF) {
		switch(c) {
			case 'f' :	full = 1;
					break;
			case 'R' :	do_rd = 1;
					break;
			case 'W' :	do_wr = 1;
					break;
			case 'n' :	do_list = 1;
					break;
			case 'e' :	all = 1;
					break;
			case 'w' :	redirect();
					break;
			case 'p' :	phys = 1;
					break;
			default  :	longjmp(syn,0);
		}
	}
	fprintf(fp, "%s", queuelist_heading);

	if(args[optind]) {
		all = 1;
		do {
			getargs(GETARGS_NO_SLOT,&arg1,&arg2);
			if(arg1 == -1) 
				continue;
			if(arg2 != -1)
				for(addr = arg1; addr <= arg2; 
				    addr = (long) (((queue_t *) addr) + 1))
					prqueuelist(all,phys,addr,&que,full,do_rd,do_wr,do_list);
			else {
				prqueuelist(all,phys,arg1,&que,do_rd,do_wr,do_list);
			}
			addr = arg1 = arg2 = -1;
		}while(args[++optind]);
	}
	else {
		funarg.all = all;
		funarg.phys = phys;
		funarg.que_p = &que;
		funarg.full = full;
		funarg.do_rd = 1;
		funarg.do_wr = 1;
		funarg.list = 1;

		do_for_all_stream(do_prqueuelist,(caddr_t) &funarg,phys);
	}
}

/*
 * Get arguments for qrun function
 *
 * qrun [-wfilename]
 */
int
getqrun()
{
	int c;

	if(!Qhead)
		if(!(Qhead = symsrch("qhead")))
			error("qhead not found in symbol table\n");
	optind = 1;
	while((c = getopt(argcnt,args,"w:")) !=EOF) {
		switch(c) {
			case 'w' :	redirect();
					break;
			default  :	longjmp(syn,0);
		}
	}
	prqrun();
}

/*
 * Controlling procedure to get the command line parameters and to enumerate
 * all open Streams and call the desired function on each stream.
 *
 * stream [-f] [-wfilename] [[-p] tbl_entry[s]]
 */
int
getstream()
{
	struct do_prstream_arg funarg;
	struct do_prfifo_arg fifo_funarg;
	int all, c, full, list, phys, newheader;
	long addr, arg1, arg2;

	full = all = list = phys = 0;
	addr = arg1 = arg2 = -1;
	read_cdevsw();
	optind = 1;

	newheader = 0;

	while((c = getopt(argcnt,args,"efnpw:")) !=EOF) {
		switch(c) {
			case 'e' :	all = 1;
					break;
			case 'f' :	full = 1;
					break;
			case 'n' : 	list = 1;
					break;
			case 'w' :	redirect();
					break;
			case 'p' :	phys = 1;
					break;
			default  :	longjmp(syn,0);
		}
	}
	if (! full)
		fprintf(fp,"%s",str_heading);

	if(args[optind]) {
		all = 1;
		do {
			getargs(GETARGS_NO_SLOT,&arg1,&arg2);
			if(arg1 == -1) 
				continue;
			if(arg2 != -1)
				for(addr = arg1; addr <= arg2;
				    addr = (long)
					     (((struct stdata *) addr) + 1))
					prstream(all, full, phys, addr,
						 str_heading, list);
			else {
				addr = arg1;
				prstream(all, full, phys,
					 addr, str_heading, list);
			}
			addr = arg1 = arg2 = -1;
		}while(args[++optind]);
	}
	else {
		funarg.all = all;
		funarg.full = full;
		funarg.phys = phys;
		funarg.heading = str_heading;
		funarg.list = list;
		newheader = do_for_all_stream(do_prstream,
				(caddr_t)&funarg, phys);

		fifo_funarg.all = all;
		fifo_funarg.full = full;
		fifo_funarg.phys = phys;
		fifo_funarg.str_heading = str_heading;
#ifdef SVR3_FIFO
		fifo_funarg.fifo_heading1 = fifo_svr3_heading1;
		fifo_funarg.fifo_heading2 = fifo_svr3_heading2;
#else
		fifo_funarg.fifo_heading1 = fifo_svr4_heading1;
		fifo_funarg.fifo_heading2 = fifo_svr4_heading2;
#endif
		fifo_funarg.list = 1; /* print detailed fifo info */
		newheader = do_for_all_fifo(do_prfifo, &fifo_funarg, phys);
	}
	return newheader;
}

/*
 * Get arguments for strstat function
 *
 * strstat [-wfilename]
 */
int
getstrstat()
{
	int c;

	optind = 1;

	while((c = getopt(argcnt,args,"w:")) !=EOF) {
		switch(c) {
			case 'w' :	redirect();
					break;
			default  :	longjmp(syn,0);
		}
	}
	prstrstat();
}

int
linkblk_init()
{
	if (linkblk_inited)
		return;

	if(!Linkblk)
		if(!(Linkblk = symsrch("linkblk")))
			error("linkblk not found in symbol table\n");
	if(!Nmuxlink)
		if(!(Nmuxlink = symsrch("nmuxlink")))
			error("nmuxlink not found in symbol table\n");

	readmem((long)Nmuxlink->n_value,1,-1,(char *)&nmuxlink,
		sizeof nmuxlink,"linkblk table size");

	linkblk_inited = 1;
	return;
}

/*
 * Initialization for namelist symbols pertaining to mbufs data structures
 */
int
mbufinit()
{
	if (mbuf_inited)
		return;

	if(!Zoneinfo_mbuf) {
		if(!(Zoneinfo_mbuf = symsrch("zoneinfo_mbuf"))) {
			error("zoneinfo_mbuf not found in symbol table\n");
			return 1;
		      }
	}
	readmem((long)Zoneinfo_mbuf->n_value,1,-1,(char *)&zinfo_mbufp,
		sizeof zinfo_mbufp,"zinfo_mbuf");
	readmem((long)zinfo_mbufp,1,-1,(char *)&zinfo_mbuf,
		sizeof zinfo_mbuf,"zinfo_mbuf");

	if(!Zoneinfo_clustermbuf) {
		if(!(Zoneinfo_clustermbuf = symsrch("zoneinfo_clustermbuf"))) {
		     error("zoneinfo_clustermbuf not found in symbol table\n");
		     return 1;
		}
	}
	readmem((long)Zoneinfo_clustermbuf->n_value,1,-1,
		(char *)&zinfo_clustermbufp,sizeof zinfo_clustermbufp,
		"zoneinfo_clustermbuf");

	readmem((long)zinfo_clustermbufp,1,-1,
		(char *)&zinfo_clustermbuf,sizeof zinfo_clustermbuf,
		"zoneinfo_clustermbuf");

	mbuf_inited = 1;
	return 0; /* success */
}

/*
 * Get class for dblock. Assumes class index is valid!
 */
int
prclass(class)
	int class;
{
	struct do_prdblk_arg funarg;

	funarg.all = 1; /* both in use and on free list */
	funarg.phys = 0;
	funarg.class = class; /* only specific stream buffer class */

	do_for_all_dblk(do_prdblk, (caddr_t)&funarg, class,
		1, /* free list only */
		0);
	return 0; /* success */
}

/*
 * Print the free list of Streams data block headers
 */
int
prdbfree(class)
	int class;
{
	struct do_prdblk_arg funarg;

	if ((class < 0) || (class > NCLASS-1))
		error("%d is out of range\n",class);

	funarg.all = 1;
	funarg.phys = 0;
	funarg.class = class;

	do_for_all_dblk(do_prdblk, (caddr_t)&funarg, class,
			-1 /* free list only */, 0);
}

/*
 * Print dballoc table
 */
int
prdballoc(class)
	int class;
{
	struct class_info class_area;
	struct strevent *next;

	readmem((long)(Class_info->n_value+class*sizeof class_area),1,-1,
		(char *)&class_area,sizeof class_area,"class_info table");
	fprintf(fp,"CLASS   RCNT    LO   MED  HIGH\n");
	fprintf(fp,"  %d    %5d %5d %5d %5d\n",
		class,
		class_area.dballoc.dba_cnt,
		class_area.dballoc.dba_lo,
		class_area.dballoc.dba_med,
		class_area.dballoc.dba_hi);

	fprintf(fp,"\tLOP:\n");
	next = class_area.dballoc.dba_lop;
	if (next)
		prfuncname(next);

	fprintf(fp,"\tMEDP:\n");
	next = class_area.dballoc.dba_medp;
	if (next)
		prfuncname(next);

	fprintf(fp,"\tHIP:\n");
	next = class_area.dballoc.dba_hip;
	if (next)
		prfuncname(next);
}

/*
 * Print all fields in a Streams data block
 */
int
prdblk(all, phys, addr, class, dbp)
	int all, phys;
	long addr;
	int class;
	dblk_t *dbp;
{
	dblk_t dblk;
	int dfree;

	if (dbp != NULL) {
		dblk = *dbp;
	} else {
		readbuf(addr,0,phys,-1,(char *)&dblk,sizeof dblk,"data block");
	}

	if (!dblk.db_ref && !all)
		return(0);

	if (class != -1 && dblk.db_class != class)
		return(0);

	if (addr == 0)
		fprintf(fp,"  -     ");
	else
		fprintf(fp,"%8x",addr);

	fprintf(fp," %5d ", dblk.db_class);

	switch (dblk.db_class) {
		case 0: fprintf(fp,"   4 "); break;
		case 1: fprintf(fp,"  16 "); break;
		case 2: fprintf(fp,"  32 "); break;
		case 3: fprintf(fp," 128 "); break;
		case 4: fprintf(fp," 256 "); break;
		case 5: fprintf(fp," 512 "); break;
		case 6: fprintf(fp,"1024 "); break;
		case 7: fprintf(fp,"2048 "); break;
		case 8: fprintf(fp,"4096 "); break;
		case 9: fprintf(fp,"8192 "); break;
		case 10: fprintf(fp,"16K  "); break;
		default: fprintf(fp,"   - ");
	}
	fprintf(fp,"%5d ", dblk.db_ref); 
	switch (dblk.db_type) {
		case M_DATA:   fprintf(fp,"data     "); break;
		case M_PROTO:  fprintf(fp,"proto    "); break;
		case M_BREAK:  fprintf(fp,"break    "); break;
		case M_PASSFP: fprintf(fp,"passfp   "); break;
		case M_SIG:    fprintf(fp,"sig      "); break;
		case M_DELAY:  fprintf(fp,"delay    "); break;
		case M_CTL:    fprintf(fp,"ctl      "); break;
		case M_IOCTL:  fprintf(fp,"ioctl    "); break;
		case M_SETOPTS: fprintf(fp,"setopts  "); break;
		case M_IOCACK:  fprintf(fp,"iocack   "); break;
		case M_IOCNAK:  fprintf(fp,"iocnak   "); break;
		case M_PCPROTO: fprintf(fp,"pcproto  "); break;
		case M_PCSIG:   fprintf(fp,"pcsig    "); break;
		case M_FLUSH:   fprintf(fp,"flush    "); break;
		case M_STOP:    fprintf(fp,"stop     "); break;
		case M_START:   fprintf(fp,"start    "); break;
		case M_HANGUP:  fprintf(fp,"hangup   "); break;
		case M_ERROR:   fprintf(fp,"error    "); break;
		default:        fprintf(fp,"-        ");
	}
	fprintf(fp,"%8x %8x ", dblk.db_base, dblk.db_lim);

	if (dblk.db_ref > 0)
		dfree = (long) NULL;
	else
		dfree = (long)((struct zonenode *) &dblk)->next;

	if (dfree != 0)
		fprintf(fp," %8x\n", dfree);
	else
		fprintf(fp," -\n");
	return (dfree);
}

/*
 * Print selected data from the buffer attached to a Streams data block.
 * The address of the data buffer header is in 'addr' which will be used
 * to read the dblk memory IF the 'dbp' address is NULL. The address of the
 * streams buffer obtained from the dblk is checked for non-zero prior
 * to dumping data at that VM address.
 * 
 */
int
prdblk_str(all, phys, addr, class, dbp)
	int all, phys;
	long addr;
	int class;
	dblk_t *dbp;
{
	unsigned char bb[32];
	dblk_t dblk;
	long bpaddr;
	int err, rtn;

	if (dbp != NULL) {
		dblk = *dbp;
	}
	else {
		readbuf(addr,0,phys,-1,(char *)&dblk,sizeof dblk,"data block");
	}

	if (dblk.db_ref == 0) { /* no buffer attached */
		return(0);
	}

	if (class != -1 && dblk.db_class != class) {
		return(0);
	}

	rtn = 1;
	bpaddr = (long)(dblk.db_base);
	if (bpaddr) {
		err = readbuf(bpaddr, 0, phys, -1, bb, sizeof bb,"strbuf");
		if (err) {
			fprintf(fp, "Invalid virtual adress= %X\n", bpaddr);
			rtn = 0;
		} else {
		        /* last param says don't print address via printf */
			prstrbuf(bpaddr, bb, sizeof(bb), 0);
		}
	}
	return rtn;
}

/*
 * Print the data structures for an SVR4 fifo data hung off a stream vnode.
 */
int
prsvr4fifo(all, full, phys, addr, heading1, heading2, list)
	int all, full, phys;
	long addr;
	char *heading1, *heading2;
	int list;
{
	struct fifonode fifonode;
	if (!list) { /* no details desired on fifo's */
		return 0; /* indicates no printing */
	}

	readbuf(addr, 0, phys, -1, (char *)&fifonode,
		sizeof(fifonode), "fifo data");
	if (!full) {
		fprintf(fp,"%s", heading1);
	}
	fprintf(fp,"%8x %8x %8x %4u %4d %4d %4d %8x\n",
		addr, fifonode.fn_mate, fifonode.fn_realvp, fifonode.fn_ino,
		fifonode.fn_wcnt, fifonode.fn_rcnt, fifonode.fn_open,
		fifonode.fn_unique);

	if (!full) {
		fprintf(fp,"%s", heading2);
	}
	fprintf(fp,"%8x %8x %8x %8x %8x ",
		fifonode.fn_atime, fifonode.fn_mtime, fifonode.fn_ctime,
		fifonode.fn_nextp, fifonode.fn_backp);

	fprintf(fp, "%s%s%s%s%s%s\n",
		((fifonode.fn_flag & ISPIPE)   ? "ISPIPE "   : ""),
		((fifonode.fn_flag & FIFOLOCK) ? "FIFOLOCK " : ""),
		((fifonode.fn_flag & FIFOWRITE)? "FIFOWRITE ": ""),
		((fifonode.fn_flag & FIFOWANT) ? "FIFOWANT " : ""),
		((fifonode.fn_flag & FIFOREAD) ? "FIFOREAD " : ""),
		((fifonode.fn_flag & FIFOPASS) ? "FIFOPASS"  : ""));
	return 1; /* indicates stuff was printed */
}

#ifdef SVR3_FIFO
/*
 * Print the data structures for an SVR3 fifo data hung off a stream vnode.
 */
int
prsvr3fifo(all, full, phys, addr, heading1, heading2, list)
	int all, full, phys;
	long addr;
	char *heading1, *heading2;
	int list;
{
	struct fifonode fifonode;
	if (!list) { /* no details desired on fifo's */
		return 0; /* indicates no printing */
	}

	readbuf(addr, 0, phys, -1, (char *)&fifonode,
		sizeof(fifonode), "fifo data");
	if (!full) {
		fprintf(fp,"%s", heading1);
	}
	fprintf(fp,"%8x %8x %8x %8x %8x %8x %8u\n",
		addr, fifonode.fn_buf, fifonode.fn_bufend, fifonode.fn_rsel,
		fifonode.fn_wsel, fifonode.fn_xsel, fifonode.fn_size);

	if (!full) {
		fprintf(fp,"%s", heading2);
	}
	fprintf(fp,"%4d %4d %4d %4d %4d ",
		fifonode.fn_wcnt, fifonode.fn_rcnt,
		fifonode.fn_wptr, fifonode.fn_rptr, fifonode.fn_nbuf);

	fprintf(fp, "%s%s%s%s%s\n",
		((fifonode.fn_flag & FIFO_RBLK) ?  "FIFO_RBLK "  : ""),
		((fifonode.fn_flag & FIFO_WBLK) ?  "FIFO_WBLK "  : ""),
		((fifonode.fn_flag & FIFO_RCOLL) ? "FIFO_RCOLL " : ""),
		((fifonode.fn_flag & FIFO_WCOLL) ? "FIFO_WCOLL " : ""),
		((fifonode.fn_flag & FIFO_XCOLL) ? "FIFO_XCOLL"  : ""));
	return 1; /* indicates stuff was printed */
}
#endif NOTYET

/*
 * Print function name for dballoc
 */
int
prfuncname(addr)
	long(addr);
{
	struct syment *sp;
	extern struct syment *findsym();
	struct strevent evbuf;
	char *name;
	
	readmem((long)addr,1,-1,(char *)&evbuf,
		sizeof evbuf,"stream event buffer");

	if(!(sp = findsym((unsigned long)evbuf.se_func))) {
		prerrmes("%8x does not match in symbol table\n",
			evbuf.se_func);
		return;
	}

	name = sp->n_name;
	fprintf(fp,"\t\t%-15s",name);
	fprintf(fp,"(%8x)\n",evbuf.se_arg);
}

/*
 * Print linkblk table
 */
int
prlinkblk(all, slot, phys, addr, max)
	int all, slot, phys, max;
	long addr;
{
	struct linkblk linkbuf;

	readbuf(addr,(long)(Linkblk->n_value+slot*sizeof linkbuf),phys,-1,
		(char *)&linkbuf,sizeof linkbuf,"linkblk table");
	if(!linkbuf.l_qtop && !all)
		return;
	if(addr != -1)
		slot = getslot(addr,(long)Linkblk->n_value,sizeof linkbuf,phys,
			max);
	if(slot == -1)
		fprintf(fp,"  - ");
	else
		fprintf(fp,"%4d", slot);
	fprintf(fp," %8x",linkbuf.l_qtop);
	fprintf(fp," %8x",linkbuf.l_qbot);
	fprintf(fp,"   %3d\n",linkbuf.l_index);
}

/*
 * Print Streams message block headers free list
 */
int
prmbfree()
{
	struct do_prmess_arg funarg;

	funarg.all = -1;
	funarg.phys = 0;

	do_for_all_mblk(do_prmess,(caddr_t) &funarg,-1,0);
}

/*
 * Print the various structure fields associated with an mbuf
 */
int
prmbuf(funarg, mbufaddr, mbufp)
	long funarg, mbufaddr;
	struct mbuf *mbufp;
{
	if (mbufaddr == 0)
		return;

	fprintf(fp,"%8x ", mbufaddr);
	fprintf(fp,"%8x ", mbufp->m_hdr.mh_next);
	fprintf(fp,"%8x ", mbufp->m_hdr.mh_nextpkt);
	fprintf(fp,"%5d ", mbufp->m_hdr.mh_len);
	fprintf(fp,"%8x ", mbufp->m_hdr.mh_data);

	switch (mbufp->m_hdr.mh_type) {
		case MT_FREE:    fprintf(fp,"free     "); break;
		case MT_DATA:    fprintf(fp,"data     "); break;
		case MT_HEADER:  fprintf(fp,"header   "); break;
		case MT_SOCKET:  fprintf(fp,"socket   "); break;
		case MT_PCB:     fprintf(fp,"pcb      "); break;
		case MT_RTABLE:  fprintf(fp,"rtable   "); break;
		case MT_HTABLE:  fprintf(fp,"htable   "); break;
		case MT_ATABLE:  fprintf(fp,"atable   "); break;
		case MT_SONAME:  fprintf(fp,"soname   "); break;
		case MT_SOOPTS:  fprintf(fp,"soopts   "); break;
		case MT_FTABLE:  fprintf(fp,"ftable   "); break;
		case MT_RIGHTS:  fprintf(fp,"rights   "); break;
		case MT_IFADDR:  fprintf(fp,"ifaddr   "); break;
		case MT_CONTROL: fprintf(fp,"control  "); break;
		case MT_OOBDATA: fprintf(fp,"oobdata  "); break;
		default:         fprintf(fp,"-        ");
	}

	if (mbufp->m_hdr.mh_flags & M_EXT)
		fprintf(fp,"ext ");
	if (mbufp->m_hdr.mh_flags & M_PKTHDR)
		fprintf(fp,"pkthdr ");
	if (mbufp->m_hdr.mh_flags & M_EOR)
		fprintf(fp,"eor ");
	if (mbufp->m_hdr.mh_flags & M_CL_FUNNY)
		fprintf(fp,"funny ");
	if (mbufp->m_hdr.mh_flags & M_BCAST)
		fprintf(fp,"bcast ");
	if (mbufp->m_hdr.mh_flags & M_MCAST)
		fprintf(fp,"mcast ");

	fprintf(fp,"\n");

	if (mbufp->m_hdr.mh_flags & M_PKTHDR) {
		fprintf(fp,"M_PKTHDR:");
		fprintf(fp,"len= %4d ", mbufp->M_dat.MH.MH_pkthdr.len);
		fprintf(fp,"rcvif= %8x\n", mbufp->M_dat.MH.MH_pkthdr.rcvif);
	}

	if (mbufp->m_hdr.mh_flags & M_EXT) {
		fprintf(fp,"M_EXT:");
		fprintf(fp,"ext_buf= %8x ",
			mbufp->M_dat.MH.MH_dat.MH_ext.ext_buf);
		fprintf(fp,"ext_free= %8x ",
			mbufp->M_dat.MH.MH_dat.MH_ext.ext_free);
		fprintf(fp,"ext_size= %8x\n",
			mbufp->M_dat.MH.MH_dat.MH_ext.ext_size);
	}

	if (mbufp->m_hdr.mh_flags & M_CL_FUNNY) {
		fprintf(fp,"M_CL_FUNNY:");
		fprintf(fp,"clfun= %8x ",
			mbufp->M_dat.MH.MH_dat.MH_cl_funny.mun_clfun);
		fprintf(fp,"clarg= %8x ",
			mbufp->M_dat.MH.MH_dat.MH_cl_funny.mun_clarg);
		fprintf(fp,"dfun= %8x ",
			mbufp->M_dat.MH.MH_dat.MH_cl_funny.mun_dfun);
		fprintf(fp,"darg= %8x\n",
			mbufp->M_dat.MH.MH_dat.MH_cl_funny.mun_darg);
	}
	return;
}

/*
 * Print a Streams message block's fields
 */
int
prmess(all, phys, addr, mbp)
	int all, phys;
	long addr;
	mblk_t *mbp;
{
	mblk_t mblk;
	int mnext, mcont, datab, mprev;

	if (mbp != NULL)
		mblk = *mbp;
	else
	     readbuf(addr,0,phys,-1,(char *)&mblk,sizeof mblk,"message block");
	if (!mblk.b_datap && !all) 
		return(-1);
	if (addr == 0)
		fprintf(fp,"  -      ");
	else
		fprintf(fp,"%8x ",addr);
	mnext = (long)mblk.b_next;
	mcont = (long)mblk.b_cont;
	mprev = (long)mblk.b_prev;
	datab = (long)mblk.b_datap;
	if (mnext != 0)
		fprintf(fp,"%8x ",mnext);
	else
		fprintf(fp,"   -     ");
	if (mcont != 0)
		fprintf(fp,"%8x ",mcont);
	else
		fprintf(fp,"   -     ");
	if (mprev != 0)
		fprintf(fp,"%8x ",mprev);
	else
		fprintf(fp,"   -     ");

	fprintf(fp,"%8x %8x ", mblk.b_rptr, mblk.b_wptr);
	if (datab != 0)
		fprintf(fp,"%8x\n",datab);
	else
		fprintf(fp,"   -\n");
	return(1);
}

/*
 * Print some number of bytes from streams buffer
 */
int
prstrbuf(addr, bp, size, options)
	unsigned int *addr;
	unsigned char *bp;
	int size, options;
{
	unsigned int curpos, inx, line_width;
	unsigned short *ptr;

	if (options) {
		if (addr == 0)
			fprintf(fp,"  -      ");
		else
			fprintf(fp,"%8x ", addr);
	}
	line_width = 72;
	curpos = 8;
	ptr = (unsigned short *)bp;
	for (inx=0; inx < size/sizeof(short); inx++) {
		if (options) {
			fprintf(fp, "%x", *ptr);
		}

		ptr++;
		if (((curpos % line_width) == 0) && (curpos != 0)) {
			/* don't print too many characters */
			fprintf(fp,"\n");
			line_width = 80;
			curpos = 0;
		} else {
			curpos++;curpos++;
		}
	}

	if ((size/sizeof(short) > 0) && ((curpos % line_width) == 0)) {
		/* print SECOND terminating CR only if necessary */
		fprintf(fp,"\n");
	}
}

/*
 * Print the data structures associated with a stream instance.
 */
int
prstream(all, full, phys, addr, heading, list)
	int all, full, phys;
	long addr;
	char *heading;
	int list;
{
	struct stdata strm;
	struct strevent evbuf, *next;
	struct pollhead phbuf;	/* pollhead buffer */
	struct pollhead *pollist;	
	struct polldat pdbuf;	/* poll data buffer for poll list */
	struct polldat *pdnext; /* next in poll list */
	struct queue que;

	readbuf(addr, 0, phys, -1, (char *)&strm, sizeof strm, "stdata");
	if (full)
		fprintf(fp,"%s", heading);

	fprintf(fp,"%8x ",addr);
	fprintf(fp,"%8x ",((long)strm.sd_wrq));
	if (strm.sd_iocblk != NULL)
		fprintf(fp,"%8x ",(long) strm.sd_iocblk);
	else
		fprintf(fp,"   -     ");
	fprintf(fp,"%8x ",((long)strm.sd_vnode));

	fprintf(fp,"%8x %5d %5d %3o ",
		strm.sd_strtab, strm.sd_iocid, strm.sd_iocwait, strm.sd_wroff,
		(strm.sd_rerror ? strm.sd_rerror : strm.sd_werror));

	fprintf(fp,"%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
		((strm.sd_flag & IOCWAIT) ? "iocw " : ""),
		((strm.sd_flag & RSLEEP) ? "rslp " : ""),
		((strm.sd_flag & WSLEEP) ? "wslp " : ""),
		((strm.sd_flag & STRPRI) ? "pri " : ""),
		((strm.sd_flag & STRHUP) ? "hup " : ""),
		((strm.sd_flag & STWOPEN) ? "stwo " : ""),
		((strm.sd_flag & STPLEX) ? "plex " : ""),
		((strm.sd_flag & RMSGDIS) ? "mdis " : ""),
		((strm.sd_flag & RMSGNODIS) ? "mnds " : ""),
		((strm.sd_flag & (STRDERR | STWRERR)) ? "err " : ""),
		((strm.sd_flag & STRTIME) ? "sttm " : ""),
		((strm.sd_flag & STR2TIME) ? "s2tm " : ""),
		((strm.sd_flag & STR3TIME) ? "s3tm " : ""),
		((strm.sd_xflag & STFIONBIO) ? "nbio " : ""),
		((strm.sd_flag & STRISTTY) ? "tty " : ""),
		((strm.sd_xflag & STFIOASYNC) ? "asyn " : ""),
		((strm.sd_xflag & STFJOBCTRL) ? "jctl " : ""),
		((strm.sd_flag & STFTOSTOP) ? "tost " : ""),
		((strm.sd_xflag & STFPENDIN) ? "pend " : ""));

	if (full) {
		fprintf(fp,"\tSID      PGID     PUSHCNT\n");
		if (strm.sd_sidp) {
			fprintf(fp,"\t%8x ", strm.sd_sidp);
		} else {
			fprintf(fp,"\t  -      ");
		}
		if (strm.sd_pgidp) {
			fprintf(fp,"%8x ", strm.sd_pgidp);
		} else {
			fprintf(fp,"  -      ");
		}
		fprintf(fp,"%7d\n", strm.sd_pushcnt);

		fprintf(fp,"\tSIGFLAGS:  %s%s%s%s\n",
			((strm.sd_sigflags & S_INPUT)  ? " input" : ""),
			((strm.sd_sigflags & S_HIPRI)  ? " hipri" : ""),
			((strm.sd_sigflags & S_OUTPUT) ? " output" : ""),
			((strm.sd_sigflags & S_MSG)    ? " msg" : ""));

		fprintf(fp,"\tSIGLIST:\n");
		next = strm.sd_siglist;
		while(next) {
			readmem((long)next,1,-1,(char *)&evbuf,
				sizeof evbuf,"stream event buffer");
			fprintf(fp,"\t\tPROC:  %3d   %s%s%s%s\n",
				proc_to_slot((long)evbuf.se_procp),
				((evbuf.se_events & S_INPUT) ? " input" : ""),
				((evbuf.se_events & S_HIPRI) ? " hipri" : ""),
				((evbuf.se_events &S_OUTPUT) ? " output" : ""),
				((evbuf.se_events & S_MSG) ? " msg" : ""));
			next = evbuf.se_next;	
		}
		pollist = &strm.sd_pollist;
		readmem((long)pollist,1,-1,(char *)&phbuf,
			sizeof phbuf,"poll head buffer");
		fprintf(fp,"\tPOLL EVENTS PENDING: %s%s%s%s%s%s\n",
			((phbuf.ph_events & POLLIN) ? " in" : ""),
			((phbuf.ph_events & POLLPRI) ? " pri" : ""),
			((phbuf.ph_events & POLLOUT) ? " out" : ""),
			((phbuf.ph_events & POLLIN) ? " rdnorm" : ""),
			((phbuf.ph_events & POLLPRI) ? " rdband" : ""),
			((phbuf.ph_events & POLLOUT) ? " wrband" : ""));
		fprintf(fp,"\tPOLLIST:\n");	
		pdnext = phbuf.ph_list;	/* get list of pollers */
		while(pdnext) {
			readmem((long)pdnext,1,-1,(char *)&pdbuf,
				sizeof pdbuf,"poll data buffer");
			/* print testable select events */
			fprintf(fp,"\t %s%s%s%s%s%s\n",
				((pdbuf.pd_events & POLLIN) ? " in" : ""),
				((pdbuf.pd_events & POLLPRI) ? " pri" : ""),
				((pdbuf.pd_events & POLLOUT) ? " out" : ""),
				((pdbuf.pd_events & POLLIN) ? " rdnorm" : ""),
				((pdbuf.pd_events & POLLPRI) ? " rdband" : ""),
				((pdbuf.pd_events& POLLOUT) ? " wrband" : ""));
			pdnext = pdbuf.pd_next;	
		}
		fprintf(fp,"\n");
	}
	if (list && strm.sd_wrq) {
		fprintf(fp,"QUEUES FOR STREAM %8x:\n",addr);
		fprintf(fp, "%s", queuelist_heading);
		prqueuelist(all,phys,(long)strm.sd_wrq,&que,full,1,1,list);
	};
	return 0; /* no new header needs to be printed */
}

/*
 * Print qrun information
 */
int
prqrun()
{
	queue_t que, *q;

	readmem((long)Qhead->n_value,1,-1,(char *)&q,
		sizeof q,"qhead");
	fprintf(fp,"Queue addresses scheduled for service: ");
	while (q) {
		fprintf(fp,"%8x ",(long) q);
		readmem((long)q,1,-1,(char *)&que,
			sizeof que,"scanning queue list");
		q = que.q_link;
	}
	fprintf(fp,"\n");
}

/*
 * Print a specific queue entry on a stream
 */
int
prqueue(all, phys, addr, que_p, full)
	int all, phys;
	long addr;
	queue_t	*que_p;
	int full;
{
	mblk_t *m;
	long qn, ql;

	readbuf(addr,0,phys,-1,
		(char *)que_p, sizeof(*que_p),"queue");
        fprintf(fp,"%8x ",addr);
	ql = (long)que_p->q_link;
	if (ql != (long) NULL)
		fprintf(fp,"%8x ",ql);
	else 
		fprintf(fp,"   -     ");
	fprintf(fp,"%8x ",que_p->q_ptr);
	fprintf(fp,"%4d ",que_p->q_count);

	m = que_p->q_first;
	if (m != NULL)
        	fprintf(fp,"%8x ", (long)m);
	else
		fprintf(fp,"   -     ");

	m = que_p->q_last;
	if (m != NULL)
        	fprintf(fp,"%8x ", (long) m);
	else
		fprintf(fp,"   -     ");

	fprintf(fp,"%4d %4d %4d %4d ",
		que_p->q_minpsz, que_p->q_maxpsz,
		que_p->q_hiwat, que_p->q_lowat);

	fprintf(fp,"%s%s%s%s%s%s\n",
		((que_p->q_flag & QENAB)  ? "EN " : ""),
		((que_p->q_flag & QNOENB) ? "NE " : ""),
		((que_p->q_flag & QWANTR) ? "WR " : ""),
		((que_p->q_flag & QWANTW) ? "WW " : ""),
		((que_p->q_flag & QFULL)  ? "FL " : ""),
		((que_p->q_flag & QREADR) ? "RR " : "")
		);

	if (! full)
		return 0;

	fprintf(fp,"\tINFO = %8x; NEXT = ",que_p->q_qinfo);
	qn = (long)que_p->q_next;
	if (qn != (long) NULL)
		fprintf(fp,"%8x\n",qn);
	else 
		fprintf(fp,"   -    \n");
	return 0;
}

/*
 * Walk and print the list of queues entries on a Streams queue.
 */
int
prqueuelist(all, phys, addr, que_p, full, do_rd, do_wr, list)
	int all, phys;
	long addr;
	queue_t	*que_p;
	int full, do_rd, do_wr, list;
{
	queue_t *np;
	long wr_addr, rd_addr;

	np = (queue_t *)addr;
	rd_addr = wr_addr = addr;

	while (addr != (long) NULL) {
		if (list || do_wr || do_rd) {
			rd_addr = wr_addr = addr;
			readbuf(addr,0,phys,-1,
				(char *)que_p, sizeof(*que_p),"queue");
			np = que_p->q_next;
			if ((que_p->q_flag & QREADR)) {
				wr_addr = (long) (WR((struct queue *) addr));
				readbuf(wr_addr,0,phys,-1,
					(char *)que_p, sizeof(*que_p),"queue");
			} else 
				rd_addr = (long) (RD((struct queue *) addr));
		};

		if (! do_rd && ! do_wr) 
			prqueue(all,phys,addr,que_p,full);
		else {
			if (do_wr)
				prqueue(all,phys,wr_addr,que_p,full);
			if (do_rd) 
				prqueue(all,phys,rd_addr,que_p,full);
		};

		if (! list)
			return 0;
		addr = (long) np;
	};
	return 0;
}

/*
 * Print the stream statistics block
*/
int
prstrstat()
{
	struct do_strstat_arg funarg;
	struct strstat strs;
	struct stdata str;
	queue_t que, *q;
	int qusecnt, susecnt, mfreecnt, musecnt, dfreecnt, dusecnt;
	int dfc[NCLASS], duc[NCLASS], dcc[NCLASS], qruncnt;
	int i,j, *p;

	qusecnt = susecnt = mfreecnt = 0;
	musecnt = dfreecnt = dusecnt = qruncnt = 0;

	for (i=0; i<NCLASS; i++) dfc[i] = duc[i] = 0;
	p = &(vbuf.v_nblk4096);

	for (i=NCLASS-1; i>=0; i--) dcc[i] = *p++;

	readmem((long)Strst->n_value,1,-1,
		&strs,sizeof strs,"streams status block");
	fprintf(fp,
"ITEM               CONFIGURED   ALLOCATED     FREE        MAX      FAILED\n");

	readmem((long)Qhead->n_value,1,-1,(char *)&q, sizeof q,"qhead");
	while (q) {
		qruncnt++;
		readmem((long)q,1,-1,(char *)&que,sizeof que,"queue run list");
		q = que.q_link;
	}
	
	qusecnt = strs.queue.use;
	susecnt = strs.stream.use;

	funarg.freecount = &mfreecnt;
	funarg.usecount = &musecnt;
	do_for_all_mblk(do_strstat_mblk,(caddr_t) &funarg,1,0);

	funarg.freecount = &dfreecnt;
	funarg.usecount = &dusecnt;
	funarg.dfc = dfc;
	funarg.duc = duc;
	do_for_all_dblk(do_strstat_dblk,(caddr_t) &funarg,
		-1,
		1, /* both in-use and on free list */
		0);

	fprintf(fp,
"streams                -           %4d        -         %4d       %4d\n",
	susecnt, strs.stream.maxusg, strs.stream.fail);

	fprintf(fp,
"queues                 -           %4d        -         %4d       %4d\n",
	qusecnt, strs.queue.maxusg, strs.queue.fail);

	fprintf(fp,
"message blocks        %4d         %4d       %4d       %4d       %4d\n",
	nmblock, musecnt, mfreecnt, strs.mblock.maxusg, strs.mblock.fail);

	fprintf(fp,
"data block totals     %4d         %4d       %4d       %4d       %4d\n",
	ndblock, dusecnt, dfreecnt, strs.dblock.maxusg, strs.dblock.fail);

	for (i=0; i<NCLASS; i++) 
		{
		fprintf(fp,"data block size ");
		switch (i) {
			case 0: fprintf(fp,"   4  "); break;
			case 1: fprintf(fp,"  16  "); break;
			case 2: fprintf(fp,"  32  "); break;
			case 3: fprintf(fp," 128  "); break;
			case 4: fprintf(fp," 256  "); break;
			case 5: fprintf(fp," 512  "); break;
			case 6: fprintf(fp,"1024  "); break;
			case 7: fprintf(fp,"2048  "); break;
			case 8: fprintf(fp,"4096  "); break;
			case 9: fprintf(fp,"8192  "); break;
			case 10: fprintf(fp," 16K  "); break;
			default: fprintf(fp,"   -  ");
			}
		fprintf(fp,"%4d         %4d       %4d       %4d       %4d\n",
			dcc[i], duc[i], dfc[i],
			strs.dblk[i].maxusg, strs.dblk[i].fail);
		}
	fprintf(fp,"\nCount of scheduled queues:%4d\n", qruncnt);
}

/*
 * Read in the cdevsw and MAJOR arrays
 * Returns 1 on failure and 0 on success.
 */
int
read_cdevsw()
{
	if (cdev_inited)
		return;
	if (!SYM_cdevcnt) {
		if (! (SYM_cdevcnt = symsrch("cdevcnt"))) {
			error("cdevcnt not found in symbol table\n");
			return 1;
		}
	}
	if (!SYM_cdevsw) {
		if (! (SYM_cdevsw = symsrch("cdevsw"))) {
			error("cdevsw not found in symbol table\n");
			return 1;
		}
	}
	if (!SYM_MAJOR) {
		if (! (SYM_MAJOR = symsrch("MAJOR"))) {
			error("MAJOR not found in symbol table\n");
			return 1;
		}
	}

	readmem(SYM_cdevcnt->n_value,1,-1,(char *) &cdevcnt,
		sizeof(cdevcnt),"cdevcnt");
	if (cdevcnt > 0) {
		if(!(cdevsw_copy=(struct cdevsw *)malloc((unsigned)
				(cdevcnt * sizeof(struct cdevsw)))))
			fatal("cannot allocate space for cdevsw\n");
		readmem(SYM_cdevsw->n_value,1,-1,(char *) cdevsw_copy,
			sizeof(struct cdevsw) * cdevcnt,"cdevsw");
	};
	readmem(SYM_MAJOR->n_value,1,-1, MAJOR,
		128,"MAJOR");

	cdev_inited = 1;
	return 0; /* success */
}
		
/*
 * Initialization for namelist symbols pertaining to Streams data structures
 */
int
streaminit()
{
	if (str_inited)
		return;

	if(!Nmblock) {
		if(!(Nmblock = symsrch("nmblock"))) {
			error("nmblock not found in symbol table\n");
			return 1;
		}
	}
	readmem((long)Nmblock->n_value,1,-1,(char *)&nmblock,
		sizeof nmblock,"nmblock");

	if(!Ndblock) {
		if(!(Ndblock = symsrch("ndblock"))) {
			error("ndblock not found in symbol table\n");
			return 1;
		}
	}
	readmem((long)Ndblock->n_value,1,-1,(char *)&ndblock,
		sizeof ndblock,"ndblock");

	if (!SYM_spec_vnodeops) {
		if (! (SYM_spec_vnodeops = symsrch("spec_vnodeops"))) {
			error("spec_vnodeops not found in symbol table\n");
			return 1;
		}
	}

	if(!Qhead) {
		if(!(Qhead = symsrch("qhead"))) {
			error("qhead not found in symbol table\n");
			return 1;
		}
	}
	if(!Zoneinfo_mblk) {
		if(!(Zoneinfo_mblk = symsrch("zoneinfo_mblk"))) {
			error("zoneinfo_mblk not found in symbol table\n");
			return 1;
		}
	}
	readmem((long)Zoneinfo_mblk->n_value,1,-1,(char *)&zinfo_mblkp,
		sizeof zinfo_mblkp,"zoneinfo_mblk");
	readmem((long)zinfo_mblkp,1,-1,(char *)&zinfo_mblk,
		sizeof zinfo_mblk,"zoneinfo_mblk");

	if(!Zoneinfo_dblk) {
		if(!(Zoneinfo_dblk = symsrch("zoneinfo_dblk"))) {
			error("zoneinfo_dblk not found in symbol table\n");
			return 1;
		}
	}
	readmem((long)Zoneinfo_dblk->n_value,1,-1,(char *)&zinfo_dblkp,
		sizeof zinfo_dblkp,"zoneinfo_mblk");
	readmem((long)zinfo_dblkp,1,-1,(char *)&zinfo_dblk,
		sizeof zinfo_dblk,"zoneinfo_dblk");

	if(!Class_info) {
		if(!(Class_info = symsrch("class_info"))) {
			error("class_info not found in symbol table\n");
			return 1;
		}
	}
	if(!Strst) {
		if(!(Strst = symsrch("strst"))) {
			error("strst not found in symbol table\n");
			return 1;
		}
	}
	str_inited = 1;
	return 0; /* success */
}
