Major destabilisation, phase 1. In this phase I've moved (I think)
[u/mdw/putty] / ldisc.c
1 /*
2 * ldisc.c: PuTTY line discipline. Sits between the input coming
3 * from keypresses in the window, and the output channel leading to
4 * the back end. Implements echo and/or local line editing,
5 * depending on what's currently configured.
6 */
7
8 #include <stdio.h>
9 #include <ctype.h>
10
11 #include "putty.h"
12 #include "terminal.h"
13
14 #define ECHOING (cfg.localecho == LD_YES || \
15 (cfg.localecho == LD_BACKEND && \
16 (back->ldisc(LD_ECHO) || term_ldisc(term, LD_ECHO))))
17 #define EDITING (cfg.localedit == LD_YES || \
18 (cfg.localedit == LD_BACKEND && \
19 (back->ldisc(LD_EDIT) || term_ldisc(term, LD_EDIT))))
20
21 static void c_write(char *buf, int len)
22 {
23 from_backend(term, 0, buf, len);
24 }
25
26 static char *term_buf = NULL;
27 static int term_buflen = 0, term_bufsiz = 0, term_quotenext = 0;
28
29 static int plen(unsigned char c)
30 {
31 if ((c >= 32 && c <= 126) || (c >= 160 && !in_utf(term)))
32 return 1;
33 else if (c < 128)
34 return 2; /* ^x for some x */
35 else
36 return 4; /* <XY> for hex XY */
37 }
38
39 static void pwrite(unsigned char c)
40 {
41 if ((c >= 32 && c <= 126) || (c >= 160 && !in_utf(term))) {
42 c_write(&c, 1);
43 } else if (c < 128) {
44 char cc[2];
45 cc[1] = (c == 127 ? '?' : c + 0x40);
46 cc[0] = '^';
47 c_write(cc, 2);
48 } else {
49 char cc[5];
50 sprintf(cc, "<%02X>", c);
51 c_write(cc, 4);
52 }
53 }
54
55 static void bsb(int n)
56 {
57 while (n--)
58 c_write("\010 \010", 3);
59 }
60
61 #define CTRL(x) (x^'@')
62 #define KCTRL(x) ((x^'@') | 0x100)
63
64 void ldisc_send(char *buf, int len, int interactive)
65 {
66 int keyflag = 0;
67 /*
68 * Called with len=0 when the options change. We must inform
69 * the front end in case it needs to know.
70 */
71 if (len == 0) {
72 void ldisc_update(int echo, int edit);
73 ldisc_update(ECHOING, EDITING);
74 return;
75 }
76 /*
77 * Less than zero means null terminated special string.
78 */
79 if (len < 0) {
80 len = strlen(buf);
81 keyflag = KCTRL('@');
82 }
83 /*
84 * Either perform local editing, or just send characters.
85 */
86 if (EDITING) {
87 while (len--) {
88 int c;
89 c = *buf++ + keyflag;
90 if (!interactive && c == '\r')
91 c += KCTRL('@');
92 switch (term_quotenext ? ' ' : c) {
93 /*
94 * ^h/^?: delete one char and output one BSB
95 * ^w: delete, and output BSBs, to return to last
96 * space/nonspace boundary
97 * ^u: delete, and output BSBs, to return to BOL
98 * ^c: Do a ^u then send a telnet IP
99 * ^z: Do a ^u then send a telnet SUSP
100 * ^\: Do a ^u then send a telnet ABORT
101 * ^r: echo "^R\n" and redraw line
102 * ^v: quote next char
103 * ^d: if at BOL, end of file and close connection,
104 * else send line and reset to BOL
105 * ^m: send line-plus-\r\n and reset to BOL
106 */
107 case KCTRL('H'):
108 case KCTRL('?'): /* backspace/delete */
109 if (term_buflen > 0) {
110 if (ECHOING)
111 bsb(plen(term_buf[term_buflen - 1]));
112 term_buflen--;
113 }
114 break;
115 case CTRL('W'): /* delete word */
116 while (term_buflen > 0) {
117 if (ECHOING)
118 bsb(plen(term_buf[term_buflen - 1]));
119 term_buflen--;
120 if (term_buflen > 0 &&
121 isspace(term_buf[term_buflen - 1]) &&
122 !isspace(term_buf[term_buflen]))
123 break;
124 }
125 break;
126 case CTRL('U'): /* delete line */
127 case CTRL('C'): /* Send IP */
128 case CTRL('\\'): /* Quit */
129 case CTRL('Z'): /* Suspend */
130 while (term_buflen > 0) {
131 if (ECHOING)
132 bsb(plen(term_buf[term_buflen - 1]));
133 term_buflen--;
134 }
135 back->special(TS_EL);
136 /*
137 * We don't send IP, SUSP or ABORT if the user has
138 * configured telnet specials off! This breaks
139 * talkers otherwise.
140 */
141 if (!cfg.telnet_keyboard)
142 goto default_case;
143 if (c == CTRL('C'))
144 back->special(TS_IP);
145 if (c == CTRL('Z'))
146 back->special(TS_SUSP);
147 if (c == CTRL('\\'))
148 back->special(TS_ABORT);
149 break;
150 case CTRL('R'): /* redraw line */
151 if (ECHOING) {
152 int i;
153 c_write("^R\r\n", 4);
154 for (i = 0; i < term_buflen; i++)
155 pwrite(term_buf[i]);
156 }
157 break;
158 case CTRL('V'): /* quote next char */
159 term_quotenext = TRUE;
160 break;
161 case CTRL('D'): /* logout or send */
162 if (term_buflen == 0) {
163 back->special(TS_EOF);
164 } else {
165 back->send(term_buf, term_buflen);
166 term_buflen = 0;
167 }
168 break;
169 /*
170 * This particularly hideous bit of code from RDB
171 * allows ordinary ^M^J to do the same thing as
172 * magic-^M when in Raw protocol. The line `case
173 * KCTRL('M'):' is _inside_ the if block. Thus:
174 *
175 * - receiving regular ^M goes straight to the
176 * default clause and inserts as a literal ^M.
177 * - receiving regular ^J _not_ directly after a
178 * literal ^M (or not in Raw protocol) fails the
179 * if condition, leaps to the bottom of the if,
180 * and falls through into the default clause
181 * again.
182 * - receiving regular ^J just after a literal ^M
183 * in Raw protocol passes the if condition,
184 * deletes the literal ^M, and falls through
185 * into the magic-^M code
186 * - receiving a magic-^M empties the line buffer,
187 * signals end-of-line in one of the various
188 * entertaining ways, and _doesn't_ fall out of
189 * the bottom of the if and through to the
190 * default clause because of the break.
191 */
192 case CTRL('J'):
193 if (cfg.protocol == PROT_RAW &&
194 term_buflen > 0 && term_buf[term_buflen - 1] == '\r') {
195 if (ECHOING)
196 bsb(plen(term_buf[term_buflen - 1]));
197 term_buflen--;
198 /* FALLTHROUGH */
199 case KCTRL('M'): /* send with newline */
200 if (term_buflen > 0)
201 back->send(term_buf, term_buflen);
202 if (cfg.protocol == PROT_RAW)
203 back->send("\r\n", 2);
204 else if (cfg.protocol == PROT_TELNET && cfg.telnet_newline)
205 back->special(TS_EOL);
206 else
207 back->send("\r", 1);
208 if (ECHOING)
209 c_write("\r\n", 2);
210 term_buflen = 0;
211 break;
212 }
213 /* FALLTHROUGH */
214 default: /* get to this label from ^V handler */
215 default_case:
216 if (term_buflen >= term_bufsiz) {
217 term_bufsiz = term_buflen + 256;
218 term_buf = saferealloc(term_buf, term_bufsiz);
219 }
220 term_buf[term_buflen++] = c;
221 if (ECHOING)
222 pwrite((unsigned char) c);
223 term_quotenext = FALSE;
224 break;
225 }
226 }
227 } else {
228 if (term_buflen != 0) {
229 back->send(term_buf, term_buflen);
230 while (term_buflen > 0) {
231 bsb(plen(term_buf[term_buflen - 1]));
232 term_buflen--;
233 }
234 }
235 if (len > 0) {
236 if (ECHOING)
237 c_write(buf, len);
238 if (keyflag && cfg.protocol == PROT_TELNET && len == 1) {
239 switch (buf[0]) {
240 case CTRL('M'):
241 if (cfg.protocol == PROT_TELNET && cfg.telnet_newline)
242 back->special(TS_EOL);
243 else
244 back->send("\r", 1);
245 break;
246 case CTRL('?'):
247 case CTRL('H'):
248 if (cfg.telnet_keyboard) {
249 back->special(TS_EC);
250 break;
251 }
252 case CTRL('C'):
253 if (cfg.telnet_keyboard) {
254 back->special(TS_IP);
255 break;
256 }
257 case CTRL('Z'):
258 if (cfg.telnet_keyboard) {
259 back->special(TS_SUSP);
260 break;
261 }
262
263 default:
264 back->send(buf, len);
265 break;
266 }
267 } else
268 back->send(buf, len);
269 }
270 }
271 }