178189Sbrian/*-
278189Sbrian * SPDX-License-Identifier: MIT-CMU
378189Sbrian *
478189Sbrian * Mach Operating System
578189Sbrian * Copyright (c) 1991,1990 Carnegie Mellon University
66059Samurai * All Rights Reserved.
778189Sbrian *
878189Sbrian * Permission to use, copy, modify and distribute this software and its
978189Sbrian * documentation is hereby granted, provided that both the copyright
1078189Sbrian * notice and this permission notice appear in all copies of the
1178189Sbrian * software, derivative works or modified versions, and any portions
1278189Sbrian * thereof, and that both notices appear in supporting documentation.
1378189Sbrian *
1478189Sbrian * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
156059Samurai * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
1678189Sbrian * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
1778189Sbrian *
1878189Sbrian * Carnegie Mellon requests users of this software to return to
1978189Sbrian *
2078189Sbrian *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
2178189Sbrian *  School of Computer Science
2278189Sbrian *  Carnegie Mellon University
2378189Sbrian *  Pittsburgh PA 15213-3890
2478189Sbrian *
2578189Sbrian * any improvements or extensions that they make and grant Carnegie the
2678189Sbrian * rights to redistribute these changes.
276059Samurai */
2850479Speter/*
296059Samurai * 	Author: David B. Golub, Carnegie Mellon University
306059Samurai *	Date:	7/90
316059Samurai */
326059Samurai
336059Samurai/*
3428679Sbrian * Printf and character output for debugger.
3528679Sbrian */
3628679Sbrian
376059Samurai#include <sys/cdefs.h>
386059Samurai#include "opt_ddb.h"
396059Samurai
406059Samurai#include <sys/param.h>
416059Samurai#include <sys/systm.h>
426059Samurai#include <sys/cons.h>
4328679Sbrian#include <sys/kdb.h>
4428679Sbrian#include <sys/kernel.h>
456059Samurai#include <sys/sysctl.h>
466059Samurai
4755272Sbrian#include <machine/stdarg.h>
486059Samurai
496059Samurai#include <ddb/ddb.h>
5026326Sbrian#include <ddb/db_output.h>
5155272Sbrian
5226326Sbrianstruct dbputchar_arg {
5326326Sbrian	size_t	da_nbufr;
5436285Sbrian	size_t	da_remain;
5536285Sbrian	char	*da_pbufr;
5636285Sbrian	char	*da_pnext;
5736285Sbrian};
5836285Sbrian
5936285Sbrian/*
606059Samurai *	Character output - tracks position in line.
6136285Sbrian *	To do this correctly, we should know how wide
6236285Sbrian *	the output device is - then we could zero
6336285Sbrian *	the line position when the output device wraps
6436285Sbrian *	around to the start of the next line.
6536285Sbrian *
6636285Sbrian *	Instead, we count the number of spaces printed
6736285Sbrian *	since the last printing character so that we
6836285Sbrian *	don't print trailing spaces.  This avoids most
6936285Sbrian *	of the wraparounds.
7036285Sbrian */
7136285Sbrianstatic int	db_output_position = 0;		/* output column */
7236285Sbrianstatic int	db_last_non_space = 0;		/* last non-space character */
7336285Sbriandb_expr_t	db_tab_stop_width = 8;		/* how wide are tab stops? */
7436285Sbrian#define	NEXT_TAB(i) rounddown((i) + db_tab_stop_width, db_tab_stop_width)
7536285Sbriandb_expr_t	db_max_width = 79;		/* output line width */
76131327Sbriandb_expr_t	db_lines_per_page = 20;		/* lines per page */
77131327Sbrianvolatile int	db_pager_quit;			/* user requested quit */
78131327Sbrianstatic int	db_newlines;			/* # lines this page */
79131327Sbrianstatic int	db_maxlines;			/* max lines/page when paging */
80131327Sbrianstatic int	ddb_use_printf = 0;
81131327SbrianSYSCTL_INT(_debug, OID_AUTO, ddb_use_printf, CTLFLAG_RW, &ddb_use_printf, 0,
8236285Sbrian    "use printf for all ddb output");
8336285Sbrian
84131327Sbrianstatic void	db_putc(int c);
8536285Sbrianstatic void	db_puts(const char *str);
86131327Sbrianstatic void	db_putchar(int c, void *arg);
87131327Sbrianstatic void	db_pager(void);
88131327Sbrian
89131327Sbrian/*
90131327Sbrian * Force pending whitespace.
9136285Sbrian */
9236285Sbrianvoid
9336285Sbriandb_force_whitespace(void)
9436285Sbrian{
9536285Sbrian	int last_print, next_tab;
9636285Sbrian
9736285Sbrian	last_print = db_last_non_space;
9836285Sbrian	while (last_print < db_output_position) {
9936285Sbrian	    next_tab = NEXT_TAB(last_print);
10036285Sbrian	    if (next_tab <= db_output_position) {
10136285Sbrian		while (last_print < next_tab) { /* DON'T send a tab!!! */
10236285Sbrian			cnputc(' ');
10336285Sbrian			db_capture_writech(' ');
10436285Sbrian			last_print++;
10536285Sbrian		}
10636285Sbrian	    }
10736285Sbrian	    else {
10836285Sbrian		cnputc(' ');
10936285Sbrian		db_capture_writech(' ');
11036285Sbrian		last_print++;
11136285Sbrian	    }
11246686Sbrian	}
113134789Sbrian	db_last_non_space = db_output_position;
114134789Sbrian}
11546686Sbrian
11646686Sbrian/*
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