119304Speter/*- 219304Speter * Copyright (c) 1992, 1993, 1994 319304Speter * The Regents of the University of California. All rights reserved. 419304Speter * Copyright (c) 1992, 1993, 1994, 1995, 1996 519304Speter * Keith Bostic. All rights reserved. 619304Speter * 719304Speter * See the LICENSE file for redistribution information. 819304Speter */ 919304Speter 1019304Speter#include "config.h" 1119304Speter 1219304Speter#ifndef lint 13254225Speterstatic const char sccsid[] = "$Id: vi.c,v 10.61 2011/12/21 13:08:30 zy Exp $"; 1419304Speter#endif /* not lint */ 1519304Speter 1619304Speter#include <sys/types.h> 1719304Speter#include <sys/queue.h> 1819304Speter#include <sys/time.h> 1919304Speter 2019304Speter#include <bitstring.h> 2119304Speter#include <ctype.h> 2219304Speter#include <errno.h> 2319304Speter#include <limits.h> 2419304Speter#include <stdio.h> 2519304Speter#include <stdlib.h> 2619304Speter#include <string.h> 2719304Speter#include <unistd.h> 2819304Speter 2919304Speter#include "../common/common.h" 3019304Speter#include "vi.h" 3119304Speter 3219304Spetertypedef enum { 3319304Speter GC_ERR, GC_ERR_NOFLUSH, GC_EVENT, GC_FATAL, GC_INTERRUPT, GC_OK 3419304Speter} gcret_t; 3519304Speter 3619304Speterstatic VIKEYS const 3719304Speter *v_alias __P((SCR *, VICMD *, VIKEYS const *)); 3819304Speterstatic gcret_t v_cmd __P((SCR *, VICMD *, VICMD *, VICMD *, int *, int *)); 3919304Speterstatic int v_count __P((SCR *, ARG_CHAR_T, u_long *)); 4019304Speterstatic void v_dtoh __P((SCR *)); 4119304Speterstatic int v_init __P((SCR *)); 4219304Speterstatic gcret_t v_key __P((SCR *, int, EVENT *, u_int32_t)); 4319304Speterstatic int v_motion __P((SCR *, VICMD *, VICMD *, int *)); 4419304Speter 4519304Speter#if defined(DEBUG) && defined(COMLOG) 4619304Speterstatic void v_comlog __P((SCR *, VICMD *)); 4719304Speter#endif 4819304Speter 4919304Speter/* 5019304Speter * Side-effect: 5119304Speter * The dot structure can be set by the underlying vi functions, 5219304Speter * see v_Put() and v_put(). 5319304Speter */ 5419304Speter#define DOT (&VIP(sp)->sdot) 5519304Speter#define DOTMOTION (&VIP(sp)->sdotmotion) 5619304Speter 5719304Speter/* 5819304Speter * vi -- 5919304Speter * Main vi command loop. 6019304Speter * 6119304Speter * PUBLIC: int vi __P((SCR **)); 6219304Speter */ 6319304Speterint 64254225Spetervi(SCR **spp) 6519304Speter{ 6619304Speter GS *gp; 6719304Speter MARK abs; 6819304Speter SCR *next, *sp; 69254225Speter VICMD cmd = { 0 }, *vp; 7019304Speter VI_PRIVATE *vip; 7119304Speter int comcount, mapped, rval; 7219304Speter 7319304Speter /* Get the first screen. */ 7419304Speter sp = *spp; 7519304Speter gp = sp->gp; 7619304Speter 77254225Speter /* Point to the command structure. */ 7819304Speter vp = &cmd; 7919304Speter 8019304Speter /* Reset strange attraction. */ 8119304Speter F_SET(vp, VM_RCM_SET); 8219304Speter 8319304Speter /* Initialize the vi screen. */ 8419304Speter if (v_init(sp)) 8519304Speter return (1); 8619304Speter 8719304Speter /* Set the focus. */ 8819304Speter (void)sp->gp->scr_rename(sp, sp->frp->name, 1); 8919304Speter 9019304Speter for (vip = VIP(sp), rval = 0;;) { 9119304Speter /* Resolve messages. */ 9219304Speter if (!MAPPED_KEYS_WAITING(sp) && vs_resolve(sp, NULL, 0)) 9319304Speter goto ret; 9419304Speter 9519304Speter /* 9619304Speter * If not skipping a refresh, return to command mode and 9719304Speter * refresh the screen. 9819304Speter */ 9919304Speter if (F_ISSET(vip, VIP_S_REFRESH)) 10019304Speter F_CLR(vip, VIP_S_REFRESH); 10119304Speter else { 10219304Speter sp->showmode = SM_COMMAND; 10319304Speter if (vs_refresh(sp, 0)) 10419304Speter goto ret; 10519304Speter } 10619304Speter 10719304Speter /* Set the new favorite position. */ 10819304Speter if (F_ISSET(vp, VM_RCM_SET | VM_RCM_SETFNB | VM_RCM_SETNNB)) { 10919304Speter F_CLR(vip, VIP_RCM_LAST); 11019304Speter (void)vs_column(sp, &sp->rcm); 11119304Speter } 11219304Speter 11319304Speter /* 11419304Speter * If not currently in a map, log the cursor position, 11519304Speter * and set a flag so that this command can become the 11619304Speter * DOT command. 11719304Speter */ 11819304Speter if (MAPPED_KEYS_WAITING(sp)) 11919304Speter mapped = 1; 12019304Speter else { 12119304Speter if (log_cursor(sp)) 12219304Speter goto err; 12319304Speter mapped = 0; 12419304Speter } 12519304Speter 12619304Speter /* 12719304Speter * There may be an ex command waiting, and we returned here 12819304Speter * only because we exited a screen or file. In this case, 12919304Speter * we simply go back into the ex parser. 13019304Speter */ 13119304Speter if (EXCMD_RUNNING(gp)) { 13219304Speter vp->kp = &vikeys[':']; 13319304Speter goto ex_continue; 13419304Speter } 13519304Speter 13619304Speter /* Refresh the command structure. */ 13719304Speter memset(vp, 0, sizeof(VICMD)); 13819304Speter 13919304Speter /* 14019304Speter * We get a command, which may or may not have an associated 14119304Speter * motion. If it does, we get it too, calling its underlying 14219304Speter * function to get the resulting mark. We then call the 14319304Speter * command setting the cursor to the resulting mark. 14419304Speter * 14519304Speter * !!! 14619304Speter * Vi historically flushed mapped characters on error, but 14719304Speter * entering extra <escape> characters at the beginning of 14819304Speter * a map wasn't considered an error -- in fact, users would 14919304Speter * put leading <escape> characters in maps to clean up vi 15019304Speter * state before the map was interpreted. Beauty! 15119304Speter */ 15219304Speter switch (v_cmd(sp, DOT, vp, NULL, &comcount, &mapped)) { 15319304Speter case GC_ERR: 15419304Speter goto err; 15519304Speter case GC_ERR_NOFLUSH: 15619304Speter goto gc_err_noflush; 15719304Speter case GC_EVENT: 15819304Speter goto gc_event; 15919304Speter case GC_FATAL: 16019304Speter goto ret; 16119304Speter case GC_INTERRUPT: 16219304Speter goto intr; 16319304Speter case GC_OK: 16419304Speter break; 16519304Speter } 16619304Speter 16719304Speter /* Check for security setting. */ 16819304Speter if (F_ISSET(vp->kp, V_SECURE) && O_ISSET(sp, O_SECURE)) { 16919304Speter ex_emsg(sp, KEY_NAME(sp, vp->key), EXM_SECURE); 17019304Speter goto err; 17119304Speter } 17219304Speter 17319304Speter /* 17419304Speter * Historical practice: if a dot command gets a new count, 17519304Speter * any motion component goes away, i.e. "d3w2." deletes a 17619304Speter * total of 5 words. 17719304Speter */ 17819304Speter if (F_ISSET(vp, VC_ISDOT) && comcount) 17919304Speter DOTMOTION->count = 1; 18019304Speter 18119304Speter /* Copy the key flags into the local structure. */ 18219304Speter F_SET(vp, vp->kp->flags); 18319304Speter 18419304Speter /* Prepare to set the previous context. */ 18519304Speter if (F_ISSET(vp, V_ABS | V_ABS_C | V_ABS_L)) { 18619304Speter abs.lno = sp->lno; 18719304Speter abs.cno = sp->cno; 18819304Speter } 18919304Speter 19019304Speter /* 19119304Speter * Set the three cursor locations to the current cursor. The 19219304Speter * underlying routines don't bother if the cursor doesn't move. 19319304Speter * This also handles line commands (e.g. Y) defaulting to the 19419304Speter * current line. 19519304Speter */ 19619304Speter vp->m_start.lno = vp->m_stop.lno = vp->m_final.lno = sp->lno; 19719304Speter vp->m_start.cno = vp->m_stop.cno = vp->m_final.cno = sp->cno; 19819304Speter 19919304Speter /* 20019304Speter * Do any required motion; v_motion sets the from MARK and the 20119304Speter * line mode flag, as well as the VM_RCM flags. 20219304Speter */ 20319304Speter if (F_ISSET(vp, V_MOTION) && 20419304Speter v_motion(sp, DOTMOTION, vp, &mapped)) { 20519304Speter if (INTERRUPTED(sp)) 20619304Speter goto intr; 20719304Speter goto err; 20819304Speter } 20919304Speter 21019304Speter /* 21119304Speter * If a count is set and the command is line oriented, set the 21219304Speter * to MARK here relative to the cursor/from MARK. This is for 21319304Speter * commands that take both counts and motions, i.e. "4yy" and 21419304Speter * "y%". As there's no way the command can know which the user 21519304Speter * did, we have to do it here. (There are commands that are 21619304Speter * line oriented and that take counts ("#G", "#H"), for which 21719304Speter * this calculation is either completely meaningless or wrong. 21819304Speter * Each command must validate the value for itself. 21919304Speter */ 22019304Speter if (F_ISSET(vp, VC_C1SET) && F_ISSET(vp, VM_LMODE)) 22119304Speter vp->m_stop.lno += vp->count - 1; 22219304Speter 22319304Speter /* Increment the command count. */ 22419304Speter ++sp->ccnt; 22519304Speter 22619304Speter#if defined(DEBUG) && defined(COMLOG) 22719304Speter v_comlog(sp, vp); 22819304Speter#endif 22919304Speter /* Call the function. */ 23019304Speterex_continue: if (vp->kp->func(sp, vp)) 23119304Speter goto err; 23219304Spetergc_event: 23319304Speter#ifdef DEBUG 23419304Speter /* Make sure no function left the temporary space locked. */ 23519304Speter if (F_ISSET(gp, G_TMP_INUSE)) { 23619304Speter F_CLR(gp, G_TMP_INUSE); 23719304Speter msgq(sp, M_ERR, 23819304Speter "232|vi: temporary buffer not released"); 23919304Speter } 24019304Speter#endif 24119304Speter /* 24219304Speter * If we're exiting this screen, move to the next one, or, if 24319304Speter * there aren't any more, return to the main editor loop. The 24419304Speter * ordering is careful, don't discard the contents of sp until 24519304Speter * the end. 24619304Speter */ 24719304Speter if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) { 24819304Speter if (file_end(sp, NULL, F_ISSET(sp, SC_EXIT_FORCE))) 24919304Speter goto ret; 25019304Speter if (vs_discard(sp, &next)) 25119304Speter goto ret; 25219304Speter if (next == NULL && vs_swap(sp, &next, NULL)) 25319304Speter goto ret; 25419304Speter *spp = next; 25519304Speter if (screen_end(sp)) 25619304Speter goto ret; 25719304Speter if (next == NULL) 25819304Speter break; 25919304Speter 26019304Speter /* Switch screens, change focus. */ 26119304Speter sp = next; 26219304Speter vip = VIP(sp); 26319304Speter (void)sp->gp->scr_rename(sp, sp->frp->name, 1); 26419304Speter 26519304Speter /* Don't trust the cursor. */ 26619304Speter F_SET(vip, VIP_CUR_INVALID); 26719304Speter 26819304Speter continue; 26919304Speter } 27019304Speter 27119304Speter /* 27219304Speter * Set the dot command structure. 27319304Speter * 27419304Speter * !!! 27519304Speter * Historically, commands which used mapped keys did not 27619304Speter * set the dot command, with the exception of the text 27719304Speter * input commands. 27819304Speter */ 27919304Speter if (F_ISSET(vp, V_DOT) && !mapped) { 28019304Speter *DOT = cmd; 28119304Speter F_SET(DOT, VC_ISDOT); 28219304Speter 28319304Speter /* 28419304Speter * If a count was supplied for both the command and 28519304Speter * its motion, the count was used only for the motion. 28619304Speter * Turn the count back on for the dot structure. 28719304Speter */ 28819304Speter if (F_ISSET(vp, VC_C1RESET)) 28919304Speter F_SET(DOT, VC_C1SET); 29019304Speter 29119304Speter /* VM flags aren't retained. */ 29219304Speter F_CLR(DOT, VM_COMMASK | VM_RCM_MASK); 29319304Speter } 29419304Speter 29519304Speter /* 29619304Speter * Some vi row movements are "attracted" to the last position 29719304Speter * set, i.e. the VM_RCM commands are moths to the VM_RCM_SET 29819304Speter * commands' candle. If the movement is to the EOL the vi 29919304Speter * command handles it. If it's to the beginning, we handle it 30019304Speter * here. 30119304Speter * 30219304Speter * Note, some commands (e.g. _, ^) don't set the VM_RCM_SETFNB 30319304Speter * flag, but do the work themselves. The reason is that they 30419304Speter * have to modify the column in case they're being used as a 30519304Speter * motion component. Other similar commands (e.g. +, -) don't 30619304Speter * have to modify the column because they are always line mode 30719304Speter * operations when used as motions, so the column number isn't 30819304Speter * of any interest. 30919304Speter * 31019304Speter * Does this totally violate the screen and editor layering? 31119304Speter * You betcha. As they say, if you think you understand it, 31219304Speter * you don't. 31319304Speter */ 31419304Speter switch (F_ISSET(vp, VM_RCM_MASK)) { 31519304Speter case 0: 31619304Speter case VM_RCM_SET: 31719304Speter break; 31819304Speter case VM_RCM: 31919304Speter vp->m_final.cno = vs_rcm(sp, 32019304Speter vp->m_final.lno, F_ISSET(vip, VIP_RCM_LAST)); 32119304Speter break; 32219304Speter case VM_RCM_SETLAST: 32319304Speter F_SET(vip, VIP_RCM_LAST); 32419304Speter break; 32519304Speter case VM_RCM_SETFNB: 32619304Speter vp->m_final.cno = 0; 32719304Speter /* FALLTHROUGH */ 32819304Speter case VM_RCM_SETNNB: 32919304Speter if (nonblank(sp, vp->m_final.lno, &vp->m_final.cno)) 33019304Speter goto err; 33119304Speter break; 33219304Speter default: 33319304Speter abort(); 33419304Speter } 33519304Speter 33619304Speter /* Update the cursor. */ 33719304Speter sp->lno = vp->m_final.lno; 33819304Speter sp->cno = vp->m_final.cno; 33919304Speter 34019304Speter /* 34119304Speter * Set the absolute mark -- set even if a tags or similar 34219304Speter * command, since the tag may be moving to the same file. 34319304Speter */ 34419304Speter if ((F_ISSET(vp, V_ABS) || 345254225Speter (F_ISSET(vp, V_ABS_L) && sp->lno != abs.lno) || 346254225Speter (F_ISSET(vp, V_ABS_C) && 347254225Speter (sp->lno != abs.lno || sp->cno != abs.cno))) && 34819304Speter mark_set(sp, ABSMARK1, &abs, 1)) 34919304Speter goto err; 35019304Speter 35119304Speter if (0) { 35219304Spetererr: if (v_event_flush(sp, CH_MAPPED)) 35319304Speter msgq(sp, M_BERR, 35419304Speter "110|Vi command failed: mapped keys discarded"); 35519304Speter } 35619304Speter 35719304Speter /* 35819304Speter * Check and clear interrupts. There's an obvious race, but 35919304Speter * it's not worth fixing. 36019304Speter */ 36119304Spetergc_err_noflush: if (INTERRUPTED(sp)) { 36219304Speterintr: CLR_INTERRUPT(sp); 36319304Speter if (v_event_flush(sp, CH_MAPPED)) 36419304Speter msgq(sp, M_ERR, 36519304Speter "231|Interrupted: mapped keys discarded"); 36619304Speter else 36719304Speter msgq(sp, M_ERR, "236|Interrupted"); 36819304Speter } 36919304Speter 37019304Speter /* If the last command switched screens, update. */ 37119304Speter if (F_ISSET(sp, SC_SSWITCH)) { 37219304Speter F_CLR(sp, SC_SSWITCH); 37319304Speter 37419304Speter /* 37519304Speter * If the current screen is still displayed, it will 37619304Speter * need a new status line. 37719304Speter */ 37819304Speter F_SET(sp, SC_STATUS); 37919304Speter 38019304Speter /* Switch screens, change focus. */ 38119304Speter sp = sp->nextdisp; 38219304Speter vip = VIP(sp); 38319304Speter (void)sp->gp->scr_rename(sp, sp->frp->name, 1); 38419304Speter 38519304Speter /* Don't trust the cursor. */ 38619304Speter F_SET(vip, VIP_CUR_INVALID); 38719304Speter 38819304Speter /* Refresh so we can display messages. */ 38919304Speter if (vs_refresh(sp, 1)) 39019304Speter return (1); 39119304Speter } 39219304Speter 39319304Speter /* If the last command switched files, change focus. */ 39419304Speter if (F_ISSET(sp, SC_FSWITCH)) { 39519304Speter F_CLR(sp, SC_FSWITCH); 39619304Speter (void)sp->gp->scr_rename(sp, sp->frp->name, 1); 39719304Speter } 39819304Speter 39919304Speter /* If leaving vi, return to the main editor loop. */ 40019304Speter if (F_ISSET(gp, G_SRESTART) || F_ISSET(sp, SC_EX)) { 40119304Speter *spp = sp; 40219304Speter v_dtoh(sp); 403254225Speter gp->scr_discard(sp, NULL); 40419304Speter break; 40519304Speter } 40619304Speter } 40719304Speter if (0) 40819304Speterret: rval = 1; 40919304Speter return (rval); 41019304Speter} 41119304Speter 41219304Speter#define KEY(key, ec_flags) { \ 41319304Speter if ((gcret = v_key(sp, 0, &ev, ec_flags)) != GC_OK) \ 41419304Speter return (gcret); \ 41519304Speter if (ev.e_value == K_ESCAPE) \ 41619304Speter goto esc; \ 41719304Speter if (F_ISSET(&ev.e_ch, CH_MAPPED)) \ 41819304Speter *mappedp = 1; \ 41919304Speter key = ev.e_c; \ 42019304Speter} 42119304Speter 42219304Speter/* 42319304Speter * The O_TILDEOP option makes the ~ command take a motion instead 42419304Speter * of a straight count. This is the replacement structure we use 42519304Speter * instead of the one currently in the VIKEYS table. 42619304Speter * 42719304Speter * XXX 42819304Speter * This should probably be deleted -- it's not all that useful, and 42919304Speter * we get help messages wrong. 43019304Speter */ 43119304SpeterVIKEYS const tmotion = { 43219304Speter v_mulcase, V_CNT|V_DOT|V_MOTION|VM_RCM_SET, 43319304Speter "[count]~[count]motion", 43419304Speter " ~ change case to motion" 43519304Speter}; 43619304Speter 43719304Speter/* 43819304Speter * v_cmd -- 43919304Speter * 44019304Speter * The command structure for vi is less complex than ex (and don't think 44119304Speter * I'm not grateful!) The command syntax is: 44219304Speter * 44319304Speter * [count] [buffer] [count] key [[motion] | [buffer] [character]] 44419304Speter * 44519304Speter * and there are several special cases. The motion value is itself a vi 44619304Speter * command, with the syntax: 44719304Speter * 44819304Speter * [count] key [character] 44919304Speter */ 45019304Speterstatic gcret_t 451254225Speterv_cmd( 452254225Speter SCR *sp, 453254225Speter VICMD *dp, 454254225Speter VICMD *vp, 455254225Speter VICMD *ismotion, /* Previous key if getting motion component. */ 456254225Speter int *comcountp, 457254225Speter int *mappedp) 45819304Speter{ 45919304Speter enum { COMMANDMODE, ISPARTIAL, NOTPARTIAL } cpart; 46019304Speter EVENT ev; 46119304Speter VIKEYS const *kp; 46219304Speter gcret_t gcret; 46319304Speter u_int flags; 46419304Speter CHAR_T key; 46519304Speter char *s; 46619304Speter 46719304Speter /* 46819304Speter * Get a key. 46919304Speter * 47019304Speter * <escape> cancels partial commands, i.e. a command where at least 47119304Speter * one non-numeric character has been entered. Otherwise, it beeps 47219304Speter * the terminal. 47319304Speter * 47419304Speter * !!! 47519304Speter * POSIX 1003.2-1992 explicitly disallows cancelling commands where 47619304Speter * all that's been entered is a number, requiring that the terminal 47719304Speter * be alerted. 47819304Speter */ 47919304Speter cpart = ismotion == NULL ? COMMANDMODE : ISPARTIAL; 48019304Speter if ((gcret = 48119304Speter v_key(sp, ismotion == NULL, &ev, EC_MAPCOMMAND)) != GC_OK) { 48219304Speter if (gcret == GC_EVENT) 48319304Speter vp->ev = ev; 48419304Speter return (gcret); 48519304Speter } 48619304Speter if (ev.e_value == K_ESCAPE) 48719304Speter goto esc; 48819304Speter if (F_ISSET(&ev.e_ch, CH_MAPPED)) 48919304Speter *mappedp = 1; 49019304Speter key = ev.e_c; 49119304Speter 49219304Speter if (ismotion == NULL) 49319304Speter cpart = NOTPARTIAL; 49419304Speter 495254225Speter /* Pick up an optional buffer. */ 49619304Speter if (key == '"') { 49719304Speter cpart = ISPARTIAL; 49819304Speter if (ismotion != NULL) { 49919304Speter v_emsg(sp, NULL, VIM_COMBUF); 50019304Speter return (GC_ERR); 50119304Speter } 50219304Speter KEY(vp->buffer, 0); 50319304Speter F_SET(vp, VC_BUFFER); 50419304Speter 50519304Speter KEY(key, EC_MAPCOMMAND); 50619304Speter } 50719304Speter 50819304Speter /* 509254225Speter * Pick up an optional count, where a leading 0 is not a count, 51019304Speter * it's a command. 51119304Speter */ 512254225Speter if (ISDIGIT(key) && key != '0') { 51319304Speter if (v_count(sp, key, &vp->count)) 51419304Speter return (GC_ERR); 51519304Speter F_SET(vp, VC_C1SET); 51619304Speter *comcountp = 1; 51719304Speter 51819304Speter KEY(key, EC_MAPCOMMAND); 51919304Speter } else 52019304Speter *comcountp = 0; 52119304Speter 52219304Speter /* Pick up optional buffer. */ 52319304Speter if (key == '"') { 52419304Speter cpart = ISPARTIAL; 52519304Speter if (F_ISSET(vp, VC_BUFFER)) { 52619304Speter msgq(sp, M_ERR, "234|Only one buffer may be specified"); 52719304Speter return (GC_ERR); 52819304Speter } 52919304Speter if (ismotion != NULL) { 53019304Speter v_emsg(sp, NULL, VIM_COMBUF); 53119304Speter return (GC_ERR); 53219304Speter } 53319304Speter KEY(vp->buffer, 0); 53419304Speter F_SET(vp, VC_BUFFER); 53519304Speter 53619304Speter KEY(key, EC_MAPCOMMAND); 53719304Speter } 53819304Speter 53919304Speter /* Check for an OOB command key. */ 54019304Speter cpart = ISPARTIAL; 54119304Speter if (key > MAXVIKEY) { 54219304Speter v_emsg(sp, KEY_NAME(sp, key), VIM_NOCOM); 54319304Speter return (GC_ERR); 54419304Speter } 54519304Speter kp = &vikeys[vp->key = key]; 54619304Speter 54719304Speter /* 54819304Speter * !!! 54919304Speter * Historically, D accepted and then ignored a count. Match it. 55019304Speter */ 55119304Speter if (vp->key == 'D' && F_ISSET(vp, VC_C1SET)) { 55219304Speter *comcountp = 0; 55319304Speter vp->count = 0; 55419304Speter F_CLR(vp, VC_C1SET); 55519304Speter } 55619304Speter 55719304Speter /* Check for command aliases. */ 55819304Speter if (kp->func == NULL && (kp = v_alias(sp, vp, kp)) == NULL) 55919304Speter return (GC_ERR); 56019304Speter 56119304Speter /* The tildeop option makes the ~ command take a motion. */ 56219304Speter if (key == '~' && O_ISSET(sp, O_TILDEOP)) 56319304Speter kp = &tmotion; 56419304Speter 56519304Speter vp->kp = kp; 56619304Speter 56719304Speter /* 56819304Speter * Find the command. The only legal command with no underlying 56919304Speter * function is dot. It's historic practice that <escape> doesn't 57019304Speter * just erase the preceding number, it beeps the terminal as well. 57119304Speter * It's a common problem, so just beep the terminal unless verbose 57219304Speter * was set. 57319304Speter */ 57419304Speter if (kp->func == NULL) { 57519304Speter if (key != '.') { 57619304Speter v_emsg(sp, KEY_NAME(sp, key), 57719304Speter ev.e_value == K_ESCAPE ? VIM_NOCOM_B : VIM_NOCOM); 57819304Speter return (GC_ERR); 57919304Speter } 58019304Speter 58119304Speter /* If called for a motion command, stop now. */ 58219304Speter if (dp == NULL) 58319304Speter goto usage; 58419304Speter 58519304Speter /* 58619304Speter * !!! 58719304Speter * If a '.' is immediately entered after an undo command, we 58819304Speter * replay the log instead of redoing the last command. This 58919304Speter * is necessary because 'u' can't set the dot command -- see 59019304Speter * vi/v_undo.c:v_undo for details. 59119304Speter */ 59219304Speter if (VIP(sp)->u_ccnt == sp->ccnt) { 59319304Speter vp->kp = &vikeys['u']; 59419304Speter F_SET(vp, VC_ISDOT); 59519304Speter return (GC_OK); 59619304Speter } 59719304Speter 59819304Speter /* Otherwise, a repeatable command must have been executed. */ 59919304Speter if (!F_ISSET(dp, VC_ISDOT)) { 60019304Speter msgq(sp, M_ERR, "208|No command to repeat"); 60119304Speter return (GC_ERR); 60219304Speter } 60319304Speter 60419304Speter /* Set new count/buffer, if any, and return. */ 60519304Speter if (F_ISSET(vp, VC_C1SET)) { 60619304Speter F_SET(dp, VC_C1SET); 60719304Speter dp->count = vp->count; 60819304Speter } 60919304Speter if (F_ISSET(vp, VC_BUFFER)) 61019304Speter dp->buffer = vp->buffer; 61119304Speter 61219304Speter *vp = *dp; 61319304Speter return (GC_OK); 61419304Speter } 61519304Speter 61619304Speter /* Set the flags based on the command flags. */ 61719304Speter flags = kp->flags; 61819304Speter 61919304Speter /* Check for illegal count. */ 62019304Speter if (F_ISSET(vp, VC_C1SET) && !LF_ISSET(V_CNT)) 62119304Speter goto usage; 62219304Speter 62319304Speter /* Illegal motion command. */ 62419304Speter if (ismotion == NULL) { 62519304Speter /* Illegal buffer. */ 62619304Speter if (!LF_ISSET(V_OBUF) && F_ISSET(vp, VC_BUFFER)) 62719304Speter goto usage; 62819304Speter 62919304Speter /* Required buffer. */ 63019304Speter if (LF_ISSET(V_RBUF)) { 63119304Speter KEY(vp->buffer, 0); 63219304Speter F_SET(vp, VC_BUFFER); 63319304Speter } 63419304Speter } 63519304Speter 63619304Speter /* 63719304Speter * Special case: '[', ']' and 'Z' commands. Doesn't the fact that 63819304Speter * the *single* characters don't mean anything but the *doubled* 63919304Speter * characters do, just frost your shorts? 64019304Speter */ 64119304Speter if (vp->key == '[' || vp->key == ']' || vp->key == 'Z') { 64219304Speter /* 64319304Speter * Historically, half entered [[, ]] or Z commands weren't 64419304Speter * cancelled by <escape>, the terminal was beeped instead. 64519304Speter * POSIX.2-1992 probably didn't notice, and requires that 64619304Speter * they be cancelled instead of beeping. Seems fine to me. 64719304Speter * 64819304Speter * Don't set the EC_MAPCOMMAND flag, apparently ] is a popular 64919304Speter * vi meta-character, and we don't want the user to wait while 65019304Speter * we time out a possible mapping. This *appears* to match 651254225Speter * historic vi practice, but with mapping characters, You Just 65219304Speter * Never Know. 65319304Speter */ 65419304Speter KEY(key, 0); 65519304Speter 65619304Speter if (vp->key != key) { 65719304Speterusage: if (ismotion == NULL) 65819304Speter s = kp->usage; 65919304Speter else if (ismotion->key == '~' && O_ISSET(sp, O_TILDEOP)) 66019304Speter s = tmotion.usage; 66119304Speter else 66219304Speter s = vikeys[ismotion->key].usage; 66319304Speter v_emsg(sp, s, VIM_USAGE); 66419304Speter return (GC_ERR); 66519304Speter } 66619304Speter } 66719304Speter /* Special case: 'z' command. */ 66819304Speter if (vp->key == 'z') { 66919304Speter KEY(vp->character, 0); 670254225Speter if (ISDIGIT(vp->character)) { 67119304Speter if (v_count(sp, vp->character, &vp->count2)) 67219304Speter return (GC_ERR); 67319304Speter F_SET(vp, VC_C2SET); 67419304Speter KEY(vp->character, 0); 67519304Speter } 67619304Speter } 67719304Speter 67819304Speter /* 679254225Speter * Commands that have motion components can be doubled to imply the 680254225Speter * current line. 68119304Speter */ 68219304Speter if (ismotion != NULL && ismotion->key != key && !LF_ISSET(V_MOVE)) { 68319304Speter msgq(sp, M_ERR, "210|%s may not be used as a motion command", 68419304Speter KEY_NAME(sp, key)); 68519304Speter return (GC_ERR); 68619304Speter } 68719304Speter 688254225Speter /* Pick up required trailing character. */ 68919304Speter if (LF_ISSET(V_CHAR)) 69019304Speter KEY(vp->character, 0); 69119304Speter 69219304Speter /* Get any associated cursor word. */ 693254225Speter if (F_ISSET(kp, V_KEYW) && v_curword(sp)) 69419304Speter return (GC_ERR); 69519304Speter 69619304Speter return (GC_OK); 69719304Speter 69819304Speteresc: switch (cpart) { 69919304Speter case COMMANDMODE: 70019304Speter msgq(sp, M_BERR, "211|Already in command mode"); 70119304Speter return (GC_ERR_NOFLUSH); 70219304Speter case ISPARTIAL: 70319304Speter break; 70419304Speter case NOTPARTIAL: 70519304Speter (void)sp->gp->scr_bell(sp); 70619304Speter break; 70719304Speter } 70819304Speter return (GC_ERR); 70919304Speter} 71019304Speter 71119304Speter/* 71219304Speter * v_motion -- 71319304Speter * 71419304Speter * Get resulting motion mark. 71519304Speter */ 71619304Speterstatic int 717254225Speterv_motion( 718254225Speter SCR *sp, 719254225Speter VICMD *dm, 720254225Speter VICMD *vp, 721254225Speter int *mappedp) 72219304Speter{ 72319304Speter VICMD motion; 72419304Speter size_t len; 72519304Speter u_long cnt; 72619304Speter u_int flags; 72719304Speter int tilde_reset, notused; 72819304Speter 72919304Speter /* 73019304Speter * If '.' command, use the dot motion, else get the motion command. 73119304Speter * Clear any line motion flags, the subsequent motion isn't always 73219304Speter * the same, i.e. "/aaa" may or may not be a line motion. 73319304Speter */ 73419304Speter if (F_ISSET(vp, VC_ISDOT)) { 73519304Speter motion = *dm; 73619304Speter F_SET(&motion, VC_ISDOT); 73719304Speter F_CLR(&motion, VM_COMMASK); 73819304Speter } else { 73919304Speter memset(&motion, 0, sizeof(VICMD)); 74019304Speter if (v_cmd(sp, NULL, &motion, vp, ¬used, mappedp) != GC_OK) 74119304Speter return (1); 74219304Speter } 74319304Speter 74419304Speter /* 74519304Speter * A count may be provided both to the command and to the motion, in 74619304Speter * which case the count is multiplicative. For example, "3y4y" is the 74719304Speter * same as "12yy". This count is provided to the motion command and 74819304Speter * not to the regular function. 74919304Speter */ 75019304Speter cnt = motion.count = F_ISSET(&motion, VC_C1SET) ? motion.count : 1; 75119304Speter if (F_ISSET(vp, VC_C1SET)) { 75219304Speter motion.count *= vp->count; 75319304Speter F_SET(&motion, VC_C1SET); 75419304Speter 75519304Speter /* 75619304Speter * Set flags to restore the original values of the command 75719304Speter * structure so dot commands can change the count values, 75819304Speter * e.g. "2dw" "3." deletes a total of five words. 75919304Speter */ 76019304Speter F_CLR(vp, VC_C1SET); 76119304Speter F_SET(vp, VC_C1RESET); 76219304Speter } 76319304Speter 76419304Speter /* 76519304Speter * Some commands can be repeated to indicate the current line. In 76619304Speter * this case, or if the command is a "line command", set the flags 76719304Speter * appropriately. If not a doubled command, run the function to get 76819304Speter * the resulting mark. 76919304Speter */ 77019304Speter if (vp->key == motion.key) { 77119304Speter F_SET(vp, VM_LDOUBLE | VM_LMODE); 77219304Speter 77319304Speter /* Set the origin of the command. */ 77419304Speter vp->m_start.lno = sp->lno; 77519304Speter vp->m_start.cno = 0; 77619304Speter 77719304Speter /* 77819304Speter * Set the end of the command. 77919304Speter * 78019304Speter * If the current line is missing, i.e. the file is empty, 78119304Speter * historic vi permitted a "cc" or "!!" command to insert 78219304Speter * text. 78319304Speter */ 78419304Speter vp->m_stop.lno = sp->lno + motion.count - 1; 78519304Speter if (db_get(sp, vp->m_stop.lno, 0, NULL, &len)) { 78619304Speter if (vp->m_stop.lno != 1 || 787254225Speter (vp->key != 'c' && vp->key != '!')) { 78819304Speter v_emsg(sp, NULL, VIM_EMPTY); 78919304Speter return (1); 79019304Speter } 79119304Speter vp->m_stop.cno = 0; 79219304Speter } else 79319304Speter vp->m_stop.cno = len ? len - 1 : 0; 79419304Speter } else { 79519304Speter /* 79619304Speter * Motion commands change the underlying movement (*snarl*). 79719304Speter * For example, "l" is illegal at the end of a line, but "dl" 79819304Speter * is not. Set flags so the function knows the situation. 79919304Speter */ 80019304Speter motion.rkp = vp->kp; 80119304Speter 80219304Speter /* 80319304Speter * XXX 80419304Speter * Use yank instead of creating a new motion command, it's a 80519304Speter * lot easier for now. 80619304Speter */ 80719304Speter if (vp->kp == &tmotion) { 80819304Speter tilde_reset = 1; 80919304Speter vp->kp = &vikeys['y']; 81019304Speter } else 81119304Speter tilde_reset = 0; 81219304Speter 81319304Speter /* 81419304Speter * Copy the key flags into the local structure, except for the 81519304Speter * RCM flags -- the motion command will set the RCM flags in 81619304Speter * the vp structure if necessary. This means that the motion 81719304Speter * command is expected to determine where the cursor ends up! 81819304Speter * However, we save off the current RCM mask and restore it if 81919304Speter * it no RCM flags are set by the motion command, with a small 82019304Speter * modification. 82119304Speter * 82219304Speter * We replace the VM_RCM_SET flag with the VM_RCM flag. This 82319304Speter * is so that cursor movement doesn't set the relative position 82419304Speter * unless the motion command explicitly specified it. This 82519304Speter * appears to match historic practice, but I've never been able 82619304Speter * to develop a hard-and-fast rule. 82719304Speter */ 82819304Speter flags = F_ISSET(vp, VM_RCM_MASK); 82919304Speter if (LF_ISSET(VM_RCM_SET)) { 83019304Speter LF_SET(VM_RCM); 83119304Speter LF_CLR(VM_RCM_SET); 83219304Speter } 83319304Speter F_CLR(vp, VM_RCM_MASK); 83419304Speter F_SET(&motion, motion.kp->flags & ~VM_RCM_MASK); 83519304Speter 83619304Speter /* 83719304Speter * Set the three cursor locations to the current cursor. This 83819304Speter * permits commands like 'j' and 'k', that are line oriented 83919304Speter * motions and have special cursor suck semantics when they are 84019304Speter * used as standalone commands, to ignore column positioning. 84119304Speter */ 84219304Speter motion.m_final.lno = 84319304Speter motion.m_stop.lno = motion.m_start.lno = sp->lno; 84419304Speter motion.m_final.cno = 84519304Speter motion.m_stop.cno = motion.m_start.cno = sp->cno; 84619304Speter 84719304Speter /* Run the function. */ 84819304Speter if ((motion.kp->func)(sp, &motion)) 84919304Speter return (1); 85019304Speter 85119304Speter /* 85219304Speter * If the current line is missing, i.e. the file is empty, 85319304Speter * historic vi allowed "c<motion>" or "!<motion>" to insert 85419304Speter * text. Otherwise fail -- most motion commands will have 85519304Speter * already failed, but some, e.g. G, succeed in empty files. 85619304Speter */ 85719304Speter if (!db_exist(sp, vp->m_stop.lno)) { 85819304Speter if (vp->m_stop.lno != 1 || 859254225Speter (vp->key != 'c' && vp->key != '!')) { 86019304Speter v_emsg(sp, NULL, VIM_EMPTY); 86119304Speter return (1); 86219304Speter } 86319304Speter vp->m_stop.cno = 0; 86419304Speter } 86519304Speter 86619304Speter /* 86719304Speter * XXX 86819304Speter * See above. 86919304Speter */ 87019304Speter if (tilde_reset) 87119304Speter vp->kp = &tmotion; 87219304Speter 87319304Speter /* 87419304Speter * Copy cut buffer, line mode and cursor position information 87519304Speter * from the motion command structure, i.e. anything that the 87619304Speter * motion command can set for us. The commands can flag the 87719304Speter * movement as a line motion (see v_sentence) as well as set 87819304Speter * the VM_RCM_* flags explicitly. 87919304Speter */ 88019304Speter F_SET(vp, F_ISSET(&motion, VM_COMMASK | VM_RCM_MASK)); 88119304Speter 88219304Speter /* 88319304Speter * If the motion command set no relative motion flags, use 88419304Speter * the (slightly) modified previous values. 88519304Speter */ 88619304Speter if (!F_ISSET(vp, VM_RCM_MASK)) 88719304Speter F_SET(vp, flags); 88819304Speter 88919304Speter /* 89019304Speter * Commands can change behaviors based on the motion command 89119304Speter * used, for example, the ! command repeated the last bang 89219304Speter * command if N or n was used as the motion. 89319304Speter */ 89419304Speter vp->rkp = motion.kp; 89519304Speter 89619304Speter /* 89719304Speter * Motion commands can reset all of the cursor information. 89819304Speter * If the motion is in the reverse direction, switch the 89919304Speter * from and to MARK's so that it's in a forward direction. 90019304Speter * Motions are from the from MARK to the to MARK (inclusive). 90119304Speter */ 90219304Speter if (motion.m_start.lno > motion.m_stop.lno || 903254225Speter (motion.m_start.lno == motion.m_stop.lno && 904254225Speter motion.m_start.cno > motion.m_stop.cno)) { 90519304Speter vp->m_start = motion.m_stop; 90619304Speter vp->m_stop = motion.m_start; 90719304Speter } else { 90819304Speter vp->m_start = motion.m_start; 90919304Speter vp->m_stop = motion.m_stop; 91019304Speter } 91119304Speter vp->m_final = motion.m_final; 91219304Speter } 91319304Speter 91419304Speter /* 91519304Speter * If the command sets dot, save the motion structure. The motion 91619304Speter * count was changed above and needs to be reset, that's why this 91719304Speter * is done here, and not in the calling routine. 91819304Speter */ 91919304Speter if (F_ISSET(vp->kp, V_DOT)) { 92019304Speter *dm = motion; 92119304Speter dm->count = cnt; 92219304Speter } 92319304Speter return (0); 92419304Speter} 92519304Speter 92619304Speter/* 92719304Speter * v_init -- 92819304Speter * Initialize the vi screen. 92919304Speter */ 93019304Speterstatic int 931254225Speterv_init(SCR *sp) 93219304Speter{ 93319304Speter GS *gp; 93419304Speter VI_PRIVATE *vip; 93519304Speter 93619304Speter gp = sp->gp; 93719304Speter vip = VIP(sp); 93819304Speter 93919304Speter /* Switch into vi. */ 94019304Speter if (gp->scr_screen(sp, SC_VI)) 94119304Speter return (1); 94219304Speter (void)gp->scr_attr(sp, SA_ALTERNATE, 1); 94319304Speter 94419304Speter F_CLR(sp, SC_EX | SC_SCR_EX); 94519304Speter F_SET(sp, SC_VI); 94619304Speter 94719304Speter /* 94819304Speter * Initialize screen values. 94919304Speter * 95019304Speter * Small windows: see vs_refresh(), section 6a. 95119304Speter * 95219304Speter * Setup: 95319304Speter * t_minrows is the minimum rows to display 95419304Speter * t_maxrows is the maximum rows to display (rows - 1) 95519304Speter * t_rows is the rows currently being displayed 95619304Speter */ 95719304Speter sp->rows = vip->srows = O_VAL(sp, O_LINES); 95819304Speter sp->cols = O_VAL(sp, O_COLUMNS); 95919304Speter sp->t_rows = sp->t_minrows = O_VAL(sp, O_WINDOW); 96019304Speter if (sp->rows != 1) { 96119304Speter if (sp->t_rows > sp->rows - 1) { 96219304Speter sp->t_minrows = sp->t_rows = sp->rows - 1; 96319304Speter msgq(sp, M_INFO, 96419304Speter "214|Windows option value is too large, max is %u", 965254225Speter (u_int)sp->t_rows); 96619304Speter } 96719304Speter sp->t_maxrows = sp->rows - 1; 96819304Speter } else 96919304Speter sp->t_maxrows = 1; 970254225Speter sp->roff = sp->coff = 0; 97119304Speter 97219304Speter /* Create a screen map. */ 97319304Speter CALLOC_RET(sp, HMAP, SMAP *, SIZE_HMAP(sp), sizeof(SMAP)); 97419304Speter TMAP = HMAP + (sp->t_rows - 1); 97519304Speter HMAP->lno = sp->lno; 97619304Speter HMAP->coff = 0; 97719304Speter HMAP->soff = 1; 97819304Speter 97919304Speter /* 98019304Speter * Fill the screen map from scratch -- try and center the line. That 98119304Speter * way if we're starting with a file we've seen before, we'll put the 98219304Speter * line in the middle, otherwise, it won't work and we'll end up with 98319304Speter * the line at the top. 98419304Speter */ 98519304Speter F_SET(sp, SC_SCR_REFORMAT | SC_SCR_CENTER); 98619304Speter 98719304Speter /* Invalidate the cursor. */ 98819304Speter F_SET(vip, VIP_CUR_INVALID); 98919304Speter 99019304Speter /* Paint the screen image from scratch. */ 99119304Speter F_SET(vip, VIP_N_EX_PAINT); 99219304Speter 99319304Speter return (0); 99419304Speter} 99519304Speter 99619304Speter/* 99719304Speter * v_dtoh -- 99819304Speter * Move all but the current screen to the hidden queue. 99919304Speter */ 100019304Speterstatic void 1001254225Speterv_dtoh(SCR *sp) 100219304Speter{ 100319304Speter GS *gp; 100419304Speter SCR *tsp; 100519304Speter int hidden; 100619304Speter 100719304Speter /* Move all screens to the hidden queue, tossing screen maps. */ 100819304Speter for (hidden = 0, gp = sp->gp; 1009254225Speter (tsp = TAILQ_FIRST(gp->dq)) != NULL; ++hidden) { 101019304Speter if (_HMAP(tsp) != NULL) { 101119304Speter free(_HMAP(tsp)); 101219304Speter _HMAP(tsp) = NULL; 101319304Speter } 1014254225Speter TAILQ_REMOVE(gp->dq, tsp, q); 1015254225Speter TAILQ_INSERT_TAIL(gp->hq, tsp, q); 1016254225Speter /* XXXX Change if hidden screens per window */ 1017254225Speter gp->scr_discard(tsp, NULL); 101819304Speter } 101919304Speter 102019304Speter /* Move current screen back to the display queue. */ 1021254225Speter TAILQ_REMOVE(gp->hq, sp, q); 1022254225Speter TAILQ_INSERT_TAIL(gp->dq, sp, q); 102319304Speter 102419304Speter if (hidden > 1) 102519304Speter msgq(sp, M_INFO, 1026254225Speter "319|%d screens backgrounded; use :display to list them", 102719304Speter hidden - 1); 102819304Speter} 102919304Speter 103019304Speter/* 1031254225Speter * v_curword -- 1032254225Speter * Get the word (tagstring, actually) the cursor is on. 1033254225Speter * 1034254225Speter * PUBLIC: int v_curword __P((SCR *)); 103519304Speter */ 1036254225Speterint 1037254225Speterv_curword(SCR *sp) 103819304Speter{ 103919304Speter VI_PRIVATE *vip; 104019304Speter size_t beg, end, len; 1041254225Speter int moved; 1042254225Speter CHAR_T *p; 104319304Speter 104419304Speter if (db_get(sp, sp->lno, DBG_FATAL, &p, &len)) 104519304Speter return (1); 104619304Speter 104719304Speter /* 104819304Speter * !!! 104919304Speter * Historically, tag commands skipped over any leading whitespace 105019304Speter * characters. Make this true in general when using cursor words. 105119304Speter * If movement, getting a cursor word implies moving the cursor to 105219304Speter * its beginning. Refresh now. 105319304Speter * 105419304Speter * !!! 105519304Speter * Find the beginning/end of the keyword. Keywords are currently 105619304Speter * used for cursor-word searching and for tags. Historical vi 105719304Speter * only used the word in a tag search from the cursor to the end 105819304Speter * of the word, i.e. if the cursor was on the 'b' in " abc ", the 105919304Speter * tag was "bc". For consistency, we make cursor word searches 106019304Speter * follow the same rule. 106119304Speter */ 106219304Speter for (moved = 0, 1063254225Speter beg = sp->cno; beg < len && ISSPACE(p[beg]); moved = 1, ++beg); 106419304Speter if (beg >= len) { 106519304Speter msgq(sp, M_BERR, "212|Cursor not in a word"); 106619304Speter return (1); 106719304Speter } 106819304Speter if (moved) { 106919304Speter sp->cno = beg; 107019304Speter (void)vs_refresh(sp, 0); 107119304Speter } 107219304Speter 1073254225Speter /* 1074254225Speter * Find the end of the word. 1075254225Speter * 1076254225Speter * !!! 1077254225Speter * Historically, vi accepted any non-blank as initial character 1078254225Speter * when building up a tagstring. Required by IEEE 1003.1-2001. 1079254225Speter */ 1080254225Speter for (end = beg; ++end < len && inword(p[end]);); 108119304Speter 108219304Speter vip = VIP(sp); 1083254225Speter vip->klen = len = (end - beg); 1084254225Speter BINC_RETW(sp, vip->keyw, vip->keywlen, len+1); 1085254225Speter MEMMOVE(vip->keyw, p + beg, len); 108619304Speter vip->keyw[len] = '\0'; /* XXX */ 108719304Speter return (0); 108819304Speter} 108919304Speter 109019304Speter/* 109119304Speter * v_alias -- 109219304Speter * Check for a command alias. 109319304Speter */ 109419304Speterstatic VIKEYS const * 1095254225Speterv_alias( 1096254225Speter SCR *sp, 1097254225Speter VICMD *vp, 1098254225Speter VIKEYS const *kp) 109919304Speter{ 110019304Speter CHAR_T push; 110119304Speter 110219304Speter switch (vp->key) { 110319304Speter case 'C': /* C -> c$ */ 110419304Speter push = '$'; 110519304Speter vp->key = 'c'; 110619304Speter break; 110719304Speter case 'D': /* D -> d$ */ 110819304Speter push = '$'; 110919304Speter vp->key = 'd'; 111019304Speter break; 111119304Speter case 'S': /* S -> c_ */ 111219304Speter push = '_'; 111319304Speter vp->key = 'c'; 111419304Speter break; 111519304Speter case 'Y': /* Y -> y_ */ 111619304Speter push = '_'; 111719304Speter vp->key = 'y'; 111819304Speter break; 111919304Speter default: 112019304Speter return (kp); 112119304Speter } 112219304Speter return (v_event_push(sp, 112319304Speter NULL, &push, 1, CH_NOMAP | CH_QUOTED) ? NULL : &vikeys[vp->key]); 112419304Speter} 112519304Speter 112619304Speter/* 112719304Speter * v_count -- 112819304Speter * Return the next count. 112919304Speter */ 113019304Speterstatic int 1131254225Speterv_count( 1132254225Speter SCR *sp, 1133254225Speter ARG_CHAR_T fkey, 1134254225Speter u_long *countp) 113519304Speter{ 113619304Speter EVENT ev; 113719304Speter u_long count, tc; 113819304Speter 113919304Speter ev.e_c = fkey; 114019304Speter count = tc = 0; 114119304Speter do { 114219304Speter /* 114319304Speter * XXX 114419304Speter * Assume that overflow results in a smaller number. 114519304Speter */ 114619304Speter tc = count * 10 + ev.e_c - '0'; 114719304Speter if (count > tc) { 114819304Speter /* Toss to the next non-digit. */ 114919304Speter do { 115019304Speter if (v_key(sp, 0, &ev, 115119304Speter EC_MAPCOMMAND | EC_MAPNODIGIT) != GC_OK) 115219304Speter return (1); 1153254225Speter } while (ISDIGIT(ev.e_c)); 115419304Speter msgq(sp, M_ERR, 115519304Speter "235|Number larger than %lu", ULONG_MAX); 115619304Speter return (1); 115719304Speter } 115819304Speter count = tc; 115919304Speter if (v_key(sp, 0, &ev, EC_MAPCOMMAND | EC_MAPNODIGIT) != GC_OK) 116019304Speter return (1); 1161254225Speter } while (ISDIGIT(ev.e_c)); 116219304Speter *countp = count; 116319304Speter return (0); 116419304Speter} 116519304Speter 116619304Speter/* 116719304Speter * v_key -- 116819304Speter * Return the next event. 116919304Speter */ 117019304Speterstatic gcret_t 1171254225Speterv_key( 1172254225Speter SCR *sp, 1173254225Speter int command_events, 1174254225Speter EVENT *evp, 1175254225Speter u_int32_t ec_flags) 117619304Speter{ 117719304Speter u_int32_t quote; 117819304Speter 117919304Speter for (quote = 0;;) { 118019304Speter if (v_event_get(sp, evp, 0, ec_flags | quote)) 118119304Speter return (GC_FATAL); 118219304Speter quote = 0; 118319304Speter 118419304Speter switch (evp->e_event) { 118519304Speter case E_CHARACTER: 118619304Speter /* 118719304Speter * !!! 118819304Speter * Historically, ^V was ignored in the command stream, 118919304Speter * although it had a useful side-effect of interrupting 119019304Speter * mappings. Adding a quoting bit to the call probably 119119304Speter * extends historic practice, but it feels right. 119219304Speter */ 119319304Speter if (evp->e_value == K_VLNEXT) { 119419304Speter quote = EC_QUOTED; 119519304Speter break; 119619304Speter } 119719304Speter return (GC_OK); 119819304Speter case E_ERR: 119919304Speter case E_EOF: 120019304Speter return (GC_FATAL); 120119304Speter case E_INTERRUPT: 120219304Speter /* 120319304Speter * !!! 120419304Speter * Historically, vi beeped on command level interrupts. 120519304Speter * 120619304Speter * Historically, vi exited to ex mode if no file was 120719304Speter * named on the command line, and two interrupts were 120819304Speter * generated in a row. (Just figured you might want 120919304Speter * to know that.) 121019304Speter */ 121119304Speter (void)sp->gp->scr_bell(sp); 121219304Speter return (GC_INTERRUPT); 121319304Speter case E_REPAINT: 121419304Speter if (vs_repaint(sp, evp)) 121519304Speter return (GC_FATAL); 121619304Speter break; 121719304Speter case E_WRESIZE: 121819304Speter return (GC_ERR); 121919304Speter /* FALLTHROUGH */ 122019304Speter default: 122119304Speter v_event_err(sp, evp); 122219304Speter return (GC_ERR); 122319304Speter } 122419304Speter } 122519304Speter /* NOTREACHED */ 122619304Speter} 122719304Speter 122819304Speter#if defined(DEBUG) && defined(COMLOG) 122919304Speter/* 123019304Speter * v_comlog -- 123119304Speter * Log the contents of the command structure. 123219304Speter */ 123319304Speterstatic void 1234254225Speterv_comlog( 1235254225Speter SCR *sp, 1236254225Speter VICMD *vp) 123719304Speter{ 1238254225Speter TRACE(sp, "vcmd: "WC, vp->key); 123919304Speter if (F_ISSET(vp, VC_BUFFER)) 1240254225Speter TRACE(sp, " buffer: "WC, vp->buffer); 124119304Speter if (F_ISSET(vp, VC_C1SET)) 124219304Speter TRACE(sp, " c1: %lu", vp->count); 124319304Speter if (F_ISSET(vp, VC_C2SET)) 124419304Speter TRACE(sp, " c2: %lu", vp->count2); 124519304Speter TRACE(sp, " flags: 0x%x\n", vp->flags); 124619304Speter} 124719304Speter#endif 1248