X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/putty/blobdiff_plain/7ecefce876e59128bd21eb9b3d3c9568968282e5..9a9a84cfc8941c3f5b5c23cbac1ebb2c33c26328:/terminal.c diff --git a/terminal.c b/terminal.c index 928f6ed8..906f2526 100644 --- a/terminal.c +++ b/terminal.c @@ -1384,7 +1384,7 @@ void term_reconfig(Terminal *term, Config *cfg) */ void term_clrsb(Terminal *term) { - termline *line; + unsigned char *line; term->disptop = 0; while ((line = delpos234(term->scrollback, 0)) != NULL) { sfree(line); /* this is compressed data, not a termline */ @@ -1535,6 +1535,11 @@ void term_size(Terminal *term, int newrows, int newcols, int newsavelines) newsavelines == term->savelines) return; /* nothing to do */ + /* Behave sensibly if we're given zero (or negative) rows/cols */ + + if (newrows < 1) newrows = 1; + if (newcols < 1) newcols = 1; + deselect(term); swap_screen(term, 0, FALSE, FALSE); @@ -2748,7 +2753,7 @@ static void term_out(Terminal *term) * Perform an actual beep if we're not overloaded. */ if (!term->cfg.bellovl || !term->beep_overloaded) { - beep(term->frontend, term->cfg.beep); + do_beep(term->frontend, term->cfg.beep); if (term->cfg.beep == BELL_VISUAL) { term_schedule_vbell(term, FALSE, 0); @@ -3899,6 +3904,7 @@ static void term_out(Terminal *term) term->curr_attr |= colour; term->default_attr &= ~ATTR_FGMASK; term->default_attr |= colour; + set_erase_char(term); } break; case ANSI('G', '='): /* set normal background */ @@ -3912,6 +3918,7 @@ static void term_out(Terminal *term) term->curr_attr |= colour; term->default_attr &= ~ATTR_BGMASK; term->default_attr |= colour; + set_erase_char(term); } break; case ANSI('L', '='): @@ -5040,10 +5047,15 @@ static void clipme(Terminal *term, pos top, pos bottom, int rect, int desel) int old_top_x; int wblen = 0; /* workbuf len */ int buflen; /* amount of memory allocated to workbuf */ + int *attrbuf; /* Attribute buffer */ + int *attrptr; + int attr; buflen = 5120; /* Default size */ workbuf = snewn(buflen, wchar_t); + attrbuf = buflen ? snewn(buflen, int) : 0; wbptr = workbuf; /* start filling here */ + attrptr = attrbuf; old_top_x = top.x; /* needed for rect==1 */ while (poslt(top, bottom)) { @@ -5106,6 +5118,7 @@ static void clipme(Terminal *term, pos top, pos bottom, int rect, int desel) while (1) { int uc = ldata->chars[x].chr; + attr = ldata->chars[x].attr; switch (uc & CSET_MASK) { case CSET_LINEDRW: @@ -5163,9 +5176,12 @@ static void clipme(Terminal *term, pos top, pos bottom, int rect, int desel) buflen += 100; workbuf = sresize(workbuf, buflen, wchar_t); wbptr = workbuf + wblen; + attrbuf = sresize(attrbuf, buflen, int); + attrptr = attrbuf + wblen; } wblen++; *wbptr++ = *p; + *attrptr++ = attr; } if (ldata->chars[x].cc_next) @@ -5180,6 +5196,7 @@ static void clipme(Terminal *term, pos top, pos bottom, int rect, int desel) for (i = 0; i < sel_nl_sz; i++) { wblen++; *wbptr++ = sel_nl[i]; + *attrptr++ = 0; } } top.y++; @@ -5191,7 +5208,7 @@ static void clipme(Terminal *term, pos top, pos bottom, int rect, int desel) wblen++; *wbptr++ = 0; #endif - write_clip(term->frontend, workbuf, wblen, desel); /* transfer to clipbd */ + write_clip(term->frontend, workbuf, attrbuf, wblen, desel); /* transfer to clipbd */ if (buflen > 0) /* indicates we allocated this buffer */ sfree(workbuf); } @@ -6255,6 +6272,24 @@ int term_data(Terminal *term, int is_stderr, const char *data, int len) return 0; } +/* + * Write untrusted data to the terminal. + * The only control character that should be honoured is \n (which + * will behave as a CRLF). + */ +int term_data_untrusted(Terminal *term, const char *data, int len) +{ + int i; + /* FIXME: more sophisticated checking? */ + for (i = 0; i < len; i++) { + if (data[i] == '\n') + term_data(term, 1, "\r\n", 2); + else if (data[i] & 0x60) + term_data(term, 1, data + i, 1); + } + return 0; /* assumes that term_data() always returns 0 */ +} + void term_provide_logctx(Terminal *term, void *logctx) { term->logctx = logctx; @@ -6279,3 +6314,130 @@ char *term_get_ttymode(Terminal *term, const char *mode) /* FIXME: perhaps we should set ONLCR based on cfg.lfhascr as well? */ return dupstr(val); } + +struct term_userpass_state { + size_t curr_prompt; + int done_prompt; /* printed out prompt yet? */ + size_t pos; /* cursor position */ +}; + +/* + * Process some terminal data in the course of username/password + * input. + */ +int term_get_userpass_input(Terminal *term, prompts_t *p, + unsigned char *in, int inlen) +{ + struct term_userpass_state *s = (struct term_userpass_state *)p->data; + if (!s) { + /* + * First call. Set some stuff up. + */ + p->data = s = snew(struct term_userpass_state); + s->curr_prompt = 0; + s->done_prompt = 0; + /* We only print the `name' caption if we have to... */ + if (p->name_reqd && p->name) { + size_t l = strlen(p->name); + term_data_untrusted(term, p->name, l); + if (p->name[l-1] != '\n') + term_data_untrusted(term, "\n", 1); + } + /* ...but we always print any `instruction'. */ + if (p->instruction) { + size_t l = strlen(p->instruction); + term_data_untrusted(term, p->instruction, l); + if (p->instruction[l-1] != '\n') + term_data_untrusted(term, "\n", 1); + } + /* + * Zero all the results, in case we abort half-way through. + */ + { + int i; + for (i = 0; i < p->n_prompts; i++) + memset(p->prompts[i]->result, 0, p->prompts[i]->result_len); + } + } + + while (s->curr_prompt < p->n_prompts) { + + prompt_t *pr = p->prompts[s->curr_prompt]; + int finished_prompt = 0; + + if (!s->done_prompt) { + term_data_untrusted(term, pr->prompt, strlen(pr->prompt)); + s->done_prompt = 1; + s->pos = 0; + } + + /* Breaking out here ensures that the prompt is printed even + * if we're now waiting for user data. */ + if (!in || !inlen) break; + + /* FIXME: should we be using local-line-editing code instead? */ + while (!finished_prompt && inlen) { + char c = *in++; + inlen--; + switch (c) { + case 10: + case 13: + term_data(term, 0, "\r\n", 2); + pr->result[s->pos] = '\0'; + pr->result[pr->result_len - 1] = '\0'; + /* go to next prompt, if any */ + s->curr_prompt++; + s->done_prompt = 0; + finished_prompt = 1; /* break out */ + break; + case 8: + case 127: + if (s->pos > 0) { + if (pr->echo) + term_data(term, 0, "\b \b", 3); + s->pos--; + } + break; + case 21: + case 27: + while (s->pos > 0) { + if (pr->echo) + term_data(term, 0, "\b \b", 3); + s->pos--; + } + break; + case 3: + case 4: + /* Immediate abort. */ + term_data(term, 0, "\r\n", 2); + sfree(s); + p->data = NULL; + return 0; /* user abort */ + default: + /* + * This simplistic check for printability is disabled + * when we're doing password input, because some people + * have control characters in their passwords. + */ + if ((!pr->echo || + (c >= ' ' && c <= '~') || + ((unsigned char) c >= 160)) + && s->pos < pr->result_len - 1) { + pr->result[s->pos++] = c; + if (pr->echo) + term_data(term, 0, &c, 1); + } + break; + } + } + + } + + if (s->curr_prompt < p->n_prompts) { + return -1; /* more data required */ + } else { + sfree(s); + p->data = NULL; + return +1; /* all done */ + } +}