]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - ldisc.c
Rethink the whole line discipline architecture. Instead of having
[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 <windows.h>
9 #include <stdio.h>
10 #include <ctype.h>
11
12 #include "putty.h"
13
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))))
20
21 static void c_write (char *buf, int len) {
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     if ((c >= 32 && c <= 126) ||
30         (c >= 160))
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     if ((c >= 32 && c <= 126) ||
40         (c >= 160)) {
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     while (n--)
56         c_write("\010 \010", 3);
57 }
58
59 #define CTRL(x) (x^'@')
60
61 void ldisc_send(char *buf, int len) {
62     /*
63      * Called with len=0 when the options change. We must inform
64      * the front end in case it needs to know.
65      */
66     if (len == 0) {
67         void ldisc_update(int echo, int edit);
68         ldisc_update(ECHOING, EDITING);
69     }
70     /*
71      * Either perform local editing, or just send characters.
72      */
73     if (EDITING) {
74         while (len--) {
75             char c;
76             c = *buf++;
77             switch (term_quotenext ? ' ' : c) {
78                 /*
79                  * ^h/^?: delete one char and output one BSB
80                  * ^w: delete, and output BSBs, to return to last
81                  * space/nonspace boundary
82                  * ^u: delete, and output BSBs, to return to BOL
83                  * ^c: Do a ^u then send a telnet IP
84                  * ^z: Do a ^u then send a telnet SUSP
85                  * ^\: Do a ^u then send a telnet ABORT
86                  * ^r: echo "^R\n" and redraw line
87                  * ^v: quote next char
88                  * ^d: if at BOL, end of file and close connection,
89                  * else send line and reset to BOL
90                  * ^m: send line-plus-\r\n and reset to BOL
91                  */
92               case CTRL('H'): case CTRL('?'):      /* backspace/delete */
93                 if (term_buflen > 0) {
94                     if (ECHOING)
95                         bsb(plen(term_buf[term_buflen-1]));
96                     term_buflen--;
97                 }
98                 break;
99               case CTRL('W'):                  /* delete word */
100                 while (term_buflen > 0) {
101                     if (ECHOING)
102                         bsb(plen(term_buf[term_buflen-1]));
103                     term_buflen--;
104                     if (term_buflen > 0 &&
105                         isspace(term_buf[term_buflen-1]) &&
106                         !isspace(term_buf[term_buflen]))
107                         break;
108                 }
109                 break;
110               case CTRL('U'):          /* delete line */
111               case CTRL('C'):          /* Send IP */
112               case CTRL('\\'):         /* Quit */
113               case CTRL('Z'):          /* Suspend */
114                 while (term_buflen > 0) {
115                     if (ECHOING)
116                         bsb(plen(term_buf[term_buflen-1]));
117                     term_buflen--;
118                 }
119                 back->special (TS_EL);
120                 if( c == CTRL('C') )  back->special (TS_IP);
121                 if( c == CTRL('Z') )  back->special (TS_SUSP);
122                 if( c == CTRL('\\') ) back->special (TS_ABORT);
123                 break;
124               case CTRL('R'):          /* redraw line */
125                 if (ECHOING) {
126                     int i;
127                     c_write("^R\r\n", 4);
128                     for (i = 0; i < term_buflen; i++)
129                         pwrite(term_buf[i]);
130                 }
131                 break;
132               case CTRL('V'):          /* quote next char */
133                 term_quotenext = TRUE;
134                 break;
135               case CTRL('D'):          /* logout or send */
136                 if (term_buflen == 0) {
137                     back->special (TS_EOF);
138                 } else {
139                     back->send(term_buf, term_buflen);
140                     term_buflen = 0;
141                 }
142                 break;
143               case CTRL('M'):          /* send with newline */
144                 if (term_buflen > 0)
145                     back->send(term_buf, term_buflen);
146                 if (cfg.protocol == PROT_RAW)
147                     back->send("\r\n", 2);
148                 else
149                     back->send("\r", 1);
150                 if (ECHOING)
151                     c_write("\r\n", 2);
152                 term_buflen = 0;
153                 break;
154               default:                     /* get to this label from ^V handler */
155                 if (term_buflen >= term_bufsiz) {
156                     term_bufsiz = term_buflen + 256;
157                     term_buf = saferealloc(term_buf, term_bufsiz);
158                 }
159                 term_buf[term_buflen++] = c;
160                 if (ECHOING)
161                     pwrite(c);
162                 term_quotenext = FALSE;
163                 break;
164             }
165         }
166     } else {
167         if( term_buflen != 0 )
168         {
169             back->send(term_buf, term_buflen);
170             while (term_buflen > 0) {
171                 bsb(plen(term_buf[term_buflen-1]));
172                 term_buflen--;
173             }
174         }
175         if (len > 0) {
176             if (ECHOING)
177                 c_write(buf, len);
178             back->send(buf, len);
179         }
180     }
181 }