xref: /freebsd/sys/ddb/db_output.c (revision 2b15cb3d0922bd70ea592f0da9b4a5b167f4d53f)
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 
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) \
75 	((((i) + db_tab_stop_width) / db_tab_stop_width) * db_tab_stop_width)
76 db_expr_t	db_max_width = 79;		/* output line width */
77 db_expr_t	db_lines_per_page = 20;		/* lines per page */
78 volatile int	db_pager_quit;			/* user requested quit */
79 static int	db_newlines;			/* # lines this page */
80 static int	db_maxlines;			/* max lines/page when paging */
81 static int	ddb_use_printf = 0;
82 SYSCTL_INT(_debug, OID_AUTO, ddb_use_printf, CTLFLAG_RW, &ddb_use_printf, 0,
83     "use printf for all ddb output");
84 
85 static void	db_putc(int c);
86 static void	db_puts(const char *str);
87 static void	db_putchar(int c, void *arg);
88 static void	db_pager(void);
89 
90 /*
91  * Force pending whitespace.
92  */
93 void
94 db_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  */
120 static void
121 db_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 
147 static void
148 db_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 
219 static void
220 db_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  */
231 void
232 db_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  */
244 void
245 db_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  */
255 void
256 db_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  */
310 int
311 db_print_position(void)
312 {
313 	return (db_output_position);
314 }
315 
316 /*
317  * Printing
318  */
319 int
320 db_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 
350 int db_indent;
351 
352 void
353 db_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  */
390 void
391 db_end_line(int field_width)
392 {
393 	if (db_output_position + field_width > db_max_width)
394 	    db_printf("\n");
395 }
396