xref: /netbsd-src/sys/ddb/db_output.c (revision 76dfffe33547c37f8bdd446e3e4ab0f3c16cea4b)
1 /*	$NetBSD: db_output.c,v 1.15 1996/10/28 08:42:13 fvdl Exp $	*/
2 
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 /*
30  * Printf and character output for debugger.
31  */
32 #include <sys/param.h>
33 
34 #include <machine/stdarg.h>
35 
36 #include <dev/cons.h>
37 
38 #include <machine/db_machdep.h>
39 
40 #include <ddb/db_command.h>
41 #include <ddb/db_output.h>
42 #include <ddb/db_interface.h>
43 #include <ddb/db_sym.h>
44 #include <ddb/db_extern.h>
45 
46 /*
47  *	Character output - tracks position in line.
48  *	To do this correctly, we should know how wide
49  *	the output device is - then we could zero
50  *	the line position when the output device wraps
51  *	around to the start of the next line.
52  *
53  *	Instead, we count the number of spaces printed
54  *	since the last printing character so that we
55  *	don't print trailing spaces.  This avoids most
56  *	of the wraparounds.
57  */
58 
59 #ifndef	DB_MAX_LINE
60 #define	DB_MAX_LINE		24	/* maximum line */
61 #define DB_MAX_WIDTH		80	/* maximum width */
62 #endif	DB_MAX_LINE
63 
64 #define DB_MIN_MAX_WIDTH	20	/* minimum max width */
65 #define DB_MIN_MAX_LINE		3	/* minimum max line */
66 #define CTRL(c)			((c) & 0xff)
67 
68 int	db_output_position = 0;		/* output column */
69 int	db_output_line = 0;		/* output line number */
70 int	db_last_non_space = 0;		/* last non-space character */
71 int	db_tab_stop_width = 8;		/* how wide are tab stops? */
72 #define	NEXT_TAB(i) \
73 	((((i) + db_tab_stop_width) / db_tab_stop_width) * db_tab_stop_width)
74 int	db_max_line = DB_MAX_LINE;	/* output max lines */
75 int	db_max_width = DB_MAX_WIDTH;	/* output line width */
76 
77 static void db_more __P((void));
78 static char *db_ksprintn __P((u_long, int, int *));
79 static void db_printf_guts __P((const char *, va_list));
80 
81 /*
82  * Force pending whitespace.
83  */
84 void
85 db_force_whitespace()
86 {
87 	register int last_print, next_tab;
88 
89 	last_print = db_last_non_space;
90 	while (last_print < db_output_position) {
91 	    next_tab = NEXT_TAB(last_print);
92 	    if (next_tab <= db_output_position) {
93 		while (last_print < next_tab) { /* DON'T send a tab!!! */
94 			cnputc(' ');
95 			last_print++;
96 		}
97 	    }
98 	    else {
99 		cnputc(' ');
100 		last_print++;
101 	    }
102 	}
103 	db_last_non_space = db_output_position;
104 }
105 
106 static void
107 db_more()
108 {
109 	register  char *p;
110 	int quit_output = 0;
111 
112 	for (p = "--db_more--"; *p; p++)
113 	    cnputc(*p);
114 	switch(cngetc()) {
115 	case ' ':
116 	    db_output_line = 0;
117 	    break;
118 	case 'q':
119 	case CTRL('c'):
120 	    db_output_line = 0;
121 	    quit_output = 1;
122 	    break;
123 	default:
124 	    db_output_line--;
125 	    break;
126 	}
127 	p = "\b\b\b\b\b\b\b\b\b\b\b           \b\b\b\b\b\b\b\b\b\b\b";
128 	while (*p)
129 	    cnputc(*p++);
130 	if (quit_output) {
131 	    db_error(0);
132 	    /* NOTREACHED */
133 	}
134 }
135 
136 /*
137  * Output character.  Buffer whitespace.
138  */
139 void
140 db_putchar(c)
141 	int	c;		/* character to output */
142 {
143 	if (db_max_line >= DB_MIN_MAX_LINE && db_output_line >= db_max_line-1)
144 	    db_more();
145 	if (c > ' ' && c <= '~') {
146 	    /*
147 	     * Printing character.
148 	     * If we have spaces to print, print them first.
149 	     * Use tabs if possible.
150 	     */
151 	    db_force_whitespace();
152 	    cnputc(c);
153 	    db_output_position++;
154 	    if (db_max_width >= DB_MIN_MAX_WIDTH
155 		&& db_output_position >= db_max_width-1) {
156 		/* auto new line */
157 		cnputc('\n');
158 		db_output_position = 0;
159 		db_last_non_space = 0;
160 		db_output_line++;
161 	    }
162 	    db_last_non_space = db_output_position;
163 	}
164 	else if (c == '\n') {
165 	    /* Return */
166 	    cnputc(c);
167 	    db_output_position = 0;
168 	    db_last_non_space = 0;
169 	    db_output_line++;
170 	    db_check_interrupt();
171 	}
172 	else if (c == '\t') {
173 	    /* assume tabs every 8 positions */
174 	    db_output_position = NEXT_TAB(db_output_position);
175 	}
176 	else if (c == ' ') {
177 	    /* space */
178 	    db_output_position++;
179 	}
180 	else if (c == '\007') {
181 	    /* bell */
182 	    cnputc(c);
183 	}
184 	/* other characters are assumed non-printing */
185 }
186 
187 /*
188  * Return output position
189  */
190 int
191 db_print_position()
192 {
193 	return (db_output_position);
194 }
195 
196 /*
197  * Printing
198  */
199 extern int	db_radix;
200 
201 /*VARARGS1*/
202 void
203 #if __STDC__
204 db_printf(const char *fmt, ...)
205 #else
206 db_printf(fmt, va_alist)
207 	const char *fmt;
208 	va_dcl
209 #endif
210 {
211 	va_list	listp;
212 	va_start(listp, fmt);
213 	db_printf_guts (fmt, listp);
214 	va_end(listp);
215 }
216 
217 /* alternate name */
218 
219 /*VARARGS1*/
220 void
221 #if __STDC__
222 kdbprintf(const char *fmt, ...)
223 #else
224 kdbprintf(fmt, va_alist)
225 	char *fmt;
226 	va_dcl
227 #endif
228 {
229 	va_list	listp;
230 	va_start(listp, fmt);
231 	db_printf_guts (fmt, listp);
232 	va_end(listp);
233 }
234 
235 /*
236  * End line if too long.
237  */
238 void
239 db_end_line()
240 {
241 	if (db_output_position >= db_max_width)
242 	    db_printf("\n");
243 }
244 
245 /*
246  * Put a number (base <= 16) in a buffer in reverse order; return an
247  * optional length and a pointer to the NULL terminated (preceded?)
248  * buffer.
249  */
250 static char *
251 db_ksprintn(ul, base, lenp)
252 	register u_long ul;
253 	register int base, *lenp;
254 {					/* A long in base 8, plus NULL. */
255 	static char buf[sizeof(long) * NBBY / 3 + 2];
256 	register char *p;
257 
258 	p = buf;
259 	do {
260 		*++p = "0123456789abcdef"[ul % base];
261 	} while (ul /= base);
262 	if (lenp)
263 		*lenp = p - buf;
264 	return (p);
265 }
266 
267 static void
268 db_printf_guts(fmt, ap)
269 	register const char *fmt;
270 	va_list ap;
271 {
272 	register char *p;
273 	register int ch, n;
274 	u_long ul;
275 	int base, lflag, tmp, width;
276 	char padc;
277 	int ladjust;
278 	int sharpflag;
279 	int neg;
280 
281 	for (;;) {
282 		padc = ' ';
283 		width = 0;
284 		while ((ch = *(const u_char *)fmt++) != '%') {
285 			if (ch == '\0')
286 				return;
287 			db_putchar(ch);
288 		}
289 		lflag = 0;
290 		ladjust = 0;
291 		sharpflag = 0;
292 		neg = 0;
293 reswitch:	switch (ch = *(const u_char *)fmt++) {
294 		case '0':
295 		case '.':
296 			padc = '0';
297 			goto reswitch;
298 		case '1': case '2': case '3': case '4':
299 		case '5': case '6': case '7': case '8': case '9':
300 			for (width = 0;; ++fmt) {
301 				width = width * 10 + ch - '0';
302 				ch = *fmt;
303 				if (ch < '0' || ch > '9')
304 					break;
305 			}
306 			goto reswitch;
307 		case 'l':
308 			lflag = 1;
309 			goto reswitch;
310 		case '-':
311 			ladjust = 1;
312 			goto reswitch;
313 		case '#':
314 			sharpflag = 1;
315 			goto reswitch;
316 		case 'b':
317 			ul = va_arg(ap, int);
318 			p = va_arg(ap, char *);
319 			for (p = db_ksprintn(ul, *p++, NULL);
320 			     (ch = *p--) !='\0';)
321 				db_putchar(ch);
322 
323 			if (!ul)
324 				break;
325 
326 			for (tmp = 0; (n = *p++) != '\0';) {
327 				if (ul & (1 << (n - 1))) {
328 					db_putchar(tmp ? ',' : '<');
329 					for (; (n = *p) > ' '; ++p)
330 						db_putchar(n);
331 					tmp = 1;
332 				} else
333 					for (; *p > ' '; ++p);
334 			}
335 			if (tmp)
336 				db_putchar('>');
337 			break;
338 		case '*':
339 			width = va_arg (ap, int);
340 			if (width < 0) {
341 				ladjust = !ladjust;
342 				width = -width;
343 			}
344 			goto reswitch;
345 		case ':':
346 			p = va_arg(ap, char *);
347 			db_printf_guts (p, va_arg(ap, va_list));
348 			break;
349 		case 'c':
350 			db_putchar(va_arg(ap, int));
351 			break;
352 		case 's':
353 			p = va_arg(ap, char *);
354 			width -= strlen (p);
355 			if (!ladjust && width > 0)
356 				while (width--)
357 					db_putchar (padc);
358 			while ((ch = *p++) != '\0')
359 				db_putchar(ch);
360 			if (ladjust && width > 0)
361 				while (width--)
362 					db_putchar (padc);
363 			break;
364 		case 'r':
365 			ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
366 			if ((long)ul < 0) {
367 				neg = 1;
368 				ul = -(long)ul;
369 			}
370 			base = db_radix;
371 			if (base < 8 || base > 16)
372 				base = 10;
373 			goto number;
374 		case 'n':
375 			ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
376 			base = db_radix;
377 			if (base < 8 || base > 16)
378 				base = 10;
379 			goto number;
380 		case 'd':
381 			ul = lflag ? va_arg(ap, long) : va_arg(ap, int);
382 			if ((long)ul < 0) {
383 				neg = 1;
384 				ul = -(long)ul;
385 			}
386 			base = 10;
387 			goto number;
388 		case 'o':
389 			ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
390 			base = 8;
391 			goto number;
392 		case 'p':
393 			db_putchar ('0');
394 			db_putchar ('x');
395 			ul = (u_long) va_arg(ap, void *);
396 			base = 16;
397 			goto number;
398 		case 'u':
399 			ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
400 			base = 10;
401 			goto number;
402 		case 'z':
403 			ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
404 			if ((long)ul < 0) {
405 				neg = 1;
406 				ul = -(long)ul;
407 			}
408 			base = 16;
409 			goto number;
410 		case 'x':
411 			ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
412 			base = 16;
413 number:			p = (char *)db_ksprintn(ul, base, &tmp);
414 			if (sharpflag && ul != 0) {
415 				if (base == 8)
416 					tmp++;
417 				else if (base == 16)
418 					tmp += 2;
419 			}
420 			if (neg)
421 				tmp++;
422 
423 			if (!ladjust && width && (width -= tmp) > 0)
424 				while (width--)
425 					db_putchar(padc);
426 			if (neg)
427 				db_putchar ('-');
428 			if (sharpflag && ul != 0) {
429 				if (base == 8) {
430 					db_putchar ('0');
431 				} else if (base == 16) {
432 					db_putchar ('0');
433 					db_putchar ('x');
434 				}
435 			}
436 			if (ladjust && width && (width -= tmp) > 0)
437 				while (width--)
438 					db_putchar(padc);
439 
440 			while ((ch = *p--) != '\0')
441 				db_putchar(ch);
442 			break;
443 		default:
444 			db_putchar('%');
445 			if (lflag)
446 				db_putchar('l');
447 			/* FALLTHROUGH */
448 		case '%':
449 			db_putchar(ch);
450 		}
451 	}
452 }
453