AmendHub

Download

jcs

/

subtext

/

ansi.c

 

(View History)

jcs   ansi: Fix panic message Latest amendment: 425 on 2023-03-15

1 /*
2 * Copyright (c) 2021 joshua stein <jcs@jcs.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <stdarg.h>
18 #include <stdio.h>
19 #include <string.h>
20
21 #include "ansi.h"
22 #include "session.h"
23 #include "util.h"
24
25 /*
26 * We may be outputting a bunch of ansi()s in a string, so they can't all
27 * return the same buffer. Rotate between a few on each iteration.
28 */
29 static char ansi_buf[10][32];
30 static short last_ansi_buf = 0;
31
32 const char *
33 ansi_bold(struct session *s)
34 {
35 static const char bold[] = "\33[1m";
36 if (s->vt100)
37 return bold;
38 return "";
39 }
40
41 const char *
42 ansi_reset(struct session *s)
43 {
44 static const char reset[] = "\33[0m";
45 if (s->vt100)
46 return reset;
47 return "";
48 }
49
50 char *
51 ansi(struct session *s, ...)
52 {
53 char ansi_tmp[10];
54 char *ansi_out;
55 va_list ap;
56 short attr, val, val2, n;
57 size_t len;
58
59 ansi_out = (char *)&ansi_buf[last_ansi_buf];
60 if (++last_ansi_buf >= nitems(ansi_buf))
61 last_ansi_buf = 0;
62
63 *ansi_out = '\0';
64
65 va_start(ap, s);
66 while ((attr = va_arg(ap, short)) != ANSI_END) {
67 len = 0;
68 switch (attr) {
69 case ANSI_RESET:
70 if (s->vt100)
71 len = strlcat(ansi_out, "\33[0m", sizeof(ansi_buf[0]));
72 break;
73 case ANSI_BOLD:
74 if (s->vt100)
75 len = strlcat(ansi_out, "\33[1m", sizeof(ansi_buf[0]));
76 break;
77 case ANSI_UNDERLINE:
78 if (s->vt100)
79 len = strlcat(ansi_out, "\33[4m", sizeof(ansi_buf[0]));
80 break;
81 case ANSI_REVERSE:
82 if (s->vt100)
83 len = strlcat(ansi_out, "\33[7m", sizeof(ansi_buf[0]));
84 break;
85
86 case ANSI_CLEAR_SCREEN:
87 if (s->vt100)
88 len = strlcat(ansi_out, "\33[2J", sizeof(ansi_buf[0]));
89 else if (s->vt52)
90 /* XXX: changes cursor position */
91 len = strlcat(ansi_out, "\33H\33J", sizeof(ansi_buf[0]));
92 break;
93 case ANSI_ERASE_LINE:
94 if (s->vt100)
95 /* XXX: this doesn't work on windows command-line telnet */
96 len = strlcat(ansi_out, "\33[2K", sizeof(ansi_buf[0]));
97 else if (s->vt52)
98 /* XXX: changes cursor position */
99 len = strlcat(ansi_out, "\r\33K", sizeof(ansi_buf[0]));
100 break;
101 case ANSI_BACKSPACE:
102 if (s->vt100)
103 len = strlcat(ansi_out, "\33[D \33[D", sizeof(ansi_buf[0]));
104 else if (s->vt52)
105 len = strlcat(ansi_out, "\33D \33D", sizeof(ansi_buf[0]));
106 else
107 len = strlcat(ansi_out, "\10 \10", sizeof(ansi_buf[0]));
108 break;
109
110 /* these require N args */
111 case ANSI_DOWN_N:
112 if (s->vt100) {
113 sprintf(ansi_tmp, "\33[%dB", va_arg(ap, short));
114 len = strlcat(ansi_out, ansi_tmp, sizeof(ansi_buf[0]));
115 } else if (s->vt52) {
116 val = va_arg(ap, short);
117 for (n = 0; n < val; n++)
118 len = strlcat(ansi_out, "\33B", sizeof(ansi_buf[0]));
119 }
120 break;
121 case ANSI_FORWARD_N:
122 if (s->vt100) {
123 sprintf(ansi_tmp, "\33[%dC", va_arg(ap, short));
124 len = strlcat(ansi_out, ansi_tmp, sizeof(ansi_buf[0]));
125 } else if (s->vt52) {
126 val = va_arg(ap, short);
127 for (n = 0; n < val; n++)
128 len = strlcat(ansi_out, "\33C", sizeof(ansi_buf[0]));
129 }
130 break;
131 case ANSI_BACK_N:
132 if (s->vt100) {
133 sprintf(ansi_tmp, "\33[%dD", va_arg(ap, short));
134 len = strlcat(ansi_out, ansi_tmp, sizeof(ansi_buf[0]));
135 } else if (s->vt52) {
136 val = va_arg(ap, short);
137 for (n = 0; n < val; n++)
138 len = strlcat(ansi_out, "\33D", sizeof(ansi_buf[0]));
139 }
140 break;
141 case ANSI_UP_N:
142 if (s->vt100) {
143 sprintf(ansi_tmp, "\33[%dF", va_arg(ap, short));
144 len = strlcat(ansi_out, ansi_tmp, sizeof(ansi_buf[0]));
145 } else if (s->vt52) {
146 val = va_arg(ap, short);
147 for (n = 0; n < val; n++)
148 len = strlcat(ansi_out, "\33A", sizeof(ansi_buf[0]));
149 }
150 break;
151 case ANSI_INSERT_LINES_N:
152 if (s->vt100) {
153 sprintf(ansi_tmp, "\33[%dL", va_arg(ap, short));
154 len = strlcat(ansi_out, ansi_tmp, sizeof(ansi_buf[0]));
155 } else if (s->vt52) {
156 val = va_arg(ap, short);
157 for (n = 0; n < val; n++)
158 len = strlcat(ansi_out, "\33L", sizeof(ansi_buf[0]));
159 }
160 break;
161 case ANSI_DELETE_LINES_N:
162 if (s->vt100) {
163 sprintf(ansi_tmp, "\33[%dM", va_arg(ap, short));
164 len = strlcat(ansi_out, ansi_tmp, sizeof(ansi_buf[0]));
165 } else if (s->vt52) {
166 val = va_arg(ap, short);
167 for (n = 0; n < val; n++)
168 len = strlcat(ansi_out, "\33M", sizeof(ansi_buf[0]));
169 }
170 break;
171 case ANSI_COL_N:
172 val = va_arg(ap, short);
173 if (val == 1)
174 len = strlcat(ansi_out, "\r", sizeof(ansi_buf[0]));
175 else {
176 if (s->vt100) {
177 /* \e[xG not supported on windows telnet, fake it */
178 sprintf(ansi_tmp, "\r\33[%dC", val);
179 len = strlcat(ansi_out, ansi_tmp, sizeof(ansi_buf[0]));
180 } else if (s->vt52) {
181 /* go to beginning of line, then move right N times */
182 strlcat(ansi_out, "\r", sizeof(ansi_buf[0]));
183 sprintf(ansi_tmp, "\33C");
184 for (n = 0; n < val; n++)
185 len = strlcat(ansi_out, ansi_tmp,
186 sizeof(ansi_buf[0]));
187 }
188 }
189 break;
190 case ANSI_CURSOR_LINE_COL:
191 val = va_arg(ap, short);
192 val2 = va_arg(ap, short);
193 if (s->vt100)
194 /* line, column */
195 sprintf(ansi_tmp, "\33[%d;%dH", val, val2);
196 else if (s->vt52)
197 /* column, line as chr(col)+31,chr(line)+31 */
198 sprintf(ansi_tmp, "\33Y%c%c", val2 + 31, val + 31);
199 len = strlcat(ansi_out, ansi_tmp, sizeof(ansi_buf[0]));
200 break;
201
202 case ANSI_SAVE_CURSOR:
203 if (s->vt100)
204 len = strlcat(ansi_out, "\33[s", sizeof(ansi_buf[0]));
205 else if (s->vt52)
206 len = strlcat(ansi_out, "\337", sizeof(ansi_buf[0]));
207 break;
208 case ANSI_RESTORE_SAVED_CURSOR:
209 if (s->vt100)
210 len = strlcat(ansi_out, "\33[u", sizeof(ansi_buf[0]));
211 else if (s->vt52)
212 len = strlcat(ansi_out, "\338", sizeof(ansi_buf[0]));
213 break;
214
215 default:
216 panic("ansi: Unknown ANSI attribute %d", attr);
217 }
218
219 if (len > sizeof(ansi_buf[0]))
220 panic("ansi: strlcat overflow; missing ANSI_END?");
221 }
222
223 va_end(ap);
224
225 return ansi_out;
226 }
227
228 size_t
229 ansi_strip(char *inp, char **outp)
230 {
231 size_t ilen = strlen(inp);
232 size_t n, olen = 0;
233 short in_csi;
234
235 if (outp) {
236 *outp = xmalloc(ilen + 1);
237 if (*outp == NULL)
238 return 0;
239 }
240
241 for (n = 0, in_csi = 0; n < ilen; ) {
242 if (in_csi) {
243 /* eat "12;34;56" */
244 while (n < ilen) {
245 if ((inp[n] >= '0' && inp[n] <= '9') || inp[n] == ';')
246 n++;
247 else
248 break;
249 }
250
251 /* command character */
252 if (n < ilen)
253 n++;
254
255 in_csi = 0;
256 } else if (inp[n] == '\33' && inp[n + 1] == '[') {
257 in_csi = 1;
258 n += 2;
259 } else if (inp[n] == '\r') {
260 /* consider this an ansi character */
261 n++;
262 } else {
263 if (outp)
264 (*outp)[olen] = inp[n];
265 olen++;
266 n++;
267 }
268 }
269
270 return olen;
271 }
272
273 void
274 ansi_probe_screen_size(struct session *s)
275 {
276 short cols, rows;
277 char c[2] = { 0 };
278
279 if (!s->vt100)
280 return;
281
282 /* save cursor position */
283 session_clear_input(s);
284 session_printf(s, "\33[s");
285
286 /* go to the (hopefully) edge of the screen */
287 session_printf(s, "\33[200B\33[200C");
288 session_flush(s);
289
290 /* report cursor position */
291 session_clear_input(s);
292 session_printf(s, "\33[6n");
293 session_flush(s);
294
295 session_wait_for_chars(s, 1500, 100);
296
297 /* \e[12;34R */
298 if (s->ibuf[0] == ESC && s->ibuf[1] == '[' &&
299 sscanf((char *)&s->ibuf + 2, "%d;%d%1[R]", &rows, &cols, &c) == 3 &&
300 c[0] == 'R') {
301 if (cols >= MIN_TERMINAL_COLUMNS && rows >= MIN_TERMINAL_LINES) {
302 s->terminal_columns = cols;
303 s->terminal_lines = rows;
304 session_logf(s, "Probed terminal size of %dx%d", cols, rows);
305 } else {
306 session_logf(s, "Bogus terminal size probe response: %dx%d",
307 cols, rows);
308 }
309 }
310
311 /* restore saved cursor position */
312 session_clear_input(s);
313 session_printf(s, "\33[u");
314 session_flush(s);
315 }