1/*	$NetBSD: db_output.c,v 1.38 2023/10/07 20:22:53 ad 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 "AS IS"
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
33#ifdef _KERNEL_OPT
34#include "opt_ddbparam.h"
35#endif
36
37#include <sys/cdefs.h>
38__KERNEL_RCSID(0, "$NetBSD: db_output.c,v 1.38 2023/10/07 20:22:53 ad Exp $");
39
40#include <sys/param.h>
41#include <sys/systm.h>
42#include <sys/stdarg.h>
43#include <sys/time.h>
44
45#include <dev/cons.h>
46
47#include <ddb/ddb.h>
48
49/*
50 *	Character output - tracks position in line.
51 *	To do this correctly, we should know how wide
52 *	the output device is - then we could zero
53 *	the line position when the output device wraps
54 *	around to the start of the next line.
55 *
56 *	Instead, we count the number of spaces printed
57 *	since the last printing character so that we
58 *	don't print trailing spaces.  This avoids most
59 *	of the wraparounds.
60 */
61
62#ifndef	DB_MAX_LINE
63#define	DB_MAX_LINE		24	/* maximum line */
64#endif	/* DB_MAX_LINE */
65#ifndef	DB_MAX_WIDTH
66#define DB_MAX_WIDTH		80	/* maximum width */
67#endif	/* DB_MAX_WIDTH */
68
69#define DB_MIN_MAX_WIDTH	20	/* minimum max width */
70#define DB_MIN_MAX_LINE		3	/* minimum max line */
71#define CTRL(c)			((c) & 0xff)
72
73int	db_output_line = 0;			/* output line number */
74int	db_tab_stop_width = 8;			/* how wide are tab stops? */
75int	db_max_line = DB_MAX_LINE;		/* output max lines */
76int	db_max_width = DB_MAX_WIDTH;		/* output line width */
77
78static int	db_output_position = 0;		/* output column */
79static int	db_last_non_space = 0;		/* last non-space character */
80
81static void db_more(void);
82
83/*
84 * Force pending whitespace.
85 */
86void
87db_force_whitespace(void)
88{
89	int last_print, next_tab;
90
91	last_print = db_last_non_space;
92	while (last_print < db_output_position) {
93		next_tab = DB_NEXT_TAB(last_print);
94		if (next_tab <= db_output_position) {
95			while (last_print < next_tab) { /* DON'T send a tab!! */
96				cnputc(' ');
97				last_print++;
98			}
99		} else {
100			cnputc(' ');
101			last_print++;
102		}
103	}
104	db_last_non_space = db_output_position;
105}
106
107
108/*
109 * End the current line if it exceeds $maxwidth
110 */
111static void
112db_check_wrap(void)
113{
114
115	if (db_max_width >= DB_MIN_MAX_WIDTH
116	    && db_output_position >= db_max_width) {
117		cnputc('\n');
118		db_output_position = 0;
119		db_last_non_space = 0;
120		db_output_line++;
121	}
122}
123
124
125static void
126db_more(void)
127{
128	const char *p;
129	int quit_output = 0;
130
131	for (p = "--db_more--"; *p; p++)
132		cnputc(*p);
133	switch (cngetc()) {
134	case ' ':
135		db_output_line = 0;
136		break;
137	case 'q':
138	case CTRL('c'):
139		db_output_line = 0;
140		quit_output = 1;
141		break;
142	default:
143		db_output_line--;
144		break;
145	}
146	p = "\b\b\b\b\b\b\b\b\b\b\b           \b\b\b\b\b\b\b\b\b\b\b";
147	while (*p)
148		cnputc(*p++);
149	if (quit_output) {
150		db_error(0);
151		/* NOTREACHED */
152	}
153}
154
155/*
156 * Output character.  Buffer whitespace.
157 */
158void
159db_putchar(int c)
160{
161	if (db_max_line >= DB_MIN_MAX_LINE && db_output_line >= db_max_line-1)
162		db_more();
163	if (c > ' ' && c <= '~') {
164		/*
165		 * Printing character.
166		 * If we have spaces to print, print them first.
167		 * Use tabs if possible.
168		 */
169		db_force_whitespace();
170		db_check_wrap();
171		cnputc(c);
172		db_output_position++;
173		db_check_wrap();
174		db_last_non_space = db_output_position;
175	} else if (c == '\n') {
176		/* Return */
177		cnputc(c);
178		db_output_position = 0;
179		db_last_non_space = 0;
180		db_output_line++;
181		db_check_interrupt();
182	} else if (c == '\t') {
183		/* assume tabs every 8 positions */
184		db_output_position = DB_NEXT_TAB(db_output_position);
185	} else if (c == ' ') {
186		/* space */
187		db_output_position++;
188	} else if (c == '\007') {
189		/* bell */
190		cnputc(c);
191	}
192	/* other characters are assumed non-printing */
193}
194
195/*
196 * Return output position
197 */
198int
199db_print_position(void)
200{
201
202	return (db_output_position);
203}
204
205/*
206 * End line if too long.
207 */
208void
209db_end_line(void)
210{
211
212	if (db_output_position >= db_max_width)
213		db_printf("\n");
214}
215
216/*
217 * Replacement for old '%r' kprintf format.
218 */
219void
220db_format_radix(char *buf, size_t bufsiz, quad_t val, int altflag)
221{
222	const char *fmt;
223
224	if (db_radix == 16) {
225		db_format_hex(buf, bufsiz, val, altflag);
226		return;
227	}
228
229	if (db_radix == 8)
230		fmt = altflag ? "-%#qo" : "-%qo";
231	else
232		fmt = altflag ? "-%#qu" : "-%qu";
233
234	if (val < 0)
235		val = -val;
236	else
237		++fmt;
238
239	snprintf(buf, bufsiz, fmt, val);
240}
241
242/*
243 * Replacement for old '%z' kprintf format.
244 */
245void
246db_format_hex(char *buf, size_t bufsiz, quad_t val, int altflag)
247{
248	/* Only use alternate form if val is nonzero. */
249	const char *fmt = (altflag && val) ? "-%#qx" : "-%qx";
250
251	if (val < 0)
252		val = -val;
253	else
254		++fmt;
255
256	snprintf(buf, bufsiz, fmt, val);
257}
258
259/*
260 * Print out a timespec value.
261 */
262void
263db_print_timespec(struct timespec *ts)
264{
265
266	db_printf("%" PRIu64 ".%09ld", (uint64_t)ts->tv_sec, ts->tv_nsec);
267}
268