📄 vi.c
字号:
/*- * Copyright (c) 1992, 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[] = "@(#)vi.c 8.61 (Berkeley) 4/10/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 "compat.h"#include <db.h>#include <regex.h>#include "vi.h"#include "vcmd.h"static int getcmd __P((SCR *, EXF *, VICMDARG *, VICMDARG *, VICMDARG *, int *));static inline int getcount __P((SCR *, ARG_CHAR_T, u_long *));static inline int getkey __P((SCR *, CH *, u_int));static int getkeyword __P((SCR *, EXF *, VICMDARG *, u_int));static int getmotion __P((SCR *, EXF *, VICMDARG *, VICMDARG *));/* * Side-effect: * The dot structure can be set by the underlying vi functions, * see v_Put() and v_put(). */#define DOT (&VIP(sp)->sdot)#define DOTMOTION (&VIP(sp)->sdotmotion)/* * vi -- * Main vi command loop. */intvi(sp, ep) SCR *sp; EXF *ep;{ MARK abs; VICMDARG cmd, *vp; u_int flags, saved_mode; int comcount, eval; /* Start vi and paint the screen. */ if (v_init(sp, ep)) return (1); if (sp->s_refresh(sp, ep)) { (void)v_end(sp); return (1); } /* Command initialization. */ memset(&cmd, 0, sizeof(VICMDARG)); for (eval = 0, vp = &cmd;;) { if (!MAPPED_KEYS_WAITING(sp) && log_cursor(sp, ep)) goto err; /* * We get a command, which may or may not have an associated * motion. If it does, we get it too, calling its underlying * function to get the resulting mark. We then call the * command setting the cursor to the resulting mark. */ if (getcmd(sp, ep, DOT, vp, NULL, &comcount)) goto err; /* * Historical practice: if a dot command gets a new count, * any motion component goes away, i.e. "d3w2." deletes a * total of 5 words. */ if (F_ISSET(vp, VC_ISDOT) && comcount) DOTMOTION->count = 1; /* Copy the key flags into the local structure. */ F_SET(vp, vp->kp->flags); /* Get any associated keyword. */ if (F_ISSET(vp, V_KEYNUM | V_KEYW) && getkeyword(sp, ep, vp, vp->flags)) goto err; /* If a non-relative movement, copy the future absolute mark. */ if (F_ISSET(vp, V_ABS)) { abs.lno = sp->lno; abs.cno = sp->cno; } /* * Set the three cursor locations to the current cursor. The * underlying routines don't bother if the cursor doesn't move. * This also handles line commands (e.g. Y) defaulting to the * current line. */ vp->m_start.lno = vp->m_stop.lno = vp->m_final.lno = sp->lno; vp->m_start.cno = vp->m_stop.cno = vp->m_final.cno = sp->cno; /* * Do any required motion; getmotion sets the from MARK and the * line mode flag. We save off the RCM mask and only restore * it if it no RCM flags are set by the motion command. This * means that the motion command is expected to determine where * the cursor ends up! */ if (F_ISSET(vp, V_MOTION)) { flags = F_ISSET(vp, VM_RCM_MASK); F_CLR(vp, VM_RCM_MASK); if (getmotion(sp, ep, DOTMOTION, vp)) goto err; if (F_ISSET(vp, VM_NOMOTION)) goto err; if (!F_ISSET(vp, VM_RCM_MASK)) F_SET(vp, flags); } /* * If a count is set and the command is line oriented, set the * to MARK here relative to the cursor/from MARK. This is for * commands that take both counts and motions, i.e. "4yy" and * "y%". As there's no way the command can know which the user * did, we have to do it here. (There are commands that are * line oriented and that take counts ("#G", "#H"), for which * this calculation is either completely meaningless or wrong. * Each command must validate the value for itself. */ if (F_ISSET(vp, VC_C1SET) && F_ISSET(vp, VM_LMODE)) vp->m_stop.lno += vp->count - 1; /* Increment the command count. */ ++sp->ccnt; /* Clear interrupt bits, save the mode and call the function. */ F_CLR(sp, S_INTERRUPTED | S_INTERRUPTIBLE); saved_mode = F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE); if ((vp->kp->func)(sp, ep, vp)) goto err;#ifdef DEBUG /* Make sure no function left the temporary space locked. */ if (F_ISSET(sp->gp, G_TMP_INUSE)) { msgq(sp, M_ERR, "Error: vi: temporary buffer not released."); return (1); }#endif /* * If that command took us out of vi or changed the screen, * then exit the loop without further action. */ if (saved_mode != F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE)) break; /* Set the absolute mark. */ if (F_ISSET(vp, V_ABS) && mark_set(sp, ep, ABSMARK1, &abs, 1)) goto err; /* Set the dot command structure. */ if (F_ISSET(vp, V_DOT)) { *DOT = cmd; F_SET(DOT, VC_ISDOT); /* * If a count was supplied for both the command and * its motion, the count was used only for the motion. * Turn the count back on for the dot structure. */ if (F_ISSET(vp, VC_C1RESET)) F_SET(DOT, VC_C1SET); } /* * Some vi row movements are "attracted" to the last position * set, i.e. the VM_RCM commands are moths to the VM_RCM_SET * commands' candle. It's broken into two parts. Here we deal * with the command flags. In sp->relative(), we deal with the * screen flags. If the movement is to the EOL the vi command * handles it. If it's to the beginning, we handle it here. * * Note, some commands (e.g. _, ^) don't set the VM_RCM_SETFNB * flag, but do the work themselves. The reason is that they * have to modify the column in case they're being used as a * motion component. Other similar commands (e.g. +, -) don't * have to modify the column because they are always line mode * operations when used as motions, so the column number isn't * of any interest. * * Does this totally violate the screen and editor layering? * You betcha. As they say, if you think you understand it, * you don't. */ switch (F_ISSET(vp, VM_RCM_MASK)) { case 0: case VM_RCM_SET: break; case VM_RCM: vp->m_final.cno = sp->s_rcm(sp, ep, vp->m_final.lno); break; case VM_RCM_SETLAST: sp->rcm_last = 1; break; case VM_RCM_SETLFNB: /* * If we changed lines, move to the first non-blank. * This is the hack that makes logical scrolling on * really long lines work. */ if (vp->m_start.lno != vp->m_final.lno) { vp->m_final.cno = 0; if (nonblank(sp, ep, vp->m_final.lno, &vp->m_final.cno)) goto err; F_SET(vp, VM_RCM_SET); } break; case VM_RCM_SETFNB: vp->m_final.cno = 0; /* FALLTHROUGH */ case VM_RCM_SETNNB: if (nonblank(sp, ep, vp->m_final.lno, &vp->m_final.cno)) goto err; F_SET(vp, VM_RCM_SET); break; default: abort(); } /* Update the cursor. */ sp->lno = vp->m_final.lno; sp->cno = vp->m_final.cno; if (!MAPPED_KEYS_WAITING(sp)) { (void)msg_rpt(sp, 1); if (0)err: term_map_flush(sp, "Vi error"); } /* Refresh the screen. */ if (sp->s_refresh(sp, ep)) { eval = 1; break; } /* Set the new favorite position. */ if (F_ISSET(vp, VM_RCM_SET)) { sp->rcm_last = 0; (void)sp->s_column(sp, ep, &sp->rcm); } } return (v_end(sp) || eval);}#define KEY(key, map) { \ if (getkey(sp, &ikey, map)) \ return (1); \ key = ikey.ch; \}/* * getcmd -- * * The command structure for vi is 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 several special cases. The motion value is itself a vi * command, with the syntax: * * [count] key [character] */static intgetcmd(sp, ep, dp, vp, ismotion, comcountp) SCR *sp; EXF *ep; VICMDARG *dp, *vp; VICMDARG *ismotion; /* Previous key if getting motion component. */ int *comcountp;{ VIKEYS const *kp; u_int flags; CH ikey; CHAR_T key; /* Refresh the command structure. */ memset(&vp->vp_startzero, 0, (char *)&vp->vp_endzero - (char *)&vp->vp_startzero); /* An escape bells the user if in command mode. */ if (getkey(sp, &ikey, TXT_MAPCOMMAND)) { if (ikey.value == K_ESCAPE && ismotion == NULL) msgq(sp, M_BERR, "Already in command mode"); return (1); } /* Get the next key. */ key = ikey.ch; /* Pick up optional buffer. */ if (key == '"') { KEY(vp->buffer, 0); F_SET(vp, VC_BUFFER); KEY(key, TXT_MAPCOMMAND); } /* * Pick up optional count, where a leading 0 is not a count, * it's a command. */ if (isdigit(key) && key != '0') { if (getcount(sp, key, &vp->count)) return (1); F_SET(vp, VC_C1SET); *comcountp = 1; KEY(key, TXT_MAPCOMMAND); } else *comcountp = 0; /* Pick up optional buffer. */ if (key == '"') { if (F_ISSET(vp, VC_BUFFER)) { msgq(sp, M_ERR, "Only one buffer can be specified."); return (1); } KEY(vp->buffer, 0); F_SET(vp, VC_BUFFER); KEY(key, TXT_MAPCOMMAND); } /* Check for an OOB command key. */ if (key > MAXVIKEY) { msgq(sp, M_BERR, "%s isn't a vi command", KEY_NAME(sp, key)); return (1); } /* * Find the command. The only legal command with no underlying * function is dot. */ kp = vp->kp = &vikeys[vp->key = key]; if (kp->func == NULL) { if (key != '.') { msgq(sp, M_ERR, "%s isn't a command", KEY_NAME(sp, key)); return (1); } /* If called for a motion command, stop now. */ if (dp == NULL) goto usage; /* A repeatable command must have been executed. */ if (!F_ISSET(dp, VC_ISDOT)) { msgq(sp, M_ERR, "No command to repeat."); return (1); } /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -