]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - ldisc.c
Oops - repercussions of the close-on-exit stuff which I forgot to
[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(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 }