1/*-
2 * Mach Operating System
3 * Copyright (c) 1991,1990 Carnegie Mellon University
4 * All Rights Reserved.
5 *
6 * Permission to use, copy, modify and distribute this software and its
7 * documentation is hereby granted, provided that both the copyright
8 * notice and this permission notice appear in all copies of the
9 * software, derivative works or modified versions, and any portions
10 * thereof, and that both notices appear in supporting documentation.
11 *
12 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
13 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
14 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
15 *
16 * Carnegie Mellon requests users of this software to return to
17 *
18 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
19 *  School of Computer Science
20 *  Carnegie Mellon University
21 *  Pittsburgh PA 15213-3890
22 *
23 * any improvements or extensions that they make and grant Carnegie the
24 * rights to redistribute these changes.
25 */
26/*
27 * 	Author: David B. Golub, Carnegie Mellon University
28 *	Date:	7/90
29 */
30
31/*
32 * Printf and character output for debugger.
33 */
34
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD$");
37
38#include "opt_ddb.h"
39
40#include <sys/param.h>
41#include <sys/systm.h>
42#include <sys/cons.h>
43#include <sys/kdb.h>
44#include <sys/kernel.h>
45#include <sys/sysctl.h>
46
47#include <machine/stdarg.h>
48
49#include <ddb/ddb.h>
50#include <ddb/db_output.h>
51
52struct dbputchar_arg {
53	size_t	da_nbufr;
54	size_t	da_remain;
55	char	*da_pbufr;
56	char	*da_pnext;
57};
58
59/*
60 *	Character output - tracks position in line.
61 *	To do this correctly, we should know how wide
62 *	the output device is - then we could zero
63 *	the line position when the output device wraps
64 *	around to the start of the next line.
65 *
66 *	Instead, we count the number of spaces printed
67 *	since the last printing character so that we
68 *	don't print trailing spaces.  This avoids most
69 *	of the wraparounds.
70 */
71static int	db_output_position = 0;		/* output column */
72static int	db_last_non_space = 0;		/* last non-space character */
73db_expr_t	db_tab_stop_width = 8;		/* how wide are tab stops? */
74#define	NEXT_TAB(i) \
75	((((i) + db_tab_stop_width) / db_tab_stop_width) * db_tab_stop_width)
76db_expr_t	db_max_width = 79;		/* output line width */
77db_expr_t	db_lines_per_page = 20;		/* lines per page */
78volatile int	db_pager_quit;			/* user requested quit */
79static int	db_newlines;			/* # lines this page */
80static int	db_maxlines;			/* max lines/page when paging */
81static int	ddb_use_printf = 0;
82SYSCTL_INT(_debug, OID_AUTO, ddb_use_printf, CTLFLAG_RW, &ddb_use_printf, 0,
83    "use printf for all ddb output");
84
85static void	db_putc(int c);
86static void	db_puts(const char *str);
87static void	db_putchar(int c, void *arg);
88static void	db_pager(void);
89
90/*
91 * Force pending whitespace.
92 */
93void
94db_force_whitespace(void)
95{
96	register int last_print, next_tab;
97
98	last_print = db_last_non_space;
99	while (last_print < db_output_position) {
100	    next_tab = NEXT_TAB(last_print);
101	    if (next_tab <= db_output_position) {
102		while (last_print < next_tab) { /* DON'T send a tab!!! */
103			cnputc(' ');
104			db_capture_writech(' ');
105			last_print++;
106		}
107	    }
108	    else {
109		cnputc(' ');
110		db_capture_writech(' ');
111		last_print++;
112	    }
113	}
114	db_last_non_space = db_output_position;
115}
116
117/*
118 * Output character.  Buffer whitespace.
119 */
120static void
121db_putchar(int c, void *arg)
122{
123	struct dbputchar_arg *dap = arg;
124
125	if (dap->da_pbufr == NULL) {
126
127		 /* No bufferized output is provided. */
128		db_putc(c);
129	} else {
130
131		*dap->da_pnext++ = c;
132		dap->da_remain--;
133
134		/* Leave always the buffer 0 terminated. */
135		*dap->da_pnext = '\0';
136
137		/* Check if the buffer needs to be flushed. */
138		if (dap->da_remain < 2 || c == '\n') {
139			db_puts(dap->da_pbufr);
140			dap->da_pnext = dap->da_pbufr;
141			dap->da_remain = dap->da_nbufr;
142			*dap->da_pnext = '\0';
143		}
144	}
145}
146
147static void
148db_putc(int c)
149{
150
151	/*
152	 * If not in the debugger or the user requests it, output data to
153	 * both the console and the message buffer.
154	 */
155	if (!kdb_active || ddb_use_printf) {
156		printf("%c", c);
157		if (!kdb_active)
158			return;
159		if (c == '\r' || c == '\n')
160			db_check_interrupt();
161		if (c == '\n' && db_maxlines > 0) {
162			db_newlines++;
163			if (db_newlines >= db_maxlines)
164				db_pager();
165		}
166		return;
167	}
168
169	/* Otherwise, output data directly to the console. */
170	if (c > ' ' && c <= '~') {
171	    /*
172	     * Printing character.
173	     * If we have spaces to print, print them first.
174	     * Use tabs if possible.
175	     */
176	    db_force_whitespace();
177	    cnputc(c);
178	    db_capture_writech(c);
179	    db_output_position++;
180	    db_last_non_space = db_output_position;
181	}
182	else if (c == '\n') {
183	    /* Newline */
184	    cnputc(c);
185	    db_capture_writech(c);
186	    db_output_position = 0;
187	    db_last_non_space = 0;
188	    db_check_interrupt();
189	    if (db_maxlines > 0) {
190		    db_newlines++;
191		    if (db_newlines >= db_maxlines)
192			    db_pager();
193	    }
194	}
195	else if (c == '\r') {
196	    /* Return */
197	    cnputc(c);
198	    db_capture_writech(c);
199	    db_output_position = 0;
200	    db_last_non_space = 0;
201	    db_check_interrupt();
202	}
203	else if (c == '\t') {
204	    /* assume tabs every 8 positions */
205	    db_output_position = NEXT_TAB(db_output_position);
206	}
207	else if (c == ' ') {
208	    /* space */
209	    db_output_position++;
210	}
211	else if (c == '\007') {
212	    /* bell */
213	    cnputc(c);
214	    /* No need to beep in a log: db_capture_writech(c); */
215	}
216	/* other characters are assumed non-printing */
217}
218
219static void
220db_puts(const char *str)
221{
222	int i;
223
224	for (i = 0; str[i] != '\0'; i++)
225		db_putc(str[i]);
226}
227
228/*
229 * Turn on the pager.
230 */
231void
232db_enable_pager(void)
233{
234	if (db_maxlines == 0) {
235		db_maxlines = db_lines_per_page;
236		db_newlines = 0;
237		db_pager_quit = 0;
238	}
239}
240
241/*
242 * Turn off the pager.
243 */
244void
245db_disable_pager(void)
246{
247	db_maxlines = 0;
248}
249
250/*
251 * A simple paging callout function.  It supports several simple more(1)-like
252 * commands as well as a quit command that sets db_pager_quit which db
253 * commands can poll to see if they should terminate early.
254 */
255void
256db_pager(void)
257{
258	int c, done;
259
260	db_capture_enterpager();
261	db_printf("--More--\r");
262	done = 0;
263	while (!done) {
264		c = cngetc();
265		switch (c) {
266		case 'e':
267		case 'j':
268		case '\n':
269			/* Just one more line. */
270			db_maxlines = 1;
271			done++;
272			break;
273		case 'd':
274			/* Half a page. */
275			db_maxlines = db_lines_per_page / 2;
276			done++;
277			break;
278		case 'f':
279		case ' ':
280			/* Another page. */
281			db_maxlines = db_lines_per_page;
282			done++;
283			break;
284		case 'q':
285		case 'Q':
286		case 'x':
287		case 'X':
288			/* Quit */
289			db_maxlines = 0;
290			db_pager_quit = 1;
291			done++;
292			break;
293#if 0
294			/* FALLTHROUGH */
295		default:
296			cnputc('\007');
297#endif
298		}
299	}
300	db_printf("        ");
301	db_force_whitespace();
302	db_printf("\r");
303	db_newlines = 0;
304	db_capture_exitpager();
305}
306
307/*
308 * Return output position
309 */
310int
311db_print_position(void)
312{
313	return (db_output_position);
314}
315
316/*
317 * Printing
318 */
319int
320db_printf(const char *fmt, ...)
321{
322#ifdef DDB_BUFR_SIZE
323	char bufr[DDB_BUFR_SIZE];
324#endif
325	struct dbputchar_arg dca;
326	va_list	listp;
327	int retval;
328
329#ifdef DDB_BUFR_SIZE
330	dca.da_pbufr = bufr;
331	dca.da_pnext = dca.da_pbufr;
332	dca.da_nbufr = sizeof(bufr);
333	dca.da_remain = sizeof(bufr);
334	*dca.da_pnext = '\0';
335#else
336	dca.da_pbufr = NULL;
337#endif
338
339	va_start(listp, fmt);
340	retval = kvprintf (fmt, db_putchar, &dca, db_radix, listp);
341	va_end(listp);
342
343#ifdef DDB_BUFR_SIZE
344	if (*dca.da_pbufr != '\0')
345		db_puts(dca.da_pbufr);
346#endif
347	return (retval);
348}
349
350int db_indent;
351
352void
353db_iprintf(const char *fmt,...)
354{
355#ifdef DDB_BUFR_SIZE
356	char bufr[DDB_BUFR_SIZE];
357#endif
358	struct dbputchar_arg dca;
359	register int i;
360	va_list listp;
361
362	for (i = db_indent; i >= 8; i -= 8)
363		db_printf("\t");
364	while (--i >= 0)
365		db_printf(" ");
366
367#ifdef DDB_BUFR_SIZE
368	dca.da_pbufr = bufr;
369	dca.da_pnext = dca.da_pbufr;
370	dca.da_nbufr = sizeof(bufr);
371	dca.da_remain = sizeof(bufr);
372	*dca.da_pnext = '\0';
373#else
374	dca.da_pbufr = NULL;
375#endif
376
377	va_start(listp, fmt);
378	kvprintf (fmt, db_putchar, &dca, db_radix, listp);
379	va_end(listp);
380
381#ifdef DDB_BUFR_SIZE
382	if (*dca.da_pbufr != '\0')
383		db_puts(dca.da_pbufr);
384#endif
385}
386
387/*
388 * End line if too long.
389 */
390void
391db_end_line(int field_width)
392{
393	if (db_output_position + field_width > db_max_width)
394	    db_printf("\n");
395}
396