📄 v_ntext.c
字号:
/*- * Copyright (c) 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#ifndef lintstatic char sccsid[] = "@(#)v_ntext.c 8.104 (Berkeley) 4/14/94";#endif /* not lint */#include <sys/types.h>#include <sys/queue.h>#include <sys/time.h>#include <bitstring.h>#include <ctype.h>#include <errno.h>#include <limits.h>#include <signal.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <termios.h>#include <unistd.h>#include "compat.h"#include <db.h>#include <regex.h>#include "vi.h"#include "seq.h"#include "vcmd.h"#include "excmd.h"static int txt_abbrev __P((SCR *, TEXT *, CHAR_T *, int, int *, int *));static void txt_ai_resolve __P((SCR *, TEXT *));static TEXT *txt_backup __P((SCR *, EXF *, TEXTH *, TEXT *, u_int *));static void txt_err __P((SCR *, EXF *, TEXTH *));static int txt_hex __P((SCR *, TEXT *));static int txt_indent __P((SCR *, TEXT *));static int txt_margin __P((SCR *, TEXT *, int *, CHAR_T *));static int txt_outdent __P((SCR *, TEXT *));static void txt_Rcleanup __P((SCR *, TEXTH *, TEXT *, const char *, const size_t));static int txt_resolve __P((SCR *, EXF *, TEXTH *, u_int));static void txt_showmatch __P((SCR *, EXF *));static void txt_unmap __P((SCR *, TEXT *, u_int *));/* Cursor character (space is hard to track on the screen). */#if defined(DEBUG) && 0#undef CH_CURSOR#define CH_CURSOR '+'#endif/* * v_ntext -- * Read in text from the user. * * !!! * Historic vi did a special screen optimization for tab characters. For * the keystrokes "iabcd<esc>0C<tab>", the tab would overwrite the rest of * the string when it was displayed. Because this implementation redisplays * the entire line on each keystroke, the "bcd" gets pushed to the right as * we ignore that the user has "promised" to change the rest of the characters. * Users have noticed, but this isn't worth fixing, and, the way that the * historic vi did it results in an even worse bug. Given the keystrokes * "iabcd<esc>0R<tab><esc>", the "bcd" disappears, and magically reappears * on the second <esc> key. */intv_ntext(sp, ep, tiqh, tm, lp, len, rp, prompt, ai_line, flags) SCR *sp; EXF *ep; TEXTH *tiqh; MARK *tm; /* To MARK. */ const char *lp; /* Input line. */ const size_t len; /* Input line length. */ MARK *rp; /* Return MARK. */ ARG_CHAR_T prompt; /* Prompt to display. */ recno_t ai_line; /* Line number to use for autoindent count. */ u_int flags; /* TXT_ flags. */{ /* State of abbreviation checks. */ enum { A_NOTSET, A_NOTWORD, A_INWORD } abb; /* State of the "[^0]^D" sequences. */ enum { C_NOTSET, C_CARATSET, C_NOCHANGE, C_ZEROSET } carat_st; /* State of the hex input character. */ enum { H_NOTSET, H_NEXTCHAR, H_INHEX } hex; /* State of quotation. */ enum { Q_NOTSET, Q_NEXTCHAR, Q_THISCHAR } quoted; CH ikey; /* Input character structure. */ CHAR_T ch; /* Input character. */ TEXT *tp, *ntp, ait; /* Input and autoindent text structures. */ size_t owrite, insert; /* Temporary copies of TEXT fields. */ size_t rcol; /* 0-N: insert offset in the replay buffer. */ size_t col; /* Current column. */ u_long margin; /* Wrapmargin value. */ u_int iflags; /* Input flags. */ int ab_cnt, ab_turnoff; /* Abbreviation count, if turned off. */ int eval; /* Routine return value. */ int replay; /* If replaying a set of input. */ int showmatch; /* Showmatch set on this character. */ int testnr; /* Test first character for nul replay. */ int max, tmp; int unmap_tst; /* Input map needs testing. */ char *p; /* * Set the input flag, so tabs get displayed correctly * and everyone knows that the text buffer is in use. */ F_SET(sp, S_INPUT); /* Local initialization. */ eval = 0; /* * Get one 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 changing a line, * copy it into the TEXT buffer. */ if (tiqh->cqh_first != (void *)tiqh) { tp = tiqh->cqh_first; if (tp->q.cqe_next != (void *)tiqh || tp->lb_len < len + 32) { text_lfree(tiqh); goto newtp; } tp->ai = tp->insert = tp->offset = tp->owrite = 0; if (lp != NULL) { tp->len = len; memmove(tp->lb, lp, len); } else tp->len = 0; } else {newtp: if ((tp = text_init(sp, lp, len, len + 32)) == NULL) return (1); CIRCLEQ_INSERT_HEAD(tiqh, tp, q); } /* Set the starting line number. */ tp->lno = sp->lno; /* * Set the insert and overwrite counts. If overwriting characters, * do insertion afterward. If not overwriting characters, assume * doing insertion. If change is to a mark, emphasize it with an * CH_ENDMARK */ if (len) { if (LF_ISSET(TXT_OVERWRITE)) { tp->owrite = (tm->cno - sp->cno) + 1; tp->insert = (len - tm->cno) - 1; } else tp->insert = len - sp->cno; if (LF_ISSET(TXT_EMARK)) tp->lb[tm->cno] = CH_ENDMARK; } /* * Many of the special cases in this routine are to handle autoindent * support. Somebody decided that it would be a good idea if "^^D" * and "0^D" deleted all of the autoindented characters. In an editor * that takes single character input from the user, this beggars the * imagination. Note also, "^^D" resets the next lines' autoindent, * but "0^D" doesn't. * * We assume that autoindent only happens on empty lines, so insert * and overwrite will be zero. If doing autoindent, figure out how * much indentation we need and fill it in. Update input column and * screen cursor as necessary. */ if (LF_ISSET(TXT_AUTOINDENT) && ai_line != OOBLNO) { if (txt_auto(sp, ep, ai_line, NULL, 0, tp)) return (1); sp->cno = tp->ai; } else { /* * The cc and S commands have a special feature -- leading * <blank> characters are handled as autoindent characters. * Beauty! */ if (LF_ISSET(TXT_AICHARS)) { tp->offset = 0; tp->ai = sp->cno; } else tp->offset = sp->cno; } /* If getting a command buffer from the user, there may be a prompt. */ if (LF_ISSET(TXT_PROMPT)) { tp->lb[sp->cno++] = prompt; ++tp->len; ++tp->offset; } /* * If appending after the end-of-line, add a space into the buffer * and move the cursor right. This space is inserted, i.e. pushed * along, and then deleted when the line is resolved. Assumes that * the cursor is already positioned at the end of the line. This * avoids the nastiness of having the cursor reside on a magical * column, i.e. a column that doesn't really exist. The only down * side is that we may wrap lines or scroll the screen before it's * strictly necessary. Not a big deal. */ if (LF_ISSET(TXT_APPENDEOL)) { tp->lb[sp->cno] = CH_CURSOR; ++tp->len; ++tp->insert; } /* * Historic practice is that the wrapmargin value was a distance * from the RIGHT-HAND column, not the left. It's more useful to * us as a distance from the left-hand column. * * !!! * Replay commands are not affected by wrapmargin values. What * I found surprising was that people actually depend on it, as * in this gem of a macro which centers lines: * * map #c $mq81a ^V^[81^V|D`qld0:s/ / /g^V^M$p * * XXX * Setting margin causes a significant performance hit. Normally * we don't update the screen if there are keys waiting, but we * have to if margin is set, otherwise the screen routines don't * know where the cursor is. */ if (LF_ISSET(TXT_REPLAY) || !LF_ISSET(TXT_WRAPMARGIN)) margin = 0; else if ((margin = O_VAL(sp, O_WRAPMARGIN)) != 0) margin = sp->cols - margin; /* Initialize abbreviations checks. */ if (F_ISSET(sp->gp, G_ABBREV) && LF_ISSET(TXT_MAPINPUT)) { abb = A_INWORD; ab_cnt = ab_turnoff = 0; } else abb = A_NOTSET; /* * Set up the dot command. Dot commands are done by saving the * actual characters and replaying the input. We have to push * the characters onto the key stack and then handle them normally, * otherwise things like wrapmargin will fail. * * XXX * It would be nice if we could swallow backspaces and such, but * it's not all that easy to do. Another possibility would be to * recognize full line insertions, which could be performed quickly, * without replay. */nullreplay: rcol = 0; if (replay = LF_ISSET(TXT_REPLAY)) { /* * !!! * Historically, it wasn't an error to replay non-existent * input. This test is necessary, we get here by the user * doing an input command followed by a nul. * * !!! * Historically, vi did not remap or reabbreviate replayed * input. It did, however, beep at you if you changed an * abbreviation and then replayed the input. We're not that * compatible. */ if (VIP(sp)->rep == NULL) return (0); if (term_push(sp, VIP(sp)->rep, VIP(sp)->rep_cnt, 0, CH_NOMAP)) return (1); testnr = 0; abb = A_NOTSET; LF_CLR(TXT_RECORD); } else testnr = 1; unmap_tst = LF_ISSET(TXT_MAPINPUT) && LF_ISSET(TXT_INFOLINE); iflags = LF_ISSET(TXT_MAPCOMMAND | TXT_MAPINPUT); for (showmatch = 0, carat_st = C_NOTSET, hex = H_NOTSET, quoted = Q_NOTSET;;) { /* * Reset the line and update the screen. (The txt_showmatch() * code refreshes the screen for us.) Don't refresh unless * we're about to wait on a character or we need to know where * the cursor really is. */ if (showmatch || margin || !KEYS_WAITING(sp)) { if (sp->s_change(sp, ep, tp->lno, LINE_RESET)) goto err; if (showmatch) { showmatch = 0; txt_showmatch(sp, ep); } else if (sp->s_refresh(sp, ep)) goto err; } /* Get the next character. */next_ch: if (term_key(sp, &ikey, quoted == Q_THISCHAR ? iflags & ~(TXT_MAPCOMMAND | TXT_MAPINPUT) : iflags) != INP_OK) goto err; ch = ikey.ch; /* Abbreviation check. See comment in txt_abbrev(). */#define MAX_ABBREVIATION_EXPANSION 256 if (ikey.flags & CH_ABBREVIATED) { if (++ab_cnt > MAX_ABBREVIATION_EXPANSION) { term_ab_flush(sp, "Abbreviation exceeded maximum number of characters"); ab_cnt = 0; continue; } } else ab_cnt = 0; /* * !!! * Historic feature. If the first character of the input is * a nul, replay the previous input. This isn't documented * anywhere, and is a great test of vi clones. */ if (ch == '\0' && testnr) { LF_SET(TXT_REPLAY); goto nullreplay; } testnr = 0; /* * Check to see if the character fits into the input (and * replay, if necessary) buffers. It isn't necessary to * have tp->len bytes, since it doesn't consider overwrite * characters, but not worth fixing. */ if (LF_ISSET(TXT_RECORD)) { BINC_GOTO(sp, VIP(sp)->rep, VIP(sp)->rep_len, rcol + 1); VIP(sp)->rep[rcol++] = ch; } BINC_GOTO(sp, tp->lb, tp->lb_len, tp->len + 1); /* * If the character was quoted, replace the last character * (the literal mark) with the new character. If quoted * by someone else, simply insert the character. */ if (ikey.flags & CH_QUOTED) goto insq_ch; if (quoted == Q_THISCHAR) { --sp->cno; ++tp->owrite; quoted = Q_NOTSET; goto insq_ch; } /* * !!! * Extension. If the user enters "<CH_HEX>[isxdigit()]*" we * will try to use the value as a character. Anything else * inserts the <CH_HEX> character, and resets hex mode. */ if (hex == H_INHEX && !isxdigit(ch)) { if (txt_hex(sp, tp)) goto err; hex = H_NOTSET; } switch (ikey.value) { case K_CR: case K_NL: /* New line. */ /* CR returns from the vi command line. */ if (LF_ISSET(TXT_CR)) { /* * If a script window and not the colon * line, push a <cr> so it gets executed. */ if (F_ISSET(sp, S_SCRIPT) && !LF_ISSET(TXT_INFOLINE)) (void)term_push(sp, "\r", 1, 0, CH_NOMAP); goto k_escape; }#define LINE_RESOLVE { \ /* \ * Handle abbreviations. If there was one, \ * discard the replay characters. \ */ \ if (abb == A_INWORD && !replay) { \ if (txt_abbrev(sp, tp, &ch, \ LF_ISSET(TXT_INFOLINE), &tmp, \ &ab_turnoff)) \ goto err; \ if (tmp) { \ if (LF_ISSET(TXT_RECORD)) \ rcol -= tmp; \ goto next_ch; \ } \ } \ if (abb != A_NOTSET) \ abb = A_NOTWORD; \ if (unmap_tst) \ txt_unmap(sp, tp, &iflags); \ /* Delete any appended cursor. */ \ if (LF_ISSET(TXT_APPENDEOL)) { \ --tp->len; \ --tp->insert; \ } \} LINE_RESOLVE; /* * Save the current line information for restoration * in txt_backup(). Set the new line length. */ tp->sv_len = tp->len; tp->sv_cno = sp->cno; tp->len = sp->cno;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -