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