home *** CD-ROM | disk | FTP | other *** search
Text File | 1992-10-22 | 55.2 KB | 2,176 lines |
- Newsgroups: comp.sources.misc
- From: jmd@cyclone.bt.co.uk (John Downey)
- Subject: v33i012: xvi - portable multi-window vi-like editor, Part03/18
- Message-ID: <1992Oct23.181052.29857@sparky.imd.sterling.com>
- X-Md4-Signature: e0b027660312cdc8c470e7d18f7d83ff
- Date: Fri, 23 Oct 1992 18:10:52 GMT
- Approved: kent@sparky.imd.sterling.com
-
- Submitted-by: jmd@cyclone.bt.co.uk (John Downey)
- Posting-number: Volume 33, Issue 12
- Archive-name: xvi/part03
- Environment: Unix, MS-DOS, OS/2, QNX
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then feed it
- # into a shell via "sh file" or similar. To overwrite existing files,
- # type "sh file -c".
- # Contents: xvi/doc/help xvi/src/normal.c
- # Wrapped by kent@sparky on Thu Oct 22 09:03:41 1992
- PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin ; export PATH
- echo If this archive is complete, you will see the following message:
- echo ' "shar: End of archive 3 (of 18)."'
- if test -f 'xvi/doc/help' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'xvi/doc/help'\"
- else
- echo shar: Extracting \"'xvi/doc/help'\" \(10258 characters\)
- sed "s/^X//" >'xvi/doc/help' <<'END_OF_FILE'
- XIf you don't know how to use vi (or xvi) at all, you should not
- Xread through this help text; it is intended simply as a reminder
- Xfor those who already know how to use the editor.
- X
- XTo get out of xvi, type the following:
- X :q!
- Xand then press RETURN.
- X
- XThe rest of this file contains a summary of xvi commands.
- XIf you remember the commands ^D (control-D) and ^U (control-U)
- Xthen that will be enough to get you around this help file.
- X
- XNumeric Prefixes
- X================
- X
- XMost commands can be prefixed with a number, which will alter
- Xtheir action. For instance, to go to line 37 of a file, you can
- Xtype
- X
- X 37G
- X
- Xbut beware - the meaning of the prefix varies between commands.
- X
- XCursor positioning
- X==================
- X
- X ^F Move forward one screenful in file
- X ^B Move backward one screenful
- X ^D Scroll down half screen
- X ^U Scroll up half screen
- X G Goto line (defaults to end of file)
- X /re Next occurence of regular expression 're'
- X ?re Prior occurence of regular expression 're'
- X n Repeat last / or ?
- X N Reverse last / or ?
- X % Find matching (, ), {, }, [, or ]
- X ]] Start of next function
- X [[ Start of previous function
- X g Go to next buffer window
- X
- XAdjusting the screen
- X====================
- X
- X ^L Redraw the screen
- X ^E Scroll window down 1 line
- X ^Y Scroll window up 1 line
- X
- X z<CR> Redraw, current line at top
- X z- ... at bottom
- X z. ... at centre
- X
- X ^T Shrink current window by 1 line
- X ^W Grow current window by 1 line
- X ^O Grow current window to maximum size
- X
- XCharacter Positioning
- X=====================
- X
- X ^ First non-white on current line
- X 0 Beginning of line
- X $ End of line
- X h Left one character
- X l Right one character
- X ^H Same as 'h'
- X space Same as 'l'
- X fx Find 'x' forward in current line
- X Fx Find 'x' backward in current line
- X tx Go up to 'x' forward in current line
- X Tx Go up to 'x' backward in current line
- X ; Repeat last f, F, t, or T
- X , Repeat last f, F, t, or T, changing direction
- X | Go to specified column in current line
- X
- XLine Positioning
- X================
- X
- X H Go to home window line
- X L Go to last window line
- X M Go to middle window line
- X + Go to next line, at first non-white
- X - Go to previous line, at first non-white
- X CR Same as +
- X j Go down, staying in same column if possible
- X k Go up, staying in same column if possible
- X
- XWords, sentences, paragraphs
- X============================
- X
- X w Go to start of next word
- X b Go to start of previous word
- X e Go to end of current word
- X W As w, but using blanks to delimit words
- X B As b, but using blanks to delimit words
- X E As e, but using blanks to delimit words
- X ) Go to start of next sentence (not yet implemented)
- X ( Go to start of previous sentence (not yet implemented)
- X } Go to start of next paragraph
- X { Go to start of previous paragraph
- X
- XMarking and Returning
- X=====================
- X
- X `` Go to previous context position
- X '' Go to previous context line
- X mx Mark current position with letter 'x'
- X `x Go to mark 'x'
- X 'x Go to mark 'x' at first non-white in line
- X :ka Mark current line with letter 'a'
- X :7ka Mark line 7 with letter 'a'
- X
- XInsert and Replace
- X==================
- X
- X a Append after cursor
- X i Insert before cursor
- X A Append at end of current line
- X I Insert before first non-blank on current line
- X o Open line below current line
- X O Open line above current line
- X rx Replace single character with 'x'
- X R Enter replace (overwrite) mode
- X
- XOperators (double to affect lines)
- X==================================
- X
- X d Delete
- X c Change
- X y Yank to buffer (to be put back with 'p' or 'P')
- X < Left shift (by no of spaces specified by "shiftwidth" parameter)
- X > Right shift (ditto)
- X ! Pipe line range through specified system command
- X
- XMiscellaneous operations
- X========================
- X
- X C Change rest of line
- X D Delete rest of line
- X s Substitute characters
- X S As cc (unimplemented)
- X J Join next line on to end of this line
- X x Delete character
- X X Delete character before cursor
- X
- XYank and Put
- X============
- X
- X y yank some text (see "operators")
- X Y Yank current line
- X :y Yank current line
- X :17y Yank line 17
- X :3,5y Yank lines from 3 to 5 inclusive
- X
- X p Put back text last yanked or deleted (see "operators")
- X P As p, but before current position
- X :put Put back text (same as 'p')
- X :5put Put back text after line 5
- X :$put Put back text at end of buffer
- X :0put Put back text at start of buffer
- X
- X "x Prefix before any of 'yYpP', uses named buffer x to store text
- X Buffer name may be any lower-case letter, '@' (the default),
- X or ':' which normally stores the last colon command executed.
- X "xp Inserts contents of named buffer x after current position
- X
- X @x Take contents of named yank buffer as command input
- X @@ Take last yanked or deleted text as input
- X @: Redo last colon command
- X
- XUndo & Redo
- X===========
- X
- X u Undo last change
- X U Restore current line (not yet implemented)
- X . Repeat last change
- X
- XGlobal Editing
- X==============
- X
- X :s/RE/SUB/ Change 1st RE in current line to SUB
- X :s/RE/SUB/g Change all RE's in current line to SUB
- X :g/RE/p Print all lines containing RE
- X :g/RE/d Delete all lines containing RE
- X :g/RE/s/... Perform 's' command on all lines containing RE
- X :& Redo last substitution
- X :~ Substitute last SUB for last RE used
- X
- X Any of the above may be preceded by a line range, e.g. 12,15.
- X
- X & Redo last substitution (same as :&)
- X
- X :x,yyank Yank lines from x through y
- X :x,ydelete Delete lines from x through y
- X :x,ycopyz Copy lines from x through y to after line z
- X :x,ymovez Move lines from x through y to after line z
- X
- XFile and buffer manipulation
- X============================
- X
- X ZZ Same as :x
- X
- X :w Write buffer to file
- X :w name Write buffer to file "name"
- X :wq Write buffer to file and then quit
- X :x Close window, if last window onto buffer then:
- X Write buffer if modified, then close buffer
- X :cl Close current window, closing buffer if only window onto it
- X :cl! Close current window, discarding any changes
- X :q Quit
- X :q! Quit, discarding any changes
- X :b Create new buffer and window
- X :b name Create new buffer and window, editing file "name"
- X :split Create a new screen window onto the current buffer
- X :e name Edit file "name" in current buffer
- X :e! Reedit current file, discarding any changes
- X :e # Edit file which was last closed
- X ^^ (Control-uparrow) Same as :e #
- X :n Edit next file in arglist
- X :n args Specify new arglist
- X :wn Write current file and edit next
- X :args Show list of files being edited
- X :rew Rewind arglist to start
- X :f Show current file and lines
- X ^G Same as :f
- X :f name Change current file name to "name"
- X :ta tag Find tag entry "tag" and go to it
- X ^] :ta, current word is tag
- X :cd dir Change current directory
- X :so file
- X Read commands from file
- X
- XDisplaying and Setting Parameters
- X=================================
- X
- X :set Display parameters which have been set
- X :set all Display values of all parameters
- X :set name Set the boolean parameter "name"
- X :set noname Unset the boolean parameter "name"
- X :set name=value Set the string or numeric parameter "name" to "value"
- X
- XKey Mapping
- X===========
- X
- X :map lhs rhs
- X Causes input of "lhs" to produce input "rhs" instead.
- X :map! lhs rhs
- X Like map, but works in insert mode instead.
- X :map Display all current maps.
- X :set timeout=<num>
- X Re-set the number of milliseconds that the editor waits
- X for another character to be entered to complete a map.
- X Default value is 200, which is okay for fast typists.
- X :set remap
- X When "remap" is set, the result of a map is re-processed
- X through the map system. This option is extremely dangerous,
- X as it can cause uncontrolled recursion.
- X
- XCommand line options
- X====================
- X
- X -s param=value Set param to value before starting
- X -t tag Edit file containing tag
- X +num Start up editing file at line num
- X +/pat Start up editing file at line containing pat
- X
- XUsing the mouse (not implemented on all systems)
- X================================================
- X
- X Click mouse button on:
- X
- X any line outside current window
- X
- X Change current window to the one indicated by the mouse
- X (can be used instead of 'g')
- X
- X top line of current window
- X
- X Scroll window downwards
- X (same as '^Y')
- X
- X bottom line of current window
- X
- X Scroll window upwards
- X (same as '^E')
- X
- X any other line of current window
- X (or either line of a 2-line window)
- X
- X Grow window by 1 line
- X (same as '^W')
- X
- X status line of current window
- X
- X Show current file and lines
- X (same as '^G')
- END_OF_FILE
- if test 10258 -ne `wc -c <'xvi/doc/help'`; then
- echo shar: \"'xvi/doc/help'\" unpacked with wrong size!
- fi
- # end of 'xvi/doc/help'
- fi
- if test -f 'xvi/src/normal.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'xvi/src/normal.c'\"
- else
- echo shar: Extracting \"'xvi/src/normal.c'\" \(41946 characters\)
- sed "s/^X//" >'xvi/src/normal.c' <<'END_OF_FILE'
- X/* Copyright (c) 1990,1991,1992 Chris and John Downey */
- X#ifndef lint
- Xstatic char *sccsid = "@(#)normal.c 2.7 (Chris & John Downey) 8/24/92";
- X#endif
- X
- X/***
- X
- X* program name:
- X xvi
- X* function:
- X PD version of UNIX "vi" editor, with extensions.
- X* module name:
- X normal.c
- X* module function:
- X Main routine for processing characters in command mode
- X as well as routines for handling the operators.
- X* history:
- X STEVIE - ST Editor for VI Enthusiasts, Version 3.10
- X Originally by Tim Thompson (twitch!tjt)
- X Extensive modifications by Tony Andrews (onecom!wldrdg!tony)
- X Heavily modified by Chris & John Downey
- X
- X***/
- X
- X#include "xvi.h"
- X
- Xstatic bool_t do_target P((int, int));
- Xstatic bool_t do_cmd P((int, int));
- Xstatic bool_t do_badcmd P((int, int));
- Xstatic bool_t do_page P((int, int));
- Xstatic bool_t do_scroll P((int, int));
- Xstatic bool_t do_word P((int, int));
- Xstatic bool_t do_csearch P((int, int));
- Xstatic bool_t do_z P((int, int));
- Xstatic bool_t do_x P((int, int));
- Xstatic bool_t do_HLM P((int, int));
- Xstatic bool_t do_rchar P((int, int));
- Xstatic bool_t do_ins P((int, int));
- Xstatic void op_shift P((int, int, int, long, Posn *, Posn *));
- Xstatic void op_delete P((int, int, long, Posn *, Posn *));
- Xstatic void op_change P((int, int, long, Posn *, Posn *));
- Xstatic void op_yank P((Posn *, Posn *));
- Xstatic bool_t dojoin P((void));
- X
- X/*
- X * Command type table. This is used for certain operations which are
- X * the same for "classes" of commands, e.g. for disallowing their use
- X * as targets of operators.
- X *
- X * Entries in this table having value 0 are unimplemented commands.
- X *
- X * If TARGET is set, the command may be used as the target for one of
- X * the operators (e.g. 'c'); the default is that targets are character-
- X * based unless TGT_LINE is set in which case they are line-based.
- X * Similarly, the default is that targets are exclusive, unless the
- X * TGT_INCLUSIVE flag is set.
- X *
- X * Q: WHAT DO WE DO ABOUT RETURN and LINEFEED???
- X * A: The ascii_map() macro (see ascii.h) handles this for QNX (I think).
- X */
- X#define COMMAND 0x1 /* is implemented */
- X#define TARGET 0x2 /* can serve as a target */
- X#define TGT_LINE 0x4 /* a line-based target */
- X#define TGT_CHAR 0 /* a char-based target */
- X#define TGT_INCLUSIVE 0x8 /* an inclusive target */
- X#define TGT_EXCLUSIVE 0 /* an exclusive target */
- X#define TWO_CHAR 0x10 /* a two-character command */
- X
- Xstatic struct {
- X bool_t (*c_func) P((int, int));
- X unsigned char c_flags;
- X} cmd_types[256] = {
- X /* ^@ */ do_badcmd, 0,
- X /* ^A */ do_badcmd, 0,
- X /* ^B */ do_page, COMMAND,
- X /* ^C */ do_badcmd, 0,
- X /* ^D */ do_scroll, COMMAND,
- X /* ^E */ do_scroll, COMMAND,
- X /* ^F */ do_page, COMMAND,
- X /* ^G */ do_cmd, COMMAND,
- X /* ^H */ do_target, COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
- X /* ^I */ do_badcmd, 0,
- X /* ^J */ do_target, COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
- X /* ^K */ do_badcmd, 0,
- X /* ^L */ do_cmd, COMMAND,
- X /* ^M */ do_target, COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
- X /* ^N */ do_target, COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
- X /* ^O */ do_cmd, COMMAND,
- X /* ^P */ do_target, COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
- X /* ^Q */ do_badcmd, 0,
- X /* ^R */ do_cmd, COMMAND,
- X /* ^S */ do_badcmd, 0,
- X /* ^T */ do_cmd, COMMAND,
- X /* ^U */ do_scroll, COMMAND,
- X /* ^V */ do_badcmd, 0,
- X /* ^W */ do_cmd, COMMAND,
- X /* ^X */ do_badcmd, 0,
- X /* ^Y */ do_scroll, COMMAND,
- X /* ^Z */ do_cmd, COMMAND,
- X /* ESCAPE */ do_cmd, COMMAND,
- X /* ^\ */ do_badcmd, 0,
- X /* ^] */ do_cmd, COMMAND,
- X /* ^^ */ do_cmd, COMMAND,
- X /* ^_ */ do_rchar, COMMAND,
- X /* space */ do_target, COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
- X /* ! */ do_cmd, COMMAND,
- X /* " */ do_cmd, COMMAND | TWO_CHAR,
- X /* # */ do_badcmd, 0,
- X /* $ */ do_target, COMMAND | TARGET | TGT_CHAR | TGT_INCLUSIVE,
- X /* % */ do_target, COMMAND | TARGET | TGT_CHAR | TGT_INCLUSIVE,
- X /* & */ do_cmd, COMMAND,
- X /* ' */ do_target, COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE | TWO_CHAR,
- X /* ( */ do_target, COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
- X /* ) */ do_target, COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
- X /* * */ do_badcmd, 0,
- X /* + */ do_target, COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
- X /* , */ do_csearch, COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
- X /* - */ do_target, COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
- X /* . */ do_cmd, COMMAND,
- X /* / */ do_cmd, COMMAND,
- X /* 0 */ do_target, COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
- X /* 1 */ do_badcmd, 0,
- X /* 2 */ do_badcmd, 0,
- X /* 3 */ do_badcmd, 0,
- X /* 4 */ do_badcmd, 0,
- X /* 5 */ do_badcmd, 0,
- X /* 6 */ do_badcmd, 0,
- X /* 7 */ do_badcmd, 0,
- X /* 8 */ do_badcmd, 0,
- X /* 9 */ do_badcmd, 0,
- X /* : */ do_cmd, COMMAND,
- X /* ; */ do_csearch, COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
- X /* < */ do_cmd, COMMAND,
- X /* = */ do_badcmd, 0,
- X /* > */ do_cmd, COMMAND,
- X /* ? */ do_cmd, COMMAND,
- X /* @ */ do_cmd, COMMAND | TWO_CHAR,
- X /* A */ do_ins, COMMAND,
- X /* B */ do_word, COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
- X /* C */ do_cmd, COMMAND,
- X /* D */ do_cmd, COMMAND,
- X /* E */ do_word, COMMAND | TARGET | TGT_CHAR | TGT_INCLUSIVE,
- X /* F */ do_csearch, COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE | TWO_CHAR,
- X /* G */ do_target, COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
- X /* H */ do_HLM, COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
- X /* I */ do_ins, COMMAND,
- X /* J */ do_cmd, COMMAND,
- X /* K */ do_badcmd, 0,
- X /* L */ do_HLM, COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
- X /* M */ do_HLM, COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
- X /* N */ do_target, COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
- X /* O */ do_ins, COMMAND,
- X /* P */ do_cmd, COMMAND,
- X /* Q */ do_badcmd, 0,
- X /* R */ do_cmd, COMMAND,
- X /* S */ do_cmd, COMMAND,
- X /* T */ do_csearch, COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE | TWO_CHAR,
- X /* U */ do_badcmd, 0,
- X /* V */ do_badcmd, 0,
- X /* W */ do_word, COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
- X /* X */ do_x, COMMAND,
- X /* Y */ do_cmd, COMMAND,
- X /* Z */ do_cmd, COMMAND | TWO_CHAR,
- X /* [ */ do_target, COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE | TWO_CHAR,
- X /* \ */ do_badcmd, 0,
- X /* ] */ do_target, COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE | TWO_CHAR,
- X /* ^ */ do_target, COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
- X /* _ */ do_target, COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
- X /* ` */ do_target, COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE | TWO_CHAR,
- X /* a */ do_ins, COMMAND,
- X /* b */ do_word, COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
- X /* c */ do_cmd, COMMAND,
- X /* d */ do_cmd, COMMAND,
- X /* e */ do_word, COMMAND | TARGET | TGT_CHAR | TGT_INCLUSIVE,
- X /* f */ do_csearch, COMMAND | TARGET | TGT_CHAR | TGT_INCLUSIVE | TWO_CHAR,
- X /* g */ do_cmd, COMMAND,
- X /* h */ do_target, COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
- X /* i */ do_ins, COMMAND,
- X /* j */ do_target, COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
- X /* k */ do_target, COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
- X /* l */ do_target, COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
- X /* m */ do_cmd, COMMAND | TWO_CHAR,
- X /* n */ do_target, COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
- X /* o */ do_ins, COMMAND,
- X /* p */ do_cmd, COMMAND,
- X /* q */ do_badcmd, 0,
- X /* r */ do_cmd, COMMAND,
- X /* s */ do_cmd, COMMAND,
- X /* t */ do_csearch, COMMAND | TARGET | TGT_CHAR | TGT_INCLUSIVE | TWO_CHAR,
- X /* u */ do_cmd, COMMAND,
- X /* v */ do_badcmd, 0,
- X /* w */ do_word, COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
- X /* x */ do_x, COMMAND,
- X /* y */ do_cmd, COMMAND,
- X /* z */ do_z, COMMAND | TWO_CHAR,
- X /* { */ do_target, COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
- X /* | */ do_target, COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
- X /* } */ do_target, COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
- X /* ~ */ do_rchar, COMMAND,
- X /* DEL */ do_badcmd, 0,
- X /* K_HELP */ do_cmd, COMMAND,
- X /* K_UNDO */ do_cmd, COMMAND,
- X /* K_INSERT */ do_ins, COMMAND,
- X /* K_HOME */ do_HLM, COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
- X /* K_UARROW */ do_target, COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
- X /* K_DARROW */ do_target, COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
- X /* K_LARROW */ do_target, COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
- X /* K_RARROW */ do_target, COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
- X /* K_CGRAVE */ do_cmd, COMMAND,
- X};
- X
- X#define NOP '\0' /* no pending operation */
- X
- Xstatic int operator = NOP; /* current pending operator */
- X
- X/*
- X * When a cursor motion command is made, it is marked as being a character
- X * or line oriented motion. Then, if an operator is in effect, the operation
- X * becomes character or line oriented accordingly.
- X *
- X * Character motions are marked as being inclusive or not. Most char.
- X * motions are inclusive, but some (e.g. 'w') are not.
- X */
- X
- Xstatic enum {
- X m_bad, /* 'bad' motion type marks unusable yank buf */
- X m_nonincl, /* non-inclusive character motion */
- X m_incl, /* inclusive character motion */
- X m_line /* line-based motion */
- X} mtype; /* type of the current cursor motion */
- X
- X/*
- X * Cursor position at start of operator.
- X */
- Xstatic Posn startop;
- X
- X/*
- X * Operators can have counts either before the operator, or between the
- X * operator and the following cursor motion as in:
- X *
- X * d3w or 3dw
- X *
- X * The number is initially stored in Prenum as it is processed.
- X *
- X * If a count is given before an operator, it is saved in opnum when the
- X * initial recognition of the operator takes place. If normal() is called
- X * with a pending operator, the count in opnum (if present) overrides
- X * any count that comes later.
- X */
- Xstatic long Prenum;
- Xstatic long opnum;
- X
- X/*
- X * This variable contains the name of the yank/put buffer we are
- X * currently using. It is set by the " command. The default value
- X * is always '@'; other values are a-z and possibly ':'.
- X */
- Xstatic int cur_yp_name = '@';
- X
- X/*
- X * This state variable is TRUE if we got a preceding buffer
- X * name: if set, the buffer_name variable contains the letter
- X * which was given as the buffer name.
- X */
- Xstatic bool_t got_name = FALSE;
- Xstatic char buffer_name;
- X
- X/*
- X * Return value of Prenum unless it is 0, in which case return the
- X * default value of 1.
- X */
- X#define LDEF1PRENUM ((Prenum == 0 ? 1L : Prenum))
- X
- X/*
- X * Return value of Prenum as an int, unless it is 0, in which case
- X * return the default value of 1.
- X *
- X * Note that this assumes that Prenum will never be negative.
- X */
- X#define IDEF1PRENUM (Prenum == 0 ? \
- X 1 : \
- X sizeof (int) == sizeof (long) || \
- X Prenum <= INT_MAX ? \
- X (int) Prenum : \
- X INT_MAX)
- X
- X/*
- X * Redo buffer.
- X */
- Xstruct redo {
- X enum {
- X r_insert,
- X r_replace1,
- X r_normal
- X } r_mode;
- X Flexbuf r_fb;
- X} Redo;
- X
- X/*
- X * Execute a command in "normal" (i.e. command) mode.
- X */
- Xbool_t
- Xnormal(c)
- Xregister int c;
- X{
- X /*
- X * This variable is used to recall whether we got
- X * an operator last time, to decide whether we
- X * should apply it this time.
- X */
- X register bool_t finish_op;
- X
- X /*
- X * TRUE if we are awaiting a second character to finish the
- X * current command - this is for two-character commands like
- X * ZZ, and for commands taking a single character argument.
- X * The "first_char" and "second_char" variables are for the
- X * first character (stored between calls), and the second one.
- X */
- X static bool_t two_char = FALSE;
- X static int first_char;
- X int second_char;
- X unsigned char cflags;
- X bool_t (*cfunc) P((int, int)) = NULL;
- X
- X
- X /*
- X * If the character is a digit, and it is not a leading '0',
- X * compute Prenum instead of doing a command. Leading zeroes
- X * are treated specially because '0' is a valid command.
- X *
- X * If two_char is set, don't treat digits this way; they are
- X * passed in as the second character. This is because none of
- X * the two-character commands are allowed to take prenums in
- X * the middle; you want a prenum, you have to type it before
- X * the command. So "t3" works as you might expect it to.
- X */
- X if (!two_char && is_digit(c) && (c != '0' || Prenum > 0)) {
- X Prenum = Prenum * 10 + (c - '0');
- X return(FALSE);
- X }
- X
- X /*
- X * If there is an operator pending, then the command we take
- X * this time will terminate it. Finish_op tells us to finish
- X * the operation before returning this time (unless it was
- X * cancelled).
- X */
- X finish_op = (operator != NOP);
- X
- X /*
- X * If we're in the middle of an operator AND we had a count before
- X * the operator, then that count overrides the current value of
- X * Prenum. What this means effectively, is that commands like
- X * "3dw" get turned into "d3w" which makes things fall into place
- X * pretty neatly.
- X */
- X if (finish_op || two_char) {
- X if (opnum != 0) {
- X Prenum = opnum;
- X }
- X } else {
- X opnum = 0;
- X }
- X
- X /*
- X * If we got given a buffer name last time around, it is only
- X * good for one operation; so at the start of each new command
- X * we set or clear the yankput module's idea of the buffer name.
- X * We don't do this if finish_op is set because it is not the
- X * start of a new command.
- X */
- X if (!finish_op) {
- X cur_yp_name = got_name ? buffer_name : '@';
- X got_name = FALSE;
- X }
- X
- X if (c > (sizeof(cmd_types) / sizeof(cmd_types[0]) - 1)) {
- X operator = NOP;
- X Prenum = 0;
- X beep(curwin);
- X return(FALSE);
- X }
- X
- X /*
- X * If two_char is set, it means we got the first character of
- X * a two-character command last time. So check the second char,
- X * and set cflags appropriately.
- X */
- X if (two_char) {
- X second_char = c;
- X two_char = FALSE;
- X cflags = cmd_types[ascii_map(first_char)].c_flags;
- X cfunc = cmd_types[ascii_map(first_char)].c_func;
- X
- X /*
- X * This seems to be a universal rule - if a two-character
- X * command has ESC as the second character, it means "abort".
- X */
- X if (second_char == ESC) {
- X operator = NOP;
- X finish_op = FALSE;
- X Prenum = 0;
- X return(FALSE);
- X }
- X
- X } else {
- X /*
- X * Received a command. Find out its characteristics ...
- X */
- X first_char = c;
- X second_char = '\0';
- X cflags = cmd_types[ascii_map(first_char)].c_flags;
- X cfunc = cmd_types[ascii_map(first_char)].c_func;
- X
- X /*
- X * It's a two-character command. So wait until we get
- X * the second character before proceeding.
- X */
- X if (cflags & TWO_CHAR) {
- X two_char = TRUE;
- X first_char = c;
- X if (Prenum != 0) {
- X opnum = Prenum;
- X Prenum = 0;
- X }
- X return(FALSE);
- X }
- X
- X /*
- X * If we got an operator last time, and the user
- X * typed the same character again, we fake out the
- X * default "apply to this line" rule by changing
- X * the input character to a '_' which means "the
- X * current line."
- X */
- X if (finish_op) {
- X if (operator == c) {
- X first_char = c = '_';
- X cflags = cmd_types[ascii_map(c)].c_flags;
- X cfunc = cmd_types[ascii_map(c)].c_func;
- X } else if (!(cflags & TARGET)) {
- X beep(curwin);
- X operator = NOP;
- X Prenum = 0;
- X return(FALSE);
- X }
- X }
- X }
- X
- X /*
- X * At this point, cfunc must be set - if not, the entry in the
- X * command table is zero, so disallow the input character.
- X */
- X if (cfunc == NULL) {
- X operator = NOP;
- X Prenum = 0;
- X beep(curwin);
- X return(FALSE);
- X }
- X
- X if (cflags & TARGET) {
- X
- X /*
- X * A cursor movement command.
- X */
- X
- X if (cflags & TGT_LINE) {
- X mtype = m_line;
- X } else {
- X if (cflags & TGT_INCLUSIVE) {
- X mtype = m_incl;
- X } else {
- X mtype = m_nonincl;
- X }
- X }
- X
- X if (!(*cfunc)(first_char, second_char)) {
- X beep(curwin);
- X operator = NOP;
- X Prenum = 0;
- X return(FALSE);
- X }
- X
- X /*
- X * If an operation is pending, handle it...
- X */
- X if (finish_op) {
- X Posn top, bot;
- X
- X top = startop;
- X bot = *curwin->w_cursor;
- X
- X /*
- X * Put the cursor back to its starting position.
- X */
- X move_cursor(curwin, startop.p_line, startop.p_index);
- X
- X if (lt(&bot, &top)) {
- X pswap(&top, &bot);
- X }
- X
- X switch (operator) {
- X case '<':
- X case '>':
- X op_shift(operator, first_char, second_char, Prenum, &top, &bot);
- X break;
- X
- X case 'd':
- X op_delete(first_char, second_char, Prenum, &top, &bot);
- X break;
- X
- X case 'y':
- X op_yank(&top, &bot);
- X break;
- X
- X case 'c':
- X op_change(first_char, second_char, Prenum, &top, &bot);
- X break;
- X
- X case '!':
- X specify_pipe_range(curwin, top.p_line, bot.p_line);
- X cmd_init(curwin, '!');
- X break;
- X
- X default:
- X beep(curwin);
- X }
- X operator = NOP;
- X }
- X } else {
- X /*
- X * A command that does something.
- X * Since it isn't a target, no operators need apply.
- X */
- X if (finish_op) {
- X beep(curwin);
- X operator = NOP;
- X }
- X
- X (void) (*cfunc)(first_char, second_char);
- X }
- X
- X Prenum = 0;
- X return(TRUE);
- X}
- X
- X/*
- X * Handle cursor movement commands.
- X * These are used simply to move the cursor somewhere,
- X * and also as targets for the various operators.
- X *
- X * If the return value is FALSE, the caller will complain
- X * loudly to the user and cancel any outstanding operator.
- X * The cursor should hopefully not have moved.
- X *
- X * Arguments are the first and second characters (where appropriate).
- X */
- Xstatic bool_t
- Xdo_target(c1, c2)
- Xint c1, c2;
- X{
- X bool_t skip_spaces = FALSE;
- X bool_t retval = TRUE;
- X
- X switch (c1) {
- X case 'G':
- X setpcmark(curwin);
- X do_goto((Prenum > 0) ? Prenum : MAX_LINENO);
- X skip_spaces = TRUE;
- X break;
- X
- X case 'l':
- X case ' ':
- X c1 = K_RARROW;
- X /* fall through ... */
- X case K_RARROW:
- X case 'h':
- X case K_LARROW:
- X case CTRL('H'):
- X {
- X register bool_t (*mvfunc) P((Xviwin *, bool_t));
- X register long n;
- X register long i;
- X
- X if (c1 == K_RARROW) {
- X mvfunc = one_right;
- X } else {
- X mvfunc = one_left;
- X }
- X
- X n = LDEF1PRENUM;
- X for (i = 0; i < n; i++) {
- X if (!(*mvfunc)(curwin, FALSE)) {
- X break;
- X }
- X }
- X if (i == 0) {
- X retval = FALSE;
- X } else {
- X curwin->w_set_want_col = TRUE;
- X }
- X break;
- X }
- X
- X case '-':
- X skip_spaces = TRUE;
- X /* FALL THROUGH */
- X case 'k':
- X case K_UARROW:
- X case CTRL('P'):
- X if (!oneup(curwin, LDEF1PRENUM)) {
- X retval = FALSE;
- X }
- X break;
- X
- X case '+':
- X case '\r':
- X skip_spaces = TRUE;
- X /* FALL THROUGH */
- X case '\n':
- X case 'j':
- X case K_DARROW:
- X case CTRL('N'):
- X if (!onedown(curwin, LDEF1PRENUM)) {
- X retval = FALSE;
- X }
- X break;
- X
- X /*
- X * This is a strange motion command that helps make
- X * operators more logical. It is actually implemented,
- X * but not documented in the real 'vi'. This motion
- X * command actually refers to "the current line".
- X * Commands like "dd" and "yy" are really an alternate
- X * form of "d_" and "y_". It does accept a count, so
- X * "d3_" works to delete 3 lines.
- X */
- X case '_':
- X (void) onedown(curwin, LDEF1PRENUM - 1);
- X break;
- X
- X case '|':
- X begin_line(curwin, FALSE);
- X
- X if (Prenum > 0) {
- X coladvance(curwin, LONG2INT(Prenum - 1));
- X }
- X curwin->w_curswant = Prenum - 1;
- X break;
- X
- X case '%':
- X {
- X Posn *pos = showmatch();
- X
- X if (pos == NULL) {
- X retval = FALSE;
- X } else {
- X setpcmark(curwin);
- X move_cursor(curwin, pos->p_line, pos->p_index);
- X curwin->w_set_want_col = TRUE;
- X }
- X }
- X break;
- X
- X case '$':
- X while (one_right(curwin, FALSE))
- X ;
- X curwin->w_curswant = INT_MAX;
- X /* so we stay at the end ... */
- X curwin->w_set_want_col = FALSE;
- X break;
- X
- X case '^':
- X case '0':
- X begin_line(curwin, c1 == '^');
- X break;
- X
- X case 'n':
- X case 'N':
- X curwin->w_set_want_col = TRUE;
- X (void) dosearch(curwin, "", c1);
- X break;
- X
- X case '(':
- X case ')':
- X case '{':
- X case '}':
- X case '[':
- X case ']':
- X {
- X int dir = FORWARD;
- X char *pattern;
- X Posn *newpos;
- X
- X switch (c1) {
- X case '(':
- X dir = BACKWARD;
- X /*FALLTHROUGH*/
- X case ')':
- X pattern = Ps(P_sentences);
- X break;
- X
- X case '{':
- X dir = BACKWARD;
- X /*FALLTHROUGH*/
- X case '}':
- X pattern = Ps(P_paragraphs);
- X break;
- X
- X case '[':
- X dir = BACKWARD;
- X /*FALLTHROUGH*/
- X case ']':
- X if (c1 != c2) {
- X retval = FALSE;
- X } else {
- X pattern = Ps(P_sections);
- X }
- X }
- X if (retval) {
- X curwin->w_set_want_col = TRUE;
- X newpos = find_pattern(pattern, dir, IDEF1PRENUM);
- X if (newpos != NULL) {
- X setpcmark(curwin);
- X move_cursor(curwin, newpos->p_line, newpos->p_index);
- X } else {
- X retval = FALSE;
- X }
- X }
- X break;
- X }
- X
- X case '\'':
- X case '`':
- X {
- X Posn *mark;
- X
- X mark = getmark(c2, curbuf);
- X if (mark == NULL) {
- X retval = FALSE;
- X } else {
- X Posn dest;
- X
- X /*
- X * Record posn before re-setting the mark -
- X * so that we don't accidentally side-effect
- X * the place we are moving to! What a hack.
- X */
- X dest = *mark;
- X
- X setpcmark(curwin);
- X
- X move_cursor(curwin, dest.p_line, c1 == '`' ? dest.p_index : 0);
- X if (c1 == '`') {
- X mtype = m_nonincl;
- X } else {
- X skip_spaces = TRUE;
- X }
- X }
- X break;
- X }
- X }
- X
- X if (retval && skip_spaces) {
- X begin_line(curwin, TRUE);
- X }
- X
- X return(retval);
- X}
- X
- Xstatic bool_t
- Xdo_cmd(c1, c2)
- Xint c1, c2;
- X{
- X switch (c1) {
- X case K_HELP:
- X do_help(curwin);
- X break;
- X
- X case CTRL('R'):
- X case CTRL('L'):
- X redraw_screen();
- X break;
- X
- X case CTRL('G'):
- X show_file_info(curwin);
- X break;
- X
- X case CTRL(']'): /* :ta to current identifier */
- X tagword();
- X break;
- X
- X /*
- X * Some convenient abbreviations...
- X */
- X case 'D':
- X stuff("\"%cd$", cur_yp_name);
- X break;
- X
- X case 'Y':
- X stuff("\"%c%dyy", cur_yp_name, IDEF1PRENUM);
- X break;
- X
- X case 'C':
- X stuff("\"%cc$", cur_yp_name);
- X break;
- X
- X case 'S':
- X stuff("\"%c%dcc", cur_yp_name, IDEF1PRENUM);
- X break;
- X
- X /*
- X * Operators.
- X */
- X case 'd':
- X case 'c':
- X case 'y':
- X case '>':
- X case '<':
- X case '!':
- X if (Prenum != 0)
- X opnum = Prenum;
- X startop = *curwin->w_cursor;
- X operator = c1;
- X break;
- X
- X case 'p':
- X case 'P':
- X Redo.r_mode = r_normal;
- X do_put(curwin, curwin->w_cursor, (c1 == 'p') ? FORWARD : BACKWARD,
- X cur_yp_name);
- X if (is_digit(cur_yp_name) && cur_yp_name != '0' && cur_yp_name != '9') {
- X cur_yp_name++;
- X }
- X flexclear(&Redo.r_fb);
- X (void) lformat(&Redo.r_fb, "\"%c%d%c", cur_yp_name, IDEF1PRENUM, c1);
- X break;
- X
- X case 's': /* substitute characters */
- X start_command(curwin);
- X replchars(curwin, curwin->w_cursor->p_line,
- X curwin->w_cursor->p_index, IDEF1PRENUM, "");
- X updateline(curwin);
- X Redo.r_mode = r_insert;
- X flexclear(&Redo.r_fb);
- X (void) lformat(&Redo.r_fb, "%lds", IDEF1PRENUM);
- X startinsert(FALSE);
- X break;
- X
- X case ':':
- X case '?':
- X case '/':
- X cmd_init(curwin, c1);
- X break;
- X
- X case '&':
- X (void) do_ampersand(curwin, curwin->w_cursor->p_line,
- X curwin->w_cursor->p_line, "");
- X begin_line(curwin, TRUE);
- X updateline(curwin);
- X break;
- X
- X case 'R':
- X case 'r':
- X Redo.r_mode = (c1 == 'r') ? r_replace1 : r_insert;
- X flexclear(&Redo.r_fb);
- X flexaddch(&Redo.r_fb, c1);
- X startreplace(c1);
- X break;
- X
- X case 'J':
- X if (!dojoin())
- X beep(curwin);
- X
- X Redo.r_mode = r_normal;
- X flexclear(&Redo.r_fb);
- X flexaddch(&Redo.r_fb, c1);
- X update_buffer(curbuf);
- X break;
- X
- X case K_CGRAVE: /* shorthand command */
- X#ifndef QNX
- X /*
- X * We can't use this key on QNX.
- X */
- X case CTRL('^'):
- X#endif
- X do_alt_edit(curwin);
- X break;
- X
- X case 'u':
- X case K_UNDO:
- X undo(curwin);
- X break;
- X
- X case CTRL('Z'): /* suspend editor */
- X do_suspend(curwin);
- X break;
- X
- X /*
- X * Buffer handling.
- X */
- X case CTRL('T'): /* shrink window */
- X resize_window(curwin, - IDEF1PRENUM);
- X move_cursor_to_window(curwin);
- X break;
- X
- X case CTRL('W'): /* grow window */
- X resize_window(curwin, IDEF1PRENUM);
- X break;
- X
- X case CTRL('O'): /* make window as large as possible */
- X resize_window(curwin, INT_MAX);
- X break;
- X
- X case 'g':
- X /*
- X * Find the next window that the cursor
- X * can be displayed in; i.e. at least one
- X * text row is displayed.
- X */
- X do {
- X curwin = next_window(curwin);
- X } while (curwin->w_nrows < 2);
- X curbuf = curwin->w_buffer;
- X move_cursor_to_window(curwin);
- X wind_goto(curwin);
- X break;
- X
- X case '"':
- X got_name = TRUE;
- X buffer_name = c2;
- X break;
- X
- X case '@':
- X yp_stuff_input(curwin, c2, TRUE);
- X break;
- X
- X /*
- X * Marks
- X */
- X case 'm':
- X if (!setmark(c2, curbuf, curwin->w_cursor))
- X beep(curwin);
- X break;
- X
- X case 'Z': /* write, if changed, and exit */
- X if (c2 != 'Z') {
- X beep(curwin);
- X break;
- X }
- X
- X /*
- X * Make like a ":x" command.
- X */
- X do_xit(curwin);
- X break;
- X
- X case '.':
- X /*
- X * '.', meaning redo. As opposed to '.' as a target.
- X */
- X stuff("%s", flexgetstr(&Redo.r_fb));
- X if (Redo.r_mode != r_normal) {
- X yp_stuff_input(curwin, '<', TRUE);
- X if (Redo.r_mode == r_insert) {
- X stuff("%c", ESC);
- X }
- X }
- X break;
- X
- X default:
- X beep(curwin);
- X break;
- X }
- X
- X return(FALSE);
- X}
- X
- Xstatic bool_t
- Xdo_badcmd(c1, c2)
- Xint c1, c2;
- X{
- X beep(curwin);
- X return(FALSE);
- X}
- X
- X/*
- X * Handle page motion (control-F or control-B).
- X */
- Xstatic bool_t
- Xdo_page(c1, c2)
- Xregister int c1, c2;
- X{
- X long overlap;
- X long n;
- X
- X /*
- X * First move the cursor to the top of the screen
- X * (for ^B), or to the top of the next screen (for ^F).
- X */
- X move_cursor(curwin, (c1 == CTRL('B')) ?
- X curwin->w_topline : curwin->w_botline, 0);
- X
- X /*
- X * Cursor could have moved to the lastline of the buffer,
- X * if the window is at the end of the buffer. Disallow
- X * the cursor from being outside the buffer's bounds.
- X */
- X if (curwin->w_cursor->p_line == curbuf->b_lastline) {
- X move_cursor(curwin, curbuf->b_lastline->l_prev, 0);
- X }
- X
- X /*
- X * Decide on the amount of overlap to use.
- X */
- X if (curwin->w_nrows > 10) {
- X /*
- X * At least 10 text lines in window.
- X */
- X overlap = 2;
- X } else if (curwin->w_nrows > 3) {
- X /*
- X * Between 3 and 9 text lines in window.
- X */
- X overlap = 1;
- X } else {
- X /*
- X * 1 or 2 text lines in window.
- X */
- X overlap = 0;
- X }
- X
- X /*
- X * Given the overlap, decide where to move the cursor;
- X * this will determine the new top line of the screen.
- X */
- X if (c1 == CTRL('F')) {
- X n = - overlap;
- X n += (LDEF1PRENUM - 1) * (curwin->w_nrows - overlap - 1);
- X } else {
- X n = (- LDEF1PRENUM) * (curwin->w_nrows - overlap - 1);
- X }
- X
- X if (n > 0) {
- X (void) onedown(curwin, n);
- X } else {
- X (void) oneup(curwin, -n);
- X }
- X
- X /*
- X * Redraw the screen with the cursor at the top.
- X */
- X begin_line(curwin, TRUE);
- X curwin->w_topline = curwin->w_cursor->p_line;
- X update_window(curwin);
- X
- X if (c1 == CTRL('B')) {
- X /*
- X * And move it to the bottom.
- X */
- X move_window_to_cursor(curwin);
- X cursupdate(curwin);
- X move_cursor(curwin, curwin->w_botline->l_prev, 0);
- X begin_line(curwin, TRUE);
- X }
- X
- X /*
- X * Finally, show where we are in the file.
- X */
- X show_file_info(curwin);
- X
- X return(FALSE);
- X}
- X
- Xstatic bool_t
- Xdo_scroll(c1, c2)
- Xint c1, c2;
- X{
- X switch (c1) {
- X case CTRL('D'):
- X scrollup(curwin, curwin->w_nrows / 2);
- X (void) onedown(curwin, (long) (curwin->w_nrows / 2));
- X break;
- X
- X case CTRL('U'):
- X scrolldown(curwin, curwin->w_nrows / 2);
- X (void) oneup(curwin, (long) (curwin->w_nrows / 2));
- X break;
- X
- X case CTRL('E'):
- X scrollup(curwin, (unsigned) IDEF1PRENUM);
- X break;
- X
- X case CTRL('Y'):
- X scrolldown(curwin, (unsigned) IDEF1PRENUM);
- X break;
- X }
- X
- X update_window(curwin);
- X move_cursor_to_window(curwin);
- X return(FALSE);
- X}
- X
- X/*
- X * Handle word motion ('w', 'W', 'b', 'B', 'e' or 'E').
- X */
- Xstatic bool_t
- Xdo_word(c1, c2)
- Xregister int c1, c2;
- X{
- X register Posn *(*func) P((Posn *, int, bool_t));
- X register long n;
- X register int lc;
- X register int type;
- X Posn pos;
- X
- X if (is_upper(c1)) {
- X type = 1;
- X lc = to_lower(c1);
- X } else {
- X type = 0;
- X lc = c1;
- X }
- X curwin->w_set_want_col = TRUE;
- X
- X switch (lc) {
- X case 'b':
- X func = bck_word;
- X break;
- X
- X case 'w':
- X func = fwd_word;
- X break;
- X
- X case 'e':
- X func = end_word;
- X break;
- X }
- X
- X pos = *curwin->w_cursor;
- X
- X for (n = LDEF1PRENUM; n > 0; n--) {
- X Posn *newpos;
- X bool_t skip_whites;
- X
- X /*
- X * "cw" is a special case; the whitespace after
- X * the end of the last word involved in the change
- X * does not get changed. The following code copes
- X * with this strangeness.
- X */
- X if (n == 1 && operator == 'c' && lc == 'w') {
- X skip_whites = FALSE;
- X mtype = m_incl;
- X } else {
- X skip_whites = TRUE;
- X }
- X
- X newpos = (*func)(&pos, type, skip_whites);
- X
- X if (newpos == NULL) {
- X return(FALSE);
- X }
- X
- X if (n == 1 && lc == 'w' && operator != NOP &&
- X newpos->p_line != pos.p_line) {
- X /*
- X * We are on the last word to be operated
- X * upon, and have crossed the line boundary.
- X * This should not happen, so back up to
- X * the end of the line the word is on.
- X */
- X while (dec(newpos) == mv_SAMELINE)
- X ;
- X mtype = m_incl;
- X }
- X
- X if (skip_whites == FALSE) {
- X (void) dec(newpos);
- X }
- X pos = *newpos;
- X }
- X move_cursor(curwin, pos.p_line, pos.p_index);
- X return(TRUE);
- X}
- X
- Xstatic bool_t
- Xdo_csearch(c1, c2)
- Xint c1, c2;
- X{
- X bool_t retval = TRUE;
- X Posn *pos;
- X int dir;
- X
- X switch (c1) {
- X case 'T':
- X case 't':
- X case 'F':
- X case 'f':
- X if (is_upper(c1)) {
- X dir = BACKWARD;
- X c1 = to_lower(c1);
- X } else {
- X dir = FORWARD;
- X }
- X
- X curwin->w_set_want_col = TRUE;
- X pos = searchc(c2, dir, (c1 == 't'), IDEF1PRENUM);
- X break;
- X
- X case ',':
- X case ';':
- X /*
- X * This should be FALSE for a backward motion.
- X * How do we know it's a backward motion?
- X *
- X * Fix it later.
- X */
- X mtype = m_incl;
- X curwin->w_set_want_col = TRUE;
- X pos = crepsearch(curbuf, c1 == ',', IDEF1PRENUM);
- X break;
- X }
- X if (pos == NULL) {
- X retval = FALSE;
- X } else {
- X move_cursor(curwin, pos->p_line, pos->p_index);
- X }
- X return(retval);
- X}
- X
- X/*
- X * Handle adjust window command ('z').
- X */
- Xstatic bool_t
- Xdo_z(c1, c2)
- Xint c1, c2;
- X{
- X Line *lp;
- X int l;
- X int znum;
- X
- X switch (c2) {
- X case '\n': /* put cursor at top of screen */
- X case '\r':
- X znum = 1;
- X break;
- X
- X case '.': /* put cursor in middle of screen */
- X znum = curwin->w_nrows / 2;
- X break;
- X
- X case '-': /* put cursor at bottom of screen */
- X znum = curwin->w_nrows - 1;
- X break;
- X
- X default:
- X return(FALSE);
- X }
- X
- X if (Prenum > 0) {
- X do_goto(Prenum);
- X }
- X lp = curwin->w_cursor->p_line;
- X for (l = 0; l < znum && lp != curbuf->b_line0; ) {
- X l += plines(curwin, lp);
- X curwin->w_topline = lp;
- X lp = lp->l_prev;
- X }
- X cursupdate(curwin);
- X update_window(curwin);
- X
- X return(TRUE);
- X}
- X
- X/*
- X * Handle character delete commands ('x' or 'X').
- X */
- Xstatic bool_t
- Xdo_x(c1, c2)
- Xint c1, c2;
- X{
- X Posn *curp;
- X Posn lastpos;
- X int nchars;
- X int i;
- X
- X nchars = IDEF1PRENUM;
- X Redo.r_mode = r_normal;
- X flexclear(&Redo.r_fb);
- X (void) lformat(&Redo.r_fb, "%d%c", nchars, c1);
- X curp = curwin->w_cursor;
- X
- X if (c1 == 'X') {
- X for (i = 0; i < nchars && one_left(curwin, FALSE); i++)
- X ;
- X nchars = i;
- X if (nchars == 0) {
- X beep(curwin);
- X return(TRUE);
- X }
- X
- X } else /* c1 == 'x' */ {
- X char *line;
- X
- X /*
- X * Ensure that nchars is not too big.
- X */
- X line = curp->p_line->l_text + curp->p_index;
- X for (i = 0; i < nchars && line[i] != '\0'; i++)
- X ;
- X nchars = i;
- X
- X if (curp->p_line->l_text[0] == '\0') {
- X /*
- X * Can't do it on a blank line.
- X */
- X beep(curwin);
- X return(TRUE);
- X }
- X }
- X
- X lastpos.p_line = curp->p_line;
- X lastpos.p_index = curp->p_index + nchars - 1;
- X yp_push_deleted();
- X (void) do_yank(curbuf, curp, &lastpos, TRUE, cur_yp_name);
- X replchars(curwin, curp->p_line, curp->p_index, nchars, "");
- X if (curp->p_line->l_text[curp->p_index] == '\0') {
- X (void) one_left(curwin, FALSE);
- X }
- X updateline(curwin);
- X return(TRUE);
- X}
- X
- X/*
- X * Handle home ('H') end of page ('L') and middle line ('M') motion commands.
- X */
- Xstatic bool_t
- Xdo_HLM(c1, c2)
- Xint c1, c2;
- X{
- X register bool_t (*mvfunc) P((Xviwin *, long));
- X register long n;
- X
- X if (c1 == K_HOME) {
- X c1 = 'H';
- X }
- X
- X /*
- X * Silly to specify a number before 'H' or 'L'
- X * which would move us off the screen.
- X */
- X if (Prenum >= curwin->w_nrows) {
- X return(FALSE);
- X }
- X
- X move_cursor(curwin, (c1 == 'L') ? curwin->w_botline->l_prev :
- X curwin->w_topline, 0);
- X
- X switch (c1) {
- X case 'H':
- X mvfunc = onedown;
- X n = Prenum - 1;
- X break;
- X case 'L':
- X mvfunc = oneup;
- X n = Prenum - 1;
- X break;
- X case 'M':
- X mvfunc = onedown;
- X n = (long) (curwin->w_nrows - 1) / 2;
- X }
- X
- X (void) (*mvfunc)(curwin, n);
- X begin_line(curwin, TRUE);
- X return(TRUE);
- X}
- X
- X/*
- X * Handle '~' and CTRL('_') commands.
- X */
- Xstatic bool_t
- Xdo_rchar(c1, c2)
- Xint c1, c2;
- X{
- X Posn *cp;
- X char *tp;
- X int c;
- X char newc[2];
- X
- X Redo.r_mode = r_normal;
- X flexclear(&Redo.r_fb);
- X flexaddch(&Redo.r_fb, c1);
- X cp = curwin->w_cursor;
- X tp = cp->p_line->l_text;
- X if (tp[0] == '\0') {
- X /*
- X * Can't do it on a blank line.
- X */
- X beep(curwin);
- X return(FALSE);
- X }
- X c = tp[cp->p_index];
- X
- X switch (c1) {
- X case '~':
- X newc[0] = is_alpha(c) ?
- X is_lower(c) ?
- X to_upper(c)
- X : to_lower(c)
- X : c;
- X break;
- X case CTRL('_'): /* flip top bit */
- X#ifdef TOP_BIT
- X newc[0] = c ^ TOP_BIT;
- X if (newc[0] == '\0') {
- X newc[0] = c;
- X }
- X#else /* not TOP_BIT */
- X beep(curwin);
- X return(FALSE);
- X#endif /* TOP_BIT */
- X }
- X
- X newc[1] = '\0';
- X replchars(curwin, cp->p_line, cp->p_index, 1, newc);
- X updateline(curwin);
- X (void) one_right(curwin, FALSE);
- X return(FALSE);
- X}
- X
- X/*
- X * Handle commands which just go into insert mode
- X * ('i', 'a', 'I', 'A', 'o', 'O').
- X */
- Xstatic bool_t
- Xdo_ins(c1, c2)
- Xint c1, c2;
- X{
- X bool_t startpos = TRUE; /* FALSE means start position moved */
- X
- X if (!start_command(curwin)) {
- X return(FALSE);
- X }
- X
- X Redo.r_mode = r_insert;
- X flexclear(&Redo.r_fb);
- X flexaddch(&Redo.r_fb, c1);
- X
- X switch (c1) {
- X case 'o':
- X case 'O':
- X if (((c1 == 'o') ? openfwd(FALSE) : openbwd()) == FALSE) {
- X beep(curwin);
- X end_command(curwin);
- X return(FALSE);
- X }
- X break;
- X
- X case 'I':
- X begin_line(curwin, TRUE);
- X startpos = FALSE;
- X break;
- X
- X case 'A':
- X while (one_right(curwin, TRUE))
- X ;
- X startpos = FALSE;
- X break;
- X
- X case 'a':
- X /*
- X * 'a' works just like an 'i' on the next character.
- X */
- X (void) one_right(curwin, TRUE);
- X startpos = FALSE;
- X }
- X
- X startinsert(startpos);
- X return(FALSE);
- X}
- X
- X/*
- X * Handle a shift operation. The prenum and operator/operands are
- X * passed, along with the first and last positions to be shifted.
- X */
- Xstatic void
- Xop_shift(op, c1, c2, num, top, bottom)
- Xint op;
- Xint c1, c2;
- Xlong num;
- XPosn *top;
- XPosn *bottom;
- X{
- X /*
- X * Do the shift.
- X */
- X tabinout(op, top->p_line, bottom->p_line);
- X
- X /*
- X * Put cursor on first non-white of line; this is good if the
- X * cursor is in the range of lines specified, which is normally
- X * will be.
- X */
- X begin_line(curwin, TRUE);
- X update_buffer(curbuf);
- X
- X /*
- X * Construct redo buffer.
- X */
- X Redo.r_mode = r_normal;
- X flexclear(&Redo.r_fb);
- X if (num != 0) {
- X (void) lformat(&Redo.r_fb, "%c%ld%c%c", op, num, c1, c2);
- X } else {
- X (void) lformat(&Redo.r_fb, "%c%c%c", op, c1, c2);
- X }
- X}
- X
- X/*
- X * op_delete - handle a delete operation
- X * The characters "c1" and "c2" are the target character and
- X * its argument (if it takes one) respectively. E.g. 'f', '/'.
- X * The "num" argument is the numeric prefix.
- X */
- Xstatic void
- Xop_delete(c1, c2, num, top, bottom)
- Xint c1, c2;
- Xlong num;
- XPosn *top;
- XPosn *bottom;
- X{
- X long nlines;
- X int n;
- X
- X /*
- X * If the target is non-inclusive, move back a character.
- X * We assume it is okay to do this, and it seems to work,
- X * so no checking is performed at the moment.
- X */
- X if (mtype == m_nonincl) {
- X (void) dec(bottom);
- X }
- X
- X nlines = cntllines(top->p_line, bottom->p_line);
- X
- X /*
- X * Do a yank of whatever we're about to delete. If there's too much
- X * stuff to fit in the yank buffer, disallow the delete, since we
- X * probably wouldn't have enough memory to do it anyway.
- X */
- X yp_push_deleted();
- X if (!do_yank(curbuf, top, bottom, (mtype != m_line), cur_yp_name)) {
- X show_error(curwin, "Not enough memory to perform delete");
- X return;
- X }
- X
- X if (mtype == m_line) {
- X /*
- X * Put the cursor at the start of the section to be deleted
- X * so that repllines will correctly update it and the screen
- X * pointer, and update the screen.
- X */
- X move_cursor(curwin, top->p_line, 0);
- X repllines(curwin, top->p_line, nlines, (Line *) NULL);
- X begin_line(curwin, TRUE);
- X } else {
- X /*
- X * After a char-based delete, the cursor should always be
- X * on the character following the last character of the
- X * section being deleted. The easiest way to achieve this
- X * is to put it on the character before the section to be
- X * deleted (which will not be affected), and then move one
- X * place right afterwards.
- X */
- X move_cursor(curwin, top->p_line,
- X top->p_index - ((top->p_index > 0) ? 1 : 0));
- X
- X if (top->p_line == bottom->p_line) {
- X /*
- X * Delete characters within line.
- X */
- X n = (bottom->p_index - top->p_index) + 1;
- X replchars(curwin, top->p_line, top->p_index, n, "");
- X } else {
- X /*
- X * Character-based delete between lines.
- X * So we actually have to do three deletes;
- X * one to delete to the end of the top line,
- X * one to delete the intervening lines, and
- X * one to delete up to the target position.
- X */
- X if (!start_command(curwin)) {
- X return;
- X }
- X
- X /*
- X * First delete part of the last line.
- X */
- X replchars(curwin, bottom->p_line, 0, bottom->p_index + 1, "");
- X
- X /*
- X * Now replace the rest of the top line with the
- X * remainder of the bottom line.
- X */
- X replchars(curwin, top->p_line, top->p_index, INT_MAX,
- X bottom->p_line->l_text);
- X
- X /*
- X * Finally, delete all lines from (top + 1) to bot,
- X * inclusive.
- X */
- X repllines(curwin, top->p_line->l_next,
- X cntllines(top->p_line, bottom->p_line) - 1,
- X (Line *) NULL);
- X
- X end_command(curwin);
- X }
- X
- X if (top->p_index > 0) {
- X (void) one_right(curwin, FALSE);
- X }
- X }
- X
- X /*
- X * Construct redo buffer.
- X */
- X Redo.r_mode = r_normal;
- X flexclear(&Redo.r_fb);
- X if (num != 0) {
- X (void) lformat(&Redo.r_fb, "d%ld%c%c", num, c1, c2);
- X } else {
- X (void) lformat(&Redo.r_fb, "d%c%c", c1, c2);
- X }
- X
- X if (mtype != m_line && nlines == 1) {
- X updateline(curwin);
- X } else {
- X update_buffer(curbuf);
- X }
- X}
- X
- X/*
- X * op_change - handle a change operation
- X */
- Xstatic void
- Xop_change(c1, c2, num, top, bottom)
- Xint c1, c2;
- Xlong num;
- XPosn *top;
- XPosn *bottom;
- X{
- X bool_t doappend; /* true if we should do append, not insert */
- X
- X /*
- X * Start the command here so the initial delete gets
- X * included in the meta-command and hence undo will
- X * work properly.
- X */
- X if (!start_command(curwin)) {
- X return;
- X }
- X
- X if (mtype == m_line) {
- X long nlines;
- X Line *lp;
- X
- X /*
- X * This is a bit awkward ... for a line-based change, we don't
- X * actually delete the whole range of lines, but instead leave
- X * the first line in place and delete its text after the cursor
- X * position. However, yanking the whole thing is probably okay.
- X */
- X yp_push_deleted();
- X if (!do_yank(curbuf, top, bottom, FALSE, cur_yp_name)) {
- X show_error(curwin, "Not enough memory to perform change");
- X return;
- X }
- X
- X lp = top->p_line;
- X
- X nlines = cntllines(lp, bottom->p_line);
- X if (nlines > 1) {
- X repllines(curwin, lp->l_next, nlines - 1, (Line *) NULL);
- X }
- X
- X move_cursor(curwin, lp, 0);
- X
- X /*
- X * This is not right; it won't do the right thing when
- X * the cursor is in the whitespace of an indented line.
- X * However, it will do for the moment.
- X */
- X begin_line(curwin, TRUE);
- X
- X replchars(curwin, lp, curwin->w_cursor->p_index,
- X strlen(lp->l_text), "");
- X update_buffer(curbuf);
- X } else {
- X /*
- X * A character-based change really is just a delete and an insert.
- X * So use the deletion code to make things easier.
- X */
- X doappend = (mtype == m_incl) && endofline(bottom);
- X
- X op_delete(c1, c2, num, top, bottom);
- X
- X if (doappend) {
- X (void) one_right(curwin, TRUE);
- X }
- X }
- X
- X Redo.r_mode = r_insert;
- X flexclear(&Redo.r_fb);
- X if (num != 0) {
- X (void) lformat(&Redo.r_fb, "c%ld%c%c", num, c1, c2);
- X } else {
- X (void) lformat(&Redo.r_fb, "c%c%c", c1, c2);
- X }
- X
- X startinsert(FALSE);
- X}
- X
- Xstatic void
- Xop_yank(top, bottom)
- XPosn *top;
- XPosn *bottom;
- X{
- X long nlines;
- X
- X /*
- X * Report on the number of lines yanked.
- X */
- X nlines = cntllines(top->p_line, bottom->p_line);
- X
- X if (nlines > Pn(P_report)) {
- X show_message(curwin, "%ld lines yanked", nlines);
- X }
- X
- X /*
- X * If the target is non-inclusive and character-based,
- X * reduce the final position by one.
- X */
- X if (mtype == m_nonincl) {
- X (void) dec(bottom);
- X }
- X
- X (void) do_yank(curbuf, top, bottom, (mtype != m_line), cur_yp_name);
- X}
- X
- Xstatic bool_t
- Xdojoin()
- X{
- X register Posn *curr_pos; /* cursor position (abbreviation) */
- X register Line *curr_line; /* line we started the join on */
- X char *nextline; /* text of subsequent line */
- X int join_index; /* index of start of new text section */
- X int size1; /* size of the first line */
- X int size2; /* size of the second line */
- X
- X curr_pos = curwin->w_cursor;
- X curr_line = curr_pos->p_line;
- X
- X /*
- X * If we are on the last line, we can't join.
- X */
- X if (curr_line->l_next == curbuf->b_lastline)
- X return(FALSE);
- X
- X if (!start_command(curwin)) {
- X return(FALSE);
- X }
- X
- X /*
- X * Move cursor to end of line, and find out
- X * exactly where we will place the new text.
- X */
- X while (one_right(curwin, FALSE))
- X ;
- X join_index = curr_pos->p_index;
- X if (curr_line->l_text[join_index] != '\0')
- X join_index += 1;
- X
- X /*
- X * Copy the text of the next line after the end of the
- X * current line, but don't copy any initial whitespace.
- X * Then delete the following line.
- X */
- X nextline = curr_line->l_next->l_text;
- X while (*nextline == ' ' || *nextline == '\t')
- X nextline++;
- X size1 = strlen(curr_line->l_text);
- X size2 = strlen(nextline);
- X
- X replchars(curwin, curr_line, join_index, 0, nextline);
- X repllines(curwin, curr_line->l_next, (long) 1, (Line *) NULL);
- X
- X if (size1 != 0 && size2 != 0) {
- X /*
- X * If there is no whitespace on this line,
- X * insert a single space.
- X */
- X if (gchar(curr_pos) != ' ' && gchar(curr_pos) != '\t') {
- X replchars(curwin, curr_line, curr_pos->p_index + 1, 0, " ");
- X }
- X
- X /*
- X * Make sure the cursor sits on the right character.
- X */
- X (void) one_right(curwin, FALSE);
- X }
- X
- X end_command(curwin);
- X
- X update_buffer(curbuf);
- X
- X return(TRUE);
- X}
- END_OF_FILE
- if test 41946 -ne `wc -c <'xvi/src/normal.c'`; then
- echo shar: \"'xvi/src/normal.c'\" unpacked with wrong size!
- fi
- # end of 'xvi/src/normal.c'
- fi
- echo shar: End of archive 3 \(of 18\).
- cp /dev/null ark3isdone
- MISSING=""
- for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked all 18 archives.
- rm -f ark[1-9]isdone ark[1-9][0-9]isdone
- else
- echo You still must unpack the following archives:
- echo " " ${MISSING}
- fi
- exit 0
- exit 0 # Just in case...
-