freeline(line);
}
+#ifdef TERM_CC_DIAGS
/*
* Diagnostic function: verify that a termline has a correct
* combining character structure.
*
- * XXX-REMOVE-BEFORE-RELEASE: This is a performance-intensive
- * check. Although it's currently really useful for getting all the
- * bugs out of the new cc stuff, it will want to be absent when we
- * make a proper release.
+ * This is a performance-intensive check, so it's no longer enabled
+ * by default.
*/
static void cc_check(termline *line)
{
sfree(flags);
}
+#endif
/*
* Add a combining character to a character cell.
line->chars[newcc].chr = chr;
line->chars[col].cc_next = newcc - col;
- cc_check(line); /* XXX-REMOVE-BEFORE-RELEASE */
+#ifdef TERM_CC_DIAGS
+ cc_check(line);
+#endif
}
/*
line->chars[origcol].cc_next = 0;
- cc_check(line); /* XXX-REMOVE-BEFORE-RELEASE */
+#ifdef TERM_CC_DIAGS
+ cc_check(line);
+#endif
}
/*
add_cc(destline, x, src->chr);
}
- cc_check(destline); /* XXX-REMOVE-BEFORE-RELEASE */
+#ifdef TERM_CC_DIAGS
+ cc_check(destline);
+#endif
}
/*
/* Ensure the original cell doesn't have a cc list. */
src->cc_next = 0;
- cc_check(line); /* XXX-REMOVE-BEFORE-RELEASE */
+#ifdef TERM_CC_DIAGS
+ cc_check(line);
+#endif
}
/*
* Diagnostics: ensure that the compressed data really does
* decompress to the right thing.
*
- * XXX-REMOVE-BEFORE-RELEASE: This is a bit performance-heavy
- * to be leaving in production code.
+ * This is a bit performance-heavy for production code.
*/
+#ifdef TERM_CC_DIAGS
#ifndef CHECK_SB_COMPRESSION
{
int dused;
freeline(dcl);
}
#endif
+#endif /* TERM_CC_DIAGS */
/*
* Trim the allocated memory so we don't waste any, and return.
for (i = oldcols; i < cols; i++)
line->chars[i] = term->basic_erase_char;
- cc_check(line); /* XXX-REMOVE-BEFORE-RELEASE */
+#ifdef TERM_CC_DIAGS
+ cc_check(line);
+#endif
}
}
*/
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 */
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);
} else {
while (lines > 0) {
line = delpos234(term->screen, topline);
- cc_check(line); /* XXX-REMOVE-BEFORE-RELEASE */
+#ifdef TERM_CC_DIAGS
+ cc_check(line);
+#endif
if (sb && term->savelines > 0) {
int sblen = count234(term->scrollback);
/*
unget = -1;
chars = NULL; /* placate compiler warnings */
- while (nchars > 0 || bufchain_size(&term->inbuf) > 0) {
+ while (nchars > 0 || unget != -1 || bufchain_size(&term->inbuf) > 0) {
if (unget == -1) {
if (nchars == 0) {
void *ret;
*/
compatibility(ANSIMIN);
if (term->ldisc) {
- char abuf[256], *s, *d;
- int state = 0;
- for (s = term->cfg.answerback, d = abuf; *s; s++) {
- if (state) {
- if (*s >= 'a' && *s <= 'z')
- *d++ = (*s - ('a' - 1));
- else if ((*s >= '@' && *s <= '_') ||
- *s == '?' || (*s & 0x80))
- *d++ = ('@' ^ *s);
- else if (*s == '~')
- *d++ = '^';
- state = 0;
- } else if (*s == '^') {
- state = 1;
- } else
- *d++ = *s;
+ char abuf[lenof(term->cfg.answerback)], *s, *d;
+ for (s = term->cfg.answerback, d = abuf; *s;) {
+ char *n;
+ char c = ctrlparse(s, &n);
+ if (n) {
+ *d++ = c;
+ s = n;
+ } else {
+ *d++ = *s++;
+ }
}
lpage_send(term->ldisc, DEFAULT_CODEPAGE,
abuf, d - abuf, 0);
* 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);
term->curr_attr |= colour;
term->default_attr &= ~ATTR_FGMASK;
term->default_attr |= colour;
+ set_erase_char(term);
}
break;
case ANSI('G', '='): /* set normal background */
term->curr_attr |= colour;
term->default_attr &= ~ATTR_BGMASK;
term->default_attr |= colour;
+ set_erase_char(term);
}
break;
case ANSI('L', '='):
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)) {
while (1) {
int uc = ldata->chars[x].chr;
+ attr = ldata->chars[x].attr;
switch (uc & CSET_MASK) {
case CSET_LINEDRW:
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)
for (i = 0; i < sel_nl_sz; i++) {
wblen++;
*wbptr++ = sel_nl[i];
+ *attrptr++ = 0;
}
}
top.y++;
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);
}
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;
term->has_focus = has_focus;
term_schedule_cblink(term);
}
+
+/*
+ * Provide "auto" settings for remote tty modes, suitable for an
+ * application with a terminal window.
+ */
+char *term_get_ttymode(Terminal *term, const char *mode)
+{
+ char *val = NULL;
+ if (strcmp(mode, "ERASE") == 0) {
+ val = term->cfg.bksp_is_delete ? "^?" : "^H";
+ }
+ /* 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 */
+ }
+}