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