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.
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))))
20 static void c_write(char *buf
, int len
)
22 from_backend(0, buf
, len
);
25 static char *term_buf
= NULL
;
26 static int term_buflen
= 0, term_bufsiz
= 0, term_quotenext
= 0;
28 static int plen(unsigned char c
)
30 if ((c
>= 32 && c
<= 126) || (c
>= 160 && !in_utf
))
33 return 2; /* ^x for some x */
35 return 4; /* <XY> for hex XY */
38 static void pwrite(unsigned char c
)
40 if ((c
>= 32 && c
<= 126) || (c
>= 160 && !in_utf
)) {
44 cc
[1] = (c
== 127 ?
'?' : c
+ 0x40);
49 sprintf(cc
, "<%02X>", c
);
54 static void bsb(int n
)
57 c_write("\010 \010", 3);
60 #define CTRL(x) (x^'@')
61 #define KCTRL(x) ((x^'@') | 0x100)
63 void ldisc_send(char *buf
, int len
, int interactive
)
67 * Called with len=0 when the options change. We must inform
68 * the front end in case it needs to know.
71 void ldisc_update(int echo
, int edit
);
72 ldisc_update(ECHOING
, EDITING
);
75 * Less than zero means null terminated special string.
82 * Either perform local editing, or just send characters.
88 if (!interactive
&& c
== '\r')
90 switch (term_quotenext ?
' ' : c
) {
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
106 case KCTRL('?'): /* backspace/delete */
107 if (term_buflen
> 0) {
109 bsb(plen(term_buf
[term_buflen
- 1]));
113 case CTRL('W'): /* delete word */
114 while (term_buflen
> 0) {
116 bsb(plen(term_buf
[term_buflen
- 1]));
118 if (term_buflen
> 0 &&
119 isspace(term_buf
[term_buflen
- 1]) &&
120 !isspace(term_buf
[term_buflen
]))
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) {
130 bsb(plen(term_buf
[term_buflen
- 1]));
133 back
->special(TS_EL
);
135 * We don't send IP, SUSP or ABORT if the user has
136 * configured telnet specials off! This breaks
139 if (!cfg
.telnet_keyboard
)
142 back
->special(TS_IP
);
144 back
->special(TS_SUSP
);
146 back
->special(TS_ABORT
);
148 case CTRL('R'): /* redraw line */
151 c_write("^R\r\n", 4);
152 for (i
= 0; i
< term_buflen
; i
++)
156 case CTRL('V'): /* quote next char */
157 term_quotenext
= TRUE
;
159 case CTRL('D'): /* logout or send */
160 if (term_buflen
== 0) {
161 back
->special(TS_EOF
);
163 back
->send(term_buf
, term_buflen
);
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:
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
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.
191 if (cfg
.protocol
== PROT_RAW
&&
192 term_buflen
> 0 && term_buf
[term_buflen
- 1] == '\r') {
194 bsb(plen(term_buf
[term_buflen
- 1]));
197 case KCTRL('M'): /* send with newline */
199 back
->send(term_buf
, term_buflen
);
200 if (cfg
.protocol
== PROT_RAW
)
201 back
->send("\r\n", 2);
202 else if (cfg
.protocol
== PROT_TELNET
&& cfg
.telnet_newline
)
203 back
->special(TS_EOL
);
212 default: /* get to this label from ^V handler */
214 if (term_buflen
>= term_bufsiz
) {
215 term_bufsiz
= term_buflen
+ 256;
216 term_buf
= saferealloc(term_buf
, term_bufsiz
);
218 term_buf
[term_buflen
++] = c
;
220 pwrite((unsigned char) c
);
221 term_quotenext
= FALSE
;
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]));
236 if (keyflag
&& cfg
.protocol
== PROT_TELNET
&& len
== 1) {
239 if (cfg
.protocol
== PROT_TELNET
&& cfg
.telnet_newline
)
240 back
->special(TS_EOL
);
246 if (cfg
.telnet_keyboard
) {
247 back
->special(TS_EC
);
251 if (cfg
.telnet_keyboard
) {
252 back
->special(TS_IP
);
256 if (cfg
.telnet_keyboard
) {
257 back
->special(TS_SUSP
);
262 back
->send(buf
, len
);
266 back
->send(buf
, len
);