db_output.c revision 207922
1193323Sed/*-
2193323Sed * Mach Operating System
3193323Sed * Copyright (c) 1991,1990 Carnegie Mellon University
4193323Sed * All Rights Reserved.
5193323Sed *
6193323Sed * Permission to use, copy, modify and distribute this software and its
7193323Sed * documentation is hereby granted, provided that both the copyright
8193323Sed * notice and this permission notice appear in all copies of the
9193323Sed * software, derivative works or modified versions, and any portions
10193323Sed * thereof, and that both notices appear in supporting documentation.
11193323Sed *
12193323Sed * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
13193323Sed * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
14193323Sed * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
15193323Sed *
16193323Sed * Carnegie Mellon requests users of this software to return to
17193323Sed *
18198090Srdivacky *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
19193323Sed *  School of Computer Science
20194178Sed *  Carnegie Mellon University
21193323Sed *  Pittsburgh PA 15213-3890
22193323Sed *
23195340Sed * any improvements or extensions that they make and grant Carnegie the
24195340Sed * rights to redistribute these changes.
25195340Sed */
26195340Sed/*
27195340Sed * 	Author: David B. Golub, Carnegie Mellon University
28195340Sed *	Date:	7/90
29195340Sed */
30195340Sed
31195340Sed/*
32195340Sed * Printf and character output for debugger.
33195340Sed */
34195340Sed
35193323Sed#include <sys/cdefs.h>
36195340Sed__FBSDID("$FreeBSD: head/sys/ddb/db_output.c 207922 2010-05-11 17:01:14Z attilio $");
37193323Sed
38193323Sed#include "opt_ddb.h"
39193323Sed
40195340Sed#include <sys/param.h>
41193323Sed#include <sys/systm.h>
42193323Sed#include <sys/cons.h>
43193323Sed#include <sys/kdb.h>
44193323Sed#include <sys/kernel.h>
45193323Sed#include <sys/sysctl.h>
46193323Sed
47193323Sed#include <machine/stdarg.h>
48193323Sed
49193323Sed#include <ddb/ddb.h>
50193323Sed#include <ddb/db_output.h>
51193323Sed
52193323Sedstruct dbputchar_arg {
53193323Sed	size_t	da_nbufr;
54193323Sed	size_t	da_remain;
55193323Sed	char	*da_pbufr;
56193323Sed	char	*da_pnext;
57195340Sed};
58198090Srdivacky
59193323Sed/*
60193323Sed *	Character output - tracks position in line.
61193323Sed *	To do this correctly, we should know how wide
62195098Sed *	the output device is - then we could zero
63193323Sed *	the line position when the output device wraps
64193323Sed *	around to the start of the next line.
65195340Sed *
66193323Sed *	Instead, we count the number of spaces printed
67195098Sed *	since the last printing character so that we
68193323Sed *	don't print trailing spaces.  This avoids most
69195098Sed *	of the wraparounds.
70195098Sed */
71195098Sedstatic int	db_output_position = 0;		/* output column */
72195098Sedstatic int	db_last_non_space = 0;		/* last non-space character */
73195340Seddb_expr_t	db_tab_stop_width = 8;		/* how wide are tab stops? */
74195098Sed#define	NEXT_TAB(i) \
75195098Sed	((((i) + db_tab_stop_width) / db_tab_stop_width) * db_tab_stop_width)
76195098Seddb_expr_t	db_max_width = 79;		/* output line width */
77195098Seddb_expr_t	db_lines_per_page = 20;		/* lines per page */
78193323Sedvolatile int	db_pager_quit;			/* user requested quit */
79193323Sedstatic int	db_newlines;			/* # lines this page */
80193323Sedstatic int	db_maxlines;			/* max lines/page when paging */
81195098Sedstatic int	ddb_use_printf = 0;
82195340SedSYSCTL_INT(_debug, OID_AUTO, ddb_use_printf, CTLFLAG_RW, &ddb_use_printf, 0,
83193323Sed    "use printf for all ddb output");
84202878Srdivacky
85193323Sedstatic void	db_putc(int c);
86193323Sedstatic void	db_puts(const char *str);
87193323Sedstatic void	db_putchar(int c, void *arg);
88193323Sedstatic void	db_pager(void);
89193323Sed
90193323Sed/*
91193323Sed * Force pending whitespace.
92193323Sed */
93193323Sedvoid
94193323Seddb_force_whitespace()
95195340Sed{
96193323Sed	register int last_print, next_tab;
97193323Sed
98193323Sed	last_print = db_last_non_space;
99193323Sed	while (last_print < db_output_position) {
100193323Sed	    next_tab = NEXT_TAB(last_print);
101202878Srdivacky	    if (next_tab <= db_output_position) {
102195098Sed		while (last_print < next_tab) { /* DON'T send a tab!!! */
103195098Sed			cnputc(' ');
104195340Sed			db_capture_writech(' ');
105195098Sed			last_print++;
106195098Sed		}
107202878Srdivacky	    }
108195098Sed	    else {
109193323Sed		cnputc(' ');
110202878Srdivacky		db_capture_writech(' ');
111193323Sed		last_print++;
112193323Sed	    }
113193323Sed	}
114193323Sed	db_last_non_space = db_output_position;
115193323Sed}
116193323Sed
117193323Sed/*
118195340Sed * Output character.  Buffer whitespace.
119202878Srdivacky */
120193323Sedstatic void
121193323Seddb_putchar(int c, void *arg)
122193323Sed{
123193323Sed	struct dbputchar_arg *dap = arg;
124193323Sed
125195340Sed	if (dap->da_pbufr == NULL) {
126195340Sed
127193323Sed		 /* No bufferized output is provided. */
128193323Sed		db_putc(c);
129193323Sed	} else {
130193323Sed
131193323Sed		*dap->da_pnext++ = c;
132193323Sed		dap->da_remain--;
133193323Sed
134195340Sed		/* Leave always the buffer 0 terminated. */
135195340Sed		*dap->da_pnext = '\0';
136195340Sed
137195340Sed		/* Check if the buffer needs to be flushed. */
138193323Sed		if (dap->da_remain < 2 || c == '\n') {
139193323Sed			db_puts(dap->da_pbufr);
140193323Sed			dap->da_pnext = dap->da_pbufr;
141193323Sed			dap->da_remain = dap->da_nbufr;
142193323Sed			*dap->da_pnext = '\0';
143193323Sed		}
144193323Sed	}
145193323Sed}
146195340Sed
147193323Sedstatic void
148195340Seddb_putc(int c)
149202878Srdivacky{
150193323Sed
151193323Sed	/*
152193323Sed	 * If not in the debugger or the user requests it, output data to
153193323Sed	 * both the console and the message buffer.
154193323Sed	 */
155193323Sed	if (!kdb_active || ddb_use_printf) {
156193323Sed		printf("%c", c);
157193323Sed		if (!kdb_active)
158193323Sed			return;
159193323Sed		if (c == '\r' || c == '\n')
160198090Srdivacky			db_check_interrupt();
161198090Srdivacky		if (c == '\n' && db_maxlines > 0) {
162198090Srdivacky			db_newlines++;
163198090Srdivacky			if (db_newlines >= db_maxlines)
164198090Srdivacky				db_pager();
165193323Sed		}
166198090Srdivacky		return;
167193323Sed	}
168193323Sed
169198090Srdivacky	/* Otherwise, output data directly to the console. */
170198090Srdivacky	if (c > ' ' && c <= '~') {
171198090Srdivacky	    /*
172198090Srdivacky	     * Printing character.
173198090Srdivacky	     * If we have spaces to print, print them first.
174198090Srdivacky	     * Use tabs if possible.
175193323Sed	     */
176193323Sed	    db_force_whitespace();
177198090Srdivacky	    cnputc(c);
178198090Srdivacky	    db_capture_writech(c);
179193323Sed	    db_output_position++;
180198090Srdivacky	    db_last_non_space = db_output_position;
181198090Srdivacky	}
182198090Srdivacky	else if (c == '\n') {
183198090Srdivacky	    /* Newline */
184198090Srdivacky	    cnputc(c);
185198090Srdivacky	    db_capture_writech(c);
186198090Srdivacky	    db_output_position = 0;
187198090Srdivacky	    db_last_non_space = 0;
188198090Srdivacky	    db_check_interrupt();
189198090Srdivacky	    if (db_maxlines > 0) {
190198090Srdivacky		    db_newlines++;
191198090Srdivacky		    if (db_newlines >= db_maxlines)
192198090Srdivacky			    db_pager();
193198090Srdivacky	    }
194198090Srdivacky	}
195198090Srdivacky	else if (c == '\r') {
196198090Srdivacky	    /* Return */
197198090Srdivacky	    cnputc(c);
198193323Sed	    db_capture_writech(c);
199193323Sed	    db_output_position = 0;
200193323Sed	    db_last_non_space = 0;
201193323Sed	    db_check_interrupt();
202193323Sed	}
203198090Srdivacky	else if (c == '\t') {
204198090Srdivacky	    /* assume tabs every 8 positions */
205198090Srdivacky	    db_output_position = NEXT_TAB(db_output_position);
206198090Srdivacky	}
207198090Srdivacky	else if (c == ' ') {
208198090Srdivacky	    /* space */
209198090Srdivacky	    db_output_position++;
210198090Srdivacky	}
211198090Srdivacky	else if (c == '\007') {
212198090Srdivacky	    /* bell */
213198090Srdivacky	    cnputc(c);
214198090Srdivacky	    /* No need to beep in a log: db_capture_writech(c); */
215198090Srdivacky	}
216198090Srdivacky	/* other characters are assumed non-printing */
217198090Srdivacky}
218198090Srdivacky
219198090Srdivackystatic void
220198090Srdivackydb_puts(const char *str)
221198090Srdivacky{
222198090Srdivacky	int i;
223198090Srdivacky
224198090Srdivacky	for (i = 0; str[i] != '\0'; i++)
225198090Srdivacky		db_putc(str[i]);
226198090Srdivacky}
227198090Srdivacky
228198090Srdivacky/*
229198090Srdivacky * Turn on the pager.
230198090Srdivacky */
231198090Srdivackyvoid
232198090Srdivackydb_enable_pager(void)
233198090Srdivacky{
234198090Srdivacky	if (db_maxlines == 0) {
235193323Sed		db_maxlines = db_lines_per_page;
236198090Srdivacky		db_newlines = 0;
237198090Srdivacky		db_pager_quit = 0;
238193323Sed	}
239193323Sed}
240193323Sed
241198090Srdivacky/*
242198090Srdivacky * Turn off the pager.
243198090Srdivacky */
244193323Sedvoid
245193323Seddb_disable_pager(void)
246193323Sed{
247198090Srdivacky	db_maxlines = 0;
248198090Srdivacky}
249193323Sed
250193323Sed/*
251193323Sed * A simple paging callout function.  It supports several simple more(1)-like
252193323Sed * commands as well as a quit command that sets db_pager_quit which db
253193323Sed * commands can poll to see if they should terminate early.
254193323Sed */
255193323Sedvoid
256193323Seddb_pager(void)
257198090Srdivacky{
258198090Srdivacky	int c, done;
259198090Srdivacky
260193323Sed	db_capture_enterpager();
261198090Srdivacky	db_printf("--More--\r");
262198090Srdivacky	done = 0;
263198090Srdivacky	while (!done) {
264198090Srdivacky		c = cngetc();
265198090Srdivacky		switch (c) {
266198090Srdivacky		case 'e':
267198090Srdivacky		case 'j':
268198090Srdivacky		case '\n':
269198090Srdivacky			/* Just one more line. */
270198090Srdivacky			db_maxlines = 1;
271198090Srdivacky			done++;
272198090Srdivacky			break;
273198090Srdivacky		case 'd':
274198090Srdivacky			/* Half a page. */
275198090Srdivacky			db_maxlines = db_lines_per_page / 2;
276198090Srdivacky			done++;
277198090Srdivacky			break;
278198090Srdivacky		case 'f':
279198090Srdivacky		case ' ':
280198090Srdivacky			/* Another page. */
281198090Srdivacky			db_maxlines = db_lines_per_page;
282198090Srdivacky			done++;
283198090Srdivacky			break;
284198090Srdivacky		case 'q':
285198090Srdivacky		case 'Q':
286198090Srdivacky		case 'x':
287198090Srdivacky		case 'X':
288198090Srdivacky			/* Quit */
289198090Srdivacky			db_maxlines = 0;
290198090Srdivacky			db_pager_quit = 1;
291198090Srdivacky			done++;
292198090Srdivacky			break;
293198090Srdivacky#if 0
294198090Srdivacky			/* FALLTHROUGH */
295198090Srdivacky		default:
296198090Srdivacky			cnputc('\007');
297198090Srdivacky#endif
298198090Srdivacky		}
299198090Srdivacky	}
300198090Srdivacky	db_printf("        ");
301198090Srdivacky	db_force_whitespace();
302198090Srdivacky	db_printf("\r");
303198090Srdivacky	db_newlines = 0;
304198090Srdivacky	db_capture_exitpager();
305198090Srdivacky}
306198090Srdivacky
307198090Srdivacky/*
308198090Srdivacky * Return output position
309198090Srdivacky */
310198090Srdivackyint
311198090Srdivackydb_print_position()
312198090Srdivacky{
313198090Srdivacky	return (db_output_position);
314198090Srdivacky}
315198090Srdivacky
316198090Srdivacky/*
317198090Srdivacky * Printing
318198090Srdivacky */
319198090Srdivackyint
320198090Srdivackydb_printf(const char *fmt, ...)
321198090Srdivacky{
322198090Srdivacky#ifdef DDB_BUFR_SIZE
323198090Srdivacky	char bufr[DDB_BUFR_SIZE];
324198090Srdivacky#endif
325198090Srdivacky	struct dbputchar_arg dca;
326198090Srdivacky	va_list	listp;
327198090Srdivacky	int retval;
328202878Srdivacky
329198090Srdivacky#ifdef DDB_BUFR_SIZE
330198090Srdivacky	dca.da_pbufr = bufr;
331198090Srdivacky	dca.da_pnext = dca.da_pbufr;
332198090Srdivacky	dca.da_nbufr = sizeof(bufr);
333193323Sed	dca.da_remain = sizeof(bufr);
334193323Sed	*dca.da_pnext = '\0';
335193323Sed#else
336193323Sed	dca.da_pbufr = NULL;
337193323Sed#endif
338193323Sed
339193323Sed	va_start(listp, fmt);
340193323Sed	retval = kvprintf (fmt, db_putchar, &dca, db_radix, listp);
341193323Sed	va_end(listp);
342193323Sed
343193323Sed#ifdef DDB_BUFR_SIZE
344193323Sed	if (*dca.da_pbufr != '\0')
345193323Sed		db_puts(dca.da_pbufr);
346193323Sed#endif
347193323Sed	return (retval);
348198090Srdivacky}
349198090Srdivacky
350193323Sedint db_indent;
351193323Sed
352193323Sedvoid
353193323Seddb_iprintf(const char *fmt,...)
354193323Sed{
355198090Srdivacky#ifdef DDB_BUFR_SIZE
356193323Sed	char bufr[DDB_BUFR_SIZE];
357193323Sed#endif
358198090Srdivacky	struct dbputchar_arg dca;
359198090Srdivacky	register int i;
360198090Srdivacky	va_list listp;
361198090Srdivacky
362200581Srdivacky	for (i = db_indent; i >= 8; i -= 8)
363193323Sed		db_printf("\t");
364193323Sed	while (--i >= 0)
365193323Sed		db_printf(" ");
366193323Sed
367193323Sed#ifdef DDB_BUFR_SIZE
368198090Srdivacky	dca.da_pbufr = bufr;
369198090Srdivacky	dca.da_pnext = dca.da_pbufr;
370198090Srdivacky	dca.da_nbufr = sizeof(bufr);
371198090Srdivacky	dca.da_remain = sizeof(bufr);
372198090Srdivacky	*dca.da_pnext = '\0';
373198090Srdivacky#else
374198090Srdivacky	dca.da_pbufr = NULL;
375198090Srdivacky#endif
376198090Srdivacky
377198090Srdivacky	va_start(listp, fmt);
378198090Srdivacky	kvprintf (fmt, db_putchar, &dca, db_radix, listp);
379198090Srdivacky	va_end(listp);
380198090Srdivacky
381198090Srdivacky#ifdef DDB_BUFR_SIZE
382198090Srdivacky	if (*dca.da_pbufr != '\0')
383198090Srdivacky		db_puts(dca.da_pbufr);
384198090Srdivacky#endif
385198090Srdivacky}
386198090Srdivacky
387193323Sed/*
388193323Sed * End line if too long.
389198090Srdivacky */
390198090Srdivackyvoid
391198090Srdivackydb_end_line(int field_width)
392198090Srdivacky{
393198090Srdivacky	if (db_output_position + field_width > db_max_width)
394193323Sed	    db_printf("\n");
395198090Srdivacky}
396198090Srdivacky