Oops - repercussions of the close-on-exit stuff which I forgot to
[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 * Notify the front end that something was pressed, in case
78 * it's depending on finding out (e.g. keypress termination for
79 * Close On Exit).
80 */
81 frontend_keypress();
82 /*
83 * Less than zero means null terminated special string.
84 */
85 if (len < 0) {
86 len = strlen(buf);
87 keyflag = KCTRL('@');
88 }
89 /*
90 * Either perform local editing, or just send characters.
91 */
92 if (EDITING) {
93 while (len--) {
94 int c;
95 c = *buf++ + keyflag;
96 if (!interactive && c == '\r')
97 c += KCTRL('@');
98 switch (term_quotenext ? ' ' : c) {
99 /*
100 * ^h/^?: delete one char and output one BSB
101 * ^w: delete, and output BSBs, to return to last
102 * space/nonspace boundary
103 * ^u: delete, and output BSBs, to return to BOL
104 * ^c: Do a ^u then send a telnet IP
105 * ^z: Do a ^u then send a telnet SUSP
106 * ^\: Do a ^u then send a telnet ABORT
107 * ^r: echo "^R\n" and redraw line
108 * ^v: quote next char
109 * ^d: if at BOL, end of file and close connection,
110 * else send line and reset to BOL
111 * ^m: send line-plus-\r\n and reset to BOL
112 */
113 case KCTRL('H'):
114 case KCTRL('?'): /* backspace/delete */
115 if (term_buflen > 0) {
116 if (ECHOING)
117 bsb(plen(term_buf[term_buflen - 1]));
118 term_buflen--;
119 }
120 break;
121 case CTRL('W'): /* delete word */
122 while (term_buflen > 0) {
123 if (ECHOING)
124 bsb(plen(term_buf[term_buflen - 1]));
125 term_buflen--;
126 if (term_buflen > 0 &&
127 isspace(term_buf[term_buflen - 1]) &&
128 !isspace(term_buf[term_buflen]))
129 break;
130 }
131 break;
132 case CTRL('U'): /* delete line */
133 case CTRL('C'): /* Send IP */
134 case CTRL('\\'): /* Quit */
135 case CTRL('Z'): /* Suspend */
136 while (term_buflen > 0) {
137 if (ECHOING)
138 bsb(plen(term_buf[term_buflen - 1]));
139 term_buflen--;
140 }
141 back->special(TS_EL);
142 /*
143 * We don't send IP, SUSP or ABORT if the user has
144 * configured telnet specials off! This breaks
145 * talkers otherwise.
146 */
147 if (!cfg.telnet_keyboard)
148 goto default_case;
149 if (c == CTRL('C'))
150 back->special(TS_IP);
151 if (c == CTRL('Z'))
152 back->special(TS_SUSP);
153 if (c == CTRL('\\'))
154 back->special(TS_ABORT);
155 break;
156 case CTRL('R'): /* redraw line */
157 if (ECHOING) {
158 int i;
159 c_write("^R\r\n", 4);
160 for (i = 0; i < term_buflen; i++)
161 pwrite(term_buf[i]);
162 }
163 break;
164 case CTRL('V'): /* quote next char */
165 term_quotenext = TRUE;
166 break;
167 case CTRL('D'): /* logout or send */
168 if (term_buflen == 0) {
169 back->special(TS_EOF);
170 } else {
171 back->send(term_buf, term_buflen);
172 term_buflen = 0;
173 }
174 break;
175 /*
176 * This particularly hideous bit of code from RDB
177 * allows ordinary ^M^J to do the same thing as
178 * magic-^M when in Raw protocol. The line `case
179 * KCTRL('M'):' is _inside_ the if block. Thus:
180 *
181 * - receiving regular ^M goes straight to the
182 * default clause and inserts as a literal ^M.
183 * - receiving regular ^J _not_ directly after a
184 * literal ^M (or not in Raw protocol) fails the
185 * if condition, leaps to the bottom of the if,
186 * and falls through into the default clause
187 * again.
188 * - receiving regular ^J just after a literal ^M
189 * in Raw protocol passes the if condition,
190 * deletes the literal ^M, and falls through
191 * into the magic-^M code
192 * - receiving a magic-^M empties the line buffer,
193 * signals end-of-line in one of the various
194 * entertaining ways, and _doesn't_ fall out of
195 * the bottom of the if and through to the
196 * default clause because of the break.
197 */
198 case CTRL('J'):
199 if (cfg.protocol == PROT_RAW &&
200 term_buflen > 0 && term_buf[term_buflen - 1] == '\r') {
201 if (ECHOING)
202 bsb(plen(term_buf[term_buflen - 1]));
203 term_buflen--;
204 /* FALLTHROUGH */
205 case KCTRL('M'): /* send with newline */
206 if (term_buflen > 0)
207 back->send(term_buf, term_buflen);
208 if (cfg.protocol == PROT_RAW)
209 back->send("\r\n", 2);
210 else if (cfg.protocol == PROT_TELNET && cfg.telnet_newline)
211 back->special(TS_EOL);
212 else
213 back->send("\r", 1);
214 if (ECHOING)
215 c_write("\r\n", 2);
216 term_buflen = 0;
217 break;
218 }
219 /* FALLTHROUGH */
220 default: /* get to this label from ^V handler */
221 default_case:
222 if (term_buflen >= term_bufsiz) {
223 term_bufsiz = term_buflen + 256;
224 term_buf = saferealloc(term_buf, term_bufsiz);
225 }
226 term_buf[term_buflen++] = c;
227 if (ECHOING)
228 pwrite((unsigned char) c);
229 term_quotenext = FALSE;
230 break;
231 }
232 }
233 } else {
234 if (term_buflen != 0) {
235 back->send(term_buf, term_buflen);
236 while (term_buflen > 0) {
237 bsb(plen(term_buf[term_buflen - 1]));
238 term_buflen--;
239 }
240 }
241 if (len > 0) {
242 if (ECHOING)
243 c_write(buf, len);
244 if (keyflag && cfg.protocol == PROT_TELNET && len == 1) {
245 switch (buf[0]) {
246 case CTRL('M'):
247 if (cfg.protocol == PROT_TELNET && cfg.telnet_newline)
248 back->special(TS_EOL);
249 else
250 back->send("\r", 1);
251 break;
252 case CTRL('?'):
253 case CTRL('H'):
254 if (cfg.telnet_keyboard) {
255 back->special(TS_EC);
256 break;
257 }
258 case CTRL('C'):
259 if (cfg.telnet_keyboard) {
260 back->special(TS_IP);
261 break;
262 }
263 case CTRL('Z'):
264 if (cfg.telnet_keyboard) {
265 back->special(TS_SUSP);
266 break;
267 }
268
269 default:
270 back->send(buf, len);
271 break;
272 }
273 } else
274 back->send(buf, len);
275 }
276 }
277 }