]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - ldisc.c
First phase of porting. pterm now compiles and runs under Linux+gtk.
[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
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))))
19
20 static void c_write(char *buf, int len)
21 {
22     from_backend(0, buf, len);
23 }
24
25 static char *term_buf = NULL;
26 static int term_buflen = 0, term_bufsiz = 0, term_quotenext = 0;
27
28 static int plen(unsigned char c)
29 {
30     if ((c >= 32 && c <= 126) || (c >= 160 && !in_utf))
31         return 1;
32     else if (c < 128)
33         return 2;                      /* ^x for some x */
34     else
35         return 4;                      /* <XY> for hex XY */
36 }
37
38 static void pwrite(unsigned char c)
39 {
40     if ((c >= 32 && c <= 126) || (c >= 160 && !in_utf)) {
41         c_write(&c, 1);
42     } else if (c < 128) {
43         char cc[2];
44         cc[1] = (c == 127 ? '?' : c + 0x40);
45         cc[0] = '^';
46         c_write(cc, 2);
47     } else {
48         char cc[5];
49         sprintf(cc, "<%02X>", c);
50         c_write(cc, 4);
51     }
52 }
53
54 static void bsb(int n)
55 {
56     while (n--)
57         c_write("\010 \010", 3);
58 }
59
60 #define CTRL(x) (x^'@')
61 #define KCTRL(x) ((x^'@') | 0x100)
62
63 void ldisc_send(char *buf, int len, int interactive)
64 {
65     int keyflag = 0;
66     /*
67      * Called with len=0 when the options change. We must inform
68      * the front end in case it needs to know.
69      */
70     if (len == 0) {
71         void ldisc_update(int echo, int edit);
72         ldisc_update(ECHOING, EDITING);
73     }
74     /*
75      * Less than zero means null terminated special string.
76      */
77     if (len < 0) {
78         len = strlen(buf);
79         keyflag = KCTRL('@');
80     }
81     /*
82      * Either perform local editing, or just send characters.
83      */
84     if (EDITING) {
85         while (len--) {
86             int c;
87             c = *buf++ + keyflag;
88             if (!interactive && c == '\r')
89                 c += KCTRL('@');
90             switch (term_quotenext ? ' ' : c) {
91                 /*
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
104                  */
105               case KCTRL('H'):
106               case KCTRL('?'):         /* backspace/delete */
107                 if (term_buflen > 0) {
108                     if (ECHOING)
109                         bsb(plen(term_buf[term_buflen - 1]));
110                     term_buflen--;
111                 }
112                 break;
113               case CTRL('W'):          /* delete word */
114                 while (term_buflen > 0) {
115                     if (ECHOING)
116                         bsb(plen(term_buf[term_buflen - 1]));
117                     term_buflen--;
118                     if (term_buflen > 0 &&
119                         isspace(term_buf[term_buflen - 1]) &&
120                         !isspace(term_buf[term_buflen]))
121                         break;
122                 }
123                 break;
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) {
129                     if (ECHOING)
130                         bsb(plen(term_buf[term_buflen - 1]));
131                     term_buflen--;
132                 }
133                 back->special(TS_EL);
134                 /*
135                  * We don't send IP, SUSP or ABORT if the user has
136                  * configured telnet specials off! This breaks
137                  * talkers otherwise.
138                  */
139                 if (!cfg.telnet_keyboard)
140                     goto default_case;
141                 if (c == CTRL('C'))
142                     back->special(TS_IP);
143                 if (c == CTRL('Z'))
144                     back->special(TS_SUSP);
145                 if (c == CTRL('\\'))
146                     back->special(TS_ABORT);
147                 break;
148               case CTRL('R'):          /* redraw line */
149                 if (ECHOING) {
150                     int i;
151                     c_write("^R\r\n", 4);
152                     for (i = 0; i < term_buflen; i++)
153                         pwrite(term_buf[i]);
154                 }
155                 break;
156               case CTRL('V'):          /* quote next char */
157                 term_quotenext = TRUE;
158                 break;
159               case CTRL('D'):          /* logout or send */
160                 if (term_buflen == 0) {
161                     back->special(TS_EOF);
162                 } else {
163                     back->send(term_buf, term_buflen);
164                     term_buflen = 0;
165                 }
166                 break;
167                 /*
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:
172                  * 
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
179                  *    again.
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.
189                  */
190               case CTRL('J'):
191                 if (cfg.protocol == PROT_RAW &&
192                     term_buflen > 0 && term_buf[term_buflen - 1] == '\r') {
193                     if (ECHOING)
194                         bsb(plen(term_buf[term_buflen - 1]));
195                     term_buflen--;
196                     /* FALLTHROUGH */
197               case KCTRL('M'):         /* send with newline */
198                     if (term_buflen > 0)
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);
204                     else
205                         back->send("\r", 1);
206                     if (ECHOING)
207                         c_write("\r\n", 2);
208                     term_buflen = 0;
209                     break;
210                 }
211                 /* FALLTHROUGH */
212               default:                 /* get to this label from ^V handler */
213                 default_case:
214                 if (term_buflen >= term_bufsiz) {
215                     term_bufsiz = term_buflen + 256;
216                     term_buf = saferealloc(term_buf, term_bufsiz);
217                 }
218                 term_buf[term_buflen++] = c;
219                 if (ECHOING)
220                     pwrite((unsigned char) c);
221                 term_quotenext = FALSE;
222                 break;
223             }
224         }
225     } else {
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]));
230                 term_buflen--;
231             }
232         }
233         if (len > 0) {
234             if (ECHOING)
235                 c_write(buf, len);
236             if (keyflag && cfg.protocol == PROT_TELNET && len == 1) {
237                 switch (buf[0]) {
238                   case CTRL('M'):
239                     if (cfg.protocol == PROT_TELNET && cfg.telnet_newline)
240                         back->special(TS_EOL);
241                     else
242                         back->send("\r", 1);
243                     break;
244                   case CTRL('?'):
245                   case CTRL('H'):
246                     if (cfg.telnet_keyboard) {
247                         back->special(TS_EC);
248                         break;
249                     }
250                   case CTRL('C'):
251                     if (cfg.telnet_keyboard) {
252                         back->special(TS_IP);
253                         break;
254                     }
255                   case CTRL('Z'):
256                     if (cfg.telnet_keyboard) {
257                         back->special(TS_SUSP);
258                         break;
259                     }
260
261                   default:
262                     back->send(buf, len);
263                     break;
264                 }
265             } else
266                 back->send(buf, len);
267         }
268     }
269 }