/* * v_key -- * Return the next event. */ static gcret_t v_key( SCR *sp, int command_events, EVENT *evp, u_int32_t ec_flags) { u_int32_t quote; for (quote = 0;;) { if (v_event_get(sp, evp, 0, ec_flags | quote)) return (GC_FATAL); quote = 0; switch (evp->e_event) { case E_CHARACTER: /* * !!! * Historically, ^V was ignored in the command stream, * although it had a useful side-effect of interrupting * mappings. Adding a quoting bit to the call probably * extends historic practice, but it feels right. */ if (evp->e_value == K_VLNEXT) { quote = EC_QUOTED; break; } return (GC_OK); case E_ERR: case E_EOF: return (GC_FATAL); case E_INTERRUPT: /* * !!! * Historically, vi beeped on command level interrupts. * * Historically, vi exited to ex mode if no file was * named on the command line, and two interrupts were * generated in a row. (Just figured you might want * to know that.) */ (void)sp->gp->scr_bell(sp); return (GC_INTERRUPT); case E_REPAINT: if (vs_repaint(sp, evp)) return (GC_FATAL); break; case E_WRESIZE: return (GC_ERR); /* FALLTHROUGH */ default: v_event_err(sp, evp); return (GC_ERR); } } /* NOTREACHED */ }
/* * ex_txt -- * Get lines from the terminal for ex. * * PUBLIC: int ex_txt(SCR *, TEXTH *, ARG_CHAR_T, u_int32_t); */ int ex_txt(SCR *sp, TEXTH *tiqh, ARG_CHAR_T prompt, u_int32_t flags) { EVENT ev; GS *gp; TEXT ait, *ntp, *tp; carat_t carat_st; size_t cnt; int rval; int nochange; rval = 0; /* * Get a TEXT structure with some initial buffer space, reusing the * last one if it's big enough. (All TEXT bookkeeping fields default * to 0 -- text_init() handles this.) */ if (!TAILQ_EMPTY(tiqh)) { tp = TAILQ_FIRST(tiqh); if (TAILQ_NEXT(tp, q) != NULL || tp->lb_len < 32) { text_lfree(tiqh); goto newtp; } tp->len = 0; } else { newtp: if ((tp = text_init(sp, NULL, 0, 32)) == NULL) goto err; TAILQ_INSERT_HEAD(tiqh, tp, q); } /* Set the starting line number. */ tp->lno = sp->lno + 1; /* * If it's a terminal, set up autoindent, put out the prompt, and * set it up so we know we were suspended. Otherwise, turn off * the autoindent flag, as that requires less special casing below. * * XXX * Historic practice is that ^Z suspended command mode (but, because * it ran in cooked mode, it was unaffected by the autowrite option.) * On restart, any "current" input was discarded, whether in insert * mode or not, and ex was in command mode. This code matches historic * practice, but not 'cause it's easier. */ gp = sp->gp; if (F_ISSET(gp, G_SCRIPTED)) LF_CLR(TXT_AUTOINDENT); else { if (LF_ISSET(TXT_AUTOINDENT)) { LF_SET(TXT_EOFCHAR); if (v_txt_auto(sp, sp->lno, NULL, 0, tp)) goto err; } txt_prompt(sp, tp, prompt, flags); } for (carat_st = C_NOTSET, nochange = 0;;) { if (v_event_get(sp, &ev, 0, 0)) goto err; /* Deal with all non-character events. */ switch (ev.e_event) { case E_CHARACTER: break; case E_ERR: goto err; case E_REPAINT: case E_WRESIZE: continue; case E_EOF: rval = 1; /* FALLTHROUGH */ case E_INTERRUPT: /* * Handle EOF/SIGINT events by discarding partially * entered text and returning. EOF returns failure, * E_INTERRUPT returns success. */ goto notlast; default: v_event_err(sp, &ev); goto notlast; } /* * Deal with character events. * * Check to see if the character fits into the input buffer. * (Use tp->len, ignore overwrite and non-printable chars.) */ BINC_GOTOW(sp, tp->lb, tp->lb_len, tp->len + 1); switch (ev.e_value) { case K_CR: /* * !!! * Historically, <carriage-return>'s in the command * weren't special, so the ex parser would return an * unknown command error message. However, if they * terminated the command if they were in a map. I'm * pretty sure this still isn't right, but it handles * what I've seen so far. */ if (!F_ISSET(&ev.e_ch, CH_MAPPED)) goto ins_ch; /* FALLTHROUGH */ case K_NL: /* * '\' can escape <carriage-return>/<newline>. We * don't discard the backslash because we need it * to get the <newline> through the ex parser. */ if (LF_ISSET(TXT_BACKSLASH) && tp->len != 0 && tp->lb[tp->len - 1] == '\\') goto ins_ch; /* * CR returns from the ex command line. * * XXX * Terminate with a nul, needed by filter. */ if (LF_ISSET(TXT_CR)) { tp->lb[tp->len] = '\0'; goto done; } /* * '.' may terminate text input mode; free the current * TEXT. */ if (LF_ISSET(TXT_DOTTERM) && tp->len == tp->ai + 1 && tp->lb[tp->len - 1] == '.') { notlast: TAILQ_REMOVE(tiqh, tp, q); text_free(tp); goto done; } /* Set up bookkeeping for the new line. */ if ((ntp = text_init(sp, NULL, 0, 32)) == NULL) goto err; ntp->lno = tp->lno + 1; /* * Reset the autoindent line value. 0^D keeps the ai * line from changing, ^D changes the level, even if * there were no characters in the old line. Note, if * using the current tp structure, use the cursor as * the length, the autoindent characters may have been * erased. */ if (LF_ISSET(TXT_AUTOINDENT)) { if (nochange) { nochange = 0; if (v_txt_auto(sp, OOBLNO, &ait, ait.ai, ntp)) goto err; free(ait.lb); } else if (v_txt_auto(sp, OOBLNO, tp, tp->len, ntp)) goto err; carat_st = C_NOTSET; } txt_prompt(sp, ntp, prompt, flags); /* * Swap old and new TEXT's, and insert the new TEXT * into the queue. */ tp = ntp; TAILQ_INSERT_TAIL(tiqh, tp, q); break; case K_CARAT: /* Delete autoindent chars. */ if (tp->len <= tp->ai && LF_ISSET(TXT_AUTOINDENT)) carat_st = C_CARATSET; goto ins_ch; case K_ZERO: /* Delete autoindent chars. */ if (tp->len <= tp->ai && LF_ISSET(TXT_AUTOINDENT)) carat_st = C_ZEROSET; goto ins_ch; case K_CNTRLD: /* Delete autoindent char. */ /* * !!! * Historically, the ^D command took (but then ignored) * a count. For simplicity, we don't return it unless * it's the first character entered. The check for len * equal to 0 is okay, TXT_AUTOINDENT won't be set. */ if (LF_ISSET(TXT_CNTRLD)) { for (cnt = 0; cnt < tp->len; ++cnt) if (!isblank(tp->lb[cnt])) break; if (cnt == tp->len) { tp->len = 1; tp->lb[0] = ev.e_c; tp->lb[1] = '\0'; /* * Put out a line separator, in case * the command fails. */ (void)putchar('\n'); goto done; } } /* * POSIX 1003.1b-1993, paragraph 7.1.1.9, states that * the EOF characters are discarded if there are other * characters to process in the line, i.e. if the EOF * is not the first character in the line. For this * reason, historic ex discarded the EOF characters, * even if occurring in the middle of the input line. * We match that historic practice. * * !!! * The test for discarding in the middle of the line is * done in the switch, because the CARAT forms are N+1, * not N. * * !!! * There's considerable magic to make the terminal code * return the EOF character at all. See that code for * details. */ if (!LF_ISSET(TXT_AUTOINDENT) || tp->len == 0) continue; switch (carat_st) { case C_CARATSET: /* ^^D */ if (tp->len > tp->ai + 1) continue; /* Save the ai string for later. */ ait.lb = NULL; ait.lb_len = 0; BINC_GOTOW(sp, ait.lb, ait.lb_len, tp->ai); MEMCPY(ait.lb, tp->lb, tp->ai); ait.ai = ait.len = tp->ai; carat_st = C_NOTSET; nochange = 1; goto leftmargin; case C_ZEROSET: /* 0^D */ if (tp->len > tp->ai + 1) continue; carat_st = C_NOTSET; leftmargin: (void)gp->scr_ex_adjust(sp, EX_TERM_CE); tp->ai = tp->len = 0; break; case C_NOTSET: /* ^D */ if (tp->len > tp->ai) continue; if (txt_dent(sp, tp)) goto err; break; default: abort(); } /* Clear and redisplay the line. */ (void)gp->scr_ex_adjust(sp, EX_TERM_CE); txt_prompt(sp, tp, prompt, flags); break; default: /* * See the TXT_BEAUTIFY comment in vi/v_txt_ev.c. * * Silently eliminate any iscntrl() character that was * not already handled specially, except for <tab> and * <ff>. */ ins_ch: if (LF_ISSET(TXT_BEAUTIFY) && ISCNTRL(ev.e_c) && ev.e_value != K_FORMFEED && ev.e_value != K_TAB) break; tp->lb[tp->len++] = ev.e_c; break; } } /* NOTREACHED */ done: return (rval); err: alloc_err: return (1); }
/* * v_replace -- [count]r<char> * * !!! * The r command in historic vi was almost beautiful in its badness. For * example, "r<erase>" and "r<word erase>" beeped the terminal and deleted * a single character. "Nr<carriage return>", where N was greater than 1, * inserted a single carriage return. "r<escape>" did cancel the command, * but "r<literal><escape>" erased a single character. To enter a literal * <literal> character, it required three <literal> characters after the * command. This may not be right, but at least it's not insane. * * PUBLIC: int v_replace __P((SCR *, VICMD *)); */ int v_replace(SCR *sp, VICMD *vp) { EVENT ev; VI_PRIVATE *vip; TEXT *tp; size_t blen, len; u_long cnt; int quote, rval; CHAR_T *bp; CHAR_T *p; vip = VIP(sp); /* * If the line doesn't exist, or it's empty, replacement isn't * allowed. It's not hard to implement, but: * * 1: It's historic practice (vi beeped before the replacement * character was even entered). * 2: For consistency, this change would require that the more * general case, "Nr", when the user is < N characters from * the end of the line, also work, which would be a bit odd. * 3: Replacing with a <newline> has somewhat odd semantics. */ if (db_get(sp, vp->m_start.lno, DBG_FATAL, &p, &len)) return (1); if (len == 0) { msgq(sp, M_BERR, "186|No characters to replace"); return (1); } /* * Figure out how many characters to be replace. For no particular * reason (other than that the semantics of replacing the newline * are confusing) only permit the replacement of the characters in * the current line. I suppose we could append replacement characters * to the line, but I see no compelling reason to do so. Check this * before we get the character to match historic practice, where Nr * failed immediately if there were less than N characters from the * cursor to the end of the line. */ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; vp->m_stop.lno = vp->m_start.lno; vp->m_stop.cno = vp->m_start.cno + cnt - 1; if (vp->m_stop.cno > len - 1) { v_eol(sp, &vp->m_start); return (1); } /* * If it's not a repeat, reset the current mode and get a replacement * character. */ quote = 0; if (!F_ISSET(vp, VC_ISDOT)) { sp->showmode = SM_REPLACE; if (vs_refresh(sp, 0)) return (1); next: if (v_event_get(sp, &ev, 0, 0)) return (1); switch (ev.e_event) { case E_CHARACTER: /* * <literal_next> means escape the next character. * <escape> means they changed their minds. */ if (!quote) { if (ev.e_value == K_VLNEXT) { quote = 1; goto next; } if (ev.e_value == K_ESCAPE) return (0); } vip->rlast = ev.e_c; vip->rvalue = ev.e_value; break; case E_ERR: case E_EOF: F_SET(sp, SC_EXIT_FORCE); return (1); case E_INTERRUPT: /* <interrupt> means they changed their minds. */ return (0); case E_WRESIZE: /* <resize> interrupts the input mode. */ v_emsg(sp, NULL, VIM_WRESIZE); return (0); case E_REPAINT: if (vs_repaint(sp, &ev)) return (1); goto next; default: v_event_err(sp, &ev); return (0); } } /* Copy the line. */ GET_SPACE_RETW(sp, bp, blen, len); MEMMOVE(bp, p, len); p = bp; /* * Versions of nvi before 1.57 created N new lines when they replaced * N characters with <carriage-return> or <newline> characters. This * is different from the historic vi, which replaced N characters with * a single new line. Users complained, so we match historic practice. */ if ((!quote && vip->rvalue == K_CR) || vip->rvalue == K_NL) { /* Set return line. */ vp->m_stop.lno = vp->m_start.lno + 1; vp->m_stop.cno = 0; /* The first part of the current line. */ if (db_set(sp, vp->m_start.lno, p, vp->m_start.cno)) goto err_ret; /* * The rest of the current line. And, of course, now it gets * tricky. If there are characters left in the line and if * the autoindent edit option is set, white space after the * replaced character is discarded, autoindent is applied, and * the cursor moves to the last indent character. */ p += vp->m_start.cno + cnt; len -= vp->m_start.cno + cnt; if (len != 0 && O_ISSET(sp, O_AUTOINDENT)) for (; len && isblank(*p); --len, ++p); if ((tp = text_init(sp, p, len, len)) == NULL) goto err_ret; if (len != 0 && O_ISSET(sp, O_AUTOINDENT)) { if (v_txt_auto(sp, vp->m_start.lno, NULL, 0, tp)) goto err_ret; vp->m_stop.cno = tp->ai ? tp->ai - 1 : 0; } else vp->m_stop.cno = 0; vp->m_stop.cno = tp->ai ? tp->ai - 1 : 0; if (db_append(sp, 1, vp->m_start.lno, tp->lb, tp->len)) err_ret: rval = 1; else { text_free(tp); rval = 0; } } else { STRSET(bp + vp->m_start.cno, vip->rlast, cnt); rval = db_set(sp, vp->m_start.lno, bp, len); } FREE_SPACEW(sp, bp, blen); vp->m_final = vp->m_stop; return (rval); }
static int s(SCR *sp, EXCMD *cmdp, char *s, regex_t *re, u_int flags) { EVENT ev; MARK from, to; TEXTH tiq; recno_t elno, lno, slno; regmatch_t match[10]; size_t blen, cnt, last, lbclen, lblen, len, llen; size_t offset, saved_offset, scno; int lflag, nflag, pflag, rflag; int didsub, do_eol_match, eflags, empty_ok, eval; int linechanged, matched, quit, rval; unsigned long ul; char *bp, *lb; NEEDFILE(sp, cmdp); slno = sp->lno; scno = sp->cno; /* * !!! * Historically, the 'g' and 'c' suffices were always toggled as flags, * so ":s/A/B/" was the same as ":s/A/B/ccgg". If O_EDCOMPATIBLE was * not set, they were initialized to 0 for all substitute commands. If * O_EDCOMPATIBLE was set, they were initialized to 0 only if the user * specified substitute/replacement patterns (see ex_s()). */ if (!O_ISSET(sp, O_EDCOMPATIBLE)) sp->c_suffix = sp->g_suffix = 0; /* * Historic vi permitted the '#', 'l' and 'p' options in vi mode, but * it only displayed the last change. I'd disallow them, but they are * useful in combination with the [v]global commands. In the current * model the problem is combining them with the 'c' flag -- the screen * would have to flip back and forth between the confirm screen and the * ex print screen, which would be pretty awful. We do display all * changes, though, for what that's worth. * * !!! * Historic vi was fairly strict about the order of "options", the * count, and "flags". I'm somewhat fuzzy on the difference between * options and flags, anyway, so this is a simpler approach, and we * just take it them in whatever order the user gives them. (The ex * usage statement doesn't reflect this.) */ lflag = nflag = pflag = rflag = 0; if (s == NULL) goto noargs; for (lno = OOBLNO; *s != '\0'; ++s) switch (*s) { case ' ': case '\t': continue; case '+': ++cmdp->flagoff; break; case '-': --cmdp->flagoff; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (lno != OOBLNO) goto usage; errno = 0; if ((ul = strtoul(s, &s, 10)) >= UINT_MAX) errno = ERANGE; if (*s == '\0') /* Loop increment correction. */ --s; if (errno == ERANGE) { if (ul >= UINT_MAX) msgq(sp, M_ERR, "Count overflow"); else msgq(sp, M_SYSERR, NULL); return (1); } lno = (recno_t)ul; /* * In historic vi, the count was inclusive from the * second address. */ cmdp->addr1.lno = cmdp->addr2.lno; cmdp->addr2.lno += lno - 1; if (!db_exist(sp, cmdp->addr2.lno) && db_last(sp, &cmdp->addr2.lno)) return (1); break; case '#': nflag = 1; break; case 'c': sp->c_suffix = !sp->c_suffix; /* Ex text structure initialization. */ if (F_ISSET(sp, SC_EX)) { memset(&tiq, 0, sizeof(TEXTH)); TAILQ_INIT(&tiq); } break; case 'g': sp->g_suffix = !sp->g_suffix; break; case 'l': lflag = 1; break; case 'p': pflag = 1; break; case 'r': if (LF_ISSET(SUB_FIRST)) { msgq(sp, M_ERR, "Regular expression specified; r flag meaningless"); return (1); } if (!F_ISSET(sp, SC_RE_SEARCH)) { ex_emsg(sp, NULL, EXM_NOPREVRE); return (1); } rflag = 1; re = &sp->re_c; break; default: goto usage; } if (*s != '\0' || (!rflag && LF_ISSET(SUB_MUSTSETR))) { usage: ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE); return (1); } noargs: if (F_ISSET(sp, SC_VI) && sp->c_suffix && (lflag || nflag || pflag)) { msgq(sp, M_ERR, "The #, l and p flags may not be combined with the c flag in vi mode"); return (1); } /* * bp: if interactive, line cache * blen: if interactive, line cache length * lb: build buffer pointer. * lbclen: current length of built buffer. * lblen; length of build buffer. */ bp = lb = NULL; blen = lbclen = lblen = 0; /* For each line... */ for (matched = quit = 0, lno = cmdp->addr1.lno, elno = cmdp->addr2.lno; !quit && lno <= elno; ++lno) { /* Someone's unhappy, time to stop. */ if (INTERRUPTED(sp)) break; /* Get the line. */ if (db_get(sp, lno, DBG_FATAL, &s, &llen)) goto err; /* * Make a local copy if doing confirmation -- when calling * the confirm routine we're likely to lose the cached copy. */ if (sp->c_suffix) { if (bp == NULL) { GET_SPACE_RET(sp, bp, blen, llen); } else ADD_SPACE_RET(sp, bp, blen, llen); memcpy(bp, s, llen); s = bp; } /* Start searching from the beginning. */ offset = 0; len = llen; /* Reset the build buffer offset. */ lbclen = 0; /* Reset empty match flag. */ empty_ok = 1; /* * We don't want to have to do a setline if the line didn't * change -- keep track of whether or not this line changed. * If doing confirmations, don't want to keep setting the * line if change is refused -- keep track of substitutions. */ didsub = linechanged = 0; /* New line, do an EOL match. */ do_eol_match = 1; /* It's not nul terminated, but we pretend it is. */ eflags = REG_STARTEND; /* * The search area is from s + offset to the EOL. * * Generally, match[0].rm_so is the offset of the start * of the match from the start of the search, and offset * is the offset of the start of the last search. */ nextmatch: match[0].rm_so = 0; match[0].rm_eo = len; /* Get the next match. */ eval = regexec(re, (char *)s + offset, 10, match, eflags); /* * There wasn't a match or if there was an error, deal with * it. If there was a previous match in this line, resolve * the changes into the database. Otherwise, just move on. */ if (eval == REG_NOMATCH) goto endmatch; if (eval != 0) { re_error(sp, eval, re); goto err; } matched = 1; /* Only the first search can match an anchored expression. */ eflags |= REG_NOTBOL; /* * !!! * It's possible to match 0-length strings -- for example, the * command s;a*;X;, when matched against the string "aabb" will * result in "XbXbX", i.e. the matches are "aa", the space * between the b's and the space between the b's and the end of * the string. There is a similar space between the beginning * of the string and the a's. The rule that we use (because vi * historically used it) is that any 0-length match, occurring * immediately after a match, is ignored. Otherwise, the above * example would have resulted in "XXbXbX". Another example is * incorrectly using " *" to replace groups of spaces with one * space. * * The way we do this is that if we just had a successful match, * the starting offset does not skip characters, and the match * is empty, ignore the match and move forward. If there's no * more characters in the string, we were attempting to match * after the last character, so quit. */ if (!empty_ok && match[0].rm_so == 0 && match[0].rm_eo == 0) { empty_ok = 1; if (len == 0) goto endmatch; BUILD(sp, s + offset, 1) ++offset; --len; goto nextmatch; } /* Confirm change. */ if (sp->c_suffix) { /* * Set the cursor position for confirmation. Note, * if we matched on a '$', the cursor may be past * the end of line. */ from.lno = to.lno = lno; from.cno = match[0].rm_so + offset; to.cno = match[0].rm_eo + offset; /* * Both ex and vi have to correct for a change before * the first character in the line. */ if (llen == 0) from.cno = to.cno = 0; if (F_ISSET(sp, SC_VI)) { /* * Only vi has to correct for a change after * the last character in the line. * * XXX * It would be nice to change the vi code so * that we could display a cursor past EOL. */ if (to.cno >= llen) to.cno = llen - 1; if (from.cno >= llen) from.cno = llen - 1; sp->lno = from.lno; sp->cno = from.cno; if (vs_refresh(sp, 1)) goto err; vs_update(sp, "Confirm change? [n]", NULL); if (v_event_get(sp, &ev, 0, 0)) goto err; switch (ev.e_event) { case E_CHARACTER: break; case E_EOF: case E_ERR: case E_INTERRUPT: goto lquit; default: v_event_err(sp, &ev); goto lquit; } } else { if (ex_print(sp, cmdp, &from, &to, 0) || ex_scprint(sp, &from, &to)) goto lquit; if (ex_txt(sp, &tiq, 0, TXT_CR)) goto err; ev.e_c = TAILQ_FIRST(&tiq)->lb[0]; } switch (ev.e_c) { case CH_YES: break; default: case CH_NO: didsub = 0; BUILD(sp, s +offset, match[0].rm_eo); goto skip; case CH_QUIT: /* Set the quit/interrupted flags. */ lquit: quit = 1; F_SET(sp->gp, G_INTERRUPTED); /* * Resolve any changes, then return to (and * exit from) the main loop. */ goto endmatch; } } /* * Set the cursor to the last position changed, converting * from 1-based to 0-based. */ sp->lno = lno; sp->cno = match[0].rm_so; /* Copy the bytes before the match into the build buffer. */ BUILD(sp, s + offset, match[0].rm_so); /* Substitute the matching bytes. */ didsub = 1; if (re_sub(sp, s + offset, &lb, &lbclen, &lblen, match)) goto err; /* Set the change flag so we know this line was modified. */ linechanged = 1; /* Move past the matched bytes. */ skip: offset += match[0].rm_eo; len -= match[0].rm_eo; /* A match cannot be followed by an empty pattern. */ empty_ok = 0; /* * If doing a global change with confirmation, we have to * update the screen. The basic idea is to store the line * so the screen update routines can find it, and restart. */ if (didsub && sp->c_suffix && sp->g_suffix) { /* * The new search offset will be the end of the * modified line. */ saved_offset = lbclen; /* Copy the rest of the line. */ if (len) BUILD(sp, s + offset, len) /* Set the new offset. */ offset = saved_offset; /* Store inserted lines, adjusting the build buffer. */ last = 0; if (sp->newl_cnt) { for (cnt = 0; cnt < sp->newl_cnt; ++cnt, ++lno, ++elno) { if (db_insert(sp, lno, lb + last, sp->newl[cnt] - last)) goto err; last = sp->newl[cnt] + 1; ++sp->rptlines[L_ADDED]; } lbclen -= last; offset -= last; sp->newl_cnt = 0; } /* Store and retrieve the line. */ if (db_set(sp, lno, lb + last, lbclen)) goto err; if (db_get(sp, lno, DBG_FATAL, &s, &llen)) goto err; ADD_SPACE_RET(sp, bp, blen, llen) memcpy(bp, s, llen); s = bp; len = llen - offset; /* Restart the build. */ lbclen = 0; BUILD(sp, s, offset); /* * If we haven't already done the after-the-string * match, do one. Set REG_NOTEOL so the '$' pattern * only matches once. */ if (!do_eol_match) goto endmatch; if (offset == len) { do_eol_match = 0; eflags |= REG_NOTEOL; } goto nextmatch; } /* * If it's a global: * * If at the end of the string, do a test for the after * the string match. Set REG_NOTEOL so the '$' pattern * only matches once. */ if (sp->g_suffix && do_eol_match) { if (len == 0) { do_eol_match = 0; eflags |= REG_NOTEOL; } goto nextmatch; } endmatch: if (!linechanged) continue; /* Copy any remaining bytes into the build buffer. */ if (len) BUILD(sp, s + offset, len) /* Store inserted lines, adjusting the build buffer. */ last = 0; if (sp->newl_cnt) { for (cnt = 0; cnt < sp->newl_cnt; ++cnt, ++lno, ++elno) { if (db_insert(sp, lno, lb + last, sp->newl[cnt] - last)) goto err; last = sp->newl[cnt] + 1; ++sp->rptlines[L_ADDED]; } lbclen -= last; sp->newl_cnt = 0; } /* Store the changed line. */ if (db_set(sp, lno, lb + last, lbclen)) goto err; /* Update changed line counter. */ if (sp->rptlchange != lno) { sp->rptlchange = lno; ++sp->rptlines[L_CHANGED]; } /* * !!! * Display as necessary. Historic practice is to only * display the last line of a line split into multiple * lines. */ if (lflag || nflag || pflag) { from.lno = to.lno = lno; from.cno = to.cno = 0; if (lflag) (void)ex_print(sp, cmdp, &from, &to, E_C_LIST); if (nflag) (void)ex_print(sp, cmdp, &from, &to, E_C_HASH); if (pflag) (void)ex_print(sp, cmdp, &from, &to, E_C_PRINT); } } /* * !!! * Historically, vi attempted to leave the cursor at the same place if * the substitution was done at the current cursor position. Otherwise * it moved it to the first non-blank of the last line changed. There * were some problems: for example, :s/$/foo/ with the cursor on the * last character of the line left the cursor on the last character, or * the & command with multiple occurrences of the matching string in the * line usually left the cursor in a fairly random position. * * We try to do the same thing, with the exception that if the user is * doing substitution with confirmation, we move to the last line about * which the user was consulted, as opposed to the last line that they * actually changed. This prevents a screen flash if the user doesn't * change many of the possible lines. */ if (!sp->c_suffix && (sp->lno != slno || sp->cno != scno)) { sp->cno = 0; (void)nonblank(sp, sp->lno, &sp->cno); } /* * If not in a global command, and nothing matched, say so. * Else, if none of the lines displayed, put something up. */ rval = 0; if (!matched) { if (!F_ISSET(sp, SC_EX_GLOBAL)) { msgq(sp, M_ERR, "No match found"); goto err; } } else if (!lflag && !nflag && !pflag) F_SET(cmdp, E_AUTOPRINT); if (0) { err: rval = 1; } if (bp != NULL) FREE_SPACE(sp, bp, blen); if (lb != NULL) free(lb); return (rval); }
/* * v_cmd -- * Get a vi command. */ static gcret_t v_cmd(SCR *sp, VICMD *dp, VICMD *vp, VICMD *ismotion, int *comcountp, int *mappedp) /* Previous key if getting motion component. */ { enum { COMMANDMODE, ISPARTIAL, NOTPARTIAL } cpart; ARG_CHAR_T key; VIKEYS const *kp; gcret_t gcret; u_int flags; const char *s; /* * Get an event command or a key. Event commands are simple, and * don't have any additional information. */ cpart = ismotion == NULL ? COMMANDMODE : ISPARTIAL; gcret = v_key(sp, vp, 1, EC_MAPCOMMAND); if (gcret != GC_OK) { if (gcret != GC_EVENT) return (gcret); if (v_event(sp, vp)) return (GC_ERR); if (ismotion != NULL && !F_ISSET(vp->kp, V_MOVE)) v_event_err(sp, &vp->ev); return (GC_EVENT); } /* * Keys are not simple. (Although vi's command structure less complex * than ex (and don't think I'm not grateful!) The command syntax is: * * [count] [buffer] [count] key [[motion] | [buffer] [character]] * * and there are, of course, several special cases. The motion value * is itself a vi command, with the syntax: * * [count] key [character] * * <escape> cancels partial commands, i.e. a command where at least * one non-numeric character has been entered. Otherwise, it beeps * the terminal. * * !!! * POSIX 1003.2-1992 explicitly disallows cancelling commands where * all that's been entered is a number, requiring that the terminal * be alerted. */ if (vp->ev.e_value == K_ESCAPE) goto esc; /* * Commands that are mapped are treated differently (e.g., they * don't set the dot command. Pass that information back. */ if (FL_ISSET(vp->ev.e_flags, CH_MAPPED)) *mappedp = 1; key = vp->ev.e_c; if (ismotion == NULL) cpart = NOTPARTIAL; /* Pick up an optional buffer. */ if (key == '"') { cpart = ISPARTIAL; if (ismotion != NULL) { v_emsg(sp, NULL, VIM_COMBUF); return (GC_ERR); } KEY(vp->buffer, 0); F_SET(vp, VC_BUFFER); KEY(key, EC_MAPCOMMAND); } /* * Pick up an optional count, where a leading 0 isn't a count, it's * a command. When a count is specified, the dot command behaves * differently, pass the information back. */ if (ISDIGIT(key) && key != '0') { if (v_count(sp, vp, key, &vp->count)) return (GC_ERR); F_SET(vp, VC_C1SET); *comcountp = 1; KEY(key, EC_MAPCOMMAND); } else *comcountp = 0; /* Pick up optional buffer. */ if (key == '"') { cpart = ISPARTIAL; if (F_ISSET(vp, VC_BUFFER)) { msgq(sp, M_ERR, "234|Only one buffer may be specified"); return (GC_ERR); } if (ismotion != NULL) { v_emsg(sp, NULL, VIM_COMBUF); return (GC_ERR); } KEY(vp->buffer, 0); F_SET(vp, VC_BUFFER); KEY(key, EC_MAPCOMMAND); } /* Check for an OOB command key. */ cpart = ISPARTIAL; if (key > MAXVIKEY) { v_emsg(sp, (const char *)KEY_NAME(sp, key), VIM_NOCOM); return (GC_ERR); } kp = &vikeys[vp->key = key]; /* * !!! * Historically, D accepted and then ignored a count. Match it. */ if (vp->key == 'D' && F_ISSET(vp, VC_C1SET)) { *comcountp = 0; vp->count = 0; F_CLR(vp, VC_C1SET); } /* * There are several commands that we implement as aliases, both * to match historic practice and to ensure consistency. Check * for command aliases. */ if (kp->func == NULL && (kp = v_alias(sp, vp, kp)) == NULL) return (GC_ERR); /* The tildeop option makes the ~ command take a motion. */ if (key == '~' && O_ISSET(sp, O_TILDEOP)) kp = &tmotion; vp->kp = kp; /* * Find the command. The only legal command with no underlying * function is dot. It's historic practice that <escape> doesn't * just erase the preceding number, it beeps the terminal as well. * It's a common problem, so just beep the terminal unless verbose * was set. */ if (kp->func == NULL) { if (key != '.') { v_emsg(sp, (const char *)KEY_NAME(sp, key), vp->ev.e_value == K_ESCAPE ? VIM_NOCOM_B : VIM_NOCOM); return (GC_ERR); } /* If called for a motion command, stop now. */ if (dp == NULL) goto usage; /* * !!! * If a '.' is immediately entered after an undo command, we * replay the log instead of redoing the last command. This * is necessary because 'u' can't set the dot command -- see * vi/v_undo.c:v_undo for details. */ if (VIP(sp)->u_ccnt == sp->ccnt) { vp->kp = &vikeys['u']; F_SET(vp, VC_ISDOT); return (GC_OK); } /* Otherwise, a repeatable command must have been executed. */ if (!F_ISSET(dp, VC_ISDOT)) { msgq(sp, M_ERR, "208|No command to repeat"); return (GC_ERR); } /* Set new count/buffer, if any, and return. */ if (F_ISSET(vp, VC_C1SET)) { F_SET(dp, VC_C1SET); dp->count = vp->count; } if (F_ISSET(vp, VC_BUFFER)) dp->buffer = vp->buffer; *vp = *dp; return (GC_OK); } /* Set the flags based on the command flags. */ flags = kp->flags; /* Check for illegal count. */ if (F_ISSET(vp, VC_C1SET) && !LF_ISSET(V_CNT)) goto usage; /* Illegal motion command. */ if (ismotion == NULL) { /* Illegal buffer. */ if (!LF_ISSET(V_OBUF) && F_ISSET(vp, VC_BUFFER)) goto usage; /* Required buffer. */ if (LF_ISSET(V_RBUF)) { KEY(vp->buffer, 0); F_SET(vp, VC_BUFFER); } } /* * Special case: '[', ']' and 'Z' commands. Doesn't the fact that * the *single* characters don't mean anything but the *doubled* * characters do, just frost your shorts? */ if (vp->key == '[' || vp->key == ']' || vp->key == 'Z') { /* * Historically, half entered [[, ]] or Z commands weren't * cancelled by <escape>, the terminal was beeped instead. * POSIX.2-1992 probably didn't notice, and requires that * they be cancelled instead of beeping. Seems fine to me. * * Don't set the EC_MAPCOMMAND flag, apparently ] is a popular * vi meta-character, and we don't want the user to wait while * we time out a possible mapping. This *appears* to match * historic vi practice, but with mapping characters, You Just * Never Know. */ KEY(key, 0); if (vp->key != key) { usage: if (ismotion == NULL) s = kp->usage; else if (ismotion->key == '~' && O_ISSET(sp, O_TILDEOP)) s = tmotion.usage; else s = vikeys[ismotion->key].usage; v_emsg(sp, s, VIM_USAGE); return (GC_ERR); } } /* Special case: 'z' command. */ if (vp->key == 'z') { KEY(vp->character, 0); if (ISDIGIT(vp->character)) { if (v_count(sp, vp, vp->character, &vp->count2)) return (GC_ERR); F_SET(vp, VC_C2SET); KEY(vp->character, 0); } } /* * Commands that have motion components can be doubled to imply the * current line. */ if (ismotion != NULL && ismotion->key != key && !LF_ISSET(V_MOVE)) { msgq(sp, M_ERR, "210|%s may not be used as a motion command", KEY_NAME(sp, key)); return (GC_ERR); } /* Pick up required trailing character. */ if (LF_ISSET(V_CHAR)) KEY(vp->character, 0); /* Get any associated cursor word. */ if (F_ISSET(kp, V_KEYW) && v_curword(sp)) return (GC_ERR); return (GC_OK); esc: switch (cpart) { case COMMANDMODE: msgq(sp, M_BERR, "211|Already in command mode"); return (GC_ERR_NOFLUSH); case ISPARTIAL: break; case NOTPARTIAL: (void)sp->gp->scr_bell(sp); break; } return (GC_ERR); }
/* * v_key -- * Return the next event. */ static gcret_t v_key(SCR *sp, VICMD *vp, int events_ok, u_int32_t ec_flags) { EVENT *evp; u_int32_t quote; for (evp = &vp->ev, quote = 0;;) { if (v_event_get(sp, evp, 0, ec_flags | quote)) return (GC_FATAL); quote = 0; switch (evp->e_event) { case E_CHARACTER: /* * !!! * Historically, ^V was ignored in the command stream, * although it had a useful side-effect of interrupting * mappings. Adding a quoting bit to the call probably * extends historic practice, but it feels right. */ if (evp->e_value == K_VLNEXT) { quote = EC_QUOTED; break; } return (GC_OK); case E_ERR: case E_EOF: return (GC_FATAL); case E_INTERRUPT: /* * !!! * Historically, vi beeped on command level interrupts. * * Historically, vi exited to ex mode if no file was * named on the command line, and two interrupts were * generated in a row. (I figured you might want to * know that, just in case there's a quiz later.) */ (void)sp->gp->scr_bell(sp); return (GC_INTERRUPT); case E_REPAINT: if (v_erepaint(sp, evp)) return (GC_FATAL); break; case E_WRESIZE: /* * !!! * We don't do anything here, just return an error. * The vi loop will return because of this, and then * the main loop will realize that we had to restart * the world and will call the vi loop again. */ return (GC_ERR); case E_IPCOMMAND: if (events_ok) return (GC_EVENT); /* FALLTHROUGH */ default: v_event_err(sp, evp); return (GC_ERR); } } /* NOTREACHED */ }