1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (C) 1996-2005 Paul Mackerras.
4 */
5#include <linux/string.h>
6#include <asm/udbg.h>
7#include <asm/time.h>
8#include "nonstdio.h"
9
10static bool paginating, paginate_skipping;
11static unsigned long paginate_lpp; /* Lines Per Page */
12static unsigned long paginate_pos;
13
14void xmon_start_pagination(void)
15{
16	paginating = true;
17	paginate_skipping = false;
18	paginate_pos = 0;
19}
20
21void xmon_end_pagination(void)
22{
23	paginating = false;
24}
25
26void xmon_set_pagination_lpp(unsigned long lpp)
27{
28	paginate_lpp = lpp;
29}
30
31static int xmon_readchar(void)
32{
33	if (udbg_getc)
34		return udbg_getc();
35	return -1;
36}
37
38static int xmon_write(const char *ptr, int nb)
39{
40	int rv = 0;
41	const char *p = ptr, *q;
42	const char msg[] = "[Hit a key (a:all, q:truncate, any:next page)]";
43
44	if (nb <= 0)
45		return rv;
46
47	if (paginating && paginate_skipping)
48		return nb;
49
50	if (paginate_lpp) {
51		while (paginating && (q = strchr(p, '\n'))) {
52			rv += udbg_write(p, q - p + 1);
53			p = q + 1;
54			paginate_pos++;
55
56			if (paginate_pos >= paginate_lpp) {
57				udbg_write(msg, strlen(msg));
58
59				switch (xmon_readchar()) {
60				case 'a':
61					paginating = false;
62					break;
63				case 'q':
64					paginate_skipping = true;
65					break;
66				default:
67					/* nothing */
68					break;
69				}
70
71				paginate_pos = 0;
72				udbg_write("\r\n", 2);
73
74				if (paginate_skipping)
75					return nb;
76			}
77		}
78	}
79
80	return rv + udbg_write(p, nb - (p - ptr));
81}
82
83int xmon_putchar(int c)
84{
85	char ch = c;
86
87	if (c == '\n')
88		xmon_putchar('\r');
89	return xmon_write(&ch, 1) == 1? c: -1;
90}
91
92static char line[256];
93static char *lineptr;
94static int lineleft;
95
96static int xmon_getchar(void)
97{
98	int c;
99
100	if (lineleft == 0) {
101		lineptr = line;
102		for (;;) {
103			c = xmon_readchar();
104			if (c == -1 || c == 4)
105				break;
106			if (c == '\r' || c == '\n') {
107				*lineptr++ = '\n';
108				xmon_putchar('\n');
109				break;
110			}
111			switch (c) {
112			case 0177:
113			case '\b':
114				if (lineptr > line) {
115					xmon_putchar('\b');
116					xmon_putchar(' ');
117					xmon_putchar('\b');
118					--lineptr;
119				}
120				break;
121			case 'U' & 0x1F:
122				while (lineptr > line) {
123					xmon_putchar('\b');
124					xmon_putchar(' ');
125					xmon_putchar('\b');
126					--lineptr;
127				}
128				break;
129			default:
130				if (lineptr >= &line[sizeof(line) - 1])
131					xmon_putchar('\a');
132				else {
133					xmon_putchar(c);
134					*lineptr++ = c;
135				}
136			}
137		}
138		lineleft = lineptr - line;
139		lineptr = line;
140	}
141	if (lineleft == 0)
142		return -1;
143	--lineleft;
144	return *lineptr++;
145}
146
147char *xmon_gets(char *str, int nb)
148{
149	char *p;
150	int c;
151
152	for (p = str; p < str + nb - 1; ) {
153		c = xmon_getchar();
154		if (c == -1) {
155			if (p == str)
156				return NULL;
157			break;
158		}
159		*p++ = c;
160		if (c == '\n')
161			break;
162	}
163	*p = 0;
164	return str;
165}
166
167void xmon_printf(const char *format, ...)
168{
169	va_list args;
170	static char xmon_outbuf[1024];
171	int rc, n;
172
173	va_start(args, format);
174	n = vsnprintf(xmon_outbuf, sizeof(xmon_outbuf), format, args);
175	va_end(args);
176
177	rc = xmon_write(xmon_outbuf, n);
178
179	if (n && rc == 0) {
180		/* No udbg hooks, fallback to printk() - dangerous */
181		pr_cont("%s", xmon_outbuf);
182	}
183}
184
185void xmon_puts(const char *str)
186{
187	xmon_write(str, strlen(str));
188}
189