ldisc.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  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. #include <stdio.h>
  8. #include <ctype.h>
  9. #include <assert.h>
  10. #include "putty.h"
  11. #include "terminal.h"
  12. #include "ldisc.h"
  13. #define ECHOING (ldisc->localecho == FORCE_ON || \
  14. (ldisc->localecho == AUTO && \
  15. (backend_ldisc_option_state(ldisc->backend, LD_ECHO))))
  16. #define EDITING (ldisc->localedit == FORCE_ON || \
  17. (ldisc->localedit == AUTO && \
  18. (backend_ldisc_option_state(ldisc->backend, LD_EDIT))))
  19. static void c_write(Ldisc *ldisc, const void *buf, int len)
  20. {
  21. seat_stdout(ldisc->seat, buf, len);
  22. }
  23. static int plen(Ldisc *ldisc, unsigned char c)
  24. {
  25. if ((c >= 32 && c <= 126) || (c >= 160 && !in_utf(ldisc->term)))
  26. return 1;
  27. else if (c < 128)
  28. return 2; /* ^x for some x */
  29. else if (in_utf(ldisc->term) && c >= 0xC0)
  30. return 1; /* UTF-8 introducer character
  31. * (FIXME: combining / wide chars) */
  32. else if (in_utf(ldisc->term) && c >= 0x80 && c < 0xC0)
  33. return 0; /* UTF-8 followup character */
  34. else
  35. return 4; /* <XY> hex representation */
  36. }
  37. static void pwrite(Ldisc *ldisc, unsigned char c)
  38. {
  39. if ((c >= 32 && c <= 126) ||
  40. (!in_utf(ldisc->term) && c >= 0xA0) ||
  41. (in_utf(ldisc->term) && c >= 0x80)) {
  42. c_write(ldisc, &c, 1);
  43. } else if (c < 128) {
  44. char cc[2];
  45. cc[1] = (c == 127 ? '?' : c + 0x40);
  46. cc[0] = '^';
  47. c_write(ldisc, cc, 2);
  48. } else {
  49. char cc[5];
  50. sprintf(cc, "<%02X>", c);
  51. c_write(ldisc, cc, 4);
  52. }
  53. }
  54. static bool char_start(Ldisc *ldisc, unsigned char c)
  55. {
  56. if (in_utf(ldisc->term))
  57. return (c < 0x80 || c >= 0xC0);
  58. else
  59. return true;
  60. }
  61. static void bsb(Ldisc *ldisc, int n)
  62. {
  63. while (n--)
  64. c_write(ldisc, "\010 \010", 3);
  65. }
  66. #define CTRL(x) (x^'@')
  67. #define KCTRL(x) ((x^'@') | 0x100)
  68. Ldisc *ldisc_create(Conf *conf, Terminal *term, Backend *backend, Seat *seat)
  69. {
  70. Ldisc *ldisc = snew(Ldisc);
  71. ldisc->buf = NULL;
  72. ldisc->buflen = 0;
  73. ldisc->bufsiz = 0;
  74. ldisc->quotenext = false;
  75. ldisc->backend = backend;
  76. ldisc->term = term;
  77. ldisc->seat = seat;
  78. ldisc_configure(ldisc, conf);
  79. /* Link ourselves into the backend and the terminal */
  80. if (term)
  81. term->ldisc = ldisc;
  82. if (backend)
  83. backend_provide_ldisc(backend, ldisc);
  84. return ldisc;
  85. }
  86. void ldisc_configure(Ldisc *ldisc, Conf *conf)
  87. {
  88. ldisc->telnet_keyboard = conf_get_bool(conf, CONF_telnet_keyboard);
  89. ldisc->telnet_newline = conf_get_bool(conf, CONF_telnet_newline);
  90. ldisc->protocol = conf_get_int(conf, CONF_protocol);
  91. ldisc->localecho = conf_get_int(conf, CONF_localecho);
  92. ldisc->localedit = conf_get_int(conf, CONF_localedit);
  93. }
  94. void ldisc_free(Ldisc *ldisc)
  95. {
  96. if (ldisc->term)
  97. ldisc->term->ldisc = NULL;
  98. if (ldisc->backend)
  99. backend_provide_ldisc(ldisc->backend, NULL);
  100. if (ldisc->buf)
  101. sfree(ldisc->buf);
  102. sfree(ldisc);
  103. }
  104. void ldisc_echoedit_update(Ldisc *ldisc)
  105. {
  106. seat_echoedit_update(ldisc->seat, ECHOING, EDITING);
  107. }
  108. void ldisc_send(Ldisc *ldisc, const void *vbuf, int len, bool interactive)
  109. {
  110. const char *buf = (const char *)vbuf;
  111. int keyflag = 0;
  112. assert(ldisc->term);
  113. if (interactive) {
  114. /*
  115. * Interrupt a paste from the clipboard, if one was in
  116. * progress when the user pressed a key. This is easier than
  117. * buffering the current piece of data and saving it until the
  118. * terminal has finished pasting, and has the potential side
  119. * benefit of permitting a user to cancel an accidental huge
  120. * paste.
  121. */
  122. term_nopaste(ldisc->term);
  123. }
  124. /*
  125. * Less than zero means null terminated special string.
  126. */
  127. if (len < 0) {
  128. len = strlen(buf);
  129. keyflag = KCTRL('@');
  130. }
  131. /*
  132. * Either perform local editing, or just send characters.
  133. */
  134. if (EDITING) {
  135. while (len--) {
  136. int c;
  137. c = (unsigned char)(*buf++) + keyflag;
  138. if (!interactive && c == '\r')
  139. c += KCTRL('@');
  140. switch (ldisc->quotenext ? ' ' : c) {
  141. /*
  142. * ^h/^?: delete, and output BSBs, to return to
  143. * last character boundary (in UTF-8 mode this may
  144. * be more than one byte)
  145. * ^w: delete, and output BSBs, to return to last
  146. * space/nonspace boundary
  147. * ^u: delete, and output BSBs, to return to BOL
  148. * ^c: Do a ^u then send a telnet IP
  149. * ^z: Do a ^u then send a telnet SUSP
  150. * ^\: Do a ^u then send a telnet ABORT
  151. * ^r: echo "^R\n" and redraw line
  152. * ^v: quote next char
  153. * ^d: if at BOL, end of file and close connection,
  154. * else send line and reset to BOL
  155. * ^m: send line-plus-\r\n and reset to BOL
  156. */
  157. case KCTRL('H'):
  158. case KCTRL('?'): /* backspace/delete */
  159. if (ldisc->buflen > 0) {
  160. do {
  161. if (ECHOING)
  162. bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));
  163. ldisc->buflen--;
  164. } while (!char_start(ldisc, ldisc->buf[ldisc->buflen]));
  165. }
  166. break;
  167. case CTRL('W'): /* delete word */
  168. while (ldisc->buflen > 0) {
  169. if (ECHOING)
  170. bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));
  171. ldisc->buflen--;
  172. if (ldisc->buflen > 0 &&
  173. isspace((unsigned char)ldisc->buf[ldisc->buflen-1]) &&
  174. !isspace((unsigned char)ldisc->buf[ldisc->buflen]))
  175. break;
  176. }
  177. break;
  178. case CTRL('U'): /* delete line */
  179. case CTRL('C'): /* Send IP */
  180. case CTRL('\\'): /* Quit */
  181. case CTRL('Z'): /* Suspend */
  182. while (ldisc->buflen > 0) {
  183. if (ECHOING)
  184. bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));
  185. ldisc->buflen--;
  186. }
  187. backend_special(ldisc->backend, SS_EL, 0);
  188. /*
  189. * We don't send IP, SUSP or ABORT if the user has
  190. * configured telnet specials off! This breaks
  191. * talkers otherwise.
  192. */
  193. if (!ldisc->telnet_keyboard)
  194. goto default_case;
  195. if (c == CTRL('C'))
  196. backend_special(ldisc->backend, SS_IP, 0);
  197. if (c == CTRL('Z'))
  198. backend_special(ldisc->backend, SS_SUSP, 0);
  199. if (c == CTRL('\\'))
  200. backend_special(ldisc->backend, SS_ABORT, 0);
  201. break;
  202. case CTRL('R'): /* redraw line */
  203. if (ECHOING) {
  204. int i;
  205. c_write(ldisc, "^R\r\n", 4);
  206. for (i = 0; i < ldisc->buflen; i++)
  207. pwrite(ldisc, ldisc->buf[i]);
  208. }
  209. break;
  210. case CTRL('V'): /* quote next char */
  211. ldisc->quotenext = true;
  212. break;
  213. case CTRL('D'): /* logout or send */
  214. if (ldisc->buflen == 0) {
  215. backend_special(ldisc->backend, SS_EOF, 0);
  216. } else {
  217. backend_send(ldisc->backend, ldisc->buf, ldisc->buflen);
  218. ldisc->buflen = 0;
  219. }
  220. break;
  221. /*
  222. * This particularly hideous bit of code from RDB
  223. * allows ordinary ^M^J to do the same thing as
  224. * magic-^M when in Raw protocol. The line `case
  225. * KCTRL('M'):' is _inside_ the if block. Thus:
  226. *
  227. * - receiving regular ^M goes straight to the
  228. * default clause and inserts as a literal ^M.
  229. * - receiving regular ^J _not_ directly after a
  230. * literal ^M (or not in Raw protocol) fails the
  231. * if condition, leaps to the bottom of the if,
  232. * and falls through into the default clause
  233. * again.
  234. * - receiving regular ^J just after a literal ^M
  235. * in Raw protocol passes the if condition,
  236. * deletes the literal ^M, and falls through
  237. * into the magic-^M code
  238. * - receiving a magic-^M empties the line buffer,
  239. * signals end-of-line in one of the various
  240. * entertaining ways, and _doesn't_ fall out of
  241. * the bottom of the if and through to the
  242. * default clause because of the break.
  243. */
  244. case CTRL('J'):
  245. if (ldisc->protocol == PROT_RAW &&
  246. ldisc->buflen > 0 && ldisc->buf[ldisc->buflen - 1] == '\r') {
  247. if (ECHOING)
  248. bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));
  249. ldisc->buflen--;
  250. /* FALLTHROUGH */
  251. case KCTRL('M'): /* send with newline */
  252. if (ldisc->buflen > 0)
  253. backend_send(ldisc->backend,
  254. ldisc->buf, ldisc->buflen);
  255. if (ldisc->protocol == PROT_RAW)
  256. backend_send(ldisc->backend, "\r\n", 2);
  257. else if (ldisc->protocol == PROT_TELNET && ldisc->telnet_newline)
  258. backend_special(ldisc->backend, SS_EOL, 0);
  259. else
  260. backend_send(ldisc->backend, "\r", 1);
  261. if (ECHOING)
  262. c_write(ldisc, "\r\n", 2);
  263. ldisc->buflen = 0;
  264. break;
  265. }
  266. /* FALLTHROUGH */
  267. default: /* get to this label from ^V handler */
  268. default_case:
  269. sgrowarray(ldisc->buf, ldisc->bufsiz, ldisc->buflen);
  270. ldisc->buf[ldisc->buflen++] = c;
  271. if (ECHOING)
  272. pwrite(ldisc, (unsigned char) c);
  273. ldisc->quotenext = false;
  274. break;
  275. }
  276. }
  277. } else {
  278. if (ldisc->buflen != 0) {
  279. backend_send(ldisc->backend, ldisc->buf, ldisc->buflen);
  280. while (ldisc->buflen > 0) {
  281. bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));
  282. ldisc->buflen--;
  283. }
  284. }
  285. if (len > 0) {
  286. if (ECHOING)
  287. c_write(ldisc, buf, len);
  288. if (keyflag && ldisc->protocol == PROT_TELNET && len == 1) {
  289. switch (buf[0]) {
  290. case CTRL('M'):
  291. if (ldisc->protocol == PROT_TELNET && ldisc->telnet_newline)
  292. backend_special(ldisc->backend, SS_EOL, 0);
  293. else
  294. backend_send(ldisc->backend, "\r", 1);
  295. break;
  296. case CTRL('?'):
  297. case CTRL('H'):
  298. if (ldisc->telnet_keyboard) {
  299. backend_special(ldisc->backend, SS_EC, 0);
  300. break;
  301. }
  302. case CTRL('C'):
  303. if (ldisc->telnet_keyboard) {
  304. backend_special(ldisc->backend, SS_IP, 0);
  305. break;
  306. }
  307. case CTRL('Z'):
  308. if (ldisc->telnet_keyboard) {
  309. backend_special(ldisc->backend, SS_SUSP, 0);
  310. break;
  311. }
  312. default:
  313. backend_send(ldisc->backend, buf, len);
  314. break;
  315. }
  316. } else
  317. backend_send(ldisc->backend, buf, len);
  318. }
  319. }
  320. }