xref: /freebsd/sys/ddb/db_output.c (revision 8aac90f18aef7c9eea906c3ff9a001ca7b94f375)
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 
52 struct 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  */
71 static int	db_output_position = 0;		/* output column */
72 static int	db_last_non_space = 0;		/* last non-space character */
73 db_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)
75 db_expr_t	db_max_width = 79;		/* output line width */
76 db_expr_t	db_lines_per_page = 20;		/* lines per page */
77 volatile int	db_pager_quit;			/* user requested quit */
78 static int	db_newlines;			/* # lines this page */
79 static int	db_maxlines;			/* max lines/page when paging */
80 static int	ddb_use_printf = 0;
81 SYSCTL_INT(_debug, OID_AUTO, ddb_use_printf, CTLFLAG_RW, &ddb_use_printf, 0,
82     "use printf for all ddb output");
83 
84 static void	db_putc(int c);
85 static void	db_puts(const char *str);
86 static void	db_putchar(int c, void *arg);
87 static void	db_pager(void);
88 
89 /*
90  * Force pending whitespace.
91  */
92 void
93 db_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  */
119 static void
120 db_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 
144 static void
145 db_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 
216 static void
217 db_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  */
228 void
229 db_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  */
241 void
242 db_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  */
252 void
253 db_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  */
307 int
308 db_print_position(void)
309 {
310 	return (db_output_position);
311 }
312 
313 /*
314  * Printing
315  */
316 int
317 db_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 
347 int db_indent;
348 
349 void
350 db_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  */
387 void
388 db_end_line(int field_width)
389 {
390 	if (db_output_position + field_width > db_max_width)
391 	    db_printf("\n");
392 }
393