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