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