diff --git a/edit.c b/edit.c index 0d868e3..8826027 100644 --- a/edit.c +++ b/edit.c @@ -28,7 +28,7 @@ #ifndef MKSH_NO_CMDLINE_EDITING -__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.301 2016/07/26 20:43:14 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.302 2016/07/26 21:37:24 tg Exp $"); /* * in later versions we might use libtermcap for this, but since external @@ -961,7 +961,6 @@ static size_t x_fword(bool); static void x_goto(char *); static char *x_bs0(char *, char *) MKSH_A_PURE; static void x_bs3(char **); -static int x_size_str(char *); static int x_size2(char *, char **); static void x_zots(char *); static void x_zotc3(char **); @@ -986,6 +985,7 @@ static void x_e_puts(const char *); static int x_fold_case(int); #endif static char *x_lastcp(void); +static void x_lastpos(void); static void do_complete(int, Comp_type); static size_t x_nb2nc(size_t) MKSH_A_PURE; @@ -1121,6 +1121,7 @@ static struct x_defbindings const x_defbindings[] = { #endif #ifndef MKSH_SMALL /* more non-standard ones */ + { XFUNC_eval_region, 1, CTRL('E') }, { XFUNC_edit_line, 2, 'e' } #endif }; @@ -1380,15 +1381,11 @@ x_ins(const char *s) x_lastcp(); x_adj_ok = tobool(xcp >= xlp); x_zots(cp); - /* has x_adjust() been called? */ - if (adj == x_adj_done) { - /* no */ - cp = xlp; - while (cp > xcp) - x_bs3(&cp); - } if (xlp == xep - 1) x_redraw('\r'); + else if (adj == x_adj_done) + /* x_adjust() has not been called */ + x_lastpos(); x_adj_ok = true; return (0); } @@ -1495,10 +1492,7 @@ x_delete(size_t nc, bool push) /*x_goto(xcp);*/ x_adj_ok = true; xlp_valid = false; - cp = x_lastcp(); - while (cp > xcp) - x_bs3(&cp); - + x_lastpos(); x_modified(); return; } @@ -1617,15 +1611,6 @@ x_bs3(char **p) x_e_putc2('\b'); } -static int -x_size_str(char *cp) -{ - int size = 0; - while (*cp) - size += x_size2(cp, &cp); - return (size); -} - static int x_size2(char *cp, char **dcp) { @@ -2066,67 +2051,70 @@ x_cls(int c MKSH_A_UNUSED) return (KSTD); } -/* output cr (if ≠ 0), then redraw the line, clearing to EOL if needed */ +/* + * clear line from x_col (current cursor position) to xx_cols - 2, + * then output lastch, then go back to x_col; if lastch is space, + * clear with termcap instead of spaces, or not if line_was_cleared; + * lastch MUST be an ASCII character with wcwidth(lastch) == 1 + */ +static void +x_clrtoeol(int lastch, bool line_was_cleared) +{ + int col; + + if (lastch == ' ' && !line_was_cleared && x_term_mode == 1) { + shf_puts("\033[K", shl_out); + line_was_cleared = true; + } + if (lastch == ' ' && line_was_cleared) + return; + + col = x_col; + while (col < (xx_cols - 2)) { + x_putc(' '); + ++col; + } + x_putc(lastch); + ++col; + while (col > x_col) { + x_putc('\b'); + --col; + } +} + +/* output the prompt, assuming a line has just been started */ +static void +x_pprompt(void) +{ + if (prompt_trunc != -1) + pprompt(prompt, prompt_trunc); + x_col = pwidth; +} + +/* output CR, then redraw the line, clearing to EOL if needed (cr ≠ 0, LF) */ static void x_redraw(int cr) { - int i, j; - char *cp; - int limit = xx_cols; /*XXX tbm in the next commit */ + int lch; x_adj_ok = false; + /* clear the line */ x_e_putc2(cr ? cr : '\r'); x_flush(); - if (xbp == xbuf) { - if (prompt_trunc != -1) - pprompt(prompt, prompt_trunc); - x_col = pwidth; - } + /* display the prompt */ + if (xbp == xbuf) + x_pprompt(); x_displen = xx_cols - 2 - x_col; + /* display the line content */ xlp_valid = false; x_zots(xbp); - if (limit >= xx_cols || xbp != xbuf || xep > xlp) - limit = xx_cols; - if (limit == xx_cols && x_term_mode == 1 && xbp == xbuf && xep <= xlp) - shf_puts("\033[K", shl_out); - else if (limit >= 0) { - if (xep > xlp) - /* we fill the line */ - i = 0; - else { - char *cpl = xbp; - - i = limit; - while (cpl < xlp) - i -= x_size2(cpl, &cpl); - } - - j = 0; - while ((j < i) || (x_col < (xx_cols - 2))) { - if (!(x_col < (xx_cols - 2))) - break; - x_e_putc2(' '); - j++; - } - i = ' '; - if (xep > xlp) { - /* more off screen */ - if (xbp > xbuf) - i = '*'; - else - i = '>'; - } else if (xbp > xbuf) - i = '<'; - x_e_putc2(i); - j++; - while (j--) - x_e_putc2('\b'); - } - cp = xlp; - while (cp > xcp) - x_bs3(&cp); + /* check whether there is more off-screen */ + lch = xep > xlp ? (xbp > xbuf ? '*' : '>') : (xbp > xbuf) ? '<' : ' '; + /* clear the rest of the line */ + x_clrtoeol(lch, !cr || cr == '\n'); + /* go back to actual cursor position */ + x_lastpos(); x_adj_ok = true; - return; } static int @@ -3240,22 +3228,10 @@ x_fold_case(int c) * NAME: * x_lastcp - last visible char * - * SYNOPSIS: - * x_lastcp() - * * DESCRIPTION: * This function returns a pointer to that char in the * edit buffer that will be the last displayed on the - * screen. The sequence: - * - * cp = x_lastcp(); - * while (cp > xcp) - * x_bs3(&cp); - * - * Will position the cursor correctly on the screen. - * - * RETURN VALUE: - * cp or NULL + * screen. */ static char * x_lastcp(void) @@ -3277,6 +3253,16 @@ x_lastcp(void) return (xlp); } +/* correctly position the cursor on the screen from end of visible area */ +static void +x_lastpos(void) +{ + char *cp = x_lastcp(); + + while (cp > xcp) + x_bs3(&cp); +} + static void x_mode(bool onoff) { @@ -5087,9 +5073,7 @@ redraw_line(bool newl) x_putc('\r'); x_putc('\n'); } - if (prompt_trunc != -1) - pprompt(prompt, prompt_trunc); - x_col = pwidth; + x_pprompt(); morec = ' '; } @@ -5244,9 +5228,7 @@ ed_mov_opt(int col, char *wb) if (col < x_col) { if (col + 1 < x_col - col) { x_putc('\r'); - if (prompt_trunc != -1) - pprompt(prompt, prompt_trunc); - x_col = pwidth; + x_pprompt(); while (x_col++ < col) x_putcf(*wb++); } else { @@ -5539,4 +5521,79 @@ x_initterm(const char *termtype) if (!strcmp(termtype, "screen") || !strncmp(termtype, "screen-", 7)) x_term_mode = 1; } + +#ifndef MKSH_SMALL +static char * +x_eval_region_helper(const char *cmd, size_t len) +{ + char * volatile cp; + newenv(E_ERRH); + + if (!kshsetjmp(e->jbuf)) { + char *wds = alloc(len + 3, ATEMP); + + wds[0] = FUNSUB; + memcpy(wds + 1, cmd, len); + wds[len + 1] = '\0'; + wds[len + 2] = EOS; + + cp = evalstr(wds, DOSCALAR); + strdupx(cp, cp, AEDIT); + } else + cp = NULL; + quitenv(NULL); + return (cp); +} + +static int +x_eval_region(int c MKSH_A_UNUSED) +{ + char *evbeg, *evend, *cp; + size_t newlen; + /* only for LINE overflow checking */ + size_t restlen; + + if (xmp == NULL) { + evbeg = xbuf; + evend = xep; + } else if (xmp < xcp) { + evbeg = xmp; + evend = xcp; + } else { + evbeg = xcp; + evend = xmp; + } + + x_e_putc2('\r'); + x_clrtoeol(' ', false); + x_flush(); + x_mode(false); + cp = x_eval_region_helper(evbeg, evend - evbeg); + x_mode(true); + + if (cp == NULL) { + /* command cannot be parsed */ + x_eval_region_err: + x_e_putc2(7); + x_redraw('\r'); + return (KSTD); + } + + newlen = strlen(cp); + restlen = xep - evend; + /* check for LINE overflow, until this is dynamically allocated */ + if (evbeg + newlen + restlen >= xend) + goto x_eval_region_err; + + xmp = evbeg; + xcp = evbeg + newlen; + xep = xcp + restlen; + memmove(xcp, evend, restlen + /* NUL */ 1); + memcpy(xmp, cp, newlen); + afree(cp, AEDIT); + x_adjust(); + x_modified(); + return (KSTD); +} +#endif /* !MKSH_SMALL */ #endif /* !MKSH_NO_CMDLINE_EDITING */ diff --git a/emacsfn.h b/emacsfn.h index ba14d09..35ecd90 100644 --- a/emacsfn.h +++ b/emacsfn.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2009, 2010, 2015 + * Copyright (c) 2009, 2010, 2015, 2016 * mirabilos * * Provided that these terms and disclaimer and all copyright notices @@ -19,7 +19,7 @@ */ #if defined(EMACSFN_DEFNS) -__RCSID("$MirOS: src/bin/mksh/emacsfn.h,v 1.7 2015/12/12 21:08:44 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/emacsfn.h,v 1.8 2016/07/26 21:37:25 tg Exp $"); #define FN(cname,sname,flags) static int x_##cname(int); #elif defined(EMACSFN_ENUMS) #define FN(cname,sname,flags) XFUNC_##cname, @@ -54,6 +54,7 @@ FN(end_of_text, "eot", 0) FN(enumerate, "list", 0) FN(eot_del, "eot-or-delete", XF_ARG) FN(error, "error", 0) +FN(eval_region, "evaluate-region", 0) FN(expand, "expand-file", 0) #ifndef MKSH_SMALL FN(fold_capitalise, "capitalize-word", XF_ARG) diff --git a/mksh.1 b/mksh.1 index 72df206..02e60d4 100644 --- a/mksh.1 +++ b/mksh.1 @@ -1,4 +1,4 @@ -.\" $MirOS: src/bin/mksh/mksh.1,v 1.404 2016/07/25 21:05:23 tg Exp $ +.\" $MirOS: src/bin/mksh/mksh.1,v 1.405 2016/07/26 21:37:26 tg Exp $ .\" $OpenBSD: ksh.1,v 1.160 2015/07/04 13:27:04 feinerer Exp $ .\"- .\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, @@ -76,7 +76,7 @@ .\" with -mandoc, it might implement .Mx itself, but we want to .\" use our own definition. And .Dd must come *first*, always. .\" -.Dd $Mdocdate: July 25 2016 $ +.Dd $Mdocdate: July 26 2016 $ .\" .\" Check which macro package we use, and do other -mdoc setup. .\" @@ -5618,6 +5618,11 @@ otherwise, .Ic delete\-char\-forward . .It error: (not bound) Error (ring the bell). +.It evaluate\-region: \*(ha[\*(haE +Evaluates the text between the mark and the cursor position +.Pq the entire line if no mark is set +as function substitution (if it cannot be parsed, the editing state is +unchanged and the bell is rung to signal an error); $? is updated accordingly. .It exchange\-point\-and\-mark: \*(haX\*(haX Places the cursor where the mark is and sets the mark to where the cursor was. .It expand\-file: \*(ha[*