816 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			816 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* vi.c */
 | |
| 
 | |
| /* Author:
 | |
|  *	Steve Kirkendall
 | |
|  *	Beaverton, OR 97005
 | |
|  *	kirkenda@cs.pdx.edu
 | |
|  */
 | |
| 
 | |
| 
 | |
| #include "config.h"
 | |
| #include "ctype.h"
 | |
| #include "vi.h"
 | |
| 
 | |
| 
 | |
| 
 | |
| /* This array describes what each key does */
 | |
| #define NO_FUNC		(MARK (*)())0
 | |
| 
 | |
| #define NO_ARGS		0
 | |
| #define CURSOR		1
 | |
| #define CURSOR_CNT_KEY	2
 | |
| #define CURSOR_MOVED	3
 | |
| #define CURSOR_EOL	4
 | |
| #define ZERO		5
 | |
| #define DIGIT		6
 | |
| #define CURSOR_TEXT	7
 | |
| #define KEYWORD		8
 | |
| #define ARGSMASK	0x0f
 | |
| #define	C_C_K_REP1	(CURSOR_CNT_KEY | 0x10)
 | |
| #define C_C_K_CUT	(CURSOR_CNT_KEY | 0x20)
 | |
| #define C_C_K_MARK	(CURSOR_CNT_KEY | 0x30)
 | |
| #define C_C_K_CHAR	(CURSOR_CNT_KEY | 0x40)
 | |
| #ifndef NO_SHOWMODE
 | |
| static int keymodes[] = {0, WHEN_REP1, WHEN_CUT, WHEN_MARK, WHEN_CHAR};
 | |
| # define KEYMODE(args) (keymodes[(args) >> 4])
 | |
| #else
 | |
| # define KEYMODE(args) 0
 | |
| #endif
 | |
| 
 | |
| static struct keystru
 | |
| {
 | |
| 	MARK	(*func)();	/* the function to run */
 | |
| 	uchar	args;		/* description of the args needed */
 | |
| #ifndef NO_VISIBLE
 | |
| 	short	flags;
 | |
| #else
 | |
| 	uchar	flags;		/* other stuff */
 | |
| #endif
 | |
| }
 | |
| 	vikeys[] =
 | |
| {
 | |
| /* NUL not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
 | |
| #ifndef NO_EXTENSIONS
 | |
| /* ^A  find cursor word */	{m_wsrch,	KEYWORD,	MVMT|NREL|VIZ},
 | |
| #else
 | |
| /* ^A  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
 | |
| #endif
 | |
| /* ^B  page backward	*/	{m_scroll,	CURSOR,		FRNT|VIZ},
 | |
| /* ^C  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
 | |
| /* ^D  scroll dn 1/2page*/	{m_scroll,	CURSOR,		NCOL|VIZ},
 | |
| /* ^E  scroll up	*/	{m_scroll,	CURSOR,		NCOL|VIZ},
 | |
| /* ^F  page forward	*/	{m_scroll,	CURSOR,		FRNT|VIZ},
 | |
| /* ^G  show file status	*/	{v_status,	NO_ARGS, 	NO_FLAGS},
 | |
| /* ^H  move left, like h*/	{m_left,	CURSOR,		MVMT|VIZ},
 | |
| /* ^I  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
 | |
| /* ^J  move down	*/	{m_updnto,	CURSOR,		MVMT|LNMD|VIZ|INCL},
 | |
| /* ^K  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
 | |
| /* ^L  redraw screen	*/	{v_redraw,	NO_ARGS,	NO_FLAGS|VIZ},
 | |
| /* ^M  mv front next ln */	{m_updnto,	CURSOR,		MVMT|FRNT|LNMD|VIZ|INCL},
 | |
| /* ^N  move down	*/	{m_updnto,	CURSOR,		MVMT|LNMD|VIZ|INCL|NCOL},
 | |
| /* ^O  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
 | |
| /* ^P  move up		*/	{m_updnto,	CURSOR,		MVMT|LNMD|VIZ|INCL|NCOL},
 | |
| /* ^Q  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
 | |
| /* ^R  redraw screen	*/	{v_redraw,	NO_ARGS,	NO_FLAGS|VIZ},
 | |
| /* ^S  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
 | |
| /* ^T  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
 | |
| /* ^U  scroll up 1/2page*/	{m_scroll,	CURSOR,		NCOL|VIZ},
 | |
| /* ^V  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
 | |
| /* ^W  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
 | |
| /* ^X  move to phys col	*/	{m_tocol,	CURSOR,		MVMT|NREL|VIZ},
 | |
| /* ^Y  scroll down	*/	{m_scroll,	CURSOR,		NCOL|VIZ},
 | |
| #ifdef SIGTSTP
 | |
| /* ^Z  suspend elvis	*/	{v_suspend,	NO_ARGS,	NO_FLAGS},
 | |
| #else
 | |
| /* ^Z  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
 | |
| #endif
 | |
| /* ESC not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
 | |
| /* ^\  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
 | |
| /* ^]  keyword is tag	*/	{v_tag,		KEYWORD,	NO_FLAGS},
 | |
| /* ^^  previous file	*/	{v_switch,	CURSOR,		NO_FLAGS},
 | |
| /* ^_  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
 | |
| /* SPC move right,like l*/	{m_right,	CURSOR,		MVMT|INCL|VIZ},
 | |
| /*  !  run thru filter	*/	{v_filter,	CURSOR_MOVED,	FRNT|LNMD|INCL|VIZ},
 | |
| /*  "  select cut buffer*/	{v_selcut,	C_C_K_CUT,	PTMV|VIZ},
 | |
| #ifndef NO_EXTENSIONS
 | |
| /*  #  increment number	*/	{v_increment,	KEYWORD,	SDOT},
 | |
| #else
 | |
| /*  #  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
 | |
| #endif
 | |
| /*  $  move to rear	*/	{m_rear,	CURSOR,		MVMT|INCL|VIZ},
 | |
| /*  %  move to match	*/	{m_match,	CURSOR,		MVMT|INCL|VIZ},
 | |
| /*  &  repeat subst	*/	{v_again,	CURSOR_MOVED,	SDOT|NCOL|LNMD|INCL},
 | |
| /*  '  move to a mark	*/	{m_tomark,	C_C_K_MARK,	MVMT|FRNT|NREL|LNMD|INCL|VIZ},
 | |
| #ifndef NO_SENTENCE
 | |
| /*  (  mv back sentence	*/	{m_sentence,	CURSOR,		MVMT|VIZ},
 | |
| /*  )  mv fwd sentence	*/	{m_sentence,	CURSOR,		MVMT|VIZ},
 | |
| #else
 | |
| /*  (  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
 | |
| /*  )  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
 | |
| #endif
 | |
| #ifndef NO_ERRLIST
 | |
| /*  *  errlist		*/	{v_errlist,	CURSOR,		FRNT|NREL},
 | |
| #else
 | |
| /*  *  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
 | |
| #endif
 | |
| /*  +  mv front next ln */	{m_updnto,	CURSOR,		MVMT|FRNT|LNMD|VIZ|INCL},
 | |
| #ifndef NO_CHARSEARCH
 | |
| /*  ,  reverse [fFtT] cmd*/	{m__ch,		CURSOR,		MVMT|INCL|VIZ},
 | |
| #else
 | |
| /*  ,  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
 | |
| #endif
 | |
| /*  -  mv front prev ln	*/	{m_updnto,	CURSOR,		MVMT|FRNT|LNMD|VIZ|INCL},
 | |
| /*  .  special...	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
 | |
| /*  /  forward search	*/	{m_fsrch,	CURSOR_TEXT,	MVMT|NREL|VIZ},
 | |
| /*  0  part of count?	*/	{NO_FUNC,	ZERO,		MVMT|PTMV|VIZ},
 | |
| /*  1  part of count	*/	{NO_FUNC,	DIGIT,		PTMV|VIZ},
 | |
| /*  2  part of count	*/	{NO_FUNC,	DIGIT,		PTMV|VIZ},
 | |
| /*  3  part of count	*/	{NO_FUNC,	DIGIT,		PTMV|VIZ},
 | |
| /*  4  part of count	*/	{NO_FUNC,	DIGIT,		PTMV|VIZ},
 | |
| /*  5  part of count	*/	{NO_FUNC,	DIGIT,		PTMV|VIZ},
 | |
| /*  6  part of count	*/	{NO_FUNC,	DIGIT,		PTMV|VIZ},
 | |
| /*  7  part of count	*/	{NO_FUNC,	DIGIT,		PTMV|VIZ},
 | |
| /*  8  part of count	*/	{NO_FUNC,	DIGIT,		PTMV|VIZ},
 | |
| /*  9  part of count	*/	{NO_FUNC,	DIGIT,		PTMV|VIZ},
 | |
| /*  :  run single EX cmd*/	{v_1ex,		CURSOR_TEXT,	NO_FLAGS},
 | |
| #ifndef NO_CHARSEARCH
 | |
| /*  ;  repeat [fFtT] cmd*/	{m__ch,		CURSOR,		MVMT|INCL|VIZ},
 | |
| #else
 | |
| /*  ;  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS|VIZ},
 | |
| #endif
 | |
| /*  <  shift text left	*/	{v_lshift,	CURSOR_MOVED,	SDOT|FRNT|LNMD|INCL|VIZ},
 | |
| /*  =  preset filter	*/	{v_reformat,	CURSOR_MOVED,	SDOT|FRNT|LNMD|INCL|VIZ},
 | |
| /*  >  shift text right	*/	{v_rshift,	CURSOR_MOVED,	SDOT|FRNT|LNMD|INCL|VIZ},
 | |
| /*  ?  backward search	*/	{m_bsrch,	CURSOR_TEXT,	MVMT|NREL|VIZ},
 | |
| #ifndef NO_AT
 | |
| /*  @  execute a cutbuf */	{v_at,		C_C_K_CUT,	NO_FLAGS},
 | |
| #else
 | |
| /*  @  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
 | |
| #endif
 | |
| /*  A  append at EOL	*/	{v_insert,	CURSOR,		SDOT},
 | |
| /*  B  move back Word	*/	{m_bword,	CURSOR,		MVMT|VIZ},
 | |
| /*  C  change to EOL	*/	{v_change,	CURSOR_EOL,	SDOT},
 | |
| /*  D  delete to EOL	*/	{v_delete,	CURSOR_EOL,	SDOT},
 | |
| /*  E  move end of Word	*/	{m_eword,	CURSOR,		MVMT|INCL|VIZ},
 | |
| #ifndef NO_CHARSEARCH
 | |
| /*  F  move bk to char	*/	{m_Fch,		C_C_K_CHAR,	MVMT|INCL|VIZ},
 | |
| #else
 | |
| /*  F  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
 | |
| #endif
 | |
| /*  G  move to line #	*/	{m_updnto,	CURSOR,		MVMT|NREL|LNMD|FRNT|INCL|VIZ},
 | |
| /*  H  move to row	*/	{m_row,		CURSOR,		MVMT|LNMD|FRNT|VIZ|INCL},
 | |
| /*  I  insert at front	*/	{v_insert,	CURSOR,		SDOT},
 | |
| /*  J  join lines	*/	{v_join,	CURSOR,		SDOT},
 | |
| #ifndef NO_EXTENSIONS
 | |
| /*  K  look up keyword	*/	{v_keyword,	KEYWORD,	NO_FLAGS},
 | |
| #else
 | |
| /*  K  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
 | |
| #endif
 | |
| /*  L  move to last row	*/	{m_row,		CURSOR,		MVMT|LNMD|FRNT|VIZ|INCL},
 | |
| /*  M  move to mid row	*/	{m_row,		CURSOR,		MVMT|LNMD|FRNT|VIZ|INCL},
 | |
| /*  N  reverse prev srch*/	{m_Nsrch,	CURSOR,		MVMT|NREL|VIZ},
 | |
| /*  O  insert above line*/	{v_insert,	CURSOR,		SDOT},
 | |
| /*  P  paste before	*/	{v_paste,	CURSOR,		SDOT},
 | |
| /*  Q  quit to EX mode	*/	{v_quit,	NO_ARGS,	NO_FLAGS},
 | |
| /*  R  overtype		*/	{v_overtype,	CURSOR,		SDOT},
 | |
| /*  S  change line	*/	{v_change,	CURSOR_MOVED,	SDOT},
 | |
| #ifndef NO_CHARSEARCH
 | |
| /*  T  move bk to char	*/	{m_Tch,		C_C_K_CHAR,	MVMT|INCL|VIZ},
 | |
| #else
 | |
| /*  T  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
 | |
| #endif
 | |
| /*  U  undo whole line	*/	{v_undoline,	CURSOR,		FRNT},
 | |
| #ifndef NO_VISIBLE
 | |
| /*  V  start visible	*/	{v_start,	CURSOR,		INCL|LNMD|VIZ},
 | |
| #else
 | |
| /*  V  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
 | |
| #endif
 | |
| /*  W  move forward Word*/	{m_fword,	CURSOR,		MVMT|INCL|VIZ},
 | |
| /*  X  delete to left	*/	{v_xchar,	CURSOR,		SDOT},
 | |
| /*  Y  yank text	*/	{v_yank,	CURSOR_MOVED,	NCOL},
 | |
| /*  Z  save file & exit	*/	{v_xit,		CURSOR_CNT_KEY,	NO_FLAGS},
 | |
| /*  [  move back section*/	{m_paragraph,	CURSOR,		MVMT|LNMD|NREL|VIZ},
 | |
| #ifndef NO_POPUP
 | |
| /*  \  pop-up menu	*/	{v_popup,	CURSOR_MOVED,	VIZ},
 | |
| #else
 | |
| /*  \  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
 | |
| #endif
 | |
| /*  ]  move fwd section */	{m_paragraph,	CURSOR,		MVMT|LNMD|NREL|VIZ},
 | |
| /*  ^  move to front	*/	{m_front,	CURSOR,		MVMT|VIZ},
 | |
| /*  _  current line	*/	{m_updnto,	CURSOR,		MVMT|LNMD|FRNT|INCL},
 | |
| /*  `  move to mark	*/	{m_tomark,	C_C_K_MARK,	MVMT|NREL|VIZ},
 | |
| /*  a  append at cursor	*/	{v_insert,	CURSOR,		SDOT},
 | |
| /*  b  move back word	*/	{m_bword,	CURSOR,		MVMT|VIZ},
 | |
| /*  c  change text	*/	{v_change,	CURSOR_MOVED,	SDOT|VIZ},
 | |
| /*  d  delete op	*/	{v_delete,	CURSOR_MOVED,	SDOT|VIZ},
 | |
| /*  e  move end word	*/	{m_eword,	CURSOR,		MVMT|INCL|VIZ},
 | |
| #ifndef NO_CHARSEARCH
 | |
| /*  f  move fwd for char*/	{m_fch,		C_C_K_CHAR,	MVMT|INCL|VIZ},
 | |
| #else
 | |
| /*  f  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
 | |
| #endif
 | |
| /*  g  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
 | |
| /*  h  move left	*/	{m_left,	CURSOR,		MVMT|VIZ},
 | |
| /*  i  insert at cursor	*/	{v_insert,	CURSOR,		SDOT},
 | |
| /*  j  move down	*/	{m_updnto,	CURSOR,		MVMT|NCOL|LNMD|VIZ|INCL},
 | |
| /*  k  move up		*/	{m_updnto,	CURSOR,		MVMT|NCOL|LNMD|VIZ|INCL},
 | |
| /*  l  move right	*/	{m_right,	CURSOR,		MVMT|INCL|VIZ},
 | |
| /*  m  define a mark	*/	{v_mark,	C_C_K_MARK,	NO_FLAGS},
 | |
| /*  n  repeat prev srch	*/	{m_nsrch,	CURSOR, 	MVMT|NREL|VIZ},
 | |
| /*  o  insert below line*/	{v_insert,	CURSOR,		SDOT},
 | |
| /*  p  paste after	*/	{v_paste,	CURSOR,		SDOT},
 | |
| /*  q  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
 | |
| /*  r  replace chars	*/	{v_replace,	C_C_K_REP1,	SDOT},
 | |
| /*  s  subst N chars	*/	{v_subst,	CURSOR,		SDOT},
 | |
| #ifndef NO_CHARSEARCH
 | |
| /*  t  move fwd to char	*/	{m_tch,		C_C_K_CHAR,	MVMT|INCL|VIZ},
 | |
| #else
 | |
| /*  t  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
 | |
| #endif
 | |
| /*  u  undo		*/	{v_undo,	CURSOR,		NO_FLAGS},
 | |
| #ifndef NO_VISIBLE
 | |
| /*  v  start visible	*/	{v_start,	CURSOR,		INCL|VIZ},
 | |
| #else
 | |
| /*  v  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
 | |
| #endif
 | |
| /*  w  move fwd word	*/	{m_fword,	CURSOR,		MVMT|INCL|VIZ},
 | |
| /*  x  delete character	*/	{v_xchar,	CURSOR,		SDOT},
 | |
| /*  y  yank text	*/	{v_yank,	CURSOR_MOVED,	NCOL|VIZ},
 | |
| /*  z  adjust scrn row	*/	{m_z, 		CURSOR_CNT_KEY,	NCOL|VIZ},
 | |
| /*  {  back paragraph	*/	{m_paragraph,	CURSOR,		MVMT|LNMD|VIZ},
 | |
| /*  |  move to column	*/	{m_tocol,	CURSOR,		MVMT|NREL|VIZ},
 | |
| /*  }  fwd paragraph	*/	{m_paragraph,	CURSOR,		MVMT|LNMD|VIZ},
 | |
| /*  ~  upper/lowercase	*/	{v_ulcase,	CURSOR,		SDOT},
 | |
| /* DEL not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS}
 | |
| };
 | |
| 
 | |
| 
 | |
| 
 | |
| void vi()
 | |
| {
 | |
| 	REG int			key;	/* keystroke from user */
 | |
| 	long			count;	/* numeric argument to some functions */
 | |
| 	REG struct keystru	*keyptr;/* pointer to vikeys[] element */
 | |
| 	MARK			tcurs;	/* temporary cursor */
 | |
| 	int			prevkey;/* previous key, if d/c/y/</>/! */
 | |
| 	MARK			range;	/* start of range for d/c/y/</>/! */
 | |
| 	char			text[132];
 | |
| 	int			dotkey;	/* last "key" of a change */
 | |
| 	int			dotpkey;/* last "prevkey" of a change */
 | |
| 	int			dotkey2;/* last extra "getkey()" of a change */
 | |
| 	int			dotcnt;	/* last "count" of a change */
 | |
| 	int			firstkey;
 | |
| 	REG int			i;
 | |
| 
 | |
| 	/* tell the redraw() function to start from scratch */
 | |
| 	redraw(MARK_UNSET, FALSE);
 | |
| 
 | |
| #ifdef lint
 | |
| 	/* lint says that "range" might be used before it is set.  This
 | |
| 	 * can't really happen due to the way "range" and "prevkey" are used,
 | |
| 	 * but lint doesn't know that.  This line is here ONLY to keep lint
 | |
| 	 * happy.
 | |
| 	 */
 | |
| 	range = 0L;
 | |
| #endif
 | |
| 
 | |
| 	/* safeguard against '.' with no previous command */
 | |
| 	dotkey = dotpkey = dotkey2 = dotcnt = 0;
 | |
| 
 | |
| 	/* go immediately into insert mode, if ":set inputmode" */
 | |
| 	firstkey = 0;
 | |
| #ifndef NO_EXTENSIONS
 | |
| 	if (*o_inputmode)
 | |
| 	{
 | |
| 		firstkey = 'i';
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
| 	/* Repeatedly handle VI commands */
 | |
| 	for (count = 0, prevkey = '\0'; mode == MODE_VI; )
 | |
| 	{
 | |
| 		/* if we've moved off the undoable line, then we can't undo it at all */
 | |
| 		if (markline(cursor) != U_line)
 | |
| 		{
 | |
| 			U_line = 0L;
 | |
| 		}
 | |
| 
 | |
| 		/* report any changes from the previous command */
 | |
| 		if (rptlines >= *o_report)
 | |
| 		{
 | |
| 			redraw(cursor, FALSE);
 | |
| 			msg("%ld line%s %s", rptlines, (rptlines==1?"":"s"), rptlabel);
 | |
| 		}
 | |
| 		rptlines = 0L;
 | |
| 
 | |
| 		/* get the next command key.  It must be ASCII */
 | |
| 		if (firstkey)
 | |
| 		{
 | |
| 			key = firstkey;
 | |
| 			firstkey = 0;
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			do
 | |
| 			{
 | |
| 				key = getkey(WHEN_VICMD);
 | |
| 			} while (key < 0 || key > 127);
 | |
| 		}
 | |
| 
 | |
| 		/* Convert a doubled-up operator such as "dd" into "d_" */
 | |
| 		if (prevkey && key == prevkey)
 | |
| 		{
 | |
| 			key = '_';
 | |
| 		}
 | |
| 
 | |
| 		/* look up the structure describing this command */
 | |
| 		keyptr = &vikeys[key];
 | |
| 
 | |
| 		/* '&' and uppercase operators always act like doubled */
 | |
| 		if (!prevkey && keyptr->args == CURSOR_MOVED
 | |
| 			&& (key == '&' || isupper(key)))
 | |
| 		{
 | |
| 			range = cursor;
 | |
| 			prevkey = key;
 | |
| 			key = '_';
 | |
| 			keyptr = &vikeys[key];
 | |
| 		}
 | |
| 
 | |
| #ifndef NO_VISIBLE
 | |
| 		/* if we're in the middle of a v/V command, reject commands
 | |
| 		 * that aren't operators or movement commands
 | |
| 		 */
 | |
| 		if (V_from && !(keyptr->flags & VIZ))
 | |
| 		{
 | |
| 			beep();
 | |
| 			prevkey = 0;
 | |
| 			count = 0;
 | |
| 			continue;
 | |
| 		}
 | |
| #endif
 | |
| 
 | |
| 		/* if we're in the middle of a d/c/y/</>/! command, reject
 | |
| 		 * anything but movement.
 | |
| 		 */
 | |
| 		if (prevkey && !(keyptr->flags & (MVMT|PTMV)))
 | |
| 		{
 | |
| 			beep();
 | |
| 			prevkey = 0;
 | |
| 			count = 0;
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		/* set the "dot" variables, if we're supposed to */
 | |
| 		if (((keyptr->flags & SDOT)
 | |
| 			|| (prevkey && vikeys[prevkey].flags & SDOT))
 | |
| #ifndef NO_VISIBLE
 | |
| 		    && !V_from
 | |
| #endif
 | |
| 		)
 | |
| 		{
 | |
| 			dotkey = key;
 | |
| 			dotpkey = prevkey;
 | |
| 			dotkey2 = '\0';
 | |
| 			dotcnt = count;
 | |
| 
 | |
| 			/* remember the line before any changes are made */
 | |
| 			if (U_line != markline(cursor))
 | |
| 			{
 | |
| 				U_line = markline(cursor);
 | |
| 				strcpy(U_text, fetchline(U_line));
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		/* if this is "." then set other vars from the "dot" vars */
 | |
| 		if (key == '.')
 | |
| 		{
 | |
| 			key = dotkey;
 | |
| 			keyptr = &vikeys[key];
 | |
| 			prevkey = dotpkey;
 | |
| 			if (prevkey)
 | |
| 			{
 | |
| 				range = cursor;
 | |
| 			}
 | |
| 			if (count == 0)
 | |
| 			{
 | |
| 				count = dotcnt;
 | |
| 			}
 | |
| 			doingdot = TRUE;
 | |
| 
 | |
| 			/* remember the line before any changes are made */
 | |
| 			if (U_line != markline(cursor))
 | |
| 			{
 | |
| 				U_line = markline(cursor);
 | |
| 				strcpy(U_text, fetchline(U_line));
 | |
| 			}
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			doingdot = FALSE;
 | |
| 		}
 | |
| 
 | |
| 		/* process the key as a command */
 | |
| 		tcurs = cursor;
 | |
| 		force_flags = NO_FLAGS;
 | |
| 		switch (keyptr->args & ARGSMASK)
 | |
| 		{
 | |
| 		  case ZERO:
 | |
| 			if (count == 0)
 | |
| 			{
 | |
| 				tcurs = cursor & ~(BLKSIZE - 1);
 | |
| 				break;
 | |
| 			}
 | |
| 			/* else fall through & treat like other digits... */
 | |
| 
 | |
| 		  case DIGIT:
 | |
| 			count = count * 10 + key - '0';
 | |
| 			break;
 | |
| 
 | |
| 		  case KEYWORD:
 | |
| 			/* if not on a keyword, fail */
 | |
| 			pfetch(markline(cursor));
 | |
| 			key = markidx(cursor);
 | |
| 			if (!isalnum(ptext[key]))
 | |
| 			{
 | |
| 				tcurs = MARK_UNSET;
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 			/* find the start of the keyword */
 | |
| 			while (key > 0 && isalnum(ptext[key - 1]))
 | |
| 			{
 | |
| 				key--;
 | |
| 			}
 | |
| 			tcurs = (cursor & ~(BLKSIZE - 1)) + key;
 | |
| 
 | |
| 			/* copy it into a buffer, and NUL-terminate it */
 | |
| 			i = 0;
 | |
| 			do
 | |
| 			{
 | |
| 				text[i++] = ptext[key++];
 | |
| 			} while (isalnum(ptext[key]));
 | |
| 			text[i] = '\0';
 | |
| 
 | |
| 			/* call the function */
 | |
| 			tcurs = (*keyptr->func)(text, tcurs, count);
 | |
| 			count = 0L;
 | |
| 			break;
 | |
| 
 | |
| 		  case NO_ARGS:
 | |
| 			if (keyptr->func)
 | |
| 			{
 | |
| 				(*keyptr->func)();
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				beep();
 | |
| 			}
 | |
| 			count = 0L;
 | |
| 			break;
 | |
| 	
 | |
| 		  case CURSOR:
 | |
| 			tcurs = (*keyptr->func)(cursor, count, key, prevkey);
 | |
| 			count = 0L;
 | |
| 			break;
 | |
| 
 | |
| 		  case CURSOR_CNT_KEY:
 | |
| 			if (doingdot)
 | |
| 			{
 | |
| 				tcurs = (*keyptr->func)(cursor, count, dotkey2);
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				/* get a key */
 | |
| 				i = getkey(KEYMODE(keyptr->args));
 | |
| 				if (i == '\033') /* ESC */
 | |
| 				{
 | |
| 					count = 0;
 | |
| 					tcurs = MARK_UNSET;
 | |
| 					break; /* exit from "case CURSOR_CNT_KEY" */
 | |
| 				}
 | |
| 				else if (i == ctrl('V'))
 | |
| 				{
 | |
| 					i = getkey(0);
 | |
| 				}
 | |
| 
 | |
| 				/* if part of an SDOT command, remember it */
 | |
| 				 if (keyptr->flags & SDOT
 | |
| 				 || (prevkey && vikeys[prevkey].flags & SDOT))
 | |
| 				{
 | |
| 					dotkey2 = i;
 | |
| 				}
 | |
| 
 | |
| 				/* do it */
 | |
| 				tcurs = (*keyptr->func)(cursor, count, i);
 | |
| 			}
 | |
| 			count = 0L;
 | |
| 			break;
 | |
| 	
 | |
| 		  case CURSOR_MOVED:
 | |
| #ifndef NO_VISIBLE
 | |
| 			if (V_from)
 | |
| 			{
 | |
| 				range = cursor;
 | |
| 				tcurs = V_from;
 | |
| 				count = 0L;
 | |
| 				prevkey = key;
 | |
| 				key = (V_linemd ? 'V' : 'v');
 | |
| 				keyptr = &vikeys[key];
 | |
| 			}
 | |
| 			else
 | |
| #endif
 | |
| 			{
 | |
| 				prevkey = key;
 | |
| 				range = cursor;
 | |
| 				force_flags = LNMD|INCL;
 | |
| 			}
 | |
| 			break;
 | |
| 
 | |
| 		  case CURSOR_EOL:
 | |
| 			prevkey = key;
 | |
| 			/* a zero-length line needs special treatment */
 | |
| 			pfetch(markline(cursor));
 | |
| 			if (plen == 0)
 | |
| 			{
 | |
| 				/* act on a zero-length section of text */
 | |
| 				range = tcurs = cursor;
 | |
| 				key = '0';
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				/* act like CURSOR_MOVED with '$' movement */
 | |
| 				range = cursor;
 | |
| 				tcurs = m_rear(cursor, 1L);
 | |
| 				key = '$';
 | |
| 			}
 | |
| 			count = 0L;
 | |
| 			keyptr = &vikeys[key];
 | |
| 			break;
 | |
| 
 | |
| 		  case CURSOR_TEXT:
 | |
| 		  	do
 | |
| 		  	{	
 | |
| 				text[0] = key;
 | |
| 				if (vgets(key, text + 1, sizeof text - 1) >= 0)
 | |
| 				{
 | |
| 					/* reassure user that <CR> was hit */
 | |
| 					qaddch('\r');
 | |
| 					refresh();
 | |
| 
 | |
| 					/* call the function with the text */
 | |
| 					tcurs = (*keyptr->func)(cursor, text);
 | |
| 				}
 | |
| 				else
 | |
| 				{
 | |
| 					if (exwrote || mode == MODE_COLON)
 | |
| 					{
 | |
| 						redraw(MARK_UNSET, FALSE);
 | |
| 					}
 | |
| 					mode = MODE_VI;
 | |
| 				}
 | |
| 			} while (mode == MODE_COLON);
 | |
| 			count = 0L;
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		/* if that command took us out of vi mode, then exit the loop
 | |
| 		 * NOW, without tweaking the cursor or anything.  This is very
 | |
| 		 * important when mode == MODE_QUIT.
 | |
| 		 */
 | |
| 		if (mode != MODE_VI)
 | |
| 		{
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		/* now move the cursor, as appropriate */
 | |
| 		if (keyptr->args == CURSOR_MOVED)
 | |
| 		{
 | |
| 			/* the < and > keys have FRNT,
 | |
| 			 * but it shouldn't be applied yet
 | |
| 			 */
 | |
| 			tcurs = adjmove(cursor, tcurs, 0);
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			tcurs = adjmove(cursor, tcurs, (int)keyptr->flags | force_flags);
 | |
| 		}
 | |
| 
 | |
| 		/* was that the end of a d/c/y/</>/! command? */
 | |
| 		if (prevkey && ((keyptr->flags & MVMT)
 | |
| #ifndef NO_VISIBLE
 | |
| 					       || V_from
 | |
| #endif
 | |
| 				) && count == 0L)
 | |
| 		{
 | |
| #ifndef NO_VISIBLE
 | |
| 			/* turn off the hilight */
 | |
| 			V_from = 0L;
 | |
| #endif
 | |
| 
 | |
| 			/* if the movement command failed, cancel operation */
 | |
| 			if (tcurs == MARK_UNSET)
 | |
| 			{
 | |
| 				prevkey = 0;
 | |
| 				count = 0;
 | |
| 				continue;
 | |
| 			}
 | |
| 
 | |
| 			/* make sure range=front and tcurs=rear.  Either way,
 | |
| 			 * leave cursor=range since that's where we started.
 | |
| 			 */
 | |
| 			cursor = range;
 | |
| 			if (tcurs < range)
 | |
| 			{
 | |
| 				range = tcurs;
 | |
| 				tcurs = cursor;
 | |
| 			}
 | |
| 
 | |
| 			/* The 'w' and 'W' destinations should never take us
 | |
| 			 * to the front of a line.  Instead, they should take
 | |
| 			 * us only to the end of the preceding line.
 | |
| 			 */
 | |
| 			if ((keyptr->flags & (MVMT|NREL|LNMD|FRNT|INCL)) == MVMT
 | |
| 			  && markline(range) < markline(tcurs)
 | |
| 			  && (markline(tcurs) > nlines || tcurs == m_front(tcurs, 0L)))
 | |
| 			{
 | |
| 				tcurs = (tcurs & ~(BLKSIZE - 1)) - BLKSIZE;
 | |
| 				pfetch(markline(tcurs));
 | |
| 				tcurs += plen;
 | |
| 			}
 | |
| 
 | |
| 			/* adjust for line mode & inclusion of last char/line */
 | |
| 			i = (keyptr->flags | vikeys[prevkey].flags);
 | |
| 			switch ((i | force_flags) & (INCL|LNMD))
 | |
| 			{
 | |
| 			  case INCL:
 | |
| 				tcurs++;
 | |
| 				break;
 | |
| 
 | |
| 			  case INCL|LNMD:
 | |
| 				tcurs += BLKSIZE;
 | |
| 				/* fall through... */
 | |
| 
 | |
| 			  case LNMD:
 | |
| 				range &= ~(BLKSIZE - 1);
 | |
| 				tcurs &= ~(BLKSIZE - 1);
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 			/* run the function */
 | |
| 			tcurs = (*vikeys[prevkey].func)(range, tcurs);
 | |
| 			if (mode == MODE_VI)
 | |
| 			{
 | |
| 				(void)adjmove(cursor, cursor, 0);
 | |
| 				cursor = adjmove(cursor, tcurs, (int)vikeys[prevkey].flags);
 | |
| 			}
 | |
| 
 | |
| 			/* cleanup */
 | |
| 			prevkey = 0;
 | |
| 		}
 | |
| 		else if (!prevkey)
 | |
| 		{
 | |
| 			if (tcurs != MARK_UNSET)
 | |
| 				cursor = tcurs;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /* This function adjusts the MARK value that they return; here we make sure
 | |
|  * it isn't past the end of the line, and that the column hasn't been
 | |
|  * *accidentally* changed.
 | |
|  */
 | |
| MARK adjmove(old, new, flags)
 | |
| 	MARK		old;	/* the cursor position before the command */
 | |
| 	REG MARK	new;	/* the cursor position after the command */
 | |
| 	int		flags;	/* various flags regarding cursor mvmt */
 | |
| {
 | |
| 	static int	colno;	/* the column number that we want */
 | |
| 	REG char	*text;	/* used to scan through the line's text */
 | |
| 	REG int		i;
 | |
| 
 | |
| #ifdef DEBUG
 | |
| 	watch();
 | |
| #endif
 | |
| 
 | |
| 	/* if the command failed, bag it! */
 | |
| 	if (new == MARK_UNSET)
 | |
| 	{
 | |
| 		beep();
 | |
| 		return old;
 | |
| 	}
 | |
| 
 | |
| 	/* if this is a non-relative movement, set the '' mark */
 | |
| 	if (flags & NREL)
 | |
| 	{
 | |
| 		mark[26] = old;
 | |
| 	}
 | |
| 
 | |
| 	/* make sure it isn't past the end of the file */
 | |
| 	if (markline(new) < 1)
 | |
| 	{
 | |
| 		new = MARK_FIRST;
 | |
| 	}
 | |
| 	else if (markline(new) > nlines)
 | |
| 	{
 | |
| 		new = MARK_LAST;
 | |
| 	}
 | |
| 
 | |
| 	/* fetch the new line */
 | |
| 	pfetch(markline(new));
 | |
| 
 | |
| 	/* move to the front, if we're supposed to */
 | |
| 	if (flags & FRNT)
 | |
| 	{
 | |
| 		new = m_front(new, 1L);
 | |
| 	}
 | |
| 
 | |
| 	/* change the column#, or change the mark to suit the column# */
 | |
| 	if (!(flags & NCOL))
 | |
| 	{
 | |
| 		/* change the column# */
 | |
| 		i = markidx(new);
 | |
| 		if (i == BLKSIZE - 1)
 | |
| 		{
 | |
| 			new &= ~(BLKSIZE - 1);
 | |
| 			if (plen > 0)
 | |
| 			{
 | |
| 				new += plen - 1;
 | |
| 			}
 | |
| 			colno = BLKSIZE * 8; /* one heck of a big colno */
 | |
| 		}
 | |
| 		else if (plen > 0)
 | |
| 		{
 | |
| 			if (i >= plen)
 | |
| 			{
 | |
| 				new = (new & ~(BLKSIZE - 1)) + plen - 1;
 | |
| 			}
 | |
| 			colno = idx2col(new, ptext, FALSE);
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			new &= ~(BLKSIZE - 1);
 | |
| 			colno = 0;
 | |
| 		}
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		/* adjust the mark to get as close as possible to column# */
 | |
| 		for (i = 0, text = ptext; i <= colno && *text; text++)
 | |
| 		{
 | |
| 			if (*text == '\t' && !*o_list)
 | |
| 			{
 | |
| 				i += *o_tabstop - (i % *o_tabstop);
 | |
| 			}
 | |
| 			else if (UCHAR(*text) < ' ' || *text == 127)
 | |
| 			{
 | |
| 				i += 2;
 | |
| 			}
 | |
| #ifndef NO_CHARATTR
 | |
| 			else if (*o_charattr && text[0] == '\\' && text[1] == 'f' && text[2])
 | |
| 			{
 | |
| 				text += 2; /* plus one more in "for()" stmt */
 | |
| 			}
 | |
| #endif
 | |
| 			else
 | |
| 			{
 | |
| 				i++;
 | |
| 			}
 | |
| 		}
 | |
| 		if (text > ptext)
 | |
| 		{
 | |
| 			text--;
 | |
| 		}
 | |
| 		new = (new & ~(BLKSIZE - 1)) + (int)(text - ptext);
 | |
| 	}
 | |
| 
 | |
| 	return new;
 | |
| }
 | |
| 
 | |
| 
 | |
| #ifdef DEBUG
 | |
| watch()
 | |
| {
 | |
| 	static wasset;
 | |
| 
 | |
| 	if (*origname)
 | |
| 	{
 | |
| 		wasset = TRUE;
 | |
| 	}
 | |
| 	else if (wasset)
 | |
| 	{
 | |
| 		mode = MODE_EX;
 | |
| 		msg("origname was clobbered");
 | |
| 		endwin();
 | |
| 		abort();
 | |
| 	}
 | |
| 
 | |
| 	if (wasset && nlines == 0)
 | |
| 	{
 | |
| 		mode = MODE_EX;
 | |
| 		msg("nlines=0");
 | |
| 		endwin();
 | |
| 		abort();
 | |
| 	}
 | |
| }
 | |
| #endif
 | 
