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 1319304Speterstatic const char sccsid[] = "@(#)vi.c 10.57 (Berkeley) 10/13/96"; 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_keyword __P((SCR *)); 4419304Speterstatic int v_motion __P((SCR *, VICMD *, VICMD *, int *)); 4519304Speter 4619304Speter#if defined(DEBUG) && defined(COMLOG) 4719304Speterstatic void v_comlog __P((SCR *, VICMD *)); 4819304Speter#endif 4919304Speter 5019304Speter/* 5119304Speter * Side-effect: 5219304Speter * The dot structure can be set by the underlying vi functions, 5319304Speter * see v_Put() and v_put(). 5419304Speter */ 5519304Speter#define DOT (&VIP(sp)->sdot) 5619304Speter#define DOTMOTION (&VIP(sp)->sdotmotion) 5719304Speter 5819304Speter/* 5919304Speter * vi -- 6019304Speter * Main vi command loop. 6119304Speter * 6219304Speter * PUBLIC: int vi __P((SCR **)); 6319304Speter */ 6419304Speterint 6519304Spetervi(spp) 6619304Speter SCR **spp; 6719304Speter{ 6819304Speter GS *gp; 6919304Speter MARK abs; 7019304Speter SCR *next, *sp; 7119304Speter VICMD cmd, *vp; 7219304Speter VI_PRIVATE *vip; 7319304Speter int comcount, mapped, rval; 7419304Speter 7519304Speter /* Get the first screen. */ 7619304Speter sp = *spp; 7719304Speter gp = sp->gp; 7819304Speter 7919304Speter /* Initialize the command structure. */ 8019304Speter vp = &cmd; 8119304Speter memset(vp, 0, sizeof(VICMD)); 8219304Speter 8319304Speter /* Reset strange attraction. */ 8419304Speter F_SET(vp, VM_RCM_SET); 8519304Speter 8619304Speter /* Initialize the vi screen. */ 8719304Speter if (v_init(sp)) 8819304Speter return (1); 8919304Speter 9019304Speter /* Set the focus. */ 9119304Speter (void)sp->gp->scr_rename(sp, sp->frp->name, 1); 9219304Speter 9319304Speter for (vip = VIP(sp), rval = 0;;) { 9419304Speter /* Resolve messages. */ 9519304Speter if (!MAPPED_KEYS_WAITING(sp) && vs_resolve(sp, NULL, 0)) 9619304Speter goto ret; 9719304Speter 9819304Speter /* 9919304Speter * If not skipping a refresh, return to command mode and 10019304Speter * refresh the screen. 10119304Speter */ 10219304Speter if (F_ISSET(vip, VIP_S_REFRESH)) 10319304Speter F_CLR(vip, VIP_S_REFRESH); 10419304Speter else { 10519304Speter sp->showmode = SM_COMMAND; 10619304Speter if (vs_refresh(sp, 0)) 10719304Speter goto ret; 10819304Speter } 10919304Speter 11019304Speter /* Set the new favorite position. */ 11119304Speter if (F_ISSET(vp, VM_RCM_SET | VM_RCM_SETFNB | VM_RCM_SETNNB)) { 11219304Speter F_CLR(vip, VIP_RCM_LAST); 11319304Speter (void)vs_column(sp, &sp->rcm); 11419304Speter } 11519304Speter 11619304Speter /* 11719304Speter * If not currently in a map, log the cursor position, 11819304Speter * and set a flag so that this command can become the 11919304Speter * DOT command. 12019304Speter */ 12119304Speter if (MAPPED_KEYS_WAITING(sp)) 12219304Speter mapped = 1; 12319304Speter else { 12419304Speter if (log_cursor(sp)) 12519304Speter goto err; 12619304Speter mapped = 0; 12719304Speter } 12819304Speter 12919304Speter /* 13019304Speter * There may be an ex command waiting, and we returned here 13119304Speter * only because we exited a screen or file. In this case, 13219304Speter * we simply go back into the ex parser. 13319304Speter */ 13419304Speter if (EXCMD_RUNNING(gp)) { 13519304Speter vp->kp = &vikeys[':']; 13619304Speter goto ex_continue; 13719304Speter } 13819304Speter 13919304Speter /* Refresh the command structure. */ 14019304Speter memset(vp, 0, sizeof(VICMD)); 14119304Speter 14219304Speter /* 14319304Speter * We get a command, which may or may not have an associated 14419304Speter * motion. If it does, we get it too, calling its underlying 14519304Speter * function to get the resulting mark. We then call the 14619304Speter * command setting the cursor to the resulting mark. 14719304Speter * 14819304Speter * !!! 14919304Speter * Vi historically flushed mapped characters on error, but 15019304Speter * entering extra <escape> characters at the beginning of 15119304Speter * a map wasn't considered an error -- in fact, users would 15219304Speter * put leading <escape> characters in maps to clean up vi 15319304Speter * state before the map was interpreted. Beauty! 15419304Speter */ 15519304Speter switch (v_cmd(sp, DOT, vp, NULL, &comcount, &mapped)) { 15619304Speter case GC_ERR: 15719304Speter goto err; 15819304Speter case GC_ERR_NOFLUSH: 15919304Speter goto gc_err_noflush; 16019304Speter case GC_EVENT: 16119304Speter if (v_event_exec(sp, vp)) 16219304Speter goto err; 16319304Speter goto gc_event; 16419304Speter case GC_FATAL: 16519304Speter goto ret; 16619304Speter case GC_INTERRUPT: 16719304Speter goto intr; 16819304Speter case GC_OK: 16919304Speter break; 17019304Speter } 17119304Speter 17219304Speter /* Check for security setting. */ 17319304Speter if (F_ISSET(vp->kp, V_SECURE) && O_ISSET(sp, O_SECURE)) { 17419304Speter ex_emsg(sp, KEY_NAME(sp, vp->key), EXM_SECURE); 17519304Speter goto err; 17619304Speter } 17719304Speter 17819304Speter /* 17919304Speter * Historical practice: if a dot command gets a new count, 18019304Speter * any motion component goes away, i.e. "d3w2." deletes a 18119304Speter * total of 5 words. 18219304Speter */ 18319304Speter if (F_ISSET(vp, VC_ISDOT) && comcount) 18419304Speter DOTMOTION->count = 1; 18519304Speter 18619304Speter /* Copy the key flags into the local structure. */ 18719304Speter F_SET(vp, vp->kp->flags); 18819304Speter 18919304Speter /* Prepare to set the previous context. */ 19019304Speter if (F_ISSET(vp, V_ABS | V_ABS_C | V_ABS_L)) { 19119304Speter abs.lno = sp->lno; 19219304Speter abs.cno = sp->cno; 19319304Speter } 19419304Speter 19519304Speter /* 19619304Speter * Set the three cursor locations to the current cursor. The 19719304Speter * underlying routines don't bother if the cursor doesn't move. 19819304Speter * This also handles line commands (e.g. Y) defaulting to the 19919304Speter * current line. 20019304Speter */ 20119304Speter vp->m_start.lno = vp->m_stop.lno = vp->m_final.lno = sp->lno; 20219304Speter vp->m_start.cno = vp->m_stop.cno = vp->m_final.cno = sp->cno; 20319304Speter 20419304Speter /* 20519304Speter * Do any required motion; v_motion sets the from MARK and the 20619304Speter * line mode flag, as well as the VM_RCM flags. 20719304Speter */ 20819304Speter if (F_ISSET(vp, V_MOTION) && 20919304Speter v_motion(sp, DOTMOTION, vp, &mapped)) { 21019304Speter if (INTERRUPTED(sp)) 21119304Speter goto intr; 21219304Speter goto err; 21319304Speter } 21419304Speter 21519304Speter /* 21619304Speter * If a count is set and the command is line oriented, set the 21719304Speter * to MARK here relative to the cursor/from MARK. This is for 21819304Speter * commands that take both counts and motions, i.e. "4yy" and 21919304Speter * "y%". As there's no way the command can know which the user 22019304Speter * did, we have to do it here. (There are commands that are 22119304Speter * line oriented and that take counts ("#G", "#H"), for which 22219304Speter * this calculation is either completely meaningless or wrong. 22319304Speter * Each command must validate the value for itself. 22419304Speter */ 22519304Speter if (F_ISSET(vp, VC_C1SET) && F_ISSET(vp, VM_LMODE)) 22619304Speter vp->m_stop.lno += vp->count - 1; 22719304Speter 22819304Speter /* Increment the command count. */ 22919304Speter ++sp->ccnt; 23019304Speter 23119304Speter#if defined(DEBUG) && defined(COMLOG) 23219304Speter v_comlog(sp, vp); 23319304Speter#endif 23419304Speter /* Call the function. */ 23519304Speterex_continue: if (vp->kp->func(sp, vp)) 23619304Speter goto err; 23719304Spetergc_event: 23819304Speter#ifdef DEBUG 23919304Speter /* Make sure no function left the temporary space locked. */ 24019304Speter if (F_ISSET(gp, G_TMP_INUSE)) { 24119304Speter F_CLR(gp, G_TMP_INUSE); 24219304Speter msgq(sp, M_ERR, 24319304Speter "232|vi: temporary buffer not released"); 24419304Speter } 24519304Speter#endif 24619304Speter /* 24719304Speter * If we're exiting this screen, move to the next one, or, if 24819304Speter * there aren't any more, return to the main editor loop. The 24919304Speter * ordering is careful, don't discard the contents of sp until 25019304Speter * the end. 25119304Speter */ 25219304Speter if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) { 25319304Speter if (file_end(sp, NULL, F_ISSET(sp, SC_EXIT_FORCE))) 25419304Speter goto ret; 25519304Speter if (vs_discard(sp, &next)) 25619304Speter goto ret; 25719304Speter if (next == NULL && vs_swap(sp, &next, NULL)) 25819304Speter goto ret; 25919304Speter *spp = next; 26019304Speter if (screen_end(sp)) 26119304Speter goto ret; 26219304Speter if (next == NULL) 26319304Speter break; 26419304Speter 26519304Speter /* Switch screens, change focus. */ 26619304Speter sp = next; 26719304Speter vip = VIP(sp); 26819304Speter (void)sp->gp->scr_rename(sp, sp->frp->name, 1); 26919304Speter 27019304Speter /* Don't trust the cursor. */ 27119304Speter F_SET(vip, VIP_CUR_INVALID); 27219304Speter 27319304Speter continue; 27419304Speter } 27519304Speter 27619304Speter /* 27719304Speter * Set the dot command structure. 27819304Speter * 27919304Speter * !!! 28019304Speter * Historically, commands which used mapped keys did not 28119304Speter * set the dot command, with the exception of the text 28219304Speter * input commands. 28319304Speter */ 28419304Speter if (F_ISSET(vp, V_DOT) && !mapped) { 28519304Speter *DOT = cmd; 28619304Speter F_SET(DOT, VC_ISDOT); 28719304Speter 28819304Speter /* 28919304Speter * If a count was supplied for both the command and 29019304Speter * its motion, the count was used only for the motion. 29119304Speter * Turn the count back on for the dot structure. 29219304Speter */ 29319304Speter if (F_ISSET(vp, VC_C1RESET)) 29419304Speter F_SET(DOT, VC_C1SET); 29519304Speter 29619304Speter /* VM flags aren't retained. */ 29719304Speter F_CLR(DOT, VM_COMMASK | VM_RCM_MASK); 29819304Speter } 29919304Speter 30019304Speter /* 30119304Speter * Some vi row movements are "attracted" to the last position 30219304Speter * set, i.e. the VM_RCM commands are moths to the VM_RCM_SET 30319304Speter * commands' candle. If the movement is to the EOL the vi 30419304Speter * command handles it. If it's to the beginning, we handle it 30519304Speter * here. 30619304Speter * 30719304Speter * Note, some commands (e.g. _, ^) don't set the VM_RCM_SETFNB 30819304Speter * flag, but do the work themselves. The reason is that they 30919304Speter * have to modify the column in case they're being used as a 31019304Speter * motion component. Other similar commands (e.g. +, -) don't 31119304Speter * have to modify the column because they are always line mode 31219304Speter * operations when used as motions, so the column number isn't 31319304Speter * of any interest. 31419304Speter * 31519304Speter * Does this totally violate the screen and editor layering? 31619304Speter * You betcha. As they say, if you think you understand it, 31719304Speter * you don't. 31819304Speter */ 31919304Speter switch (F_ISSET(vp, VM_RCM_MASK)) { 32019304Speter case 0: 32119304Speter case VM_RCM_SET: 32219304Speter break; 32319304Speter case VM_RCM: 32419304Speter vp->m_final.cno = vs_rcm(sp, 32519304Speter vp->m_final.lno, F_ISSET(vip, VIP_RCM_LAST)); 32619304Speter break; 32719304Speter case VM_RCM_SETLAST: 32819304Speter F_SET(vip, VIP_RCM_LAST); 32919304Speter break; 33019304Speter case VM_RCM_SETFNB: 33119304Speter vp->m_final.cno = 0; 33219304Speter /* FALLTHROUGH */ 33319304Speter case VM_RCM_SETNNB: 33419304Speter if (nonblank(sp, vp->m_final.lno, &vp->m_final.cno)) 33519304Speter goto err; 33619304Speter break; 33719304Speter default: 33819304Speter abort(); 33919304Speter } 34019304Speter 34119304Speter /* Update the cursor. */ 34219304Speter sp->lno = vp->m_final.lno; 34319304Speter sp->cno = vp->m_final.cno; 34419304Speter 34519304Speter /* 34619304Speter * Set the absolute mark -- set even if a tags or similar 34719304Speter * command, since the tag may be moving to the same file. 34819304Speter */ 34919304Speter if ((F_ISSET(vp, V_ABS) || 35019304Speter F_ISSET(vp, V_ABS_L) && sp->lno != abs.lno || 35119304Speter F_ISSET(vp, V_ABS_C) && 35219304Speter (sp->lno != abs.lno || sp->cno != abs.cno)) && 35319304Speter mark_set(sp, ABSMARK1, &abs, 1)) 35419304Speter goto err; 35519304Speter 35619304Speter if (0) { 35719304Spetererr: if (v_event_flush(sp, CH_MAPPED)) 35819304Speter msgq(sp, M_BERR, 35919304Speter "110|Vi command failed: mapped keys discarded"); 36019304Speter } 36119304Speter 36219304Speter /* 36319304Speter * Check and clear interrupts. There's an obvious race, but 36419304Speter * it's not worth fixing. 36519304Speter */ 36619304Spetergc_err_noflush: if (INTERRUPTED(sp)) { 36719304Speterintr: CLR_INTERRUPT(sp); 36819304Speter if (v_event_flush(sp, CH_MAPPED)) 36919304Speter msgq(sp, M_ERR, 37019304Speter "231|Interrupted: mapped keys discarded"); 37119304Speter else 37219304Speter msgq(sp, M_ERR, "236|Interrupted"); 37319304Speter } 37419304Speter 37519304Speter /* If the last command switched screens, update. */ 37619304Speter if (F_ISSET(sp, SC_SSWITCH)) { 37719304Speter F_CLR(sp, SC_SSWITCH); 37819304Speter 37919304Speter /* 38019304Speter * If the current screen is still displayed, it will 38119304Speter * need a new status line. 38219304Speter */ 38319304Speter F_SET(sp, SC_STATUS); 38419304Speter 38519304Speter /* Switch screens, change focus. */ 38619304Speter sp = sp->nextdisp; 38719304Speter vip = VIP(sp); 38819304Speter (void)sp->gp->scr_rename(sp, sp->frp->name, 1); 38919304Speter 39019304Speter /* Don't trust the cursor. */ 39119304Speter F_SET(vip, VIP_CUR_INVALID); 39219304Speter 39319304Speter /* Refresh so we can display messages. */ 39419304Speter if (vs_refresh(sp, 1)) 39519304Speter return (1); 39619304Speter } 39719304Speter 39819304Speter /* If the last command switched files, change focus. */ 39919304Speter if (F_ISSET(sp, SC_FSWITCH)) { 40019304Speter F_CLR(sp, SC_FSWITCH); 40119304Speter (void)sp->gp->scr_rename(sp, sp->frp->name, 1); 40219304Speter } 40319304Speter 40419304Speter /* If leaving vi, return to the main editor loop. */ 40519304Speter if (F_ISSET(gp, G_SRESTART) || F_ISSET(sp, SC_EX)) { 40619304Speter *spp = sp; 40719304Speter v_dtoh(sp); 40819304Speter break; 40919304Speter } 41019304Speter } 41119304Speter if (0) 41219304Speterret: rval = 1; 41319304Speter return (rval); 41419304Speter} 41519304Speter 41619304Speter#define KEY(key, ec_flags) { \ 41719304Speter if ((gcret = v_key(sp, 0, &ev, ec_flags)) != GC_OK) \ 41819304Speter return (gcret); \ 41919304Speter if (ev.e_value == K_ESCAPE) \ 42019304Speter goto esc; \ 42119304Speter if (F_ISSET(&ev.e_ch, CH_MAPPED)) \ 42219304Speter *mappedp = 1; \ 42319304Speter key = ev.e_c; \ 42419304Speter} 42519304Speter 42619304Speter/* 42719304Speter * The O_TILDEOP option makes the ~ command take a motion instead 42819304Speter * of a straight count. This is the replacement structure we use 42919304Speter * instead of the one currently in the VIKEYS table. 43019304Speter * 43119304Speter * XXX 43219304Speter * This should probably be deleted -- it's not all that useful, and 43319304Speter * we get help messages wrong. 43419304Speter */ 43519304SpeterVIKEYS const tmotion = { 43619304Speter v_mulcase, V_CNT|V_DOT|V_MOTION|VM_RCM_SET, 43719304Speter "[count]~[count]motion", 43819304Speter " ~ change case to motion" 43919304Speter}; 44019304Speter 44119304Speter/* 44219304Speter * v_cmd -- 44319304Speter * 44419304Speter * The command structure for vi is less complex than ex (and don't think 44519304Speter * I'm not grateful!) The command syntax is: 44619304Speter * 44719304Speter * [count] [buffer] [count] key [[motion] | [buffer] [character]] 44819304Speter * 44919304Speter * and there are several special cases. The motion value is itself a vi 45019304Speter * command, with the syntax: 45119304Speter * 45219304Speter * [count] key [character] 45319304Speter */ 45419304Speterstatic gcret_t 45519304Speterv_cmd(sp, dp, vp, ismotion, comcountp, mappedp) 45619304Speter SCR *sp; 45719304Speter VICMD *dp, *vp; 45819304Speter VICMD *ismotion; /* Previous key if getting motion component. */ 45919304Speter int *comcountp, *mappedp; 46019304Speter{ 46119304Speter enum { COMMANDMODE, ISPARTIAL, NOTPARTIAL } cpart; 46219304Speter EVENT ev; 46319304Speter VIKEYS const *kp; 46419304Speter gcret_t gcret; 46519304Speter u_int flags; 46619304Speter CHAR_T key; 46719304Speter char *s; 46819304Speter 46919304Speter /* 47019304Speter * Get a key. 47119304Speter * 47219304Speter * <escape> cancels partial commands, i.e. a command where at least 47319304Speter * one non-numeric character has been entered. Otherwise, it beeps 47419304Speter * the terminal. 47519304Speter * 47619304Speter * !!! 47719304Speter * POSIX 1003.2-1992 explicitly disallows cancelling commands where 47819304Speter * all that's been entered is a number, requiring that the terminal 47919304Speter * be alerted. 48019304Speter */ 48119304Speter cpart = ismotion == NULL ? COMMANDMODE : ISPARTIAL; 48219304Speter if ((gcret = 48319304Speter v_key(sp, ismotion == NULL, &ev, EC_MAPCOMMAND)) != GC_OK) { 48419304Speter if (gcret == GC_EVENT) 48519304Speter vp->ev = ev; 48619304Speter return (gcret); 48719304Speter } 48819304Speter if (ev.e_value == K_ESCAPE) 48919304Speter goto esc; 49019304Speter if (F_ISSET(&ev.e_ch, CH_MAPPED)) 49119304Speter *mappedp = 1; 49219304Speter key = ev.e_c; 49319304Speter 49419304Speter if (ismotion == NULL) 49519304Speter cpart = NOTPARTIAL; 49619304Speter 49719304Speter /* Pick up optional buffer. */ 49819304Speter if (key == '"') { 49919304Speter cpart = ISPARTIAL; 50019304Speter if (ismotion != NULL) { 50119304Speter v_emsg(sp, NULL, VIM_COMBUF); 50219304Speter return (GC_ERR); 50319304Speter } 50419304Speter KEY(vp->buffer, 0); 50519304Speter F_SET(vp, VC_BUFFER); 50619304Speter 50719304Speter KEY(key, EC_MAPCOMMAND); 50819304Speter } 50919304Speter 51019304Speter /* 51119304Speter * Pick up optional count, where a leading 0 is not a count, 51219304Speter * it's a command. 51319304Speter */ 51419304Speter if (isdigit(key) && key != '0') { 51519304Speter if (v_count(sp, key, &vp->count)) 51619304Speter return (GC_ERR); 51719304Speter F_SET(vp, VC_C1SET); 51819304Speter *comcountp = 1; 51919304Speter 52019304Speter KEY(key, EC_MAPCOMMAND); 52119304Speter } else 52219304Speter *comcountp = 0; 52319304Speter 52419304Speter /* Pick up optional buffer. */ 52519304Speter if (key == '"') { 52619304Speter cpart = ISPARTIAL; 52719304Speter if (F_ISSET(vp, VC_BUFFER)) { 52819304Speter msgq(sp, M_ERR, "234|Only one buffer may be specified"); 52919304Speter return (GC_ERR); 53019304Speter } 53119304Speter if (ismotion != NULL) { 53219304Speter v_emsg(sp, NULL, VIM_COMBUF); 53319304Speter return (GC_ERR); 53419304Speter } 53519304Speter KEY(vp->buffer, 0); 53619304Speter F_SET(vp, VC_BUFFER); 53719304Speter 53819304Speter KEY(key, EC_MAPCOMMAND); 53919304Speter } 54019304Speter 54119304Speter /* Check for an OOB command key. */ 54219304Speter cpart = ISPARTIAL; 54319304Speter if (key > MAXVIKEY) { 54419304Speter v_emsg(sp, KEY_NAME(sp, key), VIM_NOCOM); 54519304Speter return (GC_ERR); 54619304Speter } 54719304Speter kp = &vikeys[vp->key = key]; 54819304Speter 54919304Speter /* 55019304Speter * !!! 55119304Speter * Historically, D accepted and then ignored a count. Match it. 55219304Speter */ 55319304Speter if (vp->key == 'D' && F_ISSET(vp, VC_C1SET)) { 55419304Speter *comcountp = 0; 55519304Speter vp->count = 0; 55619304Speter F_CLR(vp, VC_C1SET); 55719304Speter } 55819304Speter 55919304Speter /* Check for command aliases. */ 56019304Speter if (kp->func == NULL && (kp = v_alias(sp, vp, kp)) == NULL) 56119304Speter return (GC_ERR); 56219304Speter 56319304Speter /* The tildeop option makes the ~ command take a motion. */ 56419304Speter if (key == '~' && O_ISSET(sp, O_TILDEOP)) 56519304Speter kp = &tmotion; 56619304Speter 56719304Speter vp->kp = kp; 56819304Speter 56919304Speter /* 57019304Speter * Find the command. The only legal command with no underlying 57119304Speter * function is dot. It's historic practice that <escape> doesn't 57219304Speter * just erase the preceding number, it beeps the terminal as well. 57319304Speter * It's a common problem, so just beep the terminal unless verbose 57419304Speter * was set. 57519304Speter */ 57619304Speter if (kp->func == NULL) { 57719304Speter if (key != '.') { 57819304Speter v_emsg(sp, KEY_NAME(sp, key), 57919304Speter ev.e_value == K_ESCAPE ? VIM_NOCOM_B : VIM_NOCOM); 58019304Speter return (GC_ERR); 58119304Speter } 58219304Speter 58319304Speter /* If called for a motion command, stop now. */ 58419304Speter if (dp == NULL) 58519304Speter goto usage; 58619304Speter 58719304Speter /* 58819304Speter * !!! 58919304Speter * If a '.' is immediately entered after an undo command, we 59019304Speter * replay the log instead of redoing the last command. This 59119304Speter * is necessary because 'u' can't set the dot command -- see 59219304Speter * vi/v_undo.c:v_undo for details. 59319304Speter */ 59419304Speter if (VIP(sp)->u_ccnt == sp->ccnt) { 59519304Speter vp->kp = &vikeys['u']; 59619304Speter F_SET(vp, VC_ISDOT); 59719304Speter return (GC_OK); 59819304Speter } 59919304Speter 60019304Speter /* Otherwise, a repeatable command must have been executed. */ 60119304Speter if (!F_ISSET(dp, VC_ISDOT)) { 60219304Speter msgq(sp, M_ERR, "208|No command to repeat"); 60319304Speter return (GC_ERR); 60419304Speter } 60519304Speter 60619304Speter /* Set new count/buffer, if any, and return. */ 60719304Speter if (F_ISSET(vp, VC_C1SET)) { 60819304Speter F_SET(dp, VC_C1SET); 60919304Speter dp->count = vp->count; 61019304Speter } 61119304Speter if (F_ISSET(vp, VC_BUFFER)) 61219304Speter dp->buffer = vp->buffer; 61319304Speter 61419304Speter *vp = *dp; 61519304Speter return (GC_OK); 61619304Speter } 61719304Speter 61819304Speter /* Set the flags based on the command flags. */ 61919304Speter flags = kp->flags; 62019304Speter 62119304Speter /* Check for illegal count. */ 62219304Speter if (F_ISSET(vp, VC_C1SET) && !LF_ISSET(V_CNT)) 62319304Speter goto usage; 62419304Speter 62519304Speter /* Illegal motion command. */ 62619304Speter if (ismotion == NULL) { 62719304Speter /* Illegal buffer. */ 62819304Speter if (!LF_ISSET(V_OBUF) && F_ISSET(vp, VC_BUFFER)) 62919304Speter goto usage; 63019304Speter 63119304Speter /* Required buffer. */ 63219304Speter if (LF_ISSET(V_RBUF)) { 63319304Speter KEY(vp->buffer, 0); 63419304Speter F_SET(vp, VC_BUFFER); 63519304Speter } 63619304Speter } 63719304Speter 63819304Speter /* 63919304Speter * Special case: '[', ']' and 'Z' commands. Doesn't the fact that 64019304Speter * the *single* characters don't mean anything but the *doubled* 64119304Speter * characters do, just frost your shorts? 64219304Speter */ 64319304Speter if (vp->key == '[' || vp->key == ']' || vp->key == 'Z') { 64419304Speter /* 64519304Speter * Historically, half entered [[, ]] or Z commands weren't 64619304Speter * cancelled by <escape>, the terminal was beeped instead. 64719304Speter * POSIX.2-1992 probably didn't notice, and requires that 64819304Speter * they be cancelled instead of beeping. Seems fine to me. 64919304Speter * 65019304Speter * Don't set the EC_MAPCOMMAND flag, apparently ] is a popular 65119304Speter * vi meta-character, and we don't want the user to wait while 65219304Speter * we time out a possible mapping. This *appears* to match 65319304Speter * historic vi practice, but with mapping characters, you Just 65419304Speter * Never Know. 65519304Speter */ 65619304Speter KEY(key, 0); 65719304Speter 65819304Speter if (vp->key != key) { 65919304Speterusage: if (ismotion == NULL) 66019304Speter s = kp->usage; 66119304Speter else if (ismotion->key == '~' && O_ISSET(sp, O_TILDEOP)) 66219304Speter s = tmotion.usage; 66319304Speter else 66419304Speter s = vikeys[ismotion->key].usage; 66519304Speter v_emsg(sp, s, VIM_USAGE); 66619304Speter return (GC_ERR); 66719304Speter } 66819304Speter } 66919304Speter /* Special case: 'z' command. */ 67019304Speter if (vp->key == 'z') { 67119304Speter KEY(vp->character, 0); 67219304Speter if (isdigit(vp->character)) { 67319304Speter if (v_count(sp, vp->character, &vp->count2)) 67419304Speter return (GC_ERR); 67519304Speter F_SET(vp, VC_C2SET); 67619304Speter KEY(vp->character, 0); 67719304Speter } 67819304Speter } 67919304Speter 68019304Speter /* 68119304Speter * Commands that have motion components can be doubled to 68219304Speter * imply the current line. 68319304Speter */ 68419304Speter if (ismotion != NULL && ismotion->key != key && !LF_ISSET(V_MOVE)) { 68519304Speter msgq(sp, M_ERR, "210|%s may not be used as a motion command", 68619304Speter KEY_NAME(sp, key)); 68719304Speter return (GC_ERR); 68819304Speter } 68919304Speter 69019304Speter /* Required character. */ 69119304Speter if (LF_ISSET(V_CHAR)) 69219304Speter KEY(vp->character, 0); 69319304Speter 69419304Speter /* Get any associated cursor word. */ 69519304Speter if (F_ISSET(kp, V_KEYW) && v_keyword(sp)) 69619304Speter return (GC_ERR); 69719304Speter 69819304Speter return (GC_OK); 69919304Speter 70019304Speteresc: switch (cpart) { 70119304Speter case COMMANDMODE: 70219304Speter msgq(sp, M_BERR, "211|Already in command mode"); 70319304Speter return (GC_ERR_NOFLUSH); 70419304Speter case ISPARTIAL: 70519304Speter break; 70619304Speter case NOTPARTIAL: 70719304Speter (void)sp->gp->scr_bell(sp); 70819304Speter break; 70919304Speter } 71019304Speter return (GC_ERR); 71119304Speter} 71219304Speter 71319304Speter/* 71419304Speter * v_motion -- 71519304Speter * 71619304Speter * Get resulting motion mark. 71719304Speter */ 71819304Speterstatic int 71919304Speterv_motion(sp, dm, vp, mappedp) 72019304Speter SCR *sp; 72119304Speter VICMD *dm, *vp; 72219304Speter int *mappedp; 72319304Speter{ 72419304Speter VICMD motion; 72519304Speter size_t len; 72619304Speter u_long cnt; 72719304Speter u_int flags; 72819304Speter int tilde_reset, notused; 72919304Speter 73019304Speter /* 73119304Speter * If '.' command, use the dot motion, else get the motion command. 73219304Speter * Clear any line motion flags, the subsequent motion isn't always 73319304Speter * the same, i.e. "/aaa" may or may not be a line motion. 73419304Speter */ 73519304Speter if (F_ISSET(vp, VC_ISDOT)) { 73619304Speter motion = *dm; 73719304Speter F_SET(&motion, VC_ISDOT); 73819304Speter F_CLR(&motion, VM_COMMASK); 73919304Speter } else { 74019304Speter memset(&motion, 0, sizeof(VICMD)); 74119304Speter if (v_cmd(sp, NULL, &motion, vp, ¬used, mappedp) != GC_OK) 74219304Speter return (1); 74319304Speter } 74419304Speter 74519304Speter /* 74619304Speter * A count may be provided both to the command and to the motion, in 74719304Speter * which case the count is multiplicative. For example, "3y4y" is the 74819304Speter * same as "12yy". This count is provided to the motion command and 74919304Speter * not to the regular function. 75019304Speter */ 75119304Speter cnt = motion.count = F_ISSET(&motion, VC_C1SET) ? motion.count : 1; 75219304Speter if (F_ISSET(vp, VC_C1SET)) { 75319304Speter motion.count *= vp->count; 75419304Speter F_SET(&motion, VC_C1SET); 75519304Speter 75619304Speter /* 75719304Speter * Set flags to restore the original values of the command 75819304Speter * structure so dot commands can change the count values, 75919304Speter * e.g. "2dw" "3." deletes a total of five words. 76019304Speter */ 76119304Speter F_CLR(vp, VC_C1SET); 76219304Speter F_SET(vp, VC_C1RESET); 76319304Speter } 76419304Speter 76519304Speter /* 76619304Speter * Some commands can be repeated to indicate the current line. In 76719304Speter * this case, or if the command is a "line command", set the flags 76819304Speter * appropriately. If not a doubled command, run the function to get 76919304Speter * the resulting mark. 77019304Speter */ 77119304Speter if (vp->key == motion.key) { 77219304Speter F_SET(vp, VM_LDOUBLE | VM_LMODE); 77319304Speter 77419304Speter /* Set the origin of the command. */ 77519304Speter vp->m_start.lno = sp->lno; 77619304Speter vp->m_start.cno = 0; 77719304Speter 77819304Speter /* 77919304Speter * Set the end of the command. 78019304Speter * 78119304Speter * If the current line is missing, i.e. the file is empty, 78219304Speter * historic vi permitted a "cc" or "!!" command to insert 78319304Speter * text. 78419304Speter */ 78519304Speter vp->m_stop.lno = sp->lno + motion.count - 1; 78619304Speter if (db_get(sp, vp->m_stop.lno, 0, NULL, &len)) { 78719304Speter if (vp->m_stop.lno != 1 || 78819304Speter vp->key != 'c' && vp->key != '!') { 78919304Speter v_emsg(sp, NULL, VIM_EMPTY); 79019304Speter return (1); 79119304Speter } 79219304Speter vp->m_stop.cno = 0; 79319304Speter } else 79419304Speter vp->m_stop.cno = len ? len - 1 : 0; 79519304Speter } else { 79619304Speter /* 79719304Speter * Motion commands change the underlying movement (*snarl*). 79819304Speter * For example, "l" is illegal at the end of a line, but "dl" 79919304Speter * is not. Set flags so the function knows the situation. 80019304Speter */ 80119304Speter motion.rkp = vp->kp; 80219304Speter 80319304Speter /* 80419304Speter * XXX 80519304Speter * Use yank instead of creating a new motion command, it's a 80619304Speter * lot easier for now. 80719304Speter */ 80819304Speter if (vp->kp == &tmotion) { 80919304Speter tilde_reset = 1; 81019304Speter vp->kp = &vikeys['y']; 81119304Speter } else 81219304Speter tilde_reset = 0; 81319304Speter 81419304Speter /* 81519304Speter * Copy the key flags into the local structure, except for the 81619304Speter * RCM flags -- the motion command will set the RCM flags in 81719304Speter * the vp structure if necessary. This means that the motion 81819304Speter * command is expected to determine where the cursor ends up! 81919304Speter * However, we save off the current RCM mask and restore it if 82019304Speter * it no RCM flags are set by the motion command, with a small 82119304Speter * modification. 82219304Speter * 82319304Speter * We replace the VM_RCM_SET flag with the VM_RCM flag. This 82419304Speter * is so that cursor movement doesn't set the relative position 82519304Speter * unless the motion command explicitly specified it. This 82619304Speter * appears to match historic practice, but I've never been able 82719304Speter * to develop a hard-and-fast rule. 82819304Speter */ 82919304Speter flags = F_ISSET(vp, VM_RCM_MASK); 83019304Speter if (LF_ISSET(VM_RCM_SET)) { 83119304Speter LF_SET(VM_RCM); 83219304Speter LF_CLR(VM_RCM_SET); 83319304Speter } 83419304Speter F_CLR(vp, VM_RCM_MASK); 83519304Speter F_SET(&motion, motion.kp->flags & ~VM_RCM_MASK); 83619304Speter 83719304Speter /* 83819304Speter * Set the three cursor locations to the current cursor. This 83919304Speter * permits commands like 'j' and 'k', that are line oriented 84019304Speter * motions and have special cursor suck semantics when they are 84119304Speter * used as standalone commands, to ignore column positioning. 84219304Speter */ 84319304Speter motion.m_final.lno = 84419304Speter motion.m_stop.lno = motion.m_start.lno = sp->lno; 84519304Speter motion.m_final.cno = 84619304Speter motion.m_stop.cno = motion.m_start.cno = sp->cno; 84719304Speter 84819304Speter /* Run the function. */ 84919304Speter if ((motion.kp->func)(sp, &motion)) 85019304Speter return (1); 85119304Speter 85219304Speter /* 85319304Speter * If the current line is missing, i.e. the file is empty, 85419304Speter * historic vi allowed "c<motion>" or "!<motion>" to insert 85519304Speter * text. Otherwise fail -- most motion commands will have 85619304Speter * already failed, but some, e.g. G, succeed in empty files. 85719304Speter */ 85819304Speter if (!db_exist(sp, vp->m_stop.lno)) { 85919304Speter if (vp->m_stop.lno != 1 || 86019304Speter vp->key != 'c' && vp->key != '!') { 86119304Speter v_emsg(sp, NULL, VIM_EMPTY); 86219304Speter return (1); 86319304Speter } 86419304Speter vp->m_stop.cno = 0; 86519304Speter } 86619304Speter 86719304Speter /* 86819304Speter * XXX 86919304Speter * See above. 87019304Speter */ 87119304Speter if (tilde_reset) 87219304Speter vp->kp = &tmotion; 87319304Speter 87419304Speter /* 87519304Speter * Copy cut buffer, line mode and cursor position information 87619304Speter * from the motion command structure, i.e. anything that the 87719304Speter * motion command can set for us. The commands can flag the 87819304Speter * movement as a line motion (see v_sentence) as well as set 87919304Speter * the VM_RCM_* flags explicitly. 88019304Speter */ 88119304Speter F_SET(vp, F_ISSET(&motion, VM_COMMASK | VM_RCM_MASK)); 88219304Speter 88319304Speter /* 88419304Speter * If the motion command set no relative motion flags, use 88519304Speter * the (slightly) modified previous values. 88619304Speter */ 88719304Speter if (!F_ISSET(vp, VM_RCM_MASK)) 88819304Speter F_SET(vp, flags); 88919304Speter 89019304Speter /* 89119304Speter * Commands can change behaviors based on the motion command 89219304Speter * used, for example, the ! command repeated the last bang 89319304Speter * command if N or n was used as the motion. 89419304Speter */ 89519304Speter vp->rkp = motion.kp; 89619304Speter 89719304Speter /* 89819304Speter * Motion commands can reset all of the cursor information. 89919304Speter * If the motion is in the reverse direction, switch the 90019304Speter * from and to MARK's so that it's in a forward direction. 90119304Speter * Motions are from the from MARK to the to MARK (inclusive). 90219304Speter */ 90319304Speter if (motion.m_start.lno > motion.m_stop.lno || 90419304Speter motion.m_start.lno == motion.m_stop.lno && 90519304Speter motion.m_start.cno > motion.m_stop.cno) { 90619304Speter vp->m_start = motion.m_stop; 90719304Speter vp->m_stop = motion.m_start; 90819304Speter } else { 90919304Speter vp->m_start = motion.m_start; 91019304Speter vp->m_stop = motion.m_stop; 91119304Speter } 91219304Speter vp->m_final = motion.m_final; 91319304Speter } 91419304Speter 91519304Speter /* 91619304Speter * If the command sets dot, save the motion structure. The motion 91719304Speter * count was changed above and needs to be reset, that's why this 91819304Speter * is done here, and not in the calling routine. 91919304Speter */ 92019304Speter if (F_ISSET(vp->kp, V_DOT)) { 92119304Speter *dm = motion; 92219304Speter dm->count = cnt; 92319304Speter } 92419304Speter return (0); 92519304Speter} 92619304Speter 92719304Speter/* 92819304Speter * v_init -- 92919304Speter * Initialize the vi screen. 93019304Speter */ 93119304Speterstatic int 93219304Speterv_init(sp) 93319304Speter SCR *sp; 93419304Speter{ 93519304Speter GS *gp; 93619304Speter VI_PRIVATE *vip; 93719304Speter 93819304Speter gp = sp->gp; 93919304Speter vip = VIP(sp); 94019304Speter 94119304Speter /* Switch into vi. */ 94219304Speter if (gp->scr_screen(sp, SC_VI)) 94319304Speter return (1); 94419304Speter (void)gp->scr_attr(sp, SA_ALTERNATE, 1); 94519304Speter 94619304Speter F_CLR(sp, SC_EX | SC_SCR_EX); 94719304Speter F_SET(sp, SC_VI); 94819304Speter 94919304Speter /* 95019304Speter * Initialize screen values. 95119304Speter * 95219304Speter * Small windows: see vs_refresh(), section 6a. 95319304Speter * 95419304Speter * Setup: 95519304Speter * t_minrows is the minimum rows to display 95619304Speter * t_maxrows is the maximum rows to display (rows - 1) 95719304Speter * t_rows is the rows currently being displayed 95819304Speter */ 95919304Speter sp->rows = vip->srows = O_VAL(sp, O_LINES); 96019304Speter sp->cols = O_VAL(sp, O_COLUMNS); 96119304Speter sp->t_rows = sp->t_minrows = O_VAL(sp, O_WINDOW); 96219304Speter if (sp->rows != 1) { 96319304Speter if (sp->t_rows > sp->rows - 1) { 96419304Speter sp->t_minrows = sp->t_rows = sp->rows - 1; 96519304Speter msgq(sp, M_INFO, 96619304Speter "214|Windows option value is too large, max is %u", 96719304Speter sp->t_rows); 96819304Speter } 96919304Speter sp->t_maxrows = sp->rows - 1; 97019304Speter } else 97119304Speter sp->t_maxrows = 1; 97219304Speter sp->woff = 0; 97319304Speter 97419304Speter /* Create a screen map. */ 97519304Speter CALLOC_RET(sp, HMAP, SMAP *, SIZE_HMAP(sp), sizeof(SMAP)); 97619304Speter TMAP = HMAP + (sp->t_rows - 1); 97719304Speter HMAP->lno = sp->lno; 97819304Speter HMAP->coff = 0; 97919304Speter HMAP->soff = 1; 98019304Speter 98119304Speter /* 98219304Speter * Fill the screen map from scratch -- try and center the line. That 98319304Speter * way if we're starting with a file we've seen before, we'll put the 98419304Speter * line in the middle, otherwise, it won't work and we'll end up with 98519304Speter * the line at the top. 98619304Speter */ 98719304Speter F_SET(sp, SC_SCR_REFORMAT | SC_SCR_CENTER); 98819304Speter 98919304Speter /* Invalidate the cursor. */ 99019304Speter F_SET(vip, VIP_CUR_INVALID); 99119304Speter 99219304Speter /* Paint the screen image from scratch. */ 99319304Speter F_SET(vip, VIP_N_EX_PAINT); 99419304Speter 99519304Speter return (0); 99619304Speter} 99719304Speter 99819304Speter/* 99919304Speter * v_dtoh -- 100019304Speter * Move all but the current screen to the hidden queue. 100119304Speter */ 100219304Speterstatic void 100319304Speterv_dtoh(sp) 100419304Speter SCR *sp; 100519304Speter{ 100619304Speter GS *gp; 100719304Speter SCR *tsp; 100819304Speter int hidden; 100919304Speter 101019304Speter /* Move all screens to the hidden queue, tossing screen maps. */ 101119304Speter for (hidden = 0, gp = sp->gp; 101219304Speter (tsp = gp->dq.cqh_first) != (void *)&gp->dq; ++hidden) { 101319304Speter if (_HMAP(tsp) != NULL) { 101419304Speter free(_HMAP(tsp)); 101519304Speter _HMAP(tsp) = NULL; 101619304Speter } 101719304Speter CIRCLEQ_REMOVE(&gp->dq, tsp, q); 101819304Speter CIRCLEQ_INSERT_TAIL(&gp->hq, tsp, q); 101919304Speter } 102019304Speter 102119304Speter /* Move current screen back to the display queue. */ 102219304Speter CIRCLEQ_REMOVE(&gp->hq, sp, q); 102319304Speter CIRCLEQ_INSERT_TAIL(&gp->dq, sp, q); 102419304Speter 102519304Speter /* 102619304Speter * XXX 102719304Speter * Don't bother internationalizing this message, it's going to 102819304Speter * go away as soon as we have one-line screens. --TK 102919304Speter */ 103019304Speter if (hidden > 1) 103119304Speter msgq(sp, M_INFO, 103219304Speter "%d screens backgrounded; use :display to list them", 103319304Speter hidden - 1); 103419304Speter} 103519304Speter 103619304Speter/* 103719304Speter * v_keyword -- 103819304Speter * Get the word (or non-word) the cursor is on. 103919304Speter */ 104019304Speterstatic int 104119304Speterv_keyword(sp) 104219304Speter SCR *sp; 104319304Speter{ 104419304Speter VI_PRIVATE *vip; 104519304Speter size_t beg, end, len; 104619304Speter int moved, state; 104719304Speter char *p; 104819304Speter 104919304Speter if (db_get(sp, sp->lno, DBG_FATAL, &p, &len)) 105019304Speter return (1); 105119304Speter 105219304Speter /* 105319304Speter * !!! 105419304Speter * Historically, tag commands skipped over any leading whitespace 105519304Speter * characters. Make this true in general when using cursor words. 105619304Speter * If movement, getting a cursor word implies moving the cursor to 105719304Speter * its beginning. Refresh now. 105819304Speter * 105919304Speter * !!! 106019304Speter * Find the beginning/end of the keyword. Keywords are currently 106119304Speter * used for cursor-word searching and for tags. Historical vi 106219304Speter * only used the word in a tag search from the cursor to the end 106319304Speter * of the word, i.e. if the cursor was on the 'b' in " abc ", the 106419304Speter * tag was "bc". For consistency, we make cursor word searches 106519304Speter * follow the same rule. 106619304Speter */ 106719304Speter for (moved = 0, 106819304Speter beg = sp->cno; beg < len && isspace(p[beg]); moved = 1, ++beg); 106919304Speter if (beg >= len) { 107019304Speter msgq(sp, M_BERR, "212|Cursor not in a word"); 107119304Speter return (1); 107219304Speter } 107319304Speter if (moved) { 107419304Speter sp->cno = beg; 107519304Speter (void)vs_refresh(sp, 0); 107619304Speter } 107719304Speter 107819304Speter /* Find the end of the word. */ 107919304Speter for (state = inword(p[beg]), 108019304Speter end = beg; ++end < len && state == inword(p[end]);); 108119304Speter 108219304Speter vip = VIP(sp); 108319304Speter len = (end - beg); 108419304Speter BINC_RET(sp, vip->keyw, vip->klen, len); 108519304Speter 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 * 109519304Speterv_alias(sp, vp, kp) 109619304Speter SCR *sp; 109719304Speter VICMD *vp; 109819304Speter 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 113119304Speterv_count(sp, fkey, countp) 113219304Speter SCR *sp; 113319304Speter ARG_CHAR_T fkey; 113419304Speter 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); 115319304Speter } 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); 116119304Speter } 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 117119304Speterv_key(sp, command_events, evp, ec_flags) 117219304Speter SCR *sp; 117319304Speter int command_events; 117419304Speter EVENT *evp; 117519304Speter 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 case E_QUIT: 122019304Speter case E_WRITE: 122119304Speter if (command_events) 122219304Speter return (GC_EVENT); 122319304Speter /* FALLTHROUGH */ 122419304Speter default: 122519304Speter v_event_err(sp, evp); 122619304Speter return (GC_ERR); 122719304Speter } 122819304Speter } 122919304Speter /* NOTREACHED */ 123019304Speter} 123119304Speter 123219304Speter#if defined(DEBUG) && defined(COMLOG) 123319304Speter/* 123419304Speter * v_comlog -- 123519304Speter * Log the contents of the command structure. 123619304Speter */ 123719304Speterstatic void 123819304Speterv_comlog(sp, vp) 123919304Speter SCR *sp; 124019304Speter VICMD *vp; 124119304Speter{ 124219304Speter TRACE(sp, "vcmd: %c", vp->key); 124319304Speter if (F_ISSET(vp, VC_BUFFER)) 124419304Speter TRACE(sp, " buffer: %c", vp->buffer); 124519304Speter if (F_ISSET(vp, VC_C1SET)) 124619304Speter TRACE(sp, " c1: %lu", vp->count); 124719304Speter if (F_ISSET(vp, VC_C2SET)) 124819304Speter TRACE(sp, " c2: %lu", vp->count2); 124919304Speter TRACE(sp, " flags: 0x%x\n", vp->flags); 125019304Speter} 125119304Speter#endif 1252