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