119304Speter/*- 219304Speter * Copyright (c) 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: v_txt.c,v 11.5 2013/05/19 20:37:45 bentley Exp $"; 1419304Speter#endif /* not lint */ 1519304Speter 1619304Speter#include <sys/types.h> 1719304Speter#include <sys/queue.h> 1819304Speter#include <sys/stat.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 3219304Speterstatic int txt_abbrev __P((SCR *, TEXT *, CHAR_T *, int, int *, int *)); 3319304Speterstatic void txt_ai_resolve __P((SCR *, TEXT *, int *)); 3419304Speterstatic TEXT *txt_backup __P((SCR *, TEXTH *, TEXT *, u_int32_t *)); 3519304Speterstatic int txt_dent __P((SCR *, TEXT *, int)); 3619304Speterstatic int txt_emark __P((SCR *, TEXT *, size_t)); 3719304Speterstatic void txt_err __P((SCR *, TEXTH *)); 3819304Speterstatic int txt_fc __P((SCR *, TEXT *, int *)); 3919304Speterstatic int txt_fc_col __P((SCR *, int, ARGS **)); 4019304Speterstatic int txt_hex __P((SCR *, TEXT *)); 4119304Speterstatic int txt_insch __P((SCR *, TEXT *, CHAR_T *, u_int)); 4219304Speterstatic int txt_isrch __P((SCR *, VICMD *, TEXT *, u_int8_t *)); 4319304Speterstatic int txt_map_end __P((SCR *)); 4419304Speterstatic int txt_map_init __P((SCR *)); 4519304Speterstatic int txt_margin __P((SCR *, TEXT *, TEXT *, int *, u_int32_t)); 4619304Speterstatic void txt_nomorech __P((SCR *)); 4719304Speterstatic void txt_Rresolve __P((SCR *, TEXTH *, TEXT *, const size_t)); 4819304Speterstatic int txt_resolve __P((SCR *, TEXTH *, u_int32_t)); 4919304Speterstatic int txt_showmatch __P((SCR *, TEXT *)); 5019304Speterstatic void txt_unmap __P((SCR *, TEXT *, u_int32_t *)); 5119304Speter 5219304Speter/* Cursor character (space is hard to track on the screen). */ 5319304Speter#if defined(DEBUG) && 0 5419304Speter#undef CH_CURSOR 5519304Speter#define CH_CURSOR '+' 5619304Speter#endif 5719304Speter 5819304Speter/* 5919304Speter * v_tcmd -- 6019304Speter * Fill a buffer from the terminal for vi. 6119304Speter * 6219304Speter * PUBLIC: int v_tcmd __P((SCR *, VICMD *, ARG_CHAR_T, u_int)); 6319304Speter */ 6419304Speterint 65254225Speterv_tcmd(SCR *sp, VICMD *vp, ARG_CHAR_T prompt, u_int flags) 6619304Speter{ 6719304Speter /* Normally, we end up where we started. */ 6819304Speter vp->m_final.lno = sp->lno; 6919304Speter vp->m_final.cno = sp->cno; 7019304Speter 7119304Speter /* Initialize the map. */ 7219304Speter if (txt_map_init(sp)) 7319304Speter return (1); 7419304Speter 7519304Speter /* Move to the last line. */ 7619304Speter sp->lno = TMAP[0].lno; 7719304Speter sp->cno = 0; 7819304Speter 7919304Speter /* Don't update the modeline for now. */ 8019304Speter F_SET(sp, SC_TINPUT_INFO); 8119304Speter 8219304Speter /* Set the input flags. */ 8319304Speter LF_SET(TXT_APPENDEOL | 8419304Speter TXT_CR | TXT_ESCAPE | TXT_INFOLINE | TXT_MAPINPUT); 8519304Speter if (O_ISSET(sp, O_ALTWERASE)) 8619304Speter LF_SET(TXT_ALTWERASE); 8719304Speter if (O_ISSET(sp, O_TTYWERASE)) 8819304Speter LF_SET(TXT_TTYWERASE); 8919304Speter 9019304Speter /* Do the input thing. */ 9119304Speter if (v_txt(sp, vp, NULL, NULL, 0, prompt, 0, 1, flags)) 9219304Speter return (1); 9319304Speter 9419304Speter /* Reenable the modeline updates. */ 9519304Speter F_CLR(sp, SC_TINPUT_INFO); 9619304Speter 9719304Speter /* Clean up the map. */ 9819304Speter if (txt_map_end(sp)) 9919304Speter return (1); 10019304Speter 10119304Speter if (IS_ONELINE(sp)) 10219304Speter F_SET(sp, SC_SCR_REDRAW); /* XXX */ 10319304Speter 10419304Speter /* Set the cursor to the resulting position. */ 10519304Speter sp->lno = vp->m_final.lno; 10619304Speter sp->cno = vp->m_final.cno; 10719304Speter 10819304Speter return (0); 10919304Speter} 11019304Speter 11119304Speter/* 11219304Speter * txt_map_init 11319304Speter * Initialize the screen map for colon command-line input. 11419304Speter */ 11519304Speterstatic int 116254225Spetertxt_map_init(SCR *sp) 11719304Speter{ 11819304Speter SMAP *esmp; 11919304Speter VI_PRIVATE *vip; 12019304Speter 12119304Speter vip = VIP(sp); 12219304Speter if (!IS_ONELINE(sp)) { 12319304Speter /* 12419304Speter * Fake like the user is doing input on the last line of the 12519304Speter * screen. This makes all of the scrolling work correctly, 12619304Speter * and allows us the use of the vi text editing routines, not 12719304Speter * to mention practically infinite length ex commands. 12819304Speter * 12919304Speter * Save the current location. 13019304Speter */ 13119304Speter vip->sv_tm_lno = TMAP->lno; 13219304Speter vip->sv_tm_soff = TMAP->soff; 13319304Speter vip->sv_tm_coff = TMAP->coff; 13419304Speter vip->sv_t_maxrows = sp->t_maxrows; 13519304Speter vip->sv_t_minrows = sp->t_minrows; 13619304Speter vip->sv_t_rows = sp->t_rows; 13719304Speter 13819304Speter /* 13919304Speter * If it's a small screen, TMAP may be small for the screen. 14019304Speter * Fix it, filling in fake lines as we go. 14119304Speter */ 14219304Speter if (IS_SMALL(sp)) 14319304Speter for (esmp = 14419304Speter HMAP + (sp->t_maxrows - 1); TMAP < esmp; ++TMAP) { 14519304Speter TMAP[1].lno = TMAP[0].lno + 1; 14619304Speter TMAP[1].coff = HMAP->coff; 14719304Speter TMAP[1].soff = 1; 14819304Speter } 14919304Speter 15019304Speter /* Build the fake entry. */ 15119304Speter TMAP[1].lno = TMAP[0].lno + 1; 15219304Speter TMAP[1].soff = 1; 15319304Speter TMAP[1].coff = 0; 15419304Speter SMAP_FLUSH(&TMAP[1]); 15519304Speter ++TMAP; 15619304Speter 15719304Speter /* Reset the screen information. */ 15819304Speter sp->t_rows = sp->t_minrows = ++sp->t_maxrows; 15919304Speter } 16019304Speter return (0); 16119304Speter} 16219304Speter 16319304Speter/* 16419304Speter * txt_map_end 16519304Speter * Reset the screen map for colon command-line input. 16619304Speter */ 16719304Speterstatic int 168254225Spetertxt_map_end(SCR *sp) 16919304Speter{ 17019304Speter VI_PRIVATE *vip; 17119304Speter size_t cnt; 17219304Speter 17319304Speter vip = VIP(sp); 17419304Speter if (!IS_ONELINE(sp)) { 17519304Speter /* Restore the screen information. */ 17619304Speter sp->t_rows = vip->sv_t_rows; 17719304Speter sp->t_minrows = vip->sv_t_minrows; 17819304Speter sp->t_maxrows = vip->sv_t_maxrows; 17919304Speter 18019304Speter /* 18119304Speter * If it's a small screen, TMAP may be wrong. Clear any 18219304Speter * lines that might have been overwritten. 18319304Speter */ 18419304Speter if (IS_SMALL(sp)) { 18519304Speter for (cnt = sp->t_rows; cnt <= sp->t_maxrows; ++cnt) { 18619304Speter (void)sp->gp->scr_move(sp, cnt, 0); 18719304Speter (void)sp->gp->scr_clrtoeol(sp); 18819304Speter } 18919304Speter TMAP = HMAP + (sp->t_rows - 1); 19019304Speter } else 19119304Speter --TMAP; 19219304Speter 19319304Speter /* 19419304Speter * The map may be wrong if the user entered more than one 19519304Speter * (logical) line. Fix it. If the user entered a whole 19619304Speter * screen, this will be slow, but we probably don't care. 19719304Speter */ 19819304Speter if (!O_ISSET(sp, O_LEFTRIGHT)) 19919304Speter while (vip->sv_tm_lno != TMAP->lno || 20019304Speter vip->sv_tm_soff != TMAP->soff) 20119304Speter if (vs_sm_1down(sp)) 20219304Speter return (1); 20319304Speter } 20419304Speter 20519304Speter /* 20619304Speter * Invalidate the cursor and the line size cache, the line never 20719304Speter * really existed. This fixes bugs where the user searches for 20819304Speter * the last line on the screen + 1 and the refresh routine thinks 20919304Speter * that's where we just were. 21019304Speter */ 21119304Speter VI_SCR_CFLUSH(vip); 21219304Speter F_SET(vip, VIP_CUR_INVALID); 21319304Speter 21419304Speter return (0); 21519304Speter} 21619304Speter 21719304Speter/* 21819304Speter * If doing input mapping on the colon command line, may need to unmap 21919304Speter * based on the command. 22019304Speter */ 22119304Speter#define UNMAP_TST \ 22219304Speter FL_ISSET(ec_flags, EC_MAPINPUT) && LF_ISSET(TXT_INFOLINE) 22319304Speter 22419304Speter/* 22519304Speter * Internally, we maintain tp->lno and tp->cno, externally, everyone uses 22619304Speter * sp->lno and sp->cno. Make them consistent as necessary. 22719304Speter */ 22819304Speter#define UPDATE_POSITION(sp, tp) { \ 22919304Speter (sp)->lno = (tp)->lno; \ 23019304Speter (sp)->cno = (tp)->cno; \ 23119304Speter} 23219304Speter 23319304Speter/* 23419304Speter * v_txt -- 23519304Speter * Vi text input. 23619304Speter * 23719304Speter * PUBLIC: int v_txt __P((SCR *, VICMD *, MARK *, 238254225Speter * PUBLIC: const CHAR_T *, size_t, ARG_CHAR_T, recno_t, u_long, u_int32_t)); 23919304Speter */ 24019304Speterint 241254225Speterv_txt( 242254225Speter SCR *sp, 243254225Speter VICMD *vp, 244254225Speter MARK *tm, /* To MARK. */ 245254225Speter const CHAR_T *lp, /* Input line. */ 246254225Speter size_t len, /* Input line length. */ 247254225Speter ARG_CHAR_T prompt, /* Prompt to display. */ 248254225Speter recno_t ai_line, /* Line number to use for autoindent count. */ 249254225Speter u_long rcount, /* Replay count. */ 250254225Speter u_int32_t flags) /* TXT_* flags. */ 25119304Speter{ 252254225Speter EVENT ev, *evp = NULL; /* Current event. */ 25319304Speter EVENT fc; /* File name completion event. */ 25419304Speter GS *gp; 25519304Speter TEXT *ntp, *tp; /* Input text structures. */ 25619304Speter TEXT ait; /* Autoindent text structure. */ 257254225Speter TEXT wmt = {{ 0 }}; /* Wrapmargin text structure. */ 25819304Speter TEXTH *tiqh; 25919304Speter VI_PRIVATE *vip; 26019304Speter abb_t abb; /* State of abbreviation checks. */ 26119304Speter carat_t carat; /* State of the "[^0]^D" sequences. */ 26219304Speter quote_t quote; /* State of quotation. */ 26319304Speter size_t owrite, insert; /* Temporary copies of TEXT fields. */ 26419304Speter size_t margin; /* Wrapmargin value. */ 26519304Speter size_t rcol; /* 0-N: insert offset in the replay buffer. */ 26619304Speter size_t tcol; /* Temporary column. */ 26719304Speter u_int32_t ec_flags; /* Input mapping flags. */ 26819304Speter#define IS_RESTART 0x01 /* Reset the incremental search. */ 26919304Speter#define IS_RUNNING 0x02 /* Incremental search turned on. */ 27019304Speter u_int8_t is_flags; 27119304Speter int abcnt, ab_turnoff; /* Abbreviation character count, switch. */ 27219304Speter int filec_redraw; /* Redraw after the file completion routine. */ 27319304Speter int hexcnt; /* Hex character count. */ 27419304Speter int showmatch; /* Showmatch set on this character. */ 27519304Speter int wm_set, wm_skip; /* Wrapmargin happened, blank skip flags. */ 27619304Speter int max, tmp; 277254225Speter int nochange; 278254225Speter CHAR_T *p; 27919304Speter 28019304Speter gp = sp->gp; 28119304Speter vip = VIP(sp); 28219304Speter 28319304Speter /* 28419304Speter * Set the input flag, so tabs get displayed correctly 28519304Speter * and everyone knows that the text buffer is in use. 28619304Speter */ 28719304Speter F_SET(sp, SC_TINPUT); 28819304Speter 28919304Speter /* 29019304Speter * Get one TEXT structure with some initial buffer space, reusing 29119304Speter * the last one if it's big enough. (All TEXT bookkeeping fields 29219304Speter * default to 0 -- text_init() handles this.) If changing a line, 29319304Speter * copy it into the TEXT buffer. 29419304Speter */ 295254225Speter tiqh = sp->tiq; 296254225Speter if (!TAILQ_EMPTY(tiqh)) { 297254225Speter tp = TAILQ_FIRST(tiqh); 298258231Sgjb if (TAILQ_NEXT(tp, q) != NULL || 299258231Sgjb tp->lb_len < (len + 32) * sizeof(CHAR_T)) { 30019304Speter text_lfree(tiqh); 30119304Speter goto newtp; 30219304Speter } 30319304Speter tp->ai = tp->insert = tp->offset = tp->owrite = 0; 30419304Speter if (lp != NULL) { 30519304Speter tp->len = len; 306254225Speter BINC_RETW(sp, tp->lb, tp->lb_len, len); 307254225Speter MEMMOVE(tp->lb, lp, len); 30819304Speter } else 30919304Speter tp->len = 0; 31019304Speter } else { 31119304Speternewtp: if ((tp = text_init(sp, lp, len, len + 32)) == NULL) 31219304Speter return (1); 313254225Speter TAILQ_INSERT_HEAD(tiqh, tp, q); 31419304Speter } 31519304Speter 31619304Speter /* Set default termination condition. */ 31719304Speter tp->term = TERM_OK; 31819304Speter 31919304Speter /* Set the starting line, column. */ 32019304Speter tp->lno = sp->lno; 32119304Speter tp->cno = sp->cno; 32219304Speter 32319304Speter /* 32419304Speter * Set the insert and overwrite counts. If overwriting characters, 32519304Speter * do insertion afterward. If not overwriting characters, assume 32619304Speter * doing insertion. If change is to a mark, emphasize it with an 32719304Speter * CH_ENDMARK character. 32819304Speter */ 32919304Speter if (len) { 33019304Speter if (LF_ISSET(TXT_OVERWRITE)) { 33119304Speter tp->owrite = (tm->cno - tp->cno) + 1; 33219304Speter tp->insert = (len - tm->cno) - 1; 33319304Speter } else 33419304Speter tp->insert = len - tp->cno; 33519304Speter 33619304Speter if (LF_ISSET(TXT_EMARK) && txt_emark(sp, tp, tm->cno)) 33719304Speter return (1); 33819304Speter } 33919304Speter 34019304Speter /* 34119304Speter * Many of the special cases in text input are to handle autoindent 34219304Speter * support. Somebody decided that it would be a good idea if "^^D" 34319304Speter * and "0^D" deleted all of the autoindented characters. In an editor 34419304Speter * that takes single character input from the user, this beggars the 34519304Speter * imagination. Note also, "^^D" resets the next lines' autoindent, 34619304Speter * but "0^D" doesn't. 34719304Speter * 34819304Speter * We assume that autoindent only happens on empty lines, so insert 34919304Speter * and overwrite will be zero. If doing autoindent, figure out how 35019304Speter * much indentation we need and fill it in. Update input column and 35119304Speter * screen cursor as necessary. 35219304Speter */ 35319304Speter if (LF_ISSET(TXT_AUTOINDENT) && ai_line != OOBLNO) { 35419304Speter if (v_txt_auto(sp, ai_line, NULL, 0, tp)) 35519304Speter return (1); 35619304Speter tp->cno = tp->ai; 35719304Speter } else { 35819304Speter /* 35919304Speter * The cc and S commands have a special feature -- leading 36019304Speter * <blank> characters are handled as autoindent characters. 36119304Speter * Beauty! 36219304Speter */ 36319304Speter if (LF_ISSET(TXT_AICHARS)) { 36419304Speter tp->offset = 0; 36519304Speter tp->ai = tp->cno; 36619304Speter } else 36719304Speter tp->offset = tp->cno; 36819304Speter } 36919304Speter 37019304Speter /* If getting a command buffer from the user, there may be a prompt. */ 37119304Speter if (LF_ISSET(TXT_PROMPT)) { 37219304Speter tp->lb[tp->cno++] = prompt; 37319304Speter ++tp->len; 37419304Speter ++tp->offset; 37519304Speter } 37619304Speter 37719304Speter /* 37819304Speter * If appending after the end-of-line, add a space into the buffer 37919304Speter * and move the cursor right. This space is inserted, i.e. pushed 38019304Speter * along, and then deleted when the line is resolved. Assumes that 38119304Speter * the cursor is already positioned at the end of the line. This 38219304Speter * avoids the nastiness of having the cursor reside on a magical 38319304Speter * column, i.e. a column that doesn't really exist. The only down 38419304Speter * side is that we may wrap lines or scroll the screen before it's 38519304Speter * strictly necessary. Not a big deal. 38619304Speter */ 38719304Speter if (LF_ISSET(TXT_APPENDEOL)) { 38819304Speter tp->lb[tp->cno] = CH_CURSOR; 38919304Speter ++tp->len; 39019304Speter ++tp->insert; 39119304Speter (void)vs_change(sp, tp->lno, LINE_RESET); 39219304Speter } 39319304Speter 39419304Speter /* 39519304Speter * Historic practice is that the wrapmargin value was a distance 39619304Speter * from the RIGHT-HAND margin, not the left. It's more useful to 39719304Speter * us as a distance from the left-hand margin, i.e. the same as 39819304Speter * the wraplen value. The wrapmargin option is historic practice. 39919304Speter * Nvi added the wraplen option so that it would be possible to 40019304Speter * edit files with consistent margins without knowing the number of 40119304Speter * columns in the window. 40219304Speter * 40319304Speter * XXX 40419304Speter * Setting margin causes a significant performance hit. Normally 40519304Speter * we don't update the screen if there are keys waiting, but we 40619304Speter * have to if margin is set, otherwise the screen routines don't 40719304Speter * know where the cursor is. 40819304Speter * 40919304Speter * !!! 41019304Speter * Abbreviated keys were affected by the wrapmargin option in the 41119304Speter * historic 4BSD vi. Mapped keys were usually, but sometimes not. 41219304Speter * See the comment in vi/v_text():set_txt_std for more information. 41319304Speter * 41419304Speter * !!! 41519304Speter * One more special case. If an inserted <blank> character causes 41619304Speter * wrapmargin to split the line, the next user entered character is 41719304Speter * discarded if it's a <space> character. 41819304Speter */ 41919304Speter wm_set = wm_skip = 0; 42019304Speter if (LF_ISSET(TXT_WRAPMARGIN)) 42119304Speter if ((margin = O_VAL(sp, O_WRAPMARGIN)) != 0) 42219304Speter margin = sp->cols - margin; 42319304Speter else 42419304Speter margin = O_VAL(sp, O_WRAPLEN); 42519304Speter else 42619304Speter margin = 0; 42719304Speter 42819304Speter /* Initialize abbreviation checks. */ 42919304Speter abcnt = ab_turnoff = 0; 43019304Speter abb = F_ISSET(gp, G_ABBREV) && 43119304Speter LF_ISSET(TXT_MAPINPUT) ? AB_INWORD : AB_NOTSET; 43219304Speter 43319304Speter /* 43419304Speter * Set up the dot command. Dot commands are done by saving the actual 43519304Speter * characters and then reevaluating them so that things like wrapmargin 43619304Speter * can change between the insert and the replay. 43719304Speter * 43819304Speter * !!! 43919304Speter * Historically, vi did not remap or reabbreviate replayed input. (It 44019304Speter * did beep at you if you changed an abbreviation and then replayed the 44119304Speter * input. We're not that compatible.) We don't have to do anything to 44219304Speter * avoid remapping, as we're not getting characters from the terminal 44319304Speter * routines. Turn the abbreviation check off. 44419304Speter * 44519304Speter * XXX 44619304Speter * It would be nice if we could swallow backspaces and such, but it's 44719304Speter * not all that easy to do. What we can do is turn off the common 44819304Speter * error messages during the replay. Otherwise, when the user enters 44919304Speter * an illegal command, e.g., "Ia<erase><erase><erase><erase>b<escape>", 45019304Speter * and then does a '.', they get a list of error messages after command 45119304Speter * completion. 45219304Speter */ 45319304Speter rcol = 0; 45419304Speter if (LF_ISSET(TXT_REPLAY)) { 45519304Speter abb = AB_NOTSET; 45619304Speter LF_CLR(TXT_RECORD); 45719304Speter } 45819304Speter 45919304Speter /* Other text input mode setup. */ 46019304Speter quote = Q_NOTSET; 46119304Speter carat = C_NOTSET; 462254225Speter nochange = 0; 46319304Speter FL_INIT(is_flags, 46419304Speter LF_ISSET(TXT_SEARCHINCR) ? IS_RESTART | IS_RUNNING : 0); 46519304Speter filec_redraw = hexcnt = showmatch = 0; 46619304Speter 46719304Speter /* Initialize input flags. */ 46819304Speter ec_flags = LF_ISSET(TXT_MAPINPUT) ? EC_MAPINPUT : 0; 46919304Speter 47019304Speter /* Refresh the screen. */ 47119304Speter UPDATE_POSITION(sp, tp); 47219304Speter if (vs_refresh(sp, 1)) 47319304Speter return (1); 47419304Speter 47519304Speter /* If it's dot, just do it now. */ 47619304Speter if (F_ISSET(vp, VC_ISDOT)) 47719304Speter goto replay; 47819304Speter 47919304Speter /* Get an event. */ 48019304Speter evp = &ev; 48119304Speternext: if (v_event_get(sp, evp, 0, ec_flags)) 48219304Speter return (1); 48319304Speter 48419304Speter /* 48519304Speter * If file completion overwrote part of the screen and nothing else has 48619304Speter * been displayed, clean up. We don't do this as part of the normal 48719304Speter * message resolution because we know the user is on the colon command 48819304Speter * line and there's no reason to enter explicit characters to continue. 48919304Speter */ 49019304Speter if (filec_redraw && !F_ISSET(sp, SC_SCR_EXWROTE)) { 49119304Speter filec_redraw = 0; 49219304Speter 49319304Speter fc.e_event = E_REPAINT; 49419304Speter fc.e_flno = vip->totalcount >= 49519304Speter sp->rows ? 1 : sp->rows - vip->totalcount; 49619304Speter fc.e_tlno = sp->rows; 49719304Speter vip->linecount = vip->lcontinue = vip->totalcount = 0; 49819304Speter (void)vs_repaint(sp, &fc); 49919304Speter (void)vs_refresh(sp, 1); 50019304Speter } 50119304Speter 50219304Speter /* Deal with all non-character events. */ 50319304Speter switch (evp->e_event) { 50419304Speter case E_CHARACTER: 50519304Speter break; 50619304Speter case E_ERR: 50719304Speter case E_EOF: 50819304Speter F_SET(sp, SC_EXIT_FORCE); 50919304Speter return (1); 510254225Speter case E_INTERRUPT: 511254225Speter /* 512254225Speter * !!! 513254225Speter * Historically, <interrupt> exited the user from text input 514254225Speter * mode or cancelled a colon command, and returned to command 515254225Speter * mode. It also beeped the terminal, but that seems a bit 516254225Speter * excessive. 517254225Speter */ 518254225Speter goto k_escape; 51919304Speter case E_REPAINT: 52019304Speter if (vs_repaint(sp, &ev)) 52119304Speter return (1); 52219304Speter goto next; 52319304Speter case E_WRESIZE: 52419304Speter /* <resize> interrupts the input mode. */ 52519304Speter v_emsg(sp, NULL, VIM_WRESIZE); 526254225Speter goto k_escape; 52719304Speter default: 528254225Speter v_event_err(sp, evp); 529254225Speter goto k_escape; 53019304Speter } 53119304Speter 53219304Speter /* 53319304Speter * !!! 53419304Speter * If the first character of the input is a nul, replay the previous 53519304Speter * input. (Historically, it's okay to replay non-existent input.) 53619304Speter * This was not documented as far as I know, and is a great test of vi 53719304Speter * clones. 53819304Speter */ 539208612Sjh if (LF_ISSET(TXT_RECORD) && rcol == 0 && evp->e_c == '\0') { 54019304Speter if (vip->rep == NULL) 54119304Speter goto done; 54219304Speter 54319304Speter abb = AB_NOTSET; 54419304Speter LF_CLR(TXT_RECORD); 54519304Speter LF_SET(TXT_REPLAY); 54619304Speter goto replay; 54719304Speter } 54819304Speter 54919304Speter /* 55019304Speter * File name completion and colon command-line editing. We don't 55119304Speter * have enough meta characters, so we expect people to overload 55219304Speter * them. If the two characters are the same, then we do file name 55319304Speter * completion if the cursor is past the first column, and do colon 55419304Speter * command-line editing if it's not. 55519304Speter */ 55619304Speter if (quote == Q_NOTSET) { 55719304Speter int L__cedit, L__filec; 55819304Speter 55919304Speter L__cedit = L__filec = 0; 56019304Speter if (LF_ISSET(TXT_CEDIT) && O_STR(sp, O_CEDIT) != NULL && 56119304Speter O_STR(sp, O_CEDIT)[0] == evp->e_c) 56219304Speter L__cedit = 1; 56319304Speter if (LF_ISSET(TXT_FILEC) && O_STR(sp, O_FILEC) != NULL && 56419304Speter O_STR(sp, O_FILEC)[0] == evp->e_c) 56519304Speter L__filec = 1; 56619304Speter if (L__cedit == 1 && (L__filec == 0 || tp->cno == tp->offset)) { 56719304Speter tp->term = TERM_CEDIT; 56819304Speter goto k_escape; 56919304Speter } 57019304Speter if (L__filec == 1) { 57119304Speter if (txt_fc(sp, tp, &filec_redraw)) 57219304Speter goto err; 57319304Speter goto resolve; 57419304Speter } 57519304Speter } 57619304Speter 57719304Speter /* Abbreviation overflow check. See comment in txt_abbrev(). */ 57819304Speter#define MAX_ABBREVIATION_EXPANSION 256 57919304Speter if (F_ISSET(&evp->e_ch, CH_ABBREVIATED)) { 58019304Speter if (++abcnt > MAX_ABBREVIATION_EXPANSION) { 58119304Speter if (v_event_flush(sp, CH_ABBREVIATED)) 58219304Speter msgq(sp, M_ERR, 58319304Speter"191|Abbreviation exceeded expansion limit: characters discarded"); 58419304Speter abcnt = 0; 58519304Speter if (LF_ISSET(TXT_REPLAY)) 58619304Speter goto done; 58719304Speter goto resolve; 58819304Speter } 58919304Speter } else 59019304Speter abcnt = 0; 59119304Speter 59219304Speter /* Check to see if the character fits into the replay buffers. */ 59319304Speter if (LF_ISSET(TXT_RECORD)) { 594254225Speter BINC_GOTO(sp, EVENT, vip->rep, 59519304Speter vip->rep_len, (rcol + 1) * sizeof(EVENT)); 59619304Speter vip->rep[rcol++] = *evp; 59719304Speter } 59819304Speter 599254225Speterreplay: if (LF_ISSET(TXT_REPLAY)) { 600254225Speter if (rcol == vip->rep_cnt) 601254225Speter goto k_escape; 60219304Speter evp = vip->rep + rcol++; 603254225Speter } 60419304Speter 60519304Speter /* Wrapmargin check for leading space. */ 60619304Speter if (wm_skip) { 60719304Speter wm_skip = 0; 60819304Speter if (evp->e_c == ' ') 60919304Speter goto resolve; 61019304Speter } 61119304Speter 61219304Speter /* If quoted by someone else, simply insert the character. */ 61319304Speter if (F_ISSET(&evp->e_ch, CH_QUOTED)) 61419304Speter goto insq_ch; 61519304Speter 61619304Speter /* 61719304Speter * !!! 61819304Speter * If this character was quoted by a K_VLNEXT or a backslash, replace 61919304Speter * the placeholder (a carat or a backslash) with the new character. 62019304Speter * If it was quoted by a K_VLNEXT, we've already adjusted the cursor 62119304Speter * because it has to appear on top of the placeholder character. If 62219304Speter * it was quoted by a backslash, adjust the cursor now, the cursor 62319304Speter * doesn't appear on top of it. Historic practice in both cases. 62419304Speter * 62519304Speter * Skip tests for abbreviations; ":ab xa XA" followed by "ixa^V<space>" 62619304Speter * doesn't perform an abbreviation. Special case, ^V^J (not ^V^M) is 62719304Speter * the same as ^J, historically. 62819304Speter */ 62919304Speter if (quote == Q_BTHIS || quote == Q_VTHIS) { 63019304Speter FL_CLR(ec_flags, EC_QUOTED); 63119304Speter if (LF_ISSET(TXT_MAPINPUT)) 63219304Speter FL_SET(ec_flags, EC_MAPINPUT); 63319304Speter 63419304Speter if (quote == Q_BTHIS && 63519304Speter (evp->e_value == K_VERASE || evp->e_value == K_VKILL)) { 63619304Speter quote = Q_NOTSET; 63719304Speter --tp->cno; 63819304Speter ++tp->owrite; 63919304Speter goto insl_ch; 64019304Speter } 64119304Speter if (quote == Q_VTHIS && evp->e_value != K_NL) { 64219304Speter quote = Q_NOTSET; 64319304Speter goto insl_ch; 64419304Speter } 64519304Speter quote = Q_NOTSET; 64619304Speter } 64719304Speter 64819304Speter /* 64919304Speter * !!! 65019304Speter * Translate "<CH_HEX>[isxdigit()]*" to a character with a hex value: 65119304Speter * this test delimits the value by any non-hex character. Offset by 65219304Speter * one, we use 0 to mean that we've found <CH_HEX>. 65319304Speter */ 654254225Speter if (hexcnt > 1 && !ISXDIGIT(evp->e_c)) { 65519304Speter hexcnt = 0; 65619304Speter if (txt_hex(sp, tp)) 65719304Speter goto err; 65819304Speter } 65919304Speter 66019304Speter switch (evp->e_value) { 66119304Speter case K_CR: /* Carriage return. */ 66219304Speter case K_NL: /* New line. */ 66319304Speter /* Return in script windows and the command line. */ 66419304Speterk_cr: if (LF_ISSET(TXT_CR)) { 66519304Speter /* 66619304Speter * If this was a map, we may have not displayed 66719304Speter * the line. Display it, just in case. 66819304Speter * 66919304Speter * If a script window and not the colon line, 67019304Speter * push a <cr> so it gets executed. 67119304Speter */ 67219304Speter if (LF_ISSET(TXT_INFOLINE)) { 67319304Speter if (vs_change(sp, tp->lno, LINE_RESET)) 67419304Speter goto err; 67519304Speter } else if (F_ISSET(sp, SC_SCRIPT)) 676254225Speter (void)v_event_push(sp, NULL, L("\r"), 1, CH_NOMAP); 67719304Speter 67819304Speter /* Set term condition: if empty. */ 67919304Speter if (tp->cno <= tp->offset) 68019304Speter tp->term = TERM_CR; 68119304Speter /* 68219304Speter * Set term condition: if searching incrementally and 68319304Speter * the user entered a pattern, return a completed 68419304Speter * search, regardless if the entire pattern was found. 68519304Speter */ 68619304Speter if (FL_ISSET(is_flags, IS_RUNNING) && 68719304Speter tp->cno >= tp->offset + 1) 68819304Speter tp->term = TERM_SEARCH; 68919304Speter 69019304Speter goto k_escape; 69119304Speter } 69219304Speter 69319304Speter#define LINE_RESOLVE { \ 69419304Speter /* \ 69519304Speter * Handle abbreviations. If there was one, discard the \ 69619304Speter * replay characters. \ 69719304Speter */ \ 69819304Speter if (abb == AB_INWORD && \ 69919304Speter !LF_ISSET(TXT_REPLAY) && F_ISSET(gp, G_ABBREV)) { \ 70019304Speter if (txt_abbrev(sp, tp, &evp->e_c, \ 70119304Speter LF_ISSET(TXT_INFOLINE), &tmp, \ 70219304Speter &ab_turnoff)) \ 70319304Speter goto err; \ 70419304Speter if (tmp) { \ 70519304Speter if (LF_ISSET(TXT_RECORD)) \ 70619304Speter rcol -= tmp + 1; \ 70719304Speter goto resolve; \ 70819304Speter } \ 70919304Speter } \ 71019304Speter if (abb != AB_NOTSET) \ 71119304Speter abb = AB_NOTWORD; \ 71219304Speter if (UNMAP_TST) \ 71319304Speter txt_unmap(sp, tp, &ec_flags); \ 71419304Speter /* \ 71519304Speter * Delete any appended cursor. It's possible to get in \ 71619304Speter * situations where TXT_APPENDEOL is set but tp->insert \ 71719304Speter * is 0 when using the R command and all the characters \ 71819304Speter * are tp->owrite characters. \ 71919304Speter */ \ 72019304Speter if (LF_ISSET(TXT_APPENDEOL) && tp->insert > 0) { \ 72119304Speter --tp->len; \ 72219304Speter --tp->insert; \ 72319304Speter } \ 72419304Speter} 72519304Speter LINE_RESOLVE; 72619304Speter 72719304Speter /* 72819304Speter * Save the current line information for restoration in 72919304Speter * txt_backup(), and set the line final length. 73019304Speter */ 73119304Speter tp->sv_len = tp->len; 73219304Speter tp->sv_cno = tp->cno; 73319304Speter tp->len = tp->cno; 73419304Speter 73519304Speter /* Update the old line. */ 73619304Speter if (vs_change(sp, tp->lno, LINE_RESET)) 73719304Speter goto err; 73819304Speter 73919304Speter /* 74019304Speter * Historic practice, when the autoindent edit option was set, 74119304Speter * was to delete <blank> characters following the inserted 74219304Speter * newline. This affected the 'R', 'c', and 's' commands; 'c' 74319304Speter * and 's' retained the insert characters only, 'R' moved the 74419304Speter * overwrite and insert characters into the next TEXT structure. 74519304Speter * We keep track of the number of characters erased for the 'R' 74619304Speter * command so that the final resolution of the line is correct. 74719304Speter */ 74819304Speter tp->R_erase = 0; 74919304Speter owrite = tp->owrite; 75019304Speter insert = tp->insert; 75119304Speter if (LF_ISSET(TXT_REPLACE) && owrite != 0) { 75219304Speter for (p = tp->lb + tp->cno; owrite > 0 && isblank(*p); 75319304Speter ++p, --owrite, ++tp->R_erase); 75419304Speter if (owrite == 0) 75519304Speter for (; insert > 0 && isblank(*p); 75619304Speter ++p, ++tp->R_erase, --insert); 75719304Speter } else { 75819304Speter p = tp->lb + tp->cno + owrite; 75919304Speter if (O_ISSET(sp, O_AUTOINDENT)) 76019304Speter for (; insert > 0 && 76119304Speter isblank(*p); ++p, --insert); 76219304Speter owrite = 0; 76319304Speter } 76419304Speter 76519304Speter /* 76619304Speter * !!! 76719304Speter * Create a new line and insert the new TEXT into the queue. 76819304Speter * DON'T insert until the old line has been updated, or the 76919304Speter * inserted line count in line.c:db_get() will be wrong. 77019304Speter */ 77119304Speter if ((ntp = text_init(sp, p, 77219304Speter insert + owrite, insert + owrite + 32)) == NULL) 77319304Speter goto err; 774254225Speter TAILQ_INSERT_TAIL(sp->tiq, ntp, q); 77519304Speter 77619304Speter /* Set up bookkeeping for the new line. */ 77719304Speter ntp->insert = insert; 77819304Speter ntp->owrite = owrite; 77919304Speter ntp->lno = tp->lno + 1; 78019304Speter 78119304Speter /* 78219304Speter * Reset the autoindent line value. 0^D keeps the autoindent 78319304Speter * line from changing, ^D changes the level, even if there were 78419304Speter * no characters in the old line. Note, if using the current 78519304Speter * tp structure, use the cursor as the length, the autoindent 78619304Speter * characters may have been erased. 78719304Speter */ 78819304Speter if (LF_ISSET(TXT_AUTOINDENT)) { 789254225Speter if (nochange) { 790254225Speter nochange = 0; 79119304Speter if (v_txt_auto(sp, OOBLNO, &ait, ait.ai, ntp)) 79219304Speter goto err; 793254225Speter FREE_SPACEW(sp, ait.lb, ait.lb_len); 79419304Speter } else 79519304Speter if (v_txt_auto(sp, OOBLNO, tp, tp->cno, ntp)) 79619304Speter goto err; 79719304Speter carat = C_NOTSET; 79819304Speter } 79919304Speter 80019304Speter /* Reset the cursor. */ 80119304Speter ntp->cno = ntp->ai; 80219304Speter 80319304Speter /* 80419304Speter * If we're here because wrapmargin was set and we've broken a 80519304Speter * line, there may be additional information (i.e. the start of 80619304Speter * a line) in the wmt structure. 80719304Speter */ 80819304Speter if (wm_set) { 80919304Speter if (wmt.offset != 0 || 81019304Speter wmt.owrite != 0 || wmt.insert != 0) { 81119304Speter#define WMTSPACE wmt.offset + wmt.owrite + wmt.insert 812254225Speter BINC_GOTOW(sp, ntp->lb, 81319304Speter ntp->lb_len, ntp->len + WMTSPACE + 32); 814254225Speter MEMMOVE(ntp->lb + ntp->cno, wmt.lb, WMTSPACE); 81519304Speter ntp->len += WMTSPACE; 81619304Speter ntp->cno += wmt.offset; 81719304Speter ntp->owrite = wmt.owrite; 81819304Speter ntp->insert = wmt.insert; 81919304Speter } 82019304Speter wm_set = 0; 82119304Speter } 82219304Speter 82319304Speter /* New lines are TXT_APPENDEOL. */ 82419304Speter if (ntp->owrite == 0 && ntp->insert == 0) { 825254225Speter BINC_GOTOW(sp, ntp->lb, ntp->lb_len, ntp->len + 1); 82619304Speter LF_SET(TXT_APPENDEOL); 82719304Speter ntp->lb[ntp->cno] = CH_CURSOR; 82819304Speter ++ntp->insert; 82919304Speter ++ntp->len; 83019304Speter } 83119304Speter 83219304Speter /* Swap old and new TEXT's, and update the new line. */ 83319304Speter tp = ntp; 83419304Speter if (vs_change(sp, tp->lno, LINE_INSERT)) 83519304Speter goto err; 83619304Speter 83719304Speter goto resolve; 83819304Speter case K_ESCAPE: /* Escape. */ 83919304Speter if (!LF_ISSET(TXT_ESCAPE)) 84019304Speter goto ins_ch; 84119304Speter 84219304Speter /* If we have a count, start replaying the input. */ 84319304Speter if (rcount > 1) { 84419304Speter --rcount; 84519304Speter 846254225Speter vip->rep_cnt = rcol; 84719304Speter rcol = 0; 84819304Speter abb = AB_NOTSET; 84919304Speter LF_CLR(TXT_RECORD); 85019304Speter LF_SET(TXT_REPLAY); 85119304Speter 85219304Speter /* 85319304Speter * Some commands (e.g. 'o') need a <newline> for each 85419304Speter * repetition. 85519304Speter */ 85619304Speter if (LF_ISSET(TXT_ADDNEWLINE)) 85719304Speter goto k_cr; 85819304Speter 85919304Speter /* 86019304Speter * The R command turns into the 'a' command after the 86119304Speter * first repetition. 86219304Speter */ 86319304Speter if (LF_ISSET(TXT_REPLACE)) { 86419304Speter tp->insert = tp->owrite; 86519304Speter tp->owrite = 0; 86619304Speter LF_CLR(TXT_REPLACE); 86719304Speter } 86819304Speter goto replay; 86919304Speter } 87019304Speter 87119304Speter /* Set term condition: if empty. */ 87219304Speter if (tp->cno <= tp->offset) 87319304Speter tp->term = TERM_ESC; 87419304Speter /* 87519304Speter * Set term condition: if searching incrementally and the user 87619304Speter * entered a pattern, return a completed search, regardless if 87719304Speter * the entire pattern was found. 87819304Speter */ 87919304Speter if (FL_ISSET(is_flags, IS_RUNNING) && tp->cno >= tp->offset + 1) 88019304Speter tp->term = TERM_SEARCH; 88119304Speter 88219304Speterk_escape: LINE_RESOLVE; 88319304Speter 88419304Speter /* 88519304Speter * Clean up for the 'R' command, restoring overwrite 88619304Speter * characters, and making them into insert characters. 88719304Speter */ 88819304Speter if (LF_ISSET(TXT_REPLACE)) 889254225Speter txt_Rresolve(sp, sp->tiq, tp, len); 89019304Speter 89119304Speter /* 89219304Speter * If there are any overwrite characters, copy down 89319304Speter * any insert characters, and decrement the length. 89419304Speter */ 89519304Speter if (tp->owrite) { 89619304Speter if (tp->insert) 897254225Speter MEMMOVE(tp->lb + tp->cno, 89819304Speter tp->lb + tp->cno + tp->owrite, tp->insert); 89919304Speter tp->len -= tp->owrite; 90019304Speter } 90119304Speter 90219304Speter /* 90319304Speter * Optionally resolve the lines into the file. If not 90419304Speter * resolving the lines into the file, end the line with 90519304Speter * a nul. If the line is empty, then set the length to 90619304Speter * 0, the termination condition has already been set. 90719304Speter * 90819304Speter * XXX 90919304Speter * This is wrong, should pass back a length. 91019304Speter */ 91119304Speter if (LF_ISSET(TXT_RESOLVE)) { 912254225Speter if (txt_resolve(sp, sp->tiq, flags)) 91319304Speter goto err; 91419304Speter } else { 915254225Speter BINC_GOTOW(sp, tp->lb, tp->lb_len, tp->len + 1); 91619304Speter tp->lb[tp->len] = '\0'; 91719304Speter } 91819304Speter 91919304Speter /* 92019304Speter * Set the return cursor position to rest on the last 92119304Speter * inserted character. 92219304Speter */ 92319304Speter if (tp->cno != 0) 92419304Speter --tp->cno; 92519304Speter 92619304Speter /* Update the last line. */ 92719304Speter if (vs_change(sp, tp->lno, LINE_RESET)) 92819304Speter return (1); 92919304Speter goto done; 93019304Speter case K_CARAT: /* Delete autoindent chars. */ 93119304Speter if (tp->cno <= tp->ai && LF_ISSET(TXT_AUTOINDENT)) 93219304Speter carat = C_CARATSET; 93319304Speter goto ins_ch; 93419304Speter case K_ZERO: /* Delete autoindent chars. */ 93519304Speter if (tp->cno <= tp->ai && LF_ISSET(TXT_AUTOINDENT)) 93619304Speter carat = C_ZEROSET; 93719304Speter goto ins_ch; 93819304Speter case K_CNTRLD: /* Delete autoindent char. */ 93919304Speter /* 94019304Speter * If in the first column or no characters to erase, ignore 94119304Speter * the ^D (this matches historic practice). If not doing 94219304Speter * autoindent or already inserted non-ai characters, it's a 94319304Speter * literal. The latter test is done in the switch, as the 94419304Speter * CARAT forms are N + 1, not N. 94519304Speter */ 94619304Speter if (!LF_ISSET(TXT_AUTOINDENT)) 94719304Speter goto ins_ch; 94819304Speter if (tp->cno == 0) 94919304Speter goto resolve; 95019304Speter 95119304Speter switch (carat) { 95219304Speter case C_CARATSET: /* ^^D */ 95319304Speter if (tp->ai == 0 || tp->cno > tp->ai + tp->offset + 1) 95419304Speter goto ins_ch; 95519304Speter 95619304Speter /* Save the ai string for later. */ 95719304Speter ait.lb = NULL; 95819304Speter ait.lb_len = 0; 959254225Speter BINC_GOTOW(sp, ait.lb, ait.lb_len, tp->ai); 960254225Speter MEMMOVE(ait.lb, tp->lb, tp->ai); 96119304Speter ait.ai = ait.len = tp->ai; 96219304Speter 963254225Speter carat = C_NOTSET; 964254225Speter nochange = 1; 96519304Speter goto leftmargin; 96619304Speter case C_ZEROSET: /* 0^D */ 96719304Speter if (tp->ai == 0 || tp->cno > tp->ai + tp->offset + 1) 96819304Speter goto ins_ch; 96919304Speter 97019304Speter carat = C_NOTSET; 97119304Speterleftmargin: tp->lb[tp->cno - 1] = ' '; 97219304Speter tp->owrite += tp->cno - tp->offset; 97319304Speter tp->ai = 0; 97419304Speter tp->cno = tp->offset; 97519304Speter break; 97619304Speter case C_NOTSET: /* ^D */ 97719304Speter if (tp->ai == 0 || tp->cno > tp->ai + tp->offset) 97819304Speter goto ins_ch; 97919304Speter 98019304Speter (void)txt_dent(sp, tp, 0); 98119304Speter break; 98219304Speter default: 98319304Speter abort(); 98419304Speter } 98519304Speter break; 98619304Speter case K_VERASE: /* Erase the last character. */ 98719304Speter /* If can erase over the prompt, return. */ 98819304Speter if (tp->cno <= tp->offset && LF_ISSET(TXT_BS)) { 98919304Speter tp->term = TERM_BS; 99019304Speter goto done; 99119304Speter } 99219304Speter 99319304Speter /* 99419304Speter * If at the beginning of the line, try and drop back to a 99519304Speter * previously inserted line. 99619304Speter */ 99719304Speter if (tp->cno == 0) { 99819304Speter if ((ntp = 999254225Speter txt_backup(sp, sp->tiq, tp, &flags)) == NULL) 100019304Speter goto err; 100119304Speter tp = ntp; 100219304Speter break; 100319304Speter } 100419304Speter 100519304Speter /* If nothing to erase, bell the user. */ 100619304Speter if (tp->cno <= tp->offset) { 100719304Speter if (!LF_ISSET(TXT_REPLAY)) 100819304Speter txt_nomorech(sp); 100919304Speter break; 101019304Speter } 101119304Speter 101219304Speter /* Drop back one character. */ 101319304Speter --tp->cno; 101419304Speter 101519304Speter /* 101619304Speter * Historically, vi didn't replace the erased characters with 101719304Speter * <blank>s, presumably because it's easier to fix a minor 101819304Speter * typing mistake and continue on if the previous letters are 101919304Speter * already there. This is a problem for incremental searching, 102019304Speter * because the user can no longer tell where they are in the 102119304Speter * colon command line because the cursor is at the last search 102219304Speter * point in the screen. So, if incrementally searching, erase 102319304Speter * the erased characters from the screen. 102419304Speter */ 102519304Speter if (FL_ISSET(is_flags, IS_RUNNING)) 102619304Speter tp->lb[tp->cno] = ' '; 102719304Speter 102819304Speter /* 102919304Speter * Increment overwrite, decrement ai if deleted. 103019304Speter * 103119304Speter * !!! 103219304Speter * Historic vi did not permit users to use erase characters 103319304Speter * to delete autoindent characters. We do. Eat hot death, 103419304Speter * POSIX. 103519304Speter */ 103619304Speter ++tp->owrite; 103719304Speter if (tp->cno < tp->ai) 103819304Speter --tp->ai; 103919304Speter 104019304Speter /* Reset if we deleted an incremental search character. */ 104119304Speter if (FL_ISSET(is_flags, IS_RUNNING)) 104219304Speter FL_SET(is_flags, IS_RESTART); 104319304Speter break; 104419304Speter case K_VWERASE: /* Skip back one word. */ 104519304Speter /* 104619304Speter * If at the beginning of the line, try and drop back to a 104719304Speter * previously inserted line. 104819304Speter */ 104919304Speter if (tp->cno == 0) { 105019304Speter if ((ntp = 1051254225Speter txt_backup(sp, sp->tiq, tp, &flags)) == NULL) 105219304Speter goto err; 105319304Speter tp = ntp; 105419304Speter } 105519304Speter 105619304Speter /* 105719304Speter * If at offset, nothing to erase so bell the user. 105819304Speter */ 105919304Speter if (tp->cno <= tp->offset) { 106019304Speter if (!LF_ISSET(TXT_REPLAY)) 106119304Speter txt_nomorech(sp); 106219304Speter break; 106319304Speter } 106419304Speter 106519304Speter /* 106619304Speter * The first werase goes back to any autoindent column and the 106719304Speter * second werase goes back to the offset. 106819304Speter * 106919304Speter * !!! 107019304Speter * Historic vi did not permit users to use erase characters to 107119304Speter * delete autoindent characters. 107219304Speter */ 107319304Speter if (tp->ai && tp->cno > tp->ai) 107419304Speter max = tp->ai; 107519304Speter else { 107619304Speter tp->ai = 0; 107719304Speter max = tp->offset; 107819304Speter } 107919304Speter 108019304Speter /* Skip over trailing space characters. */ 1081254225Speter while (tp->cno > max && ISBLANK(tp->lb[tp->cno - 1])) { 108219304Speter --tp->cno; 108319304Speter ++tp->owrite; 108419304Speter } 108519304Speter if (tp->cno == max) 108619304Speter break; 108719304Speter /* 108819304Speter * There are three types of word erase found on UNIX systems. 108919304Speter * They can be identified by how the string /a/b/c is treated 109019304Speter * -- as 1, 3, or 6 words. Historic vi had two classes of 109119304Speter * characters, and strings were delimited by them and 109219304Speter * <blank>'s, so, 6 words. The historic tty interface used 109319304Speter * <blank>'s to delimit strings, so, 1 word. The algorithm 109419304Speter * offered in the 4.4BSD tty interface (as stty altwerase) 109519304Speter * treats it as 3 words -- there are two classes of 109619304Speter * characters, and strings are delimited by them and 109719304Speter * <blank>'s. The difference is that the type of the first 109819304Speter * erased character erased is ignored, which is exactly right 109919304Speter * when erasing pathname components. The edit options 110019304Speter * TXT_ALTWERASE and TXT_TTYWERASE specify the 4.4BSD tty 110119304Speter * interface and the historic tty driver behavior, 110219304Speter * respectively, and the default is the same as the historic 110319304Speter * vi behavior. 110419304Speter * 110519304Speter * Overwrite erased characters if doing incremental search; 110619304Speter * see comment above. 110719304Speter */ 110819304Speter if (LF_ISSET(TXT_TTYWERASE)) 110919304Speter while (tp->cno > max) { 1110254225Speter if (ISBLANK(tp->lb[tp->cno - 1])) 1111254225Speter break; 111219304Speter --tp->cno; 111319304Speter ++tp->owrite; 111419304Speter if (FL_ISSET(is_flags, IS_RUNNING)) 111519304Speter tp->lb[tp->cno] = ' '; 111619304Speter } 111719304Speter else { 111819304Speter if (LF_ISSET(TXT_ALTWERASE)) { 111919304Speter --tp->cno; 112019304Speter ++tp->owrite; 112119304Speter if (FL_ISSET(is_flags, IS_RUNNING)) 112219304Speter tp->lb[tp->cno] = ' '; 112319304Speter } 112419304Speter if (tp->cno > max) 112519304Speter tmp = inword(tp->lb[tp->cno - 1]); 112619304Speter while (tp->cno > max) { 1127254225Speter if (tmp != inword(tp->lb[tp->cno - 1]) 1128254225Speter || ISBLANK(tp->lb[tp->cno - 1])) 1129254225Speter break; 113019304Speter --tp->cno; 113119304Speter ++tp->owrite; 113219304Speter if (FL_ISSET(is_flags, IS_RUNNING)) 113319304Speter tp->lb[tp->cno] = ' '; 113419304Speter } 113519304Speter } 113619304Speter 113719304Speter /* Reset if we deleted an incremental search character. */ 113819304Speter if (FL_ISSET(is_flags, IS_RUNNING)) 113919304Speter FL_SET(is_flags, IS_RESTART); 114019304Speter break; 114119304Speter case K_VKILL: /* Restart this line. */ 114219304Speter /* 114319304Speter * !!! 114419304Speter * If at the beginning of the line, try and drop back to a 114519304Speter * previously inserted line. Historic vi did not permit 114619304Speter * users to go back to previous lines. 114719304Speter */ 114819304Speter if (tp->cno == 0) { 114919304Speter if ((ntp = 1150254225Speter txt_backup(sp, sp->tiq, tp, &flags)) == NULL) 115119304Speter goto err; 115219304Speter tp = ntp; 115319304Speter } 115419304Speter 115519304Speter /* If at offset, nothing to erase so bell the user. */ 115619304Speter if (tp->cno <= tp->offset) { 115719304Speter if (!LF_ISSET(TXT_REPLAY)) 115819304Speter txt_nomorech(sp); 115919304Speter break; 116019304Speter } 116119304Speter 116219304Speter /* 116319304Speter * First kill goes back to any autoindent and second kill goes 116419304Speter * back to the offset. 116519304Speter * 116619304Speter * !!! 116719304Speter * Historic vi did not permit users to use erase characters to 116819304Speter * delete autoindent characters. 116919304Speter */ 117019304Speter if (tp->ai && tp->cno > tp->ai) 117119304Speter max = tp->ai; 117219304Speter else { 117319304Speter tp->ai = 0; 117419304Speter max = tp->offset; 117519304Speter } 117619304Speter tp->owrite += tp->cno - max; 117719304Speter 117819304Speter /* 117919304Speter * Overwrite erased characters if doing incremental search; 118019304Speter * see comment above. 118119304Speter */ 118219304Speter if (FL_ISSET(is_flags, IS_RUNNING)) 118319304Speter do { 118419304Speter tp->lb[--tp->cno] = ' '; 118519304Speter } while (tp->cno > max); 118619304Speter else 118719304Speter tp->cno = max; 118819304Speter 118919304Speter /* Reset if we deleted an incremental search character. */ 119019304Speter if (FL_ISSET(is_flags, IS_RUNNING)) 119119304Speter FL_SET(is_flags, IS_RESTART); 119219304Speter break; 119319304Speter case K_CNTRLT: /* Add autoindent characters. */ 119419304Speter if (!LF_ISSET(TXT_CNTRLT)) 119519304Speter goto ins_ch; 119619304Speter if (txt_dent(sp, tp, 1)) 119719304Speter goto err; 119819304Speter goto ebuf_chk; 119919304Speter case K_BACKSLASH: /* Quote next erase/kill. */ 120019304Speter /* 120119304Speter * !!! 120219304Speter * Historic vi tried to make abbreviations after a backslash 120319304Speter * escape work. If you did ":ab x y", and inserted "x\^H", 120419304Speter * (assuming the erase character was ^H) you got "x^H", and 120519304Speter * no abbreviation was done. If you inserted "x\z", however, 120619304Speter * it tried to back up and do the abbreviation, i.e. replace 120719304Speter * 'x' with 'y'. The problem was it got it wrong, and you 120819304Speter * ended up with "zy\". 120919304Speter * 121019304Speter * This is really hard to do (you have to remember the 121119304Speter * word/non-word state, for example), and doesn't make any 121219304Speter * sense to me. Both backslash and the characters it 121319304Speter * (usually) escapes will individually trigger the 121419304Speter * abbreviation, so I don't see why the combination of them 121519304Speter * wouldn't. I don't expect to get caught on this one, 121619304Speter * particularly since it never worked right, but I've been 121719304Speter * wrong before. 121819304Speter * 121919304Speter * Do the tests for abbreviations, so ":ab xa XA", 122019304Speter * "ixa\<K_VERASE>" performs the abbreviation. 122119304Speter */ 122219304Speter quote = Q_BNEXT; 122319304Speter goto insq_ch; 122419304Speter case K_VLNEXT: /* Quote next character. */ 122519304Speter evp->e_c = '^'; 122619304Speter quote = Q_VNEXT; 122719304Speter /* 122819304Speter * Turn on the quote flag so that the underlying routines 122919304Speter * quote the next character where it's possible. Turn off 123019304Speter * the input mapbiting flag so that we don't remap the next 123119304Speter * character. 123219304Speter */ 123319304Speter FL_SET(ec_flags, EC_QUOTED); 123419304Speter FL_CLR(ec_flags, EC_MAPINPUT); 123519304Speter 123619304Speter /* 123719304Speter * !!! 123819304Speter * Skip the tests for abbreviations, so ":ab xa XA", 123919304Speter * "ixa^V<space>" doesn't perform the abbreviation. 124019304Speter */ 124119304Speter goto insl_ch; 124219304Speter case K_HEXCHAR: 124319304Speter hexcnt = 1; 124419304Speter goto insq_ch; 124519304Speter default: /* Insert the character. */ 1246254225Speter if (LF_ISSET(TXT_SHOWMATCH)) { 1247254225Speter CHAR_T *match_chars, *cp; 1248254225Speter 1249254225Speter match_chars = VIP(sp)->mcs; 1250254225Speter cp = STRCHR(match_chars, evp->e_c); 1251254225Speter if (cp != NULL && (cp - match_chars) & 1) 1252254225Speter showmatch = 1; 1253254225Speter } 125419304Speterins_ch: /* 125519304Speter * Historically, vi eliminated nul's out of hand. If the 125619304Speter * beautify option was set, it also deleted any unknown 125719304Speter * ASCII value less than space (040) and the del character 125819304Speter * (0177), except for tabs. Unknown is a key word here. 125919304Speter * Most vi documentation claims that it deleted everything 126019304Speter * but <tab>, <nl> and <ff>, as that's what the original 126119304Speter * 4BSD documentation said. This is obviously wrong, 126219304Speter * however, as <esc> would be included in that list. What 126319304Speter * we do is eliminate any unquoted, iscntrl() character that 126419304Speter * wasn't a replay and wasn't handled specially, except 126519304Speter * <tab> or <ff>. 126619304Speter */ 1267254225Speter if (LF_ISSET(TXT_BEAUTIFY) && ISCNTRL(evp->e_c) && 126819304Speter evp->e_value != K_FORMFEED && evp->e_value != K_TAB) { 126919304Speter msgq(sp, M_BERR, 127019304Speter "192|Illegal character; quote to enter"); 127119304Speter if (LF_ISSET(TXT_REPLAY)) 127219304Speter goto done; 127319304Speter break; 127419304Speter } 127519304Speter 127619304Speterinsq_ch: /* 127719304Speter * If entering a non-word character after a word, check for 127819304Speter * abbreviations. If there was one, discard replay characters. 127919304Speter * If entering a blank character, check for unmap commands, 128019304Speter * as well. 128119304Speter */ 128219304Speter if (!inword(evp->e_c)) { 128319304Speter if (abb == AB_INWORD && 128419304Speter !LF_ISSET(TXT_REPLAY) && F_ISSET(gp, G_ABBREV)) { 128519304Speter if (txt_abbrev(sp, tp, &evp->e_c, 128619304Speter LF_ISSET(TXT_INFOLINE), &tmp, &ab_turnoff)) 128719304Speter goto err; 128819304Speter if (tmp) { 128919304Speter if (LF_ISSET(TXT_RECORD)) 129019304Speter rcol -= tmp + 1; 129119304Speter goto resolve; 129219304Speter } 129319304Speter } 129419304Speter if (isblank(evp->e_c) && UNMAP_TST) 129519304Speter txt_unmap(sp, tp, &ec_flags); 129619304Speter } 129719304Speter if (abb != AB_NOTSET) 129819304Speter abb = inword(evp->e_c) ? AB_INWORD : AB_NOTWORD; 129919304Speter 130019304Speterinsl_ch: if (txt_insch(sp, tp, &evp->e_c, flags)) 130119304Speter goto err; 130219304Speter 130319304Speter /* 130419304Speter * If we're using K_VLNEXT to quote the next character, then 130519304Speter * we want the cursor to position itself on the ^ placeholder 130619304Speter * we're displaying, to match historic practice. 130719304Speter */ 130819304Speter if (quote == Q_VNEXT) { 130919304Speter --tp->cno; 131019304Speter ++tp->owrite; 131119304Speter } 131219304Speter 131319304Speter /* 131419304Speter * !!! 131519304Speter * Translate "<CH_HEX>[isxdigit()]*" to a character with 131619304Speter * a hex value: this test delimits the value by the max 131719304Speter * number of hex bytes. Offset by one, we use 0 to mean 131819304Speter * that we've found <CH_HEX>. 131919304Speter */ 1320254225Speter if (hexcnt != 0 && hexcnt++ == 3) { 132119304Speter hexcnt = 0; 132219304Speter if (txt_hex(sp, tp)) 132319304Speter goto err; 132419304Speter } 132519304Speter 132619304Speter /* 132719304Speter * Check to see if we've crossed the margin. 132819304Speter * 132919304Speter * !!! 133019304Speter * In the historic vi, the wrapmargin value was figured out 133119304Speter * using the display widths of the characters, i.e. <tab> 133219304Speter * characters were counted as two characters if the list edit 133319304Speter * option is set, but as the tabstop edit option number of 133419304Speter * characters otherwise. That's what the vs_column() function 133519304Speter * gives us, so we use it. 133619304Speter */ 133719304Speter if (margin != 0) { 133819304Speter if (vs_column(sp, &tcol)) 133919304Speter goto err; 134019304Speter if (tcol >= margin) { 134119304Speter if (txt_margin(sp, tp, &wmt, &tmp, flags)) 134219304Speter goto err; 134319304Speter if (tmp) { 134419304Speter if (isblank(evp->e_c)) 134519304Speter wm_skip = 1; 134619304Speter wm_set = 1; 134719304Speter goto k_cr; 134819304Speter } 134919304Speter } 135019304Speter } 135119304Speter 135219304Speter /* 135319304Speter * If we've reached the end of the buffer, then we need to 135419304Speter * switch into insert mode. This happens when there's a 135519304Speter * change to a mark and the user puts in more characters than 135619304Speter * the length of the motion. 135719304Speter */ 135819304Speterebuf_chk: if (tp->cno >= tp->len) { 1359254225Speter BINC_GOTOW(sp, tp->lb, tp->lb_len, tp->len + 1); 136019304Speter LF_SET(TXT_APPENDEOL); 136119304Speter 136219304Speter tp->lb[tp->cno] = CH_CURSOR; 136319304Speter ++tp->insert; 136419304Speter ++tp->len; 136519304Speter } 136619304Speter 136719304Speter /* Step the quote state forward. */ 136819304Speter if (quote != Q_NOTSET) { 136919304Speter if (quote == Q_BNEXT) 137019304Speter quote = Q_BTHIS; 137119304Speter if (quote == Q_VNEXT) 137219304Speter quote = Q_VTHIS; 137319304Speter } 137419304Speter break; 137519304Speter } 137619304Speter 137719304Speter#ifdef DEBUG 137819304Speter if (tp->cno + tp->insert + tp->owrite != tp->len) { 137919304Speter msgq(sp, M_ERR, 1380254225Speter "len %zu != cno: %zu ai: %zu insert %zu overwrite %zu", 138119304Speter tp->len, tp->cno, tp->ai, tp->insert, tp->owrite); 138219304Speter if (LF_ISSET(TXT_REPLAY)) 138319304Speter goto done; 138419304Speter tp->len = tp->cno + tp->insert + tp->owrite; 138519304Speter } 138619304Speter#endif 138719304Speter 138819304Speterresolve:/* 138919304Speter * 1: If we don't need to know where the cursor really is and we're 139019304Speter * replaying text, keep going. 139119304Speter */ 139219304Speter if (margin == 0 && LF_ISSET(TXT_REPLAY)) 139319304Speter goto replay; 139419304Speter 139519304Speter /* 139619304Speter * 2: Reset the line. Don't bother unless we're about to wait on 139719304Speter * a character or we need to know where the cursor really is. 139819304Speter * We have to do this before showing matching characters so the 139919304Speter * user can see what they're matching. 140019304Speter */ 140119304Speter if ((margin != 0 || !KEYS_WAITING(sp)) && 140219304Speter vs_change(sp, tp->lno, LINE_RESET)) 140319304Speter return (1); 140419304Speter 140519304Speter /* 140619304Speter * 3: If there aren't keys waiting, display the matching character. 140719304Speter * We have to do this before resolving any messages, otherwise 140819304Speter * the error message from a missing match won't appear correctly. 140919304Speter */ 141019304Speter if (showmatch) { 141119304Speter if (!KEYS_WAITING(sp) && txt_showmatch(sp, tp)) 141219304Speter return (1); 141319304Speter showmatch = 0; 141419304Speter } 141519304Speter 141619304Speter /* 141719304Speter * 4: If there have been messages and we're not editing on the colon 141819304Speter * command line or doing file name completion, resolve them. 141919304Speter */ 142019304Speter if ((vip->totalcount != 0 || F_ISSET(gp, G_BELLSCHED)) && 142119304Speter !F_ISSET(sp, SC_TINPUT_INFO) && !filec_redraw && 142219304Speter vs_resolve(sp, NULL, 0)) 142319304Speter return (1); 142419304Speter 142519304Speter /* 142619304Speter * 5: Refresh the screen if we're about to wait on a character or we 142719304Speter * need to know where the cursor really is. 142819304Speter */ 142919304Speter if (margin != 0 || !KEYS_WAITING(sp)) { 143019304Speter UPDATE_POSITION(sp, tp); 143119304Speter if (vs_refresh(sp, margin != 0)) 143219304Speter return (1); 143319304Speter } 143419304Speter 143519304Speter /* 6: Proceed with the incremental search. */ 143619304Speter if (FL_ISSET(is_flags, IS_RUNNING) && txt_isrch(sp, vp, tp, &is_flags)) 143719304Speter return (1); 143819304Speter 143919304Speter /* 7: Next character... */ 144019304Speter if (LF_ISSET(TXT_REPLAY)) 144119304Speter goto replay; 144219304Speter goto next; 144319304Speter 144419304Speterdone: /* Leave input mode. */ 144519304Speter F_CLR(sp, SC_TINPUT); 144619304Speter 144719304Speter /* If recording for playback, save it. */ 144819304Speter if (LF_ISSET(TXT_RECORD)) 144919304Speter vip->rep_cnt = rcol; 145019304Speter 145119304Speter /* 145219304Speter * If not working on the colon command line, set the final cursor 145319304Speter * position. 145419304Speter */ 145519304Speter if (!F_ISSET(sp, SC_TINPUT_INFO)) { 145619304Speter vp->m_final.lno = tp->lno; 145719304Speter vp->m_final.cno = tp->cno; 145819304Speter } 145919304Speter return (0); 146019304Speter 146119304Spetererr: 146219304Speteralloc_err: 1463208612Sjh F_CLR(sp, SC_TINPUT); 1464254225Speter txt_err(sp, sp->tiq); 146519304Speter return (1); 146619304Speter} 146719304Speter 146819304Speter/* 146919304Speter * txt_abbrev -- 147019304Speter * Handle abbreviations. 147119304Speter */ 147219304Speterstatic int 1473254225Spetertxt_abbrev(SCR *sp, TEXT *tp, CHAR_T *pushcp, int isinfoline, int *didsubp, int *turnoffp) 147419304Speter{ 147519304Speter VI_PRIVATE *vip; 147619304Speter CHAR_T ch, *p; 147719304Speter SEQ *qp; 147819304Speter size_t len, off; 147919304Speter 148019304Speter /* Check to make sure we're not at the start of an append. */ 148119304Speter *didsubp = 0; 148219304Speter if (tp->cno == tp->offset) 148319304Speter return (0); 148419304Speter 148519304Speter vip = VIP(sp); 148619304Speter 148719304Speter /* 148819304Speter * Find the start of the "word". 148919304Speter * 149019304Speter * !!! 149119304Speter * We match historic practice, which, as far as I can tell, had an 149219304Speter * off-by-one error. The way this worked was that when the inserted 149319304Speter * text switched from a "word" character to a non-word character, 149419304Speter * vi would check for possible abbreviations. It would then take the 149519304Speter * type (i.e. word/non-word) of the character entered TWO characters 149619304Speter * ago, and move backward in the text until reaching a character that 149719304Speter * was not that type, or the beginning of the insert, the line, or 149819304Speter * the file. For example, in the string "abc<space>", when the <space> 149919304Speter * character triggered the abbreviation check, the type of the 'b' 150019304Speter * character was used for moving through the string. Maybe there's a 150119304Speter * reason for not using the first (i.e. 'c') character, but I can't 150219304Speter * think of one. 150319304Speter * 150419304Speter * Terminate at the beginning of the insert or the character after the 150519304Speter * offset character -- both can be tested for using tp->offset. 150619304Speter */ 150719304Speter off = tp->cno - 1; /* Previous character. */ 150819304Speter p = tp->lb + off; 150919304Speter len = 1; /* One character test. */ 151019304Speter if (off == tp->offset || isblank(p[-1])) 151119304Speter goto search; 151219304Speter if (inword(p[-1])) /* Move backward to change. */ 151319304Speter for (;;) { 151419304Speter --off; --p; ++len; 151519304Speter if (off == tp->offset || !inword(p[-1])) 151619304Speter break; 151719304Speter } 151819304Speter else 151919304Speter for (;;) { 152019304Speter --off; --p; ++len; 152119304Speter if (off == tp->offset || 152219304Speter inword(p[-1]) || isblank(p[-1])) 152319304Speter break; 152419304Speter } 152519304Speter 152619304Speter /* 152719304Speter * !!! 152819304Speter * Historic vi exploded abbreviations on the command line. This has 152919304Speter * obvious problems in that unabbreviating the string can be extremely 153019304Speter * tricky, particularly if the string has, say, an embedded escape 153119304Speter * character. Personally, I think it's a stunningly bad idea. Other 153219304Speter * examples of problems this caused in historic vi are: 153319304Speter * :ab foo bar 153419304Speter * :ab foo baz 153519304Speter * results in "bar" being abbreviated to "baz", which wasn't what the 153619304Speter * user had in mind at all. Also, the commands: 153719304Speter * :ab foo bar 153819304Speter * :unab foo<space> 153919304Speter * resulted in an error message that "bar" wasn't mapped. Finally, 154019304Speter * since the string was already exploded by the time the unabbreviate 154119304Speter * command got it, all it knew was that an abbreviation had occurred. 154219304Speter * Cleverly, it checked the replacement string for its unabbreviation 154319304Speter * match, which meant that the commands: 154419304Speter * :ab foo1 bar 154519304Speter * :ab foo2 bar 154619304Speter * :unab foo2 154719304Speter * unabbreviate "foo1", and the commands: 154819304Speter * :ab foo bar 154919304Speter * :ab bar baz 155019304Speter * unabbreviate "foo"! 155119304Speter * 155219304Speter * Anyway, people neglected to first ask my opinion before they wrote 155319304Speter * macros that depend on this stuff, so, we make this work as follows. 155419304Speter * When checking for an abbreviation on the command line, if we get a 155519304Speter * string which is <blank> terminated and which starts at the beginning 155619304Speter * of the line, we check to see it is the abbreviate or unabbreviate 155719304Speter * commands. If it is, turn abbreviations off and return as if no 155819304Speter * abbreviation was found. Note also, minor trickiness, so that if 155919304Speter * the user erases the line and starts another command, we turn the 156019304Speter * abbreviations back on. 156119304Speter * 156219304Speter * This makes the layering look like a Nachos Supreme. 156319304Speter */ 156419304Spetersearch: if (isinfoline) 156519304Speter if (off == tp->ai || off == tp->offset) 156619304Speter if (ex_is_abbrev(p, len)) { 156719304Speter *turnoffp = 1; 156819304Speter return (0); 156919304Speter } else 157019304Speter *turnoffp = 0; 157119304Speter else 157219304Speter if (*turnoffp) 157319304Speter return (0); 157419304Speter 157519304Speter /* Check for any abbreviations. */ 157619304Speter if ((qp = seq_find(sp, NULL, NULL, p, len, SEQ_ABBREV, NULL)) == NULL) 157719304Speter return (0); 157819304Speter 157919304Speter /* 158019304Speter * Push the abbreviation onto the tty stack. Historically, characters 158119304Speter * resulting from an abbreviation expansion were themselves subject to 158219304Speter * map expansions, O_SHOWMATCH matching etc. This means the expanded 158319304Speter * characters will be re-tested for abbreviations. It's difficult to 158419304Speter * know what historic practice in this case was, since abbreviations 158519304Speter * were applied to :colon command lines, so entering abbreviations that 158619304Speter * looped was tricky, although possible. In addition, obvious loops 158719304Speter * didn't work as expected. (The command ':ab a b|ab b c|ab c a' will 158819304Speter * silently only implement and/or display the last abbreviation.) 158919304Speter * 159019304Speter * This implementation doesn't recover well from such abbreviations. 159119304Speter * The main input loop counts abbreviated characters, and, when it 159219304Speter * reaches a limit, discards any abbreviated characters on the queue. 159319304Speter * It's difficult to back up to the original position, as the replay 159419304Speter * queue would have to be adjusted, and the line state when an initial 159519304Speter * abbreviated character was received would have to be saved. 159619304Speter */ 159719304Speter ch = *pushcp; 159819304Speter if (v_event_push(sp, NULL, &ch, 1, CH_ABBREVIATED)) 159919304Speter return (1); 160019304Speter if (v_event_push(sp, NULL, qp->output, qp->olen, CH_ABBREVIATED)) 160119304Speter return (1); 160219304Speter 160319304Speter /* 160419304Speter * If the size of the abbreviation is larger than or equal to the size 160519304Speter * of the original text, move to the start of the replaced characters, 160619304Speter * and add their length to the overwrite count. 160719304Speter * 160819304Speter * If the abbreviation is smaller than the original text, we have to 160919304Speter * delete the additional overwrite characters and copy down any insert 161019304Speter * characters. 161119304Speter */ 161219304Speter tp->cno -= len; 161319304Speter if (qp->olen >= len) 161419304Speter tp->owrite += len; 161519304Speter else { 161619304Speter if (tp->insert) 1617254225Speter MEMMOVE(tp->lb + tp->cno + qp->olen, 161819304Speter tp->lb + tp->cno + tp->owrite + len, tp->insert); 161919304Speter tp->owrite += qp->olen; 162019304Speter tp->len -= len - qp->olen; 162119304Speter } 162219304Speter 162319304Speter /* 162419304Speter * We return the length of the abbreviated characters. This is so 162519304Speter * the calling routine can replace the replay characters with the 162619304Speter * abbreviation. This means that subsequent '.' commands will produce 162719304Speter * the same text, regardless of intervening :[un]abbreviate commands. 162819304Speter * This is historic practice. 162919304Speter */ 163019304Speter *didsubp = len; 163119304Speter return (0); 163219304Speter} 163319304Speter 163419304Speter/* 163519304Speter * txt_unmap -- 163619304Speter * Handle the unmap command. 163719304Speter */ 163819304Speterstatic void 1639254225Spetertxt_unmap(SCR *sp, TEXT *tp, u_int32_t *ec_flagsp) 164019304Speter{ 164119304Speter size_t len, off; 1642254225Speter CHAR_T *p; 164319304Speter 164419304Speter /* Find the beginning of this "word". */ 164519304Speter for (off = tp->cno - 1, p = tp->lb + off, len = 0;; --p, --off) { 164619304Speter if (isblank(*p)) { 164719304Speter ++p; 164819304Speter break; 164919304Speter } 165019304Speter ++len; 165119304Speter if (off == tp->ai || off == tp->offset) 165219304Speter break; 165319304Speter } 165419304Speter 165519304Speter /* 165619304Speter * !!! 165719304Speter * Historic vi exploded input mappings on the command line. See the 165819304Speter * txt_abbrev() routine for an explanation of the problems inherent 165919304Speter * in this. 166019304Speter * 166119304Speter * We make this work as follows. If we get a string which is <blank> 166219304Speter * terminated and which starts at the beginning of the line, we check 166319304Speter * to see it is the unmap command. If it is, we return that the input 166419304Speter * mapping should be turned off. Note also, minor trickiness, so that 166519304Speter * if the user erases the line and starts another command, we go ahead 166619304Speter * an turn mapping back on. 166719304Speter */ 166819304Speter if ((off == tp->ai || off == tp->offset) && ex_is_unmap(p, len)) 166919304Speter FL_CLR(*ec_flagsp, EC_MAPINPUT); 167019304Speter else 167119304Speter FL_SET(*ec_flagsp, EC_MAPINPUT); 167219304Speter} 167319304Speter 167419304Speter/* 167519304Speter * txt_ai_resolve -- 167619304Speter * When a line is resolved by <esc>, review autoindent characters. 167719304Speter */ 167819304Speterstatic void 1679254225Spetertxt_ai_resolve(SCR *sp, TEXT *tp, int *changedp) 168019304Speter{ 168119304Speter u_long ts; 168219304Speter int del; 168319304Speter size_t cno, len, new, old, scno, spaces, tab_after_sp, tabs; 1684254225Speter CHAR_T *p; 168519304Speter 168619304Speter *changedp = 0; 168719304Speter 168819304Speter /* 168919304Speter * If the line is empty, has an offset, or no autoindent 169019304Speter * characters, we're done. 169119304Speter */ 169219304Speter if (!tp->len || tp->offset || !tp->ai) 169319304Speter return; 169419304Speter 169519304Speter /* 169619304Speter * If the length is less than or equal to the autoindent 169719304Speter * characters, delete them. 169819304Speter */ 169919304Speter if (tp->len <= tp->ai) { 170019304Speter tp->ai = tp->cno = tp->len = 0; 170119304Speter return; 170219304Speter } 170319304Speter 170419304Speter /* 170519304Speter * The autoindent characters plus any leading <blank> characters 170619304Speter * in the line are resolved into the minimum number of characters. 170719304Speter * Historic practice. 170819304Speter */ 170919304Speter ts = O_VAL(sp, O_TABSTOP); 171019304Speter 171119304Speter /* Figure out the last <blank> screen column. */ 171219304Speter for (p = tp->lb, scno = 0, len = tp->len, 171319304Speter spaces = tab_after_sp = 0; len-- && isblank(*p); ++p) 171419304Speter if (*p == '\t') { 171519304Speter if (spaces) 171619304Speter tab_after_sp = 1; 171719304Speter scno += COL_OFF(scno, ts); 171819304Speter } else { 171919304Speter ++spaces; 172019304Speter ++scno; 172119304Speter } 172219304Speter 172319304Speter /* 172419304Speter * If there are no spaces, or no tabs after spaces and less than 172519304Speter * ts spaces, it's already minimal. 172619304Speter */ 1727254225Speter if (!spaces || (!tab_after_sp && spaces < ts)) 172819304Speter return; 172919304Speter 173019304Speter /* Count up spaces/tabs needed to get to the target. */ 173119304Speter for (cno = 0, tabs = 0; cno + COL_OFF(cno, ts) <= scno; ++tabs) 173219304Speter cno += COL_OFF(cno, ts); 173319304Speter spaces = scno - cno; 173419304Speter 173519304Speter /* 173619304Speter * Figure out how many characters we're dropping -- if we're not 173719304Speter * dropping any, it's already minimal, we're done. 173819304Speter */ 173919304Speter old = p - tp->lb; 174019304Speter new = spaces + tabs; 174119304Speter if (old == new) 174219304Speter return; 174319304Speter 174419304Speter /* Shift the rest of the characters down, adjust the counts. */ 174519304Speter del = old - new; 1746254225Speter MEMMOVE(p - del, p, tp->len - old); 174719304Speter tp->len -= del; 174819304Speter tp->cno -= del; 174919304Speter 175019304Speter /* Fill in space/tab characters. */ 175119304Speter for (p = tp->lb; tabs--;) 175219304Speter *p++ = '\t'; 175319304Speter while (spaces--) 175419304Speter *p++ = ' '; 175519304Speter *changedp = 1; 175619304Speter} 175719304Speter 175819304Speter/* 175919304Speter * v_txt_auto -- 176019304Speter * Handle autoindent. If aitp isn't NULL, use it, otherwise, 176119304Speter * retrieve the line. 176219304Speter * 176319304Speter * PUBLIC: int v_txt_auto __P((SCR *, recno_t, TEXT *, size_t, TEXT *)); 176419304Speter */ 176519304Speterint 1766254225Speterv_txt_auto(SCR *sp, recno_t lno, TEXT *aitp, size_t len, TEXT *tp) 176719304Speter{ 176819304Speter size_t nlen; 1769254225Speter CHAR_T *p, *t; 177019304Speter 177119304Speter if (aitp == NULL) { 177219304Speter /* 177319304Speter * If the ex append command is executed with an address of 0, 177419304Speter * it's possible to get here with a line number of 0. Return 177519304Speter * an indent of 0. 177619304Speter */ 177719304Speter if (lno == 0) { 177819304Speter tp->ai = 0; 177919304Speter return (0); 178019304Speter } 178119304Speter if (db_get(sp, lno, DBG_FATAL, &t, &len)) 178219304Speter return (1); 178319304Speter } else 178419304Speter t = aitp->lb; 178519304Speter 178619304Speter /* Count whitespace characters. */ 178719304Speter for (p = t; len > 0; ++p, --len) 178819304Speter if (!isblank(*p)) 178919304Speter break; 179019304Speter 179119304Speter /* Set count, check for no indentation. */ 179219304Speter if ((nlen = (p - t)) == 0) 179319304Speter return (0); 179419304Speter 179519304Speter /* Make sure the buffer's big enough. */ 1796254225Speter BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + nlen); 179719304Speter 179819304Speter /* Copy the buffer's current contents up. */ 179919304Speter if (tp->len != 0) 1800254225Speter MEMMOVE(tp->lb + nlen, tp->lb, tp->len); 180119304Speter tp->len += nlen; 180219304Speter 180319304Speter /* Copy the indentation into the new buffer. */ 1804254225Speter MEMMOVE(tp->lb, t, nlen); 180519304Speter 180619304Speter /* Set the autoindent count. */ 180719304Speter tp->ai = nlen; 180819304Speter return (0); 180919304Speter} 181019304Speter 181119304Speter/* 181219304Speter * txt_backup -- 181319304Speter * Back up to the previously edited line. 181419304Speter */ 181519304Speterstatic TEXT * 1816254225Spetertxt_backup(SCR *sp, TEXTH *tiqh, TEXT *tp, u_int32_t *flagsp) 181719304Speter{ 181819304Speter VI_PRIVATE *vip; 181919304Speter TEXT *ntp; 182019304Speter 182119304Speter /* Get a handle on the previous TEXT structure. */ 1822254225Speter if ((ntp = TAILQ_PREV(tp, _texth, q)) == NULL) { 182319304Speter if (!FL_ISSET(*flagsp, TXT_REPLAY)) 182419304Speter msgq(sp, M_BERR, 182519304Speter "193|Already at the beginning of the insert"); 182619304Speter return (tp); 182719304Speter } 182819304Speter 182919304Speter /* Bookkeeping. */ 183019304Speter ntp->len = ntp->sv_len; 183119304Speter 183219304Speter /* Handle appending to the line. */ 183319304Speter vip = VIP(sp); 183419304Speter if (ntp->owrite == 0 && ntp->insert == 0) { 183519304Speter ntp->lb[ntp->len] = CH_CURSOR; 183619304Speter ++ntp->insert; 183719304Speter ++ntp->len; 183819304Speter FL_SET(*flagsp, TXT_APPENDEOL); 183919304Speter } else 184019304Speter FL_CLR(*flagsp, TXT_APPENDEOL); 184119304Speter 184219304Speter /* Release the current TEXT. */ 1843254225Speter TAILQ_REMOVE(tiqh, tp, q); 184419304Speter text_free(tp); 184519304Speter 184619304Speter /* Update the old line on the screen. */ 184719304Speter if (vs_change(sp, ntp->lno + 1, LINE_DELETE)) 184819304Speter return (NULL); 184919304Speter 185019304Speter /* Return the new/current TEXT. */ 185119304Speter return (ntp); 185219304Speter} 185319304Speter 185419304Speter/* 185519304Speter * Text indentation is truly strange. ^T and ^D do movements to the next or 185619304Speter * previous shiftwidth value, i.e. for a 1-based numbering, with shiftwidth=3, 185719304Speter * ^T moves a cursor on the 7th, 8th or 9th column to the 10th column, and ^D 185819304Speter * moves it back. 185919304Speter * 186019304Speter * !!! 186119304Speter * The ^T and ^D characters in historical vi had special meaning only when they 186219304Speter * were the first characters entered after entering text input mode. As normal 186319304Speter * erase characters couldn't erase autoindent characters (^T in this case), it 186419304Speter * meant that inserting text into previously existing text was strange -- ^T 186519304Speter * only worked if it was the first keystroke(s), and then could only be erased 186619304Speter * using ^D. This implementation treats ^T specially anywhere it occurs in the 186719304Speter * input, and permits the standard erase characters to erase the characters it 186819304Speter * inserts. 186919304Speter * 187019304Speter * !!! 187119304Speter * A fun test is to try: 187219304Speter * :se sw=4 ai list 187319304Speter * i<CR>^Tx<CR>^Tx<CR>^Tx<CR>^Dx<CR>^Dx<CR>^Dx<esc> 187419304Speter * Historic vi loses some of the '$' marks on the line ends, but otherwise gets 187519304Speter * it right. 187619304Speter * 187719304Speter * XXX 187819304Speter * Technically, txt_dent should be part of the screen interface, as it requires 187919304Speter * knowledge of character sizes, including <space>s, on the screen. It's here 188019304Speter * because it's a complicated little beast, and I didn't want to shove it down 1881254225Speter * into the screen. It's probable that KEY_COL will call into the screen once 188219304Speter * there are screens with different character representations. 188319304Speter * 188419304Speter * txt_dent -- 188519304Speter * Handle ^T indents, ^D outdents. 188619304Speter * 188719304Speter * If anything changes here, check the ex version to see if it needs similar 188819304Speter * changes. 188919304Speter */ 189019304Speterstatic int 1891254225Spetertxt_dent(SCR *sp, TEXT *tp, int isindent) 189219304Speter{ 189319304Speter CHAR_T ch; 189419304Speter u_long sw, ts; 1895254225Speter size_t cno, current, spaces, target, tabs; 189619304Speter int ai_reset; 189719304Speter 189819304Speter ts = O_VAL(sp, O_TABSTOP); 189919304Speter sw = O_VAL(sp, O_SHIFTWIDTH); 190019304Speter 190119304Speter /* 190219304Speter * Since we don't know what precedes the character(s) being inserted 190319304Speter * (or deleted), the preceding whitespace characters must be resolved. 190419304Speter * An example is a <tab>, which doesn't need a full shiftwidth number 190519304Speter * of columns because it's preceded by <space>s. This is easy to get 190619304Speter * if the user sets shiftwidth to a value less than tabstop (or worse, 190719304Speter * something for which tabstop isn't a multiple) and then uses ^T to 190819304Speter * indent, and ^D to outdent. 190919304Speter * 191019304Speter * Figure out the current and target screen columns. In the historic 191119304Speter * vi, the autoindent column was NOT determined using display widths 191219304Speter * of characters as was the wrapmargin column. For that reason, we 191319304Speter * can't use the vs_column() function, but have to calculate it here. 191419304Speter * This is slow, but it's normally only on the first few characters of 191519304Speter * a line. 191619304Speter */ 191719304Speter for (current = cno = 0; cno < tp->cno; ++cno) 191819304Speter current += tp->lb[cno] == '\t' ? 1919254225Speter COL_OFF(current, ts) : KEY_COL(sp, tp->lb[cno]); 192019304Speter 192119304Speter target = current; 192219304Speter if (isindent) 192319304Speter target += COL_OFF(target, sw); 1924246874Sdim else { 1925246874Sdim --target; 1926246874Sdim target -= target % sw; 1927246874Sdim } 192819304Speter 192919304Speter /* 193019304Speter * The AI characters will be turned into overwrite characters if the 193119304Speter * cursor immediately follows them. We test both the cursor position 193219304Speter * and the indent flag because there's no single test. (^T can only 193319304Speter * be detected by the cursor position, and while we know that the test 193419304Speter * is always true for ^D, the cursor can be in more than one place, as 193519304Speter * "0^D" and "^D" are different.) 193619304Speter */ 193719304Speter ai_reset = !isindent || tp->cno == tp->ai + tp->offset; 193819304Speter 193919304Speter /* 194019304Speter * Back up over any previous <blank> characters, changing them into 194119304Speter * overwrite characters (including any ai characters). Then figure 194219304Speter * out the current screen column. 194319304Speter */ 194419304Speter for (; tp->cno > tp->offset && 194519304Speter (tp->lb[tp->cno - 1] == ' ' || tp->lb[tp->cno - 1] == '\t'); 194619304Speter --tp->cno, ++tp->owrite); 194719304Speter for (current = cno = 0; cno < tp->cno; ++cno) 194819304Speter current += tp->lb[cno] == '\t' ? 1949254225Speter COL_OFF(current, ts) : KEY_COL(sp, tp->lb[cno]); 195019304Speter 195119304Speter /* 195219304Speter * If we didn't move up to or past the target, it's because there 195319304Speter * weren't enough characters to delete, e.g. the first character 195419304Speter * of the line was a tp->offset character, and the user entered 195519304Speter * ^D to move to the beginning of a line. An example of this is: 195619304Speter * 195719304Speter * :set ai sw=4<cr>i<space>a<esc>i^T^D 195819304Speter * 195919304Speter * Otherwise, count up the total spaces/tabs needed to get from the 196019304Speter * beginning of the line (or the last non-<blank> character) to the 196119304Speter * target. 196219304Speter */ 196319304Speter if (current >= target) 196419304Speter spaces = tabs = 0; 196519304Speter else { 196619304Speter for (cno = current, 196719304Speter tabs = 0; cno + COL_OFF(cno, ts) <= target; ++tabs) 196819304Speter cno += COL_OFF(cno, ts); 196919304Speter spaces = target - cno; 197019304Speter } 197119304Speter 197219304Speter /* If we overwrote ai characters, reset the ai count. */ 197319304Speter if (ai_reset) 197419304Speter tp->ai = tabs + spaces; 197519304Speter 197619304Speter /* 197719304Speter * Call txt_insch() to insert each character, so that we get the 197819304Speter * correct effect when we add a <tab> to replace N <spaces>. 197919304Speter */ 198019304Speter for (ch = '\t'; tabs > 0; --tabs) 198119304Speter (void)txt_insch(sp, tp, &ch, 0); 198219304Speter for (ch = ' '; spaces > 0; --spaces) 198319304Speter (void)txt_insch(sp, tp, &ch, 0); 198419304Speter return (0); 198519304Speter} 198619304Speter 198719304Speter/* 198819304Speter * txt_fc -- 1989254225Speter * File name and ex command completion. 199019304Speter */ 199119304Speterstatic int 1992254225Spetertxt_fc(SCR *sp, TEXT *tp, int *redrawp) 199319304Speter{ 199419304Speter struct stat sb; 199519304Speter ARGS **argv; 199619304Speter EXCMD cmd; 199719304Speter size_t indx, len, nlen, off; 1998254225Speter int argc; 1999254225Speter CHAR_T *p, *t, *bp; 2000254225Speter char *np, *epd = NULL; 2001254225Speter size_t nplen; 2002254225Speter int fstwd = 1; 200319304Speter 200419304Speter *redrawp = 0; 2005254225Speter ex_cinit(sp, &cmd, 0, 0, OOBLNO, OOBLNO, 0); 200619304Speter 200719304Speter /* 200819304Speter * Find the beginning of this "word" -- if we're at the beginning 200919304Speter * of the line, it's a special case. 201019304Speter */ 201119304Speter if (tp->cno == 1) { 201219304Speter len = 0; 201319304Speter p = tp->lb; 2014254225Speter } else { 2015254225Speter CHAR_T *ap; 2016254225Speter 2017254225Speter for (len = 0, 2018254225Speter off = MAX(tp->ai, tp->offset), ap = tp->lb + off, p = ap; 2019254225Speter off < tp->cno; ++off, ++ap) { 2020254225Speter if (IS_ESCAPE(sp, &cmd, *ap)) { 2021254225Speter if (++off == tp->cno) 2022254225Speter break; 2023254225Speter ++ap; 2024254225Speter len += 2; 2025254225Speter } else if (cmdskip(*ap)) { 2026254225Speter p = ap + 1; 2027254225Speter if (len > 0) 2028254225Speter fstwd = 0; 2029254225Speter len = 0; 2030254225Speter } else 2031254225Speter ++len; 203219304Speter } 2033254225Speter } 203419304Speter 203519304Speter /* 2036254225Speter * If we are at the first word, do ex command completion instead of 2037254225Speter * file name completion. 203819304Speter */ 2039254225Speter if (fstwd) 2040254225Speter (void)argv_flt_ex(sp, &cmd, p, len); 2041254225Speter else { 2042254225Speter if ((bp = argv_uesc(sp, &cmd, p, len)) == NULL) 2043254225Speter return (1); 2044254225Speter if (argv_flt_path(sp, &cmd, bp, STRLEN(bp))) { 2045254225Speter FREE_SPACEW(sp, bp, 0); 2046254225Speter return (0); 2047254225Speter } 2048254225Speter FREE_SPACEW(sp, bp, 0); 204919304Speter } 205019304Speter argc = cmd.argc; 205119304Speter argv = cmd.argv; 205219304Speter 205319304Speter switch (argc) { 205419304Speter case 0: /* No matches. */ 2055254225Speter (void)sp->gp->scr_bell(sp); 205619304Speter return (0); 205719304Speter case 1: /* One match. */ 2058254225Speter /* Always overwrite the old text. */ 2059254225Speter nlen = STRLEN(cmd.argv[0]->bp); 2060254225Speter break; 206119304Speter default: /* Multiple matches. */ 206219304Speter *redrawp = 1; 206319304Speter if (txt_fc_col(sp, argc, argv)) 206419304Speter return (1); 206519304Speter 206619304Speter /* Find the length of the shortest match. */ 206719304Speter for (nlen = cmd.argv[0]->len; --argc > 0;) { 206819304Speter if (cmd.argv[argc]->len < nlen) 206919304Speter nlen = cmd.argv[argc]->len; 207019304Speter for (indx = 0; indx < nlen && 207119304Speter cmd.argv[argc]->bp[indx] == cmd.argv[0]->bp[indx]; 207219304Speter ++indx); 207319304Speter nlen = indx; 207419304Speter } 207519304Speter break; 207619304Speter } 207719304Speter 2078254225Speter /* Escape the matched part of the path. */ 2079254225Speter if (fstwd) 2080254225Speter bp = cmd.argv[0]->bp; 2081254225Speter else { 2082254225Speter if ((bp = argv_esc(sp, &cmd, cmd.argv[0]->bp, nlen)) == NULL) 2083254225Speter return (1); 2084254225Speter nlen = STRLEN(bp); 2085254225Speter } 2086254225Speter 208719304Speter /* Overwrite the expanded text first. */ 2088254225Speter for (t = bp; len > 0 && nlen > 0; --len, --nlen) 208919304Speter *p++ = *t++; 209019304Speter 209119304Speter /* If lost text, make the remaining old text overwrite characters. */ 209219304Speter if (len) { 209319304Speter tp->cno -= len; 209419304Speter tp->owrite += len; 209519304Speter } 209619304Speter 209719304Speter /* Overwrite any overwrite characters next. */ 209819304Speter for (; nlen > 0 && tp->owrite > 0; --nlen, --tp->owrite, ++tp->cno) 209919304Speter *p++ = *t++; 210019304Speter 210119304Speter /* Shift remaining text up, and move the cursor to the end. */ 210219304Speter if (nlen) { 210319304Speter off = p - tp->lb; 2104254225Speter BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + nlen); 210519304Speter p = tp->lb + off; 210619304Speter 210719304Speter tp->cno += nlen; 210819304Speter tp->len += nlen; 210919304Speter 211019304Speter if (tp->insert != 0) 2111254225Speter (void)MEMMOVE(p + nlen, p, tp->insert); 211219304Speter while (nlen--) 211319304Speter *p++ = *t++; 211419304Speter } 211519304Speter 2116254225Speter if (!fstwd) 2117254225Speter FREE_SPACEW(sp, bp, 0); 2118254225Speter 2119254225Speter /* If not a single match of path, we've done. */ 2120254225Speter if (argc != 1 || fstwd) 2121254225Speter return (0); 2122254225Speter 2123254225Speter /* If a single match and it's a directory, append a '/'. */ 2124254225Speter INT2CHAR(sp, cmd.argv[0]->bp, cmd.argv[0]->len + 1, np, nplen); 2125254225Speter if ((epd = expanduser(np)) != NULL) 2126254225Speter np = epd; 2127254225Speter if (!stat(np, &sb) && S_ISDIR(sb.st_mode)) { 2128254225Speter if (tp->owrite == 0) { 212919304Speter off = p - tp->lb; 2130254225Speter BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + 1); 213119304Speter p = tp->lb + off; 213219304Speter if (tp->insert != 0) 2133254225Speter (void)MEMMOVE(p + 1, p, tp->insert); 213419304Speter ++tp->len; 213519304Speter } else 213619304Speter --tp->owrite; 213719304Speter 213819304Speter ++tp->cno; 213919304Speter *p++ = '/'; 214019304Speter } 2141254225Speter free(epd); 214219304Speter return (0); 214319304Speter} 214419304Speter 214519304Speter/* 214619304Speter * txt_fc_col -- 214719304Speter * Display file names for file name completion. 214819304Speter */ 214919304Speterstatic int 2150254225Spetertxt_fc_col(SCR *sp, int argc, ARGS **argv) 215119304Speter{ 215219304Speter ARGS **av; 215319304Speter CHAR_T *p; 215419304Speter GS *gp; 215519304Speter size_t base, cnt, col, colwidth, numrows, numcols, prefix, row; 215619304Speter int ac, nf, reset; 2157254225Speter char *np, *pp; 2158254225Speter size_t nlen; 215919304Speter 216019304Speter gp = sp->gp; 216119304Speter 216219304Speter /* Trim any directory prefix common to all of the files. */ 2163254225Speter INT2CHAR(sp, argv[0]->bp, argv[0]->len + 1, np, nlen); 2164254225Speter if ((pp = strrchr(np, '/')) == NULL) 216519304Speter prefix = 0; 216619304Speter else { 2167254225Speter prefix = (pp - np) + 1; 216819304Speter for (ac = argc - 1, av = argv + 1; ac > 0; --ac, ++av) 216919304Speter if (av[0]->len < prefix || 2170254225Speter MEMCMP(av[0]->bp, argv[0]->bp, 2171254225Speter prefix)) { 217219304Speter prefix = 0; 217319304Speter break; 217419304Speter } 217519304Speter } 217619304Speter 217719304Speter /* 217819304Speter * Figure out the column width for the longest name. Output is done on 217919304Speter * 6 character "tab" boundaries for no particular reason. (Since we 218019304Speter * don't output tab characters, we ignore the terminal's tab settings.) 218119304Speter * Ignore the user's tab setting because we have no idea how reasonable 218219304Speter * it is. 218319304Speter */ 218419304Speter for (ac = argc, av = argv, colwidth = 0; ac > 0; --ac, ++av) { 218519304Speter for (col = 0, p = av[0]->bp + prefix; *p != '\0'; ++p) 2186254225Speter col += KEY_COL(sp, *p); 218719304Speter if (col > colwidth) 218819304Speter colwidth = col; 218919304Speter } 219019304Speter colwidth += COL_OFF(colwidth, 6); 219119304Speter 219219304Speter /* 219319304Speter * Writing to the bottom line of the screen is always turned off when 219419304Speter * SC_TINPUT_INFO is set. Turn it back on, we know what we're doing. 219519304Speter */ 219619304Speter if (F_ISSET(sp, SC_TINPUT_INFO)) { 219719304Speter reset = 1; 219819304Speter F_CLR(sp, SC_TINPUT_INFO); 219919304Speter } else 220019304Speter reset = 0; 220119304Speter 220219304Speter#define CHK_INTR \ 220319304Speter if (F_ISSET(gp, G_INTERRUPTED)) \ 220419304Speter goto intr; 220519304Speter 220619304Speter /* If the largest file name is too large, just print them. */ 2207254225Speter if (colwidth >= sp->cols) { 220819304Speter for (ac = argc, av = argv; ac > 0; --ac, ++av) { 2209254225Speter INT2CHAR(sp, av[0]->bp+prefix, av[0]->len+1-prefix, 2210254225Speter np, nlen); 2211254225Speter pp = msg_print(sp, np, &nf); 2212254225Speter (void)ex_printf(sp, "%s\n", pp); 2213254225Speter if (nf) 2214254225Speter FREE_SPACE(sp, pp, 0); 221519304Speter if (F_ISSET(gp, G_INTERRUPTED)) 221619304Speter break; 221719304Speter } 221819304Speter CHK_INTR; 221919304Speter } else { 222019304Speter /* Figure out the number of columns. */ 222119304Speter numcols = (sp->cols - 1) / colwidth; 222219304Speter if (argc > numcols) { 222319304Speter numrows = argc / numcols; 222419304Speter if (argc % numcols) 222519304Speter ++numrows; 222619304Speter } else 222719304Speter numrows = 1; 222819304Speter 222919304Speter /* Display the files in sorted order. */ 223019304Speter for (row = 0; row < numrows; ++row) { 223119304Speter for (base = row, col = 0; col < numcols; ++col) { 2232254225Speter INT2CHAR(sp, argv[base]->bp+prefix, 2233254225Speter argv[base]->len+1-prefix, np, nlen); 2234254225Speter pp = msg_print(sp, np, &nf); 2235254225Speter cnt = ex_printf(sp, "%s", pp); 223619304Speter if (nf) 2237254225Speter FREE_SPACE(sp, pp, 0); 223819304Speter CHK_INTR; 223919304Speter if ((base += numrows) >= argc) 224019304Speter break; 224119304Speter (void)ex_printf(sp, 224219304Speter "%*s", (int)(colwidth - cnt), ""); 224319304Speter CHK_INTR; 224419304Speter } 224519304Speter (void)ex_puts(sp, "\n"); 224619304Speter CHK_INTR; 224719304Speter } 224819304Speter (void)ex_puts(sp, "\n"); 224919304Speter CHK_INTR; 225019304Speter } 225119304Speter (void)ex_fflush(sp); 225219304Speter 225319304Speter if (0) { 225419304Speterintr: F_CLR(gp, G_INTERRUPTED); 225519304Speter } 225619304Speter if (reset) 225719304Speter F_SET(sp, SC_TINPUT_INFO); 225819304Speter 225919304Speter return (0); 226019304Speter} 226119304Speter 226219304Speter/* 226319304Speter * txt_emark -- 226419304Speter * Set the end mark on the line. 226519304Speter */ 226619304Speterstatic int 2267254225Spetertxt_emark(SCR *sp, TEXT *tp, size_t cno) 226819304Speter{ 2269254225Speter CHAR_T ch; 2270254225Speter u_char *kp; 227119304Speter size_t chlen, nlen, olen; 2272254225Speter CHAR_T *p; 227319304Speter 227419304Speter ch = CH_ENDMARK; 227519304Speter 227619304Speter /* 227719304Speter * The end mark may not be the same size as the current character. 227819304Speter * Don't let the line shift. 227919304Speter */ 2280254225Speter nlen = KEY_COL(sp, ch); 228119304Speter if (tp->lb[cno] == '\t') 228219304Speter (void)vs_columns(sp, tp->lb, tp->lno, &cno, &olen); 228319304Speter else 2284254225Speter olen = KEY_COL(sp, tp->lb[cno]); 228519304Speter 228619304Speter /* 228719304Speter * If the line got longer, well, it's weird, but it's easy. If 228819304Speter * it's the same length, it's easy. If it got shorter, we have 228919304Speter * to fix it up. 229019304Speter */ 229119304Speter if (olen > nlen) { 2292254225Speter BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + olen); 229319304Speter chlen = olen - nlen; 229419304Speter if (tp->insert != 0) 2295254225Speter MEMMOVE(tp->lb + cno + 1 + chlen, 229619304Speter tp->lb + cno + 1, tp->insert); 229719304Speter 229819304Speter tp->len += chlen; 229919304Speter tp->owrite += chlen; 230019304Speter p = tp->lb + cno; 2301254225Speter if (tp->lb[cno] == '\t' || 2302254225Speter KEY_NEEDSWIDE(sp, tp->lb[cno])) 230319304Speter for (cno += chlen; chlen--;) 230419304Speter *p++ = ' '; 230519304Speter else 2306254225Speter for (kp = (u_char *) 2307254225Speter KEY_NAME(sp, tp->lb[cno]), 230819304Speter cno += chlen; chlen--;) 230919304Speter *p++ = *kp++; 231019304Speter } 231119304Speter tp->lb[cno] = ch; 231219304Speter return (vs_change(sp, tp->lno, LINE_RESET)); 231319304Speter} 231419304Speter 231519304Speter/* 231619304Speter * txt_err -- 231719304Speter * Handle an error during input processing. 231819304Speter */ 231919304Speterstatic void 2320254225Spetertxt_err(SCR *sp, TEXTH *tiqh) 232119304Speter{ 232219304Speter recno_t lno; 232319304Speter 232419304Speter /* 232519304Speter * The problem with input processing is that the cursor is at an 232619304Speter * indeterminate position since some input may have been lost due 232719304Speter * to a malloc error. So, try to go back to the place from which 232819304Speter * the cursor started, knowing that it may no longer be available. 232919304Speter * 233019304Speter * We depend on at least one line number being set in the text 233119304Speter * chain. 233219304Speter */ 2333254225Speter for (lno = TAILQ_FIRST(tiqh)->lno; 233419304Speter !db_exist(sp, lno) && lno > 0; --lno); 233519304Speter 233619304Speter sp->lno = lno == 0 ? 1 : lno; 233719304Speter sp->cno = 0; 233819304Speter 233919304Speter /* Redraw the screen, just in case. */ 234019304Speter F_SET(sp, SC_SCR_REDRAW); 234119304Speter} 234219304Speter 234319304Speter/* 234419304Speter * txt_hex -- 234519304Speter * Let the user insert any character value they want. 234619304Speter * 234719304Speter * !!! 234819304Speter * This is an extension. The pattern "^X[0-9a-fA-F]*" is a way 234919304Speter * for the user to specify a character value which their keyboard 235019304Speter * may not be able to enter. 235119304Speter */ 235219304Speterstatic int 2353254225Spetertxt_hex(SCR *sp, TEXT *tp) 235419304Speter{ 235519304Speter CHAR_T savec; 235619304Speter size_t len, off; 235719304Speter u_long value; 2358254225Speter CHAR_T *p, *wp; 235919304Speter 236019304Speter /* 236119304Speter * Null-terminate the string. Since nul isn't a legal hex value, 236219304Speter * this should be okay, and lets us use a local routine, which 236319304Speter * presumably understands the character set, to convert the value. 236419304Speter */ 236519304Speter savec = tp->lb[tp->cno]; 236619304Speter tp->lb[tp->cno] = 0; 236719304Speter 236819304Speter /* Find the previous CH_HEX character. */ 236919304Speter for (off = tp->cno - 1, p = tp->lb + off, len = 0;; --p, --off, ++len) { 237019304Speter if (*p == CH_HEX) { 237119304Speter wp = p + 1; 237219304Speter break; 237319304Speter } 237419304Speter /* Not on this line? Shouldn't happen. */ 237519304Speter if (off == tp->ai || off == tp->offset) 237619304Speter goto nothex; 237719304Speter } 237819304Speter 237919304Speter /* If length of 0, then it wasn't a hex value. */ 238019304Speter if (len == 0) 238119304Speter goto nothex; 238219304Speter 238319304Speter /* Get the value. */ 238419304Speter errno = 0; 2385254225Speter value = STRTOL(wp, NULL, 16); 2386254225Speter if (errno || value > UCHAR_MAX) { 238719304Speternothex: tp->lb[tp->cno] = savec; 238819304Speter return (0); 238919304Speter } 239019304Speter 239119304Speter /* Restore the original character. */ 239219304Speter tp->lb[tp->cno] = savec; 239319304Speter 239419304Speter /* Adjust the bookkeeping. */ 239519304Speter tp->cno -= len; 239619304Speter tp->len -= len; 239719304Speter tp->lb[tp->cno - 1] = value; 239819304Speter 239919304Speter /* Copy down any overwrite characters. */ 240019304Speter if (tp->owrite) 2401254225Speter MEMMOVE(tp->lb + tp->cno, tp->lb + tp->cno + len, 2402254225Speter tp->owrite); 240319304Speter 240419304Speter /* Copy down any insert characters. */ 240519304Speter if (tp->insert) 2406254225Speter MEMMOVE(tp->lb + tp->cno + tp->owrite, 2407254225Speter tp->lb + tp->cno + tp->owrite + len, 2408254225Speter tp->insert); 240919304Speter 241019304Speter return (0); 241119304Speter} 241219304Speter 241319304Speter/* 241419304Speter * txt_insch -- 241519304Speter * 241619304Speter * !!! 241719304Speter * Historic vi did a special screen optimization for tab characters. As an 241819304Speter * example, for the keystrokes "iabcd<esc>0C<tab>", the tab overwrote the 241919304Speter * rest of the string when it was displayed. 242019304Speter * 242119304Speter * Because early versions of this implementation redisplayed the entire line 242219304Speter * on each keystroke, the "bcd" was pushed to the right as it ignored that 242319304Speter * the user had "promised" to change the rest of the characters. However, 242419304Speter * the historic vi implementation had an even worse bug: given the keystrokes 242519304Speter * "iabcd<esc>0R<tab><esc>", the "bcd" disappears, and magically reappears 242619304Speter * on the second <esc> key. 242719304Speter * 242819304Speter * POSIX 1003.2 requires (will require) that this be fixed, specifying that 242919304Speter * vi overwrite characters the user has committed to changing, on the basis 243019304Speter * of the screen space they require, but that it not overwrite other characters. 243119304Speter */ 243219304Speterstatic int 2433254225Spetertxt_insch(SCR *sp, TEXT *tp, CHAR_T *chp, u_int flags) 243419304Speter{ 2435254225Speter u_char *kp; 2436254225Speter CHAR_T savech; 243719304Speter size_t chlen, cno, copydown, olen, nlen; 2438254225Speter CHAR_T *p; 243919304Speter 244019304Speter /* 244119304Speter * The 'R' command does one-for-one replacement, because there's 244219304Speter * no way to know how many characters the user intends to replace. 244319304Speter */ 244419304Speter if (LF_ISSET(TXT_REPLACE)) { 244519304Speter if (tp->owrite) { 244619304Speter --tp->owrite; 244719304Speter tp->lb[tp->cno++] = *chp; 244819304Speter return (0); 244919304Speter } 245019304Speter } else if (tp->owrite) { /* Overwrite a character. */ 245119304Speter cno = tp->cno; 245219304Speter 245319304Speter /* 245419304Speter * If the old or new characters are tabs, then the length of the 245519304Speter * display depends on the character position in the display. We 245619304Speter * don't even try to handle this here, just ask the screen. 245719304Speter */ 245819304Speter if (*chp == '\t') { 245919304Speter savech = tp->lb[cno]; 246019304Speter tp->lb[cno] = '\t'; 246119304Speter (void)vs_columns(sp, tp->lb, tp->lno, &cno, &nlen); 246219304Speter tp->lb[cno] = savech; 246319304Speter } else 2464254225Speter nlen = KEY_COL(sp, *chp); 246519304Speter 246619304Speter /* 246719304Speter * Eat overwrite characters until we run out of them or we've 246819304Speter * handled the length of the new character. If we only eat 246919304Speter * part of an overwrite character, break it into its component 247019304Speter * elements and display the remaining components. 247119304Speter */ 247219304Speter for (copydown = 0; nlen != 0 && tp->owrite != 0;) { 247319304Speter --tp->owrite; 247419304Speter 247519304Speter if (tp->lb[cno] == '\t') 247619304Speter (void)vs_columns(sp, 247719304Speter tp->lb, tp->lno, &cno, &olen); 247819304Speter else 2479254225Speter olen = KEY_COL(sp, tp->lb[cno]); 248019304Speter 248119304Speter if (olen == nlen) { 248219304Speter nlen = 0; 248319304Speter break; 248419304Speter } 248519304Speter if (olen < nlen) { 248619304Speter ++copydown; 248719304Speter nlen -= olen; 248819304Speter } else { 2489254225Speter BINC_RETW(sp, 249019304Speter tp->lb, tp->lb_len, tp->len + olen); 249119304Speter chlen = olen - nlen; 2492254225Speter MEMMOVE(tp->lb + cno + 1 + chlen, 2493254225Speter tp->lb + cno + 1, 2494254225Speter tp->owrite + tp->insert); 249519304Speter 249619304Speter tp->len += chlen; 249719304Speter tp->owrite += chlen; 2498254225Speter if (tp->lb[cno] == '\t' || 2499254225Speter KEY_NEEDSWIDE(sp, tp->lb[cno])) 250019304Speter for (p = tp->lb + cno + 1; chlen--;) 250119304Speter *p++ = ' '; 250219304Speter else 2503254225Speter for (kp = (u_char *) 250419304Speter KEY_NAME(sp, tp->lb[cno]) + nlen, 250519304Speter p = tp->lb + cno + 1; chlen--;) 250619304Speter *p++ = *kp++; 250719304Speter nlen = 0; 250819304Speter break; 250919304Speter } 251019304Speter } 251119304Speter 251219304Speter /* 251319304Speter * If had to erase several characters, we adjust the total 251419304Speter * count, and if there are any characters left, shift them 251519304Speter * into position. 251619304Speter */ 251719304Speter if (copydown != 0 && (tp->len -= copydown) != 0) 2518254225Speter MEMMOVE(tp->lb + cno, tp->lb + cno + copydown, 251919304Speter tp->owrite + tp->insert + copydown); 252019304Speter 252119304Speter /* If we had enough overwrite characters, we're done. */ 252219304Speter if (nlen == 0) { 252319304Speter tp->lb[tp->cno++] = *chp; 252419304Speter return (0); 252519304Speter } 252619304Speter } 252719304Speter 252819304Speter /* Check to see if the character fits into the input buffer. */ 2529254225Speter BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + 1); 253019304Speter 253119304Speter ++tp->len; 253219304Speter if (tp->insert) { /* Insert a character. */ 253319304Speter if (tp->insert == 1) 253419304Speter tp->lb[tp->cno + 1] = tp->lb[tp->cno]; 253519304Speter else 2536254225Speter MEMMOVE(tp->lb + tp->cno + 1, 253719304Speter tp->lb + tp->cno, tp->owrite + tp->insert); 253819304Speter } 253919304Speter tp->lb[tp->cno++] = *chp; 254019304Speter return (0); 254119304Speter} 254219304Speter 254319304Speter/* 254419304Speter * txt_isrch -- 254519304Speter * Do an incremental search. 254619304Speter */ 254719304Speterstatic int 2548254225Spetertxt_isrch(SCR *sp, VICMD *vp, TEXT *tp, u_int8_t *is_flagsp) 254919304Speter{ 255019304Speter MARK start; 255119304Speter recno_t lno; 255219304Speter u_int sf; 255319304Speter 255419304Speter /* If it's a one-line screen, we don't do incrementals. */ 255519304Speter if (IS_ONELINE(sp)) { 255619304Speter FL_CLR(*is_flagsp, IS_RUNNING); 255719304Speter return (0); 255819304Speter } 255919304Speter 256019304Speter /* 256119304Speter * If the user erases back to the beginning of the buffer, there's 256219304Speter * nothing to search for. Reset the cursor to the starting point. 256319304Speter */ 256419304Speter if (tp->cno <= 1) { 256519304Speter vp->m_final = vp->m_start; 256619304Speter return (0); 256719304Speter } 256819304Speter 256919304Speter /* 257019304Speter * If it's an RE quote character, and not quoted, ignore it until 257119304Speter * we get another character. 257219304Speter */ 257319304Speter if (tp->lb[tp->cno - 1] == '\\' && 257419304Speter (tp->cno == 2 || tp->lb[tp->cno - 2] != '\\')) 257519304Speter return (0); 257619304Speter 257719304Speter /* 257819304Speter * If it's a magic shell character, and not quoted, reset the cursor 257919304Speter * to the starting point. 258019304Speter */ 2581254225Speter if (IS_SHELLMETA(sp, tp->lb[tp->cno - 1]) && 258219304Speter (tp->cno == 2 || tp->lb[tp->cno - 2] != '\\')) 258319304Speter vp->m_final = vp->m_start; 258419304Speter 258519304Speter /* 258619304Speter * If we see the search pattern termination character, then quit doing 258719304Speter * an incremental search. There may be more, e.g., ":/foo/;/bar/", 258819304Speter * and we can't handle that incrementally. Also, reset the cursor to 258919304Speter * the original location, the ex search routines don't know anything 259019304Speter * about incremental searches. 259119304Speter */ 259219304Speter if (tp->lb[0] == tp->lb[tp->cno - 1] && 259319304Speter (tp->cno == 2 || tp->lb[tp->cno - 2] != '\\')) { 259419304Speter vp->m_final = vp->m_start; 259519304Speter FL_CLR(*is_flagsp, IS_RUNNING); 259619304Speter return (0); 259719304Speter } 259819304Speter 259919304Speter /* 260019304Speter * Remember the input line and discard the special input map, 260119304Speter * but don't overwrite the input line on the screen. 260219304Speter */ 260319304Speter lno = tp->lno; 260419304Speter F_SET(VIP(sp), VIP_S_MODELINE); 260519304Speter F_CLR(sp, SC_TINPUT | SC_TINPUT_INFO); 260619304Speter if (txt_map_end(sp)) 260719304Speter return (1); 260819304Speter 260919304Speter /* 261019304Speter * Specify a starting point and search. If we find a match, move to 261119304Speter * it and refresh the screen. If we didn't find the match, then we 261219304Speter * beep the screen. When searching from the original cursor position, 261319304Speter * we have to move the cursor, otherwise, we don't want to move the 261419304Speter * cursor in case the text at the current position continues to match. 261519304Speter */ 261619304Speter if (FL_ISSET(*is_flagsp, IS_RESTART)) { 261719304Speter start = vp->m_start; 261819304Speter sf = SEARCH_SET; 261919304Speter } else { 262019304Speter start = vp->m_final; 262119304Speter sf = SEARCH_INCR | SEARCH_SET; 262219304Speter } 262319304Speter 262419304Speter if (tp->lb[0] == '/' ? 262519304Speter !f_search(sp, 262619304Speter &start, &vp->m_final, tp->lb + 1, tp->cno - 1, NULL, sf) : 262719304Speter !b_search(sp, 262819304Speter &start, &vp->m_final, tp->lb + 1, tp->cno - 1, NULL, sf)) { 262919304Speter sp->lno = vp->m_final.lno; 263019304Speter sp->cno = vp->m_final.cno; 263119304Speter FL_CLR(*is_flagsp, IS_RESTART); 263219304Speter 263319304Speter if (!KEYS_WAITING(sp) && vs_refresh(sp, 0)) 263419304Speter return (1); 263519304Speter } else 263619304Speter FL_SET(*is_flagsp, IS_RESTART); 263719304Speter 263819304Speter /* Reinstantiate the special input map. */ 263919304Speter if (txt_map_init(sp)) 264019304Speter return (1); 264119304Speter F_CLR(VIP(sp), VIP_S_MODELINE); 264219304Speter F_SET(sp, SC_TINPUT | SC_TINPUT_INFO); 264319304Speter 264419304Speter /* Reset the line number of the input line. */ 264519304Speter tp->lno = TMAP[0].lno; 264619304Speter 264719304Speter /* 264819304Speter * If the colon command-line moved, i.e. the screen scrolled, 264919304Speter * refresh the input line. 265019304Speter * 265119304Speter * XXX 265219304Speter * We shouldn't be calling vs_line, here -- we need dirty bits 265319304Speter * on entries in the SMAP array. 265419304Speter */ 265519304Speter if (lno != TMAP[0].lno) { 265619304Speter if (vs_line(sp, &TMAP[0], NULL, NULL)) 265719304Speter return (1); 265819304Speter (void)sp->gp->scr_refresh(sp, 0); 265919304Speter } 266019304Speter return (0); 266119304Speter} 266219304Speter 266319304Speter/* 266419304Speter * txt_resolve -- 266519304Speter * Resolve the input text chain into the file. 266619304Speter */ 266719304Speterstatic int 2668254225Spetertxt_resolve(SCR *sp, TEXTH *tiqh, u_int32_t flags) 266919304Speter{ 267019304Speter VI_PRIVATE *vip; 267119304Speter TEXT *tp; 267219304Speter recno_t lno; 267319304Speter int changed; 267419304Speter 267519304Speter /* 267619304Speter * The first line replaces a current line, and all subsequent lines 267719304Speter * are appended into the file. Resolve autoindented characters for 267819304Speter * each line before committing it. If the latter causes the line to 267919304Speter * change, we have to redisplay it, otherwise the information cached 268019304Speter * about the line will be wrong. 268119304Speter */ 268219304Speter vip = VIP(sp); 2683254225Speter tp = TAILQ_FIRST(tiqh); 268419304Speter 268519304Speter if (LF_ISSET(TXT_AUTOINDENT)) 268619304Speter txt_ai_resolve(sp, tp, &changed); 268719304Speter else 268819304Speter changed = 0; 268919304Speter if (db_set(sp, tp->lno, tp->lb, tp->len) || 2690254225Speter (changed && vs_change(sp, tp->lno, LINE_RESET))) 269119304Speter return (1); 269219304Speter 2693254225Speter for (lno = tp->lno; (tp = TAILQ_NEXT(tp, q)) != NULL; ++lno) { 269419304Speter if (LF_ISSET(TXT_AUTOINDENT)) 269519304Speter txt_ai_resolve(sp, tp, &changed); 269619304Speter else 269719304Speter changed = 0; 269819304Speter if (db_append(sp, 0, lno, tp->lb, tp->len) || 2699254225Speter (changed && vs_change(sp, tp->lno, LINE_RESET))) 270019304Speter return (1); 270119304Speter } 270219304Speter 270319304Speter /* 270419304Speter * Clear the input flag, the look-aside buffer is no longer valid. 270519304Speter * Has to be done as part of text resolution, or upon return we'll 270619304Speter * be looking at incorrect data. 270719304Speter */ 270819304Speter F_CLR(sp, SC_TINPUT); 270919304Speter 271019304Speter return (0); 271119304Speter} 271219304Speter 271319304Speter/* 271419304Speter * txt_showmatch -- 271519304Speter * Show a character match. 271619304Speter * 271719304Speter * !!! 271819304Speter * Historic vi tried to display matches even in the :colon command line. 271919304Speter * I think not. 272019304Speter */ 272119304Speterstatic int 2722254225Spetertxt_showmatch(SCR *sp, TEXT *tp) 272319304Speter{ 272419304Speter GS *gp; 272519304Speter VCS cs; 272619304Speter MARK m; 272719304Speter int cnt, endc, startc; 272819304Speter 272919304Speter gp = sp->gp; 273019304Speter 273119304Speter /* 273219304Speter * Do a refresh first, in case we haven't done one in awhile, 273319304Speter * so the user can see what we're complaining about. 273419304Speter */ 273519304Speter UPDATE_POSITION(sp, tp); 273619304Speter if (vs_refresh(sp, 1)) 273719304Speter return (1); 273819304Speter 273919304Speter /* 274019304Speter * We don't display the match if it's not on the screen. Find 274119304Speter * out what the first character on the screen is. 274219304Speter */ 274319304Speter if (vs_sm_position(sp, &m, 0, P_TOP)) 274419304Speter return (1); 274519304Speter 274619304Speter /* Initialize the getc() interface. */ 274719304Speter cs.cs_lno = tp->lno; 274819304Speter cs.cs_cno = tp->cno - 1; 274919304Speter if (cs_init(sp, &cs)) 275019304Speter return (1); 2751254225Speter startc = STRCHR(VIP(sp)->mcs, endc = cs.cs_ch)[-1]; 275219304Speter 275319304Speter /* Search for the match. */ 275419304Speter for (cnt = 1;;) { 275519304Speter if (cs_prev(sp, &cs)) 275619304Speter return (1); 275719304Speter if (cs.cs_flags != 0) { 275819304Speter if (cs.cs_flags == CS_EOF || cs.cs_flags == CS_SOF) { 275919304Speter msgq(sp, M_BERR, 276019304Speter "Unmatched %s", KEY_NAME(sp, endc)); 276119304Speter return (0); 276219304Speter } 276319304Speter continue; 276419304Speter } 276519304Speter if (cs.cs_ch == endc) 276619304Speter ++cnt; 276719304Speter else if (cs.cs_ch == startc && --cnt == 0) 276819304Speter break; 276919304Speter } 277019304Speter 277119304Speter /* If the match is on the screen, move to it. */ 2772254225Speter if (cs.cs_lno < m.lno || (cs.cs_lno == m.lno && cs.cs_cno < m.cno)) 277319304Speter return (0); 277419304Speter sp->lno = cs.cs_lno; 277519304Speter sp->cno = cs.cs_cno; 277619304Speter if (vs_refresh(sp, 1)) 277719304Speter return (1); 277819304Speter 277919304Speter /* Wait for timeout or character arrival. */ 278019304Speter return (v_event_get(sp, 278119304Speter NULL, O_VAL(sp, O_MATCHTIME) * 100, EC_TIMEOUT)); 278219304Speter} 278319304Speter 278419304Speter/* 278519304Speter * txt_margin -- 278619304Speter * Handle margin wrap. 278719304Speter */ 278819304Speterstatic int 2789254225Spetertxt_margin(SCR *sp, TEXT *tp, TEXT *wmtp, int *didbreak, u_int32_t flags) 279019304Speter{ 279119304Speter VI_PRIVATE *vip; 279219304Speter size_t len, off; 2793254225Speter CHAR_T *p, *wp; 279419304Speter 279519304Speter /* Find the nearest previous blank. */ 279619304Speter for (off = tp->cno - 1, p = tp->lb + off, len = 0;; --off, --p, ++len) { 279719304Speter if (isblank(*p)) { 279819304Speter wp = p + 1; 279919304Speter break; 280019304Speter } 280119304Speter 280219304Speter /* 280319304Speter * If reach the start of the line, there's nowhere to break. 280419304Speter * 280519304Speter * !!! 280619304Speter * Historic vi belled each time a character was entered after 280719304Speter * crossing the margin until a space was entered which could 280819304Speter * be used to break the line. I don't as it tends to wake the 280919304Speter * cats. 281019304Speter */ 281119304Speter if (off == tp->ai || off == tp->offset) { 281219304Speter *didbreak = 0; 281319304Speter return (0); 281419304Speter } 281519304Speter } 281619304Speter 281719304Speter /* 281819304Speter * Store saved information about the rest of the line in the 281919304Speter * wrapmargin TEXT structure. 282019304Speter * 282119304Speter * !!! 282219304Speter * The offset field holds the length of the current characters 282319304Speter * that the user entered, but which are getting split to the new 282419304Speter * line -- it's going to be used to set the cursor value when we 282519304Speter * move to the new line. 282619304Speter */ 282719304Speter vip = VIP(sp); 282819304Speter wmtp->lb = p + 1; 282919304Speter wmtp->offset = len; 283019304Speter wmtp->insert = LF_ISSET(TXT_APPENDEOL) ? tp->insert - 1 : tp->insert; 283119304Speter wmtp->owrite = tp->owrite; 283219304Speter 283319304Speter /* Correct current bookkeeping information. */ 283419304Speter tp->cno -= len; 283519304Speter if (LF_ISSET(TXT_APPENDEOL)) { 283619304Speter tp->len -= len + tp->owrite + (tp->insert - 1); 283719304Speter tp->insert = 1; 283819304Speter } else { 283919304Speter tp->len -= len + tp->owrite + tp->insert; 284019304Speter tp->insert = 0; 284119304Speter } 284219304Speter tp->owrite = 0; 284319304Speter 284419304Speter /* 284519304Speter * !!! 284619304Speter * Delete any trailing whitespace from the current line. 284719304Speter */ 284819304Speter for (;; --p, --off) { 284919304Speter if (!isblank(*p)) 285019304Speter break; 285119304Speter --tp->cno; 285219304Speter --tp->len; 285319304Speter if (off == tp->ai || off == tp->offset) 285419304Speter break; 285519304Speter } 285619304Speter *didbreak = 1; 285719304Speter return (0); 285819304Speter} 285919304Speter 286019304Speter/* 286119304Speter * txt_Rresolve -- 286219304Speter * Resolve the input line for the 'R' command. 286319304Speter */ 286419304Speterstatic void 2865254225Spetertxt_Rresolve(SCR *sp, TEXTH *tiqh, TEXT *tp, const size_t orig_len) 286619304Speter{ 286719304Speter TEXT *ttp; 286819304Speter size_t input_len, retain; 2869254225Speter CHAR_T *p; 287019304Speter 287119304Speter /* 287219304Speter * Check to make sure that the cursor hasn't moved beyond 287319304Speter * the end of the line. 287419304Speter */ 287519304Speter if (tp->owrite == 0) 287619304Speter return; 287719304Speter 287819304Speter /* 287919304Speter * Calculate how many characters the user has entered, 288019304Speter * plus the blanks erased by <carriage-return>/<newline>s. 288119304Speter */ 2882254225Speter for (ttp = TAILQ_FIRST(tiqh), input_len = 0;;) { 288319304Speter input_len += ttp == tp ? tp->cno : ttp->len + ttp->R_erase; 2884254225Speter if ((ttp = TAILQ_NEXT(ttp, q)) == NULL) 288519304Speter break; 288619304Speter } 288719304Speter 288819304Speter /* 288919304Speter * If the user has entered less characters than the original line 289019304Speter * was long, restore any overwriteable characters to the original 289119304Speter * characters. These characters are entered as "insert characters", 289219304Speter * because they're after the cursor and we don't want to lose them. 289319304Speter * (This is okay because the R command has no insert characters.) 289419304Speter * We set owrite to 0 so that the insert characters don't get copied 289519304Speter * to somewhere else, which means that the line and the length have 289619304Speter * to be adjusted here as well. 289719304Speter * 289819304Speter * We have to retrieve the original line because the original pinned 289919304Speter * page has long since been discarded. If it doesn't exist, that's 290019304Speter * okay, the user just extended the file. 290119304Speter */ 290219304Speter if (input_len < orig_len) { 290319304Speter retain = MIN(tp->owrite, orig_len - input_len); 290419304Speter if (db_get(sp, 2905254225Speter TAILQ_FIRST(tiqh)->lno, DBG_FATAL | DBG_NOCACHE, &p, NULL)) 290619304Speter return; 2907254225Speter MEMCPY(tp->lb + tp->cno, p + input_len, retain); 290819304Speter tp->len -= tp->owrite - retain; 290919304Speter tp->owrite = 0; 291019304Speter tp->insert += retain; 291119304Speter } 291219304Speter} 291319304Speter 291419304Speter/* 291519304Speter * txt_nomorech -- 291619304Speter * No more characters message. 291719304Speter */ 291819304Speterstatic void 2919254225Spetertxt_nomorech(SCR *sp) 292019304Speter{ 292119304Speter msgq(sp, M_BERR, "194|No more characters to erase"); 292219304Speter} 2923