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 
10 static bool paginating, paginate_skipping;
11 static unsigned long paginate_lpp; /* Lines Per Page */
12 static unsigned long paginate_pos;
13 
xmon_start_pagination(void)14 void xmon_start_pagination(void)
15 {
16 	paginating = true;
17 	paginate_skipping = false;
18 	paginate_pos = 0;
19 }
20 
xmon_end_pagination(void)21 void xmon_end_pagination(void)
22 {
23 	paginating = false;
24 }
25 
xmon_set_pagination_lpp(unsigned long lpp)26 void xmon_set_pagination_lpp(unsigned long lpp)
27 {
28 	paginate_lpp = lpp;
29 }
30 
xmon_readchar(void)31 static int xmon_readchar(void)
32 {
33 	if (udbg_getc)
34 		return udbg_getc();
35 	return -1;
36 }
37 
xmon_write(const char * ptr,int nb)38 static 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 
xmon_putchar(int c)83 int 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 
92 static char line[256];
93 static char *lineptr;
94 static int lineleft;
95 
xmon_getchar(void)96 static 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 
xmon_gets(char * str,int nb)147 char *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 
xmon_printf(const char * format,...)167 void 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 
xmon_puts(const char * str)185 void xmon_puts(const char *str)
186 {
187 	xmon_write(str, strlen(str));
188 }
189