/ lib / libedit / src / vi.c
vi.c
   1  /*	$NetBSD: vi.c,v 1.34 2011/02/22 05:45:08 joerg Exp $	*/
   2  
   3  /*-
   4   * Copyright (c) 1992, 1993
   5   *	The Regents of the University of California.  All rights reserved.
   6   *
   7   * This code is derived from software contributed to Berkeley by
   8   * Christos Zoulas of Cornell University.
   9   *
  10   * Redistribution and use in source and binary forms, with or without
  11   * modification, are permitted provided that the following conditions
  12   * are met:
  13   * 1. Redistributions of source code must retain the above copyright
  14   *    notice, this list of conditions and the following disclaimer.
  15   * 2. Redistributions in binary form must reproduce the above copyright
  16   *    notice, this list of conditions and the following disclaimer in the
  17   *    documentation and/or other materials provided with the distribution.
  18   * 3. Neither the name of the University nor the names of its contributors
  19   *    may be used to endorse or promote products derived from this software
  20   *    without specific prior written permission.
  21   *
  22   * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  23   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  24   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  25   * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  26   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  27   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  28   * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  29   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  30   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  31   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  32   * SUCH DAMAGE.
  33   */
  34  
  35  #include "config.h"
  36  #include <stdlib.h>
  37  #include <unistd.h>
  38  #include <limits.h>
  39  #include <sys/wait.h>
  40  
  41  #if !defined(lint) && !defined(SCCSID)
  42  #if 0
  43  static char sccsid[] = "@(#)vi.c	8.1 (Berkeley) 6/4/93";
  44  #else
  45  __RCSID("$NetBSD: vi.c,v 1.34 2011/02/22 05:45:08 joerg Exp $");
  46  #endif
  47  #endif /* not lint && not SCCSID */
  48  
  49  /*
  50   * vi.c: Vi mode commands.
  51   */
  52  #include "el.h"
  53  
  54  private el_action_t	cv_action(EditLine *, Int);
  55  private el_action_t	cv_paste(EditLine *, Int);
  56  
  57  /* cv_action():
  58   *	Handle vi actions.
  59   */
  60  private el_action_t
  61  cv_action(EditLine *el, Int c)
  62  {
  63  
  64  	if (el->el_chared.c_vcmd.action != NOP) {
  65  		/* 'cc', 'dd' and (possibly) friends */
  66  		if (c != el->el_chared.c_vcmd.action)
  67  			return CC_ERROR;
  68  
  69  		if (!(c & YANK))
  70  			cv_undo(el);
  71  		cv_yank(el, el->el_line.buffer,
  72  		    (int)(el->el_line.lastchar - el->el_line.buffer));
  73  		el->el_chared.c_vcmd.action = NOP;
  74  		el->el_chared.c_vcmd.pos = 0;
  75  		if (!(c & YANK)) {
  76  			el->el_line.lastchar = el->el_line.buffer;
  77  			el->el_line.cursor = el->el_line.buffer;
  78  		}
  79  		if (c & INSERT)
  80  			el->el_map.current = el->el_map.key;
  81  
  82  		return (CC_REFRESH);
  83  	}
  84  	el->el_chared.c_vcmd.pos = el->el_line.cursor;
  85  	el->el_chared.c_vcmd.action = c;
  86  	return (CC_ARGHACK);
  87  }
  88  
  89  /* cv_paste():
  90   *	Paste previous deletion before or after the cursor
  91   */
  92  private el_action_t
  93  cv_paste(EditLine *el, Int c)
  94  {
  95  	c_kill_t *k = &el->el_chared.c_kill;
  96  	size_t len = (size_t)(k->last - k->buf);
  97  
  98  	if (k->buf == NULL || len == 0)
  99  		return (CC_ERROR);
 100  #ifdef DEBUG_PASTE
 101  	(void) fprintf(el->el_errfile, "Paste: \"%.*s\"\n", (int)len, k->buf);
 102  #endif
 103  
 104  	cv_undo(el);
 105  
 106  	if (!c && el->el_line.cursor < el->el_line.lastchar)
 107  		el->el_line.cursor++;
 108  
 109  	c_insert(el, (int)len);
 110  	if (el->el_line.cursor + len > el->el_line.lastchar)
 111  		return (CC_ERROR);
 112  	(void) memcpy(el->el_line.cursor, k->buf, len *
 113  	    sizeof(*el->el_line.cursor));
 114  
 115  	return (CC_REFRESH);
 116  }
 117  
 118  
 119  /* vi_paste_next():
 120   *	Vi paste previous deletion to the right of the cursor
 121   *	[p]
 122   */
 123  protected el_action_t
 124  /*ARGSUSED*/
 125  vi_paste_next(EditLine *el, Int c __attribute__((__unused__)))
 126  {
 127  
 128  	return (cv_paste(el, 0));
 129  }
 130  
 131  
 132  /* vi_paste_prev():
 133   *	Vi paste previous deletion to the left of the cursor
 134   *	[P]
 135   */
 136  protected el_action_t
 137  /*ARGSUSED*/
 138  vi_paste_prev(EditLine *el, Int c __attribute__((__unused__)))
 139  {
 140  
 141  	return (cv_paste(el, 1));
 142  }
 143  
 144  
 145  /* vi_prev_big_word():
 146   *	Vi move to the previous space delimited word
 147   *	[B]
 148   */
 149  protected el_action_t
 150  /*ARGSUSED*/
 151  vi_prev_big_word(EditLine *el, Int c __attribute__((__unused__)))
 152  {
 153  
 154  	if (el->el_line.cursor == el->el_line.buffer)
 155  		return (CC_ERROR);
 156  
 157  	el->el_line.cursor = cv_prev_word(el->el_line.cursor,
 158  	    el->el_line.buffer,
 159  	    el->el_state.argument,
 160  	    cv__isWord);
 161  
 162  	if (el->el_chared.c_vcmd.action != NOP) {
 163  		cv_delfini(el);
 164  		return (CC_REFRESH);
 165  	}
 166  	return (CC_CURSOR);
 167  }
 168  
 169  
 170  /* vi_prev_word():
 171   *	Vi move to the previous word
 172   *	[b]
 173   */
 174  protected el_action_t
 175  /*ARGSUSED*/
 176  vi_prev_word(EditLine *el, Int c __attribute__((__unused__)))
 177  {
 178  
 179  	if (el->el_line.cursor == el->el_line.buffer)
 180  		return (CC_ERROR);
 181  
 182  	el->el_line.cursor = cv_prev_word(el->el_line.cursor,
 183  	    el->el_line.buffer,
 184  	    el->el_state.argument,
 185  	    cv__isword);
 186  
 187  	if (el->el_chared.c_vcmd.action != NOP) {
 188  		cv_delfini(el);
 189  		return (CC_REFRESH);
 190  	}
 191  	return (CC_CURSOR);
 192  }
 193  
 194  
 195  /* vi_next_big_word():
 196   *	Vi move to the next space delimited word
 197   *	[W]
 198   */
 199  protected el_action_t
 200  /*ARGSUSED*/
 201  vi_next_big_word(EditLine *el, Int c __attribute__((__unused__)))
 202  {
 203  
 204  	if (el->el_line.cursor >= el->el_line.lastchar - 1)
 205  		return (CC_ERROR);
 206  
 207  	el->el_line.cursor = cv_next_word(el, el->el_line.cursor,
 208  	    el->el_line.lastchar, el->el_state.argument, cv__isWord);
 209  
 210  	if (el->el_map.type == MAP_VI)
 211  		if (el->el_chared.c_vcmd.action != NOP) {
 212  			cv_delfini(el);
 213  			return (CC_REFRESH);
 214  		}
 215  	return (CC_CURSOR);
 216  }
 217  
 218  
 219  /* vi_next_word():
 220   *	Vi move to the next word
 221   *	[w]
 222   */
 223  protected el_action_t
 224  /*ARGSUSED*/
 225  vi_next_word(EditLine *el, Int c __attribute__((__unused__)))
 226  {
 227  
 228  	if (el->el_line.cursor >= el->el_line.lastchar - 1)
 229  		return (CC_ERROR);
 230  
 231  	el->el_line.cursor = cv_next_word(el, el->el_line.cursor,
 232  	    el->el_line.lastchar, el->el_state.argument, cv__isword);
 233  
 234  	if (el->el_map.type == MAP_VI)
 235  		if (el->el_chared.c_vcmd.action != NOP) {
 236  			cv_delfini(el);
 237  			return (CC_REFRESH);
 238  		}
 239  	return (CC_CURSOR);
 240  }
 241  
 242  
 243  /* vi_change_case():
 244   *	Vi change case of character under the cursor and advance one character
 245   *	[~]
 246   */
 247  protected el_action_t
 248  vi_change_case(EditLine *el, Int c)
 249  {
 250  	int i;
 251  
 252  	if (el->el_line.cursor >= el->el_line.lastchar)
 253  		return (CC_ERROR);
 254  	cv_undo(el);
 255  	for (i = 0; i < el->el_state.argument; i++) {
 256  
 257  		c = *el->el_line.cursor;
 258  		if (Isupper(c))
 259  			*el->el_line.cursor = Tolower(c);
 260  		else if (Islower(c))
 261  			*el->el_line.cursor = Toupper(c);
 262  
 263  		if (++el->el_line.cursor >= el->el_line.lastchar) {
 264  			el->el_line.cursor--;
 265  			re_fastaddc(el);
 266  			break;
 267  		}
 268  		re_fastaddc(el);
 269  	}
 270  	return CC_NORM;
 271  }
 272  
 273  
 274  /* vi_change_meta():
 275   *	Vi change prefix command
 276   *	[c]
 277   */
 278  protected el_action_t
 279  /*ARGSUSED*/
 280  vi_change_meta(EditLine *el, Int c __attribute__((__unused__)))
 281  {
 282  
 283  	/*
 284           * Delete with insert == change: first we delete and then we leave in
 285           * insert mode.
 286           */
 287  	return (cv_action(el, DELETE | INSERT));
 288  }
 289  
 290  
 291  /* vi_insert_at_bol():
 292   *	Vi enter insert mode at the beginning of line
 293   *	[I]
 294   */
 295  protected el_action_t
 296  /*ARGSUSED*/
 297  vi_insert_at_bol(EditLine *el, Int c __attribute__((__unused__)))
 298  {
 299  
 300  	el->el_line.cursor = el->el_line.buffer;
 301  	cv_undo(el);
 302  	el->el_map.current = el->el_map.key;
 303  	return (CC_CURSOR);
 304  }
 305  
 306  
 307  /* vi_replace_char():
 308   *	Vi replace character under the cursor with the next character typed
 309   *	[r]
 310   */
 311  protected el_action_t
 312  /*ARGSUSED*/
 313  vi_replace_char(EditLine *el, Int c __attribute__((__unused__)))
 314  {
 315  
 316  	if (el->el_line.cursor >= el->el_line.lastchar)
 317  		return CC_ERROR;
 318  
 319  	el->el_map.current = el->el_map.key;
 320  	el->el_state.inputmode = MODE_REPLACE_1;
 321  	cv_undo(el);
 322  	return (CC_ARGHACK);
 323  }
 324  
 325  
 326  /* vi_replace_mode():
 327   *	Vi enter replace mode
 328   *	[R]
 329   */
 330  protected el_action_t
 331  /*ARGSUSED*/
 332  vi_replace_mode(EditLine *el, Int c __attribute__((__unused__)))
 333  {
 334  
 335  	el->el_map.current = el->el_map.key;
 336  	el->el_state.inputmode = MODE_REPLACE;
 337  	cv_undo(el);
 338  	return (CC_NORM);
 339  }
 340  
 341  
 342  /* vi_substitute_char():
 343   *	Vi replace character under the cursor and enter insert mode
 344   *	[s]
 345   */
 346  protected el_action_t
 347  /*ARGSUSED*/
 348  vi_substitute_char(EditLine *el, Int c __attribute__((__unused__)))
 349  {
 350  
 351  	c_delafter(el, el->el_state.argument);
 352  	el->el_map.current = el->el_map.key;
 353  	return (CC_REFRESH);
 354  }
 355  
 356  
 357  /* vi_substitute_line():
 358   *	Vi substitute entire line
 359   *	[S]
 360   */
 361  protected el_action_t
 362  /*ARGSUSED*/
 363  vi_substitute_line(EditLine *el, Int c __attribute__((__unused__)))
 364  {
 365  
 366  	cv_undo(el);
 367  	cv_yank(el, el->el_line.buffer,
 368  	    (int)(el->el_line.lastchar - el->el_line.buffer));
 369  	(void) em_kill_line(el, 0);
 370  	el->el_map.current = el->el_map.key;
 371  	return (CC_REFRESH);
 372  }
 373  
 374  
 375  /* vi_change_to_eol():
 376   *	Vi change to end of line
 377   *	[C]
 378   */
 379  protected el_action_t
 380  /*ARGSUSED*/
 381  vi_change_to_eol(EditLine *el, Int c __attribute__((__unused__)))
 382  {
 383  
 384  	cv_undo(el);
 385  	cv_yank(el, el->el_line.cursor,
 386  	    (int)(el->el_line.lastchar - el->el_line.cursor));
 387  	(void) ed_kill_line(el, 0);
 388  	el->el_map.current = el->el_map.key;
 389  	return (CC_REFRESH);
 390  }
 391  
 392  
 393  /* vi_insert():
 394   *	Vi enter insert mode
 395   *	[i]
 396   */
 397  protected el_action_t
 398  /*ARGSUSED*/
 399  vi_insert(EditLine *el, Int c __attribute__((__unused__)))
 400  {
 401  
 402  	el->el_map.current = el->el_map.key;
 403  	cv_undo(el);
 404  	return (CC_NORM);
 405  }
 406  
 407  
 408  /* vi_add():
 409   *	Vi enter insert mode after the cursor
 410   *	[a]
 411   */
 412  protected el_action_t
 413  /*ARGSUSED*/
 414  vi_add(EditLine *el, Int c __attribute__((__unused__)))
 415  {
 416  	int ret;
 417  
 418  	el->el_map.current = el->el_map.key;
 419  	if (el->el_line.cursor < el->el_line.lastchar) {
 420  		el->el_line.cursor++;
 421  		if (el->el_line.cursor > el->el_line.lastchar)
 422  			el->el_line.cursor = el->el_line.lastchar;
 423  		ret = CC_CURSOR;
 424  	} else
 425  		ret = CC_NORM;
 426  
 427  	cv_undo(el);
 428  
 429  	return (ret);
 430  }
 431  
 432  
 433  /* vi_add_at_eol():
 434   *	Vi enter insert mode at end of line
 435   *	[A]
 436   */
 437  protected el_action_t
 438  /*ARGSUSED*/
 439  vi_add_at_eol(EditLine *el, Int c __attribute__((__unused__)))
 440  {
 441  
 442  	el->el_map.current = el->el_map.key;
 443  	el->el_line.cursor = el->el_line.lastchar;
 444  	cv_undo(el);
 445  	return (CC_CURSOR);
 446  }
 447  
 448  
 449  /* vi_delete_meta():
 450   *	Vi delete prefix command
 451   *	[d]
 452   */
 453  protected el_action_t
 454  /*ARGSUSED*/
 455  vi_delete_meta(EditLine *el, Int c __attribute__((__unused__)))
 456  {
 457  
 458  	return (cv_action(el, DELETE));
 459  }
 460  
 461  
 462  /* vi_end_big_word():
 463   *	Vi move to the end of the current space delimited word
 464   *	[E]
 465   */
 466  protected el_action_t
 467  /*ARGSUSED*/
 468  vi_end_big_word(EditLine *el, Int c)
 469  {
 470  
 471  	if (el->el_line.cursor == el->el_line.lastchar)
 472  		return (CC_ERROR);
 473  
 474  	el->el_line.cursor = cv__endword(el->el_line.cursor,
 475  	    el->el_line.lastchar, el->el_state.argument, cv__isWord);
 476  
 477  	if (el->el_chared.c_vcmd.action != NOP) {
 478  		el->el_line.cursor++;
 479  		cv_delfini(el);
 480  		return (CC_REFRESH);
 481  	}
 482  	return (CC_CURSOR);
 483  }
 484  
 485  
 486  /* vi_end_word():
 487   *	Vi move to the end of the current word
 488   *	[e]
 489   */
 490  protected el_action_t
 491  /*ARGSUSED*/
 492  vi_end_word(EditLine *el, Int c __attribute__((__unused__)))
 493  {
 494  
 495  	if (el->el_line.cursor == el->el_line.lastchar)
 496  		return (CC_ERROR);
 497  
 498  	el->el_line.cursor = cv__endword(el->el_line.cursor,
 499  	    el->el_line.lastchar, el->el_state.argument, cv__isword);
 500  
 501  	if (el->el_chared.c_vcmd.action != NOP) {
 502  		el->el_line.cursor++;
 503  		cv_delfini(el);
 504  		return (CC_REFRESH);
 505  	}
 506  	return (CC_CURSOR);
 507  }
 508  
 509  
 510  /* vi_undo():
 511   *	Vi undo last change
 512   *	[u]
 513   */
 514  protected el_action_t
 515  /*ARGSUSED*/
 516  vi_undo(EditLine *el, Int c __attribute__((__unused__)))
 517  {
 518  	c_undo_t un = el->el_chared.c_undo;
 519  
 520  	if (un.len == -1)
 521  		return CC_ERROR;
 522  
 523  	/* switch line buffer and undo buffer */
 524  	el->el_chared.c_undo.buf = el->el_line.buffer;
 525  	el->el_chared.c_undo.len = el->el_line.lastchar - el->el_line.buffer;
 526  	el->el_chared.c_undo.cursor =
 527  	    (int)(el->el_line.cursor - el->el_line.buffer);
 528  	el->el_line.limit = un.buf + (el->el_line.limit - el->el_line.buffer);
 529  	el->el_line.buffer = un.buf;
 530  	el->el_line.cursor = un.buf + un.cursor;
 531  	el->el_line.lastchar = un.buf + un.len;
 532  
 533  	return (CC_REFRESH);
 534  }
 535  
 536  
 537  /* vi_command_mode():
 538   *	Vi enter command mode (use alternative key bindings)
 539   *	[<ESC>]
 540   */
 541  protected el_action_t
 542  /*ARGSUSED*/
 543  vi_command_mode(EditLine *el, Int c __attribute__((__unused__)))
 544  {
 545  
 546  	/* [Esc] cancels pending action */
 547  	el->el_chared.c_vcmd.action = NOP;
 548  	el->el_chared.c_vcmd.pos = 0;
 549  
 550  	el->el_state.doingarg = 0;
 551  
 552  	el->el_state.inputmode = MODE_INSERT;
 553  	el->el_map.current = el->el_map.alt;
 554  #ifdef VI_MOVE
 555  	if (el->el_line.cursor > el->el_line.buffer)
 556  		el->el_line.cursor--;
 557  #endif
 558  	return (CC_CURSOR);
 559  }
 560  
 561  
 562  /* vi_zero():
 563   *	Vi move to the beginning of line
 564   *	[0]
 565   */
 566  protected el_action_t
 567  vi_zero(EditLine *el, Int c)
 568  {
 569  
 570  	if (el->el_state.doingarg)
 571  		return ed_argument_digit(el, c);
 572  
 573  	el->el_line.cursor = el->el_line.buffer;
 574  	if (el->el_chared.c_vcmd.action != NOP) {
 575  		cv_delfini(el);
 576  		return (CC_REFRESH);
 577  	}
 578  	return (CC_CURSOR);
 579  }
 580  
 581  
 582  /* vi_delete_prev_char():
 583   * 	Vi move to previous character (backspace)
 584   *	[^H] in insert mode only
 585   */
 586  protected el_action_t
 587  /*ARGSUSED*/
 588  vi_delete_prev_char(EditLine *el, Int c __attribute__((__unused__)))
 589  {
 590  
 591  	if (el->el_line.cursor <= el->el_line.buffer)
 592  		return (CC_ERROR);
 593  
 594  	c_delbefore1(el);
 595  	el->el_line.cursor--;
 596  	return (CC_REFRESH);
 597  }
 598  
 599  
 600  /* vi_list_or_eof():
 601   *	Vi list choices for completion or indicate end of file if empty line
 602   *	[^D]
 603   */
 604  protected el_action_t
 605  /*ARGSUSED*/
 606  vi_list_or_eof(EditLine *el, Int c)
 607  {
 608  
 609  	if (el->el_line.cursor == el->el_line.lastchar) {
 610  		if (el->el_line.cursor == el->el_line.buffer) {
 611  			term_writec(el, c);	/* then do a EOF */
 612  			return (CC_EOF);
 613  		} else {
 614  			/*
 615  			 * Here we could list completions, but it is an
 616  			 * error right now
 617  			 */
 618  			term_beep(el);
 619  			return (CC_ERROR);
 620  		}
 621  	} else {
 622  #ifdef notyet
 623  		re_goto_bottom(el);
 624  		*el->el_line.lastchar = '\0';	/* just in case */
 625  		return (CC_LIST_CHOICES);
 626  #else
 627  		/*
 628  		 * Just complain for now.
 629  		 */
 630  		term_beep(el);
 631  		return (CC_ERROR);
 632  #endif
 633  	}
 634  }
 635  
 636  
 637  /* vi_kill_line_prev():
 638   *	Vi cut from beginning of line to cursor
 639   *	[^U]
 640   */
 641  protected el_action_t
 642  /*ARGSUSED*/
 643  vi_kill_line_prev(EditLine *el, Int c __attribute__((__unused__)))
 644  {
 645  	Char *kp, *cp;
 646  
 647  	cp = el->el_line.buffer;
 648  	kp = el->el_chared.c_kill.buf;
 649  	while (cp < el->el_line.cursor)
 650  		*kp++ = *cp++;	/* copy it */
 651  	el->el_chared.c_kill.last = kp;
 652  	c_delbefore(el, (int)(el->el_line.cursor - el->el_line.buffer));
 653  	el->el_line.cursor = el->el_line.buffer;	/* zap! */
 654  	return (CC_REFRESH);
 655  }
 656  
 657  
 658  /* vi_search_prev():
 659   *	Vi search history previous
 660   *	[?]
 661   */
 662  protected el_action_t
 663  /*ARGSUSED*/
 664  vi_search_prev(EditLine *el, Int c __attribute__((__unused__)))
 665  {
 666  
 667  	return (cv_search(el, ED_SEARCH_PREV_HISTORY));
 668  }
 669  
 670  
 671  /* vi_search_next():
 672   *	Vi search history next
 673   *	[/]
 674   */
 675  protected el_action_t
 676  /*ARGSUSED*/
 677  vi_search_next(EditLine *el, Int c __attribute__((__unused__)))
 678  {
 679  
 680  	return (cv_search(el, ED_SEARCH_NEXT_HISTORY));
 681  }
 682  
 683  
 684  /* vi_repeat_search_next():
 685   *	Vi repeat current search in the same search direction
 686   *	[n]
 687   */
 688  protected el_action_t
 689  /*ARGSUSED*/
 690  vi_repeat_search_next(EditLine *el, Int c __attribute__((__unused__)))
 691  {
 692  
 693  	if (el->el_search.patlen == 0)
 694  		return (CC_ERROR);
 695  	else
 696  		return (cv_repeat_srch(el, el->el_search.patdir));
 697  }
 698  
 699  
 700  /* vi_repeat_search_prev():
 701   *	Vi repeat current search in the opposite search direction
 702   *	[N]
 703   */
 704  /*ARGSUSED*/
 705  protected el_action_t
 706  vi_repeat_search_prev(EditLine *el, Int c __attribute__((__unused__)))
 707  {
 708  
 709  	if (el->el_search.patlen == 0)
 710  		return (CC_ERROR);
 711  	else
 712  		return (cv_repeat_srch(el,
 713  		    el->el_search.patdir == ED_SEARCH_PREV_HISTORY ?
 714  		    ED_SEARCH_NEXT_HISTORY : ED_SEARCH_PREV_HISTORY));
 715  }
 716  
 717  
 718  /* vi_next_char():
 719   *	Vi move to the character specified next
 720   *	[f]
 721   */
 722  protected el_action_t
 723  /*ARGSUSED*/
 724  vi_next_char(EditLine *el, Int c __attribute__((__unused__)))
 725  {
 726  	return cv_csearch(el, CHAR_FWD, -1, el->el_state.argument, 0);
 727  }
 728  
 729  
 730  /* vi_prev_char():
 731   *	Vi move to the character specified previous
 732   *	[F]
 733   */
 734  protected el_action_t
 735  /*ARGSUSED*/
 736  vi_prev_char(EditLine *el, Int c __attribute__((__unused__)))
 737  {
 738  	return cv_csearch(el, CHAR_BACK, -1, el->el_state.argument, 0);
 739  }
 740  
 741  
 742  /* vi_to_next_char():
 743   *	Vi move up to the character specified next
 744   *	[t]
 745   */
 746  protected el_action_t
 747  /*ARGSUSED*/
 748  vi_to_next_char(EditLine *el, Int c __attribute__((__unused__)))
 749  {
 750  	return cv_csearch(el, CHAR_FWD, -1, el->el_state.argument, 1);
 751  }
 752  
 753  
 754  /* vi_to_prev_char():
 755   *	Vi move up to the character specified previous
 756   *	[T]
 757   */
 758  protected el_action_t
 759  /*ARGSUSED*/
 760  vi_to_prev_char(EditLine *el, Int c __attribute__((__unused__)))
 761  {
 762  	return cv_csearch(el, CHAR_BACK, -1, el->el_state.argument, 1);
 763  }
 764  
 765  
 766  /* vi_repeat_next_char():
 767   *	Vi repeat current character search in the same search direction
 768   *	[;]
 769   */
 770  protected el_action_t
 771  /*ARGSUSED*/
 772  vi_repeat_next_char(EditLine *el, Int c __attribute__((__unused__)))
 773  {
 774  
 775  	return cv_csearch(el, el->el_search.chadir, el->el_search.chacha,
 776  		el->el_state.argument, el->el_search.chatflg);
 777  }
 778  
 779  
 780  /* vi_repeat_prev_char():
 781   *	Vi repeat current character search in the opposite search direction
 782   *	[,]
 783   */
 784  protected el_action_t
 785  /*ARGSUSED*/
 786  vi_repeat_prev_char(EditLine *el, Int c __attribute__((__unused__)))
 787  {
 788  	el_action_t r;
 789  	int dir = el->el_search.chadir;
 790  
 791  	r = cv_csearch(el, -dir, el->el_search.chacha,
 792  		el->el_state.argument, el->el_search.chatflg);
 793  	el->el_search.chadir = dir;
 794  	return r;
 795  }
 796  
 797  
 798  /* vi_match():
 799   *	Vi go to matching () {} or []
 800   *	[%]
 801   */
 802  protected el_action_t
 803  /*ARGSUSED*/
 804  vi_match(EditLine *el, Int c)
 805  {
 806  	const Char match_chars[] = STR("()[]{}");
 807  	Char *cp;
 808  	size_t delta, i, count;
 809  	Char o_ch, c_ch;
 810  
 811  	*el->el_line.lastchar = '\0';		/* just in case */
 812  
 813  	i = Strcspn(el->el_line.cursor, match_chars);
 814  	o_ch = el->el_line.cursor[i];
 815  	if (o_ch == 0)
 816  		return CC_ERROR;
 817  	delta = Strchr(match_chars, o_ch) - match_chars;
 818  	c_ch = match_chars[delta ^ 1];
 819  	count = 1;
 820  	delta = 1 - (delta & 1) * 2;
 821  
 822  	for (cp = &el->el_line.cursor[i]; count; ) {
 823  		cp += delta;
 824  		if (cp < el->el_line.buffer || cp >= el->el_line.lastchar)
 825  			return CC_ERROR;
 826  		if (*cp == o_ch)
 827  			count++;
 828  		else if (*cp == c_ch)
 829  			count--;
 830  	}
 831  
 832  	el->el_line.cursor = cp;
 833  
 834  	if (el->el_chared.c_vcmd.action != NOP) {
 835  		/* NB posix says char under cursor should NOT be deleted
 836  		   for -ve delta - this is different to netbsd vi. */
 837  		if (delta > 0)
 838  			el->el_line.cursor++;
 839  		cv_delfini(el);
 840  		return (CC_REFRESH);
 841  	}
 842  	return (CC_CURSOR);
 843  }
 844  
 845  /* vi_undo_line():
 846   *	Vi undo all changes to line
 847   *	[U]
 848   */
 849  protected el_action_t
 850  /*ARGSUSED*/
 851  vi_undo_line(EditLine *el, Int c)
 852  {
 853  
 854  	cv_undo(el);
 855  	return hist_get(el);
 856  }
 857  
 858  /* vi_to_column():
 859   *	Vi go to specified column
 860   *	[|]
 861   * NB netbsd vi goes to screen column 'n', posix says nth character
 862   */
 863  protected el_action_t
 864  /*ARGSUSED*/
 865  vi_to_column(EditLine *el, Int c)
 866  {
 867  
 868  	el->el_line.cursor = el->el_line.buffer;
 869  	el->el_state.argument--;
 870  	return ed_next_char(el, 0);
 871  }
 872  
 873  /* vi_yank_end():
 874   *	Vi yank to end of line
 875   *	[Y]
 876   */
 877  protected el_action_t
 878  /*ARGSUSED*/
 879  vi_yank_end(EditLine *el, Int c)
 880  {
 881  
 882  	cv_yank(el, el->el_line.cursor,
 883  	    (int)(el->el_line.lastchar - el->el_line.cursor));
 884  	return CC_REFRESH;
 885  }
 886  
 887  /* vi_yank():
 888   *	Vi yank
 889   *	[y]
 890   */
 891  protected el_action_t
 892  /*ARGSUSED*/
 893  vi_yank(EditLine *el, Int c)
 894  {
 895  
 896  	return cv_action(el, YANK);
 897  }
 898  
 899  /* vi_comment_out():
 900   *	Vi comment out current command
 901   *	[#]
 902   */
 903  protected el_action_t
 904  /*ARGSUSED*/
 905  vi_comment_out(EditLine *el, Int c)
 906  {
 907  
 908  	el->el_line.cursor = el->el_line.buffer;
 909  	c_insert(el, 1);
 910  	*el->el_line.cursor = '#';
 911  	re_refresh(el);
 912  	return ed_newline(el, 0);
 913  }
 914  
 915  /* vi_alias():
 916   *	Vi include shell alias
 917   *	[@]
 918   * NB: posix implies that we should enter insert mode, however
 919   * this is against historical precedent...
 920   */
 921  #undef __weak_reference /* __weak_reference is different on freebsd */
 922  #ifdef __weak_reference
 923  __weakref_visible char *my_get_alias_text(const char *)
 924      __weak_reference(get_alias_text);
 925  #endif
 926  protected el_action_t
 927  /*ARGSUSED*/
 928  vi_alias(EditLine *el, Int c)
 929  {
 930  #ifdef __weak_reference
 931  	char alias_name[3];
 932  	char *alias_text;
 933  
 934  	if (my_get_alias_text == 0) {
 935  		return CC_ERROR;
 936  	}
 937  
 938  	alias_name[0] = '_';
 939  	alias_name[2] = 0;
 940  	if (el_getc(el, &alias_name[1]) != 1)
 941  		return CC_ERROR;
 942  
 943  	alias_text = my_get_alias_text(alias_name);
 944  	if (alias_text != NULL)
 945  		FUN(el,push)(el, ct_decode_string(alias_text, &el->el_scratch));
 946  	return CC_NORM;
 947  #else
 948  	return CC_ERROR;
 949  #endif
 950  }
 951  
 952  /* vi_to_history_line():
 953   *	Vi go to specified history file line.
 954   *	[G]
 955   */
 956  protected el_action_t
 957  /*ARGSUSED*/
 958  vi_to_history_line(EditLine *el, Int c)
 959  {
 960  	int sv_event_no = el->el_history.eventno;
 961  	el_action_t rval;
 962  
 963  
 964  	if (el->el_history.eventno == 0) {
 965  		 (void) Strncpy(el->el_history.buf, el->el_line.buffer,
 966  		     EL_BUFSIZ);
 967  		 el->el_history.last = el->el_history.buf +
 968  			 (el->el_line.lastchar - el->el_line.buffer);
 969  	}
 970  
 971  	/* Lack of a 'count' means oldest, not 1 */
 972  	if (!el->el_state.doingarg) {
 973  		el->el_history.eventno = 0x7fffffff;
 974  		hist_get(el);
 975  	} else {
 976  		/* This is brain dead, all the rest of this code counts
 977  		 * upwards going into the past.  Here we need count in the
 978  		 * other direction (to match the output of fc -l).
 979  		 * I could change the world, but this seems to suffice.
 980  		 */
 981  		el->el_history.eventno = 1;
 982  		if (hist_get(el) == CC_ERROR)
 983  			return CC_ERROR;
 984  		el->el_history.eventno = 1 + el->el_history.ev.num
 985  					- el->el_state.argument;
 986  		if (el->el_history.eventno < 0) {
 987  			el->el_history.eventno = sv_event_no;
 988  			return CC_ERROR;
 989  		}
 990  	}
 991  	rval = hist_get(el);
 992  	if (rval == CC_ERROR)
 993  		el->el_history.eventno = sv_event_no;
 994  	return rval;
 995  }
 996  
 997  /* vi_histedit():
 998   *	Vi edit history line with vi
 999   *	[v]
1000   */
1001  protected el_action_t
1002  /*ARGSUSED*/
1003  vi_histedit(EditLine *el, Int c)
1004  {
1005  	int fd;
1006  	pid_t pid;
1007  	ssize_t st;
1008  	int status;
1009  	char tempfile[] = "/tmp/histedit.XXXXXXXXXX";
1010  	char *cp;
1011  	size_t len;
1012  	Char *line;
1013  
1014  	if (el->el_state.doingarg) {
1015  		if (vi_to_history_line(el, 0) == CC_ERROR)
1016  			return CC_ERROR;
1017  	}
1018  
1019  	fd = mkstemp(tempfile);
1020  	if (fd < 0)
1021  		return CC_ERROR;
1022  	len = (size_t)(el->el_line.lastchar - el->el_line.buffer);
1023  #define TMP_BUFSIZ (EL_BUFSIZ * MB_LEN_MAX)
1024  	cp = el_malloc(TMP_BUFSIZ);
1025  	if (cp == NULL) {
1026  		unlink(tempfile);
1027  		close(fd);
1028  		return CC_ERROR;
1029  	}
1030  	line = el_malloc(len * sizeof(*line));
1031  	if (line == NULL) {
1032  		el_free((ptr_t)cp);
1033  		return CC_ERROR;
1034  	}
1035  	Strncpy(line, el->el_line.buffer, len);
1036  	line[len] = '\0';
1037  	ct_wcstombs(cp, line, TMP_BUFSIZ - 1);
1038  	cp[TMP_BUFSIZ - 1] = '\0';
1039  	len = strlen(cp);
1040  	write(fd, cp, len);
1041  	write(fd, "\n", 1);
1042  	pid = fork();
1043  	switch (pid) {
1044  	case -1:
1045  		close(fd);
1046  		unlink(tempfile);
1047  		el_free(cp);
1048                  el_free(line);
1049  		return CC_ERROR;
1050  	case 0:
1051  		close(fd);
1052  		execlp("vi", "vi", tempfile, (char *)NULL);
1053  		exit(0);
1054  		/*NOTREACHED*/
1055  	default:
1056  		while (waitpid(pid, &status, 0) != pid)
1057  			continue;
1058  		lseek(fd, (off_t)0, SEEK_SET);
1059  		st = read(fd, cp, TMP_BUFSIZ);
1060  		if (st > 0) {
1061  			len = (size_t)(el->el_line.lastchar -
1062  			    el->el_line.buffer);
1063  			len = ct_mbstowcs(el->el_line.buffer, cp, len);
1064  			if (len > 0 && el->el_line.buffer[len -1] == '\n')
1065  				--len;
1066  		}
1067  		else
1068  			len = 0;
1069                  el->el_line.cursor = el->el_line.buffer;
1070                  el->el_line.lastchar = el->el_line.buffer + len;
1071  		el_free(cp);
1072                  el_free(line);
1073  		break;
1074  	}
1075  
1076  	close(fd);
1077  	unlink(tempfile);
1078  	/* return CC_REFRESH; */
1079  	return ed_newline(el, 0);
1080  }
1081  
1082  /* vi_history_word():
1083   *	Vi append word from previous input line
1084   *	[_]
1085   * Who knows where this one came from!
1086   * '_' in vi means 'entire current line', so 'cc' is a synonym for 'c_'
1087   */
1088  protected el_action_t
1089  /*ARGSUSED*/
1090  vi_history_word(EditLine *el, Int c)
1091  {
1092  	const Char *wp = HIST_FIRST(el);
1093  	const Char *wep, *wsp;
1094  	int len;
1095  	Char *cp;
1096  	const Char *lim;
1097  
1098  	if (wp == NULL)
1099  		return CC_ERROR;
1100  
1101  	wep = wsp = 0;
1102  	do {
1103  		while (Isspace(*wp))
1104  			wp++;
1105  		if (*wp == 0)
1106  			break;
1107  		wsp = wp;
1108  		while (*wp && !Isspace(*wp))
1109  			wp++;
1110  		wep = wp;
1111  	} while ((!el->el_state.doingarg || --el->el_state.argument > 0)
1112  	    && *wp != 0);
1113  
1114  	if (wsp == 0 || (el->el_state.doingarg && el->el_state.argument != 0))
1115  		return CC_ERROR;
1116  
1117  	cv_undo(el);
1118  	len = (int)(wep - wsp);
1119  	if (el->el_line.cursor < el->el_line.lastchar)
1120  		el->el_line.cursor++;
1121  	c_insert(el, len + 1);
1122  	cp = el->el_line.cursor;
1123  	lim = el->el_line.limit;
1124  	if (cp < lim)
1125  		*cp++ = ' ';
1126  	while (wsp < wep && cp < lim)
1127  		*cp++ = *wsp++;
1128  	el->el_line.cursor = cp;
1129  
1130  	el->el_map.current = el->el_map.key;
1131  	return CC_REFRESH;
1132  }
1133  
1134  /* vi_redo():
1135   *	Vi redo last non-motion command
1136   *	[.]
1137   */
1138  protected el_action_t
1139  /*ARGSUSED*/
1140  vi_redo(EditLine *el, Int c)
1141  {
1142  	c_redo_t *r = &el->el_chared.c_redo;
1143  
1144  	if (!el->el_state.doingarg && r->count) {
1145  		el->el_state.doingarg = 1;
1146  		el->el_state.argument = r->count;
1147  	}
1148  
1149  	el->el_chared.c_vcmd.pos = el->el_line.cursor;
1150  	el->el_chared.c_vcmd.action = r->action;
1151  	if (r->pos != r->buf) {
1152  		if (r->pos + 1 > r->lim)
1153  			/* sanity */
1154  			r->pos = r->lim - 1;
1155  		r->pos[0] = 0;
1156  		FUN(el,push)(el, r->buf);
1157  	}
1158  
1159  	el->el_state.thiscmd = r->cmd;
1160  	el->el_state.thisch = r->ch;
1161  	return  (*el->el_map.func[r->cmd])(el, r->ch);
1162  }