3963 lines
		
	
	
		
			112 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			3963 lines
		
	
	
		
			112 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* vi: set sw=8 ts=8: */
 | 
						||
/*
 | 
						||
 * tiny vi.c: A small 'vi' clone
 | 
						||
 * Copyright (C) 2000, 2001 Sterling Huxley <sterling@europa.com>
 | 
						||
 *
 | 
						||
 *  This program is free software; you can redistribute it and/or modify
 | 
						||
 *  it under the terms of the GNU General Public License as published by
 | 
						||
 *  the Free Software Foundation; either version 2 of the License, or
 | 
						||
 *  (at your option) any later version.
 | 
						||
 *
 | 
						||
 *  This program is distributed in the hope that it will be useful,
 | 
						||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						||
 *  GNU General Public License for more details.
 | 
						||
 *
 | 
						||
 *  You should have received a copy of the GNU General Public License
 | 
						||
 *  along with this program; if not, write to the Free Software
 | 
						||
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
						||
 */
 | 
						||
 | 
						||
static const char vi_Version[] =
 | 
						||
	"$Id: vi.c,v 1.20 2001/12/20 23:12:47 kraai Exp $";
 | 
						||
 | 
						||
/*
 | 
						||
 * To compile for standalone use:
 | 
						||
 *	gcc -Wall -Os -s -DSTANDALONE -o vi vi.c
 | 
						||
 *	  or
 | 
						||
 *	gcc -Wall -Os -s -DSTANDALONE -DCONFIG_FEATURE_VI_CRASHME -o vi vi.c		# include testing features
 | 
						||
 *	strip vi
 | 
						||
 */
 | 
						||
 | 
						||
/*
 | 
						||
 * Things To Do:
 | 
						||
 *	EXINIT
 | 
						||
 *	$HOME/.exrc  and  ./.exrc
 | 
						||
 *	add magic to search	/foo.*bar
 | 
						||
 *	add :help command
 | 
						||
 *	:map macros
 | 
						||
 *	how about mode lines:   vi: set sw=8 ts=8:
 | 
						||
 *	if mark[] values were line numbers rather than pointers
 | 
						||
 *	   it would be easier to change the mark when add/delete lines
 | 
						||
 *	More intelligence in refresh()
 | 
						||
 *	":r !cmd"  and  "!cmd"  to filter text through an external command
 | 
						||
 *	A true "undo" facility
 | 
						||
 *	An "ex" line oriented mode- maybe using "cmdedit"
 | 
						||
 */
 | 
						||
 | 
						||
//----  Feature --------------  Bytes to immplement
 | 
						||
#ifdef STANDALONE
 | 
						||
#define vi_main			main
 | 
						||
#define CONFIG_FEATURE_VI_COLON	// 4288
 | 
						||
#define CONFIG_FEATURE_VI_YANKMARK	// 1408
 | 
						||
#define CONFIG_FEATURE_VI_SEARCH	// 1088
 | 
						||
#define CONFIG_FEATURE_VI_USE_SIGNALS	// 1056
 | 
						||
#define CONFIG_FEATURE_VI_DOT_CMD	//  576
 | 
						||
#define CONFIG_FEATURE_VI_READONLY	//  128
 | 
						||
#define CONFIG_FEATURE_VI_SETOPTS	//  576
 | 
						||
#define CONFIG_FEATURE_VI_SET	//  224
 | 
						||
#define CONFIG_FEATURE_VI_WIN_RESIZE	//  256  WIN_RESIZE
 | 
						||
// To test editor using CRASHME:
 | 
						||
//    vi -C filename
 | 
						||
// To stop testing, wait until all to text[] is deleted, or
 | 
						||
//    Ctrl-Z and kill -9 %1
 | 
						||
// while in the editor Ctrl-T will toggle the crashme function on and off.
 | 
						||
//#define CONFIG_FEATURE_VI_CRASHME		// randomly pick commands to execute
 | 
						||
#endif							/* STANDALONE */
 | 
						||
 | 
						||
#include <stdio.h>
 | 
						||
#include <stdlib.h>
 | 
						||
#include <string.h>
 | 
						||
#include <termios.h>
 | 
						||
#include <unistd.h>
 | 
						||
#include <sys/ioctl.h>
 | 
						||
#include <sys/time.h>
 | 
						||
#include <sys/types.h>
 | 
						||
#include <sys/stat.h>
 | 
						||
#include <time.h>
 | 
						||
#include <fcntl.h>
 | 
						||
#include <signal.h>
 | 
						||
#include <setjmp.h>
 | 
						||
#include <regex.h>
 | 
						||
#include <ctype.h>
 | 
						||
#include <assert.h>
 | 
						||
#include <errno.h>
 | 
						||
#include <stdarg.h>
 | 
						||
#ifndef STANDALONE
 | 
						||
#include "busybox.h"
 | 
						||
#endif							/* STANDALONE */
 | 
						||
 | 
						||
#ifndef TRUE
 | 
						||
#define TRUE			((int)1)
 | 
						||
#define FALSE			((int)0)
 | 
						||
#endif							/* TRUE */
 | 
						||
#define MAX_SCR_COLS		BUFSIZ
 | 
						||
 | 
						||
// Misc. non-Ascii keys that report an escape sequence
 | 
						||
#define VI_K_UP			128	// cursor key Up
 | 
						||
#define VI_K_DOWN		129	// cursor key Down
 | 
						||
#define VI_K_RIGHT		130	// Cursor Key Right
 | 
						||
#define VI_K_LEFT		131	// cursor key Left
 | 
						||
#define VI_K_HOME		132	// Cursor Key Home
 | 
						||
#define VI_K_END		133	// Cursor Key End
 | 
						||
#define VI_K_INSERT		134	// Cursor Key Insert
 | 
						||
#define VI_K_PAGEUP		135	// Cursor Key Page Up
 | 
						||
#define VI_K_PAGEDOWN		136	// Cursor Key Page Down
 | 
						||
#define VI_K_FUN1		137	// Function Key F1
 | 
						||
#define VI_K_FUN2		138	// Function Key F2
 | 
						||
#define VI_K_FUN3		139	// Function Key F3
 | 
						||
#define VI_K_FUN4		140	// Function Key F4
 | 
						||
#define VI_K_FUN5		141	// Function Key F5
 | 
						||
#define VI_K_FUN6		142	// Function Key F6
 | 
						||
#define VI_K_FUN7		143	// Function Key F7
 | 
						||
#define VI_K_FUN8		144	// Function Key F8
 | 
						||
#define VI_K_FUN9		145	// Function Key F9
 | 
						||
#define VI_K_FUN10		146	// Function Key F10
 | 
						||
#define VI_K_FUN11		147	// Function Key F11
 | 
						||
#define VI_K_FUN12		148	// Function Key F12
 | 
						||
 | 
						||
static const int YANKONLY = FALSE;
 | 
						||
static const int YANKDEL = TRUE;
 | 
						||
static const int FORWARD = 1;	// code depends on "1"  for array index
 | 
						||
static const int BACK = -1;	// code depends on "-1" for array index
 | 
						||
static const int LIMITED = 0;	// how much of text[] in char_search
 | 
						||
static const int FULL = 1;	// how much of text[] in char_search
 | 
						||
 | 
						||
static const int S_BEFORE_WS = 1;	// used in skip_thing() for moving "dot"
 | 
						||
static const int S_TO_WS = 2;		// used in skip_thing() for moving "dot"
 | 
						||
static const int S_OVER_WS = 3;		// used in skip_thing() for moving "dot"
 | 
						||
static const int S_END_PUNCT = 4;	// used in skip_thing() for moving "dot"
 | 
						||
static const int S_END_ALNUM = 5;	// used in skip_thing() for moving "dot"
 | 
						||
 | 
						||
typedef unsigned char Byte;
 | 
						||
 | 
						||
 | 
						||
static int editing;		// >0 while we are editing a file
 | 
						||
static int cmd_mode;		// 0=command  1=insert
 | 
						||
static int file_modified;	// buffer contents changed
 | 
						||
static int err_method;		// indicate error with beep or flash
 | 
						||
static int fn_start;		// index of first cmd line file name
 | 
						||
static int save_argc;		// how many file names on cmd line
 | 
						||
static int cmdcnt;		// repetition count
 | 
						||
static fd_set rfds;		// use select() for small sleeps
 | 
						||
static struct timeval tv;	// use select() for small sleeps
 | 
						||
static char erase_char;		// the users erase character
 | 
						||
static int rows, columns;	// the terminal screen is this size
 | 
						||
static int crow, ccol, offset;	// cursor is on Crow x Ccol with Horz Ofset
 | 
						||
static char *SOs, *SOn;		// terminal standout start/normal ESC sequence
 | 
						||
static char *bell;		// terminal bell sequence
 | 
						||
static char *Ceol, *Ceos;	// Clear-end-of-line and Clear-end-of-screen ESC sequence
 | 
						||
static char *CMrc;		// Cursor motion arbitrary destination ESC sequence
 | 
						||
static char *CMup, *CMdown;	// Cursor motion up and down ESC sequence
 | 
						||
static Byte *status_buffer;	// mesages to the user
 | 
						||
static Byte last_input_char;	// last char read from user
 | 
						||
static Byte last_forward_char;	// last char searched for with 'f'
 | 
						||
static Byte *cfn;		// previous, current, and next file name
 | 
						||
static Byte *text, *end, *textend;	// pointers to the user data in memory
 | 
						||
static Byte *screen;		// pointer to the virtual screen buffer
 | 
						||
static int screensize;		//            and its size
 | 
						||
static Byte *screenbegin;	// index into text[], of top line on the screen
 | 
						||
static Byte *dot;		// where all the action takes place
 | 
						||
static int tabstop;
 | 
						||
static struct termios term_orig, term_vi;	// remember what the cooked mode was
 | 
						||
 | 
						||
#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
 | 
						||
static int last_row;		// where the cursor was last moved to
 | 
						||
#endif							/* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
 | 
						||
#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
 | 
						||
static jmp_buf restart;		// catch_sig()
 | 
						||
#endif							/* CONFIG_FEATURE_VI_USE_SIGNALS */
 | 
						||
#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
 | 
						||
static struct winsize winsize;	// remember the window size
 | 
						||
#endif							/* CONFIG_FEATURE_VI_WIN_RESIZE */
 | 
						||
#ifdef CONFIG_FEATURE_VI_DOT_CMD
 | 
						||
static int adding2q;		// are we currently adding user input to q
 | 
						||
static Byte *last_modifying_cmd;	// last modifying cmd for "."
 | 
						||
static Byte *ioq, *ioq_start;	// pointer to string for get_one_char to "read"
 | 
						||
#endif							/* CONFIG_FEATURE_VI_DOT_CMD */
 | 
						||
#if	defined(CONFIG_FEATURE_VI_DOT_CMD) || defined(CONFIG_FEATURE_VI_YANKMARK)
 | 
						||
static Byte *modifying_cmds;	// cmds that modify text[]
 | 
						||
#endif							/* CONFIG_FEATURE_VI_DOT_CMD || CONFIG_FEATURE_VI_YANKMARK */
 | 
						||
#ifdef CONFIG_FEATURE_VI_READONLY
 | 
						||
static int vi_readonly, readonly;
 | 
						||
#endif							/* CONFIG_FEATURE_VI_READONLY */
 | 
						||
#ifdef CONFIG_FEATURE_VI_SETOPTS
 | 
						||
static int autoindent;
 | 
						||
static int showmatch;
 | 
						||
static int ignorecase;
 | 
						||
#endif							/* CONFIG_FEATURE_VI_SETOPTS */
 | 
						||
#ifdef CONFIG_FEATURE_VI_YANKMARK
 | 
						||
static Byte *reg[28];		// named register a-z, "D", and "U" 0-25,26,27
 | 
						||
static int YDreg, Ureg;		// default delete register and orig line for "U"
 | 
						||
static Byte *mark[28];		// user marks points somewhere in text[]-  a-z and previous context ''
 | 
						||
static Byte *context_start, *context_end;
 | 
						||
#endif							/* CONFIG_FEATURE_VI_YANKMARK */
 | 
						||
#ifdef CONFIG_FEATURE_VI_SEARCH
 | 
						||
static Byte *last_search_pattern;	// last pattern from a '/' or '?' search
 | 
						||
#endif							/* CONFIG_FEATURE_VI_SEARCH */
 | 
						||
 | 
						||
 | 
						||
static void edit_file(Byte *);	// edit one file
 | 
						||
static void do_cmd(Byte);	// execute a command
 | 
						||
static void sync_cursor(Byte *, int *, int *);	// synchronize the screen cursor to dot
 | 
						||
static Byte *begin_line(Byte *);	// return pointer to cur line B-o-l
 | 
						||
static Byte *end_line(Byte *);	// return pointer to cur line E-o-l
 | 
						||
static Byte *dollar_line(Byte *);	// return pointer to just before NL
 | 
						||
static Byte *prev_line(Byte *);	// return pointer to prev line B-o-l
 | 
						||
static Byte *next_line(Byte *);	// return pointer to next line B-o-l
 | 
						||
static Byte *end_screen(void);	// get pointer to last char on screen
 | 
						||
static int count_lines(Byte *, Byte *);	// count line from start to stop
 | 
						||
static Byte *find_line(int);	// find begining of line #li
 | 
						||
static Byte *move_to_col(Byte *, int);	// move "p" to column l
 | 
						||
static int isblnk(Byte);	// is the char a blank or tab
 | 
						||
static void dot_left(void);	// move dot left- dont leave line
 | 
						||
static void dot_right(void);	// move dot right- dont leave line
 | 
						||
static void dot_begin(void);	// move dot to B-o-l
 | 
						||
static void dot_end(void);	// move dot to E-o-l
 | 
						||
static void dot_next(void);	// move dot to next line B-o-l
 | 
						||
static void dot_prev(void);	// move dot to prev line B-o-l
 | 
						||
static void dot_scroll(int, int);	// move the screen up or down
 | 
						||
static void dot_skip_over_ws(void);	// move dot pat WS
 | 
						||
static void dot_delete(void);	// delete the char at 'dot'
 | 
						||
static Byte *bound_dot(Byte *);	// make sure  text[0] <= P < "end"
 | 
						||
static Byte *new_screen(int, int);	// malloc virtual screen memory
 | 
						||
static Byte *new_text(int);	// malloc memory for text[] buffer
 | 
						||
static Byte *char_insert(Byte *, Byte);	// insert the char c at 'p'
 | 
						||
static Byte *stupid_insert(Byte *, Byte);	// stupidly insert the char c at 'p'
 | 
						||
static Byte find_range(Byte **, Byte **, Byte);	// return pointers for an object
 | 
						||
static int st_test(Byte *, int, int, Byte *);	// helper for skip_thing()
 | 
						||
static Byte *skip_thing(Byte *, int, int, int);	// skip some object
 | 
						||
static Byte *find_pair(Byte *, Byte);	// find matching pair ()  []  {}
 | 
						||
static Byte *text_hole_delete(Byte *, Byte *);	// at "p", delete a 'size' byte hole
 | 
						||
static Byte *text_hole_make(Byte *, int);	// at "p", make a 'size' byte hole
 | 
						||
static Byte *yank_delete(Byte *, Byte *, int, int);	// yank text[] into register then delete
 | 
						||
static void show_help(void);	// display some help info
 | 
						||
static void print_literal(Byte *, Byte *);	// copy s to buf, convert unprintable
 | 
						||
static void rawmode(void);	// set "raw" mode on tty
 | 
						||
static void cookmode(void);	// return to "cooked" mode on tty
 | 
						||
static int mysleep(int);	// sleep for 'h' 1/100 seconds
 | 
						||
static Byte readit(void);	// read (maybe cursor) key from stdin
 | 
						||
static Byte get_one_char(void);	// read 1 char from stdin
 | 
						||
static int file_size(Byte *);	// what is the byte size of "fn"
 | 
						||
static int file_insert(Byte *, Byte *, int);
 | 
						||
static int file_write(Byte *, Byte *, Byte *);
 | 
						||
static void place_cursor(int, int, int);
 | 
						||
static void screen_erase(void);
 | 
						||
static void clear_to_eol(void);
 | 
						||
static void clear_to_eos(void);
 | 
						||
static void standout_start(void);	// send "start reverse video" sequence
 | 
						||
static void standout_end(void);	// send "end reverse video" sequence
 | 
						||
static void flash(int);		// flash the terminal screen
 | 
						||
static void beep(void);		// beep the terminal
 | 
						||
static void indicate_error(char);	// use flash or beep to indicate error
 | 
						||
static void show_status_line(void);	// put a message on the bottom line
 | 
						||
static void psb(char *, ...);	// Print Status Buf
 | 
						||
static void psbs(char *, ...);	// Print Status Buf in standout mode
 | 
						||
static void ni(Byte *);		// display messages
 | 
						||
static void edit_status(void);	// show file status on status line
 | 
						||
static void redraw(int);	// force a full screen refresh
 | 
						||
static void format_line(Byte*, Byte*, int);
 | 
						||
static void refresh(int);	// update the terminal from screen[]
 | 
						||
 | 
						||
#ifdef CONFIG_FEATURE_VI_SEARCH
 | 
						||
static Byte *char_search(Byte *, Byte *, int, int);	// search for pattern starting at p
 | 
						||
static int mycmp(Byte *, Byte *, int);	// string cmp based in "ignorecase"
 | 
						||
#endif							/* CONFIG_FEATURE_VI_SEARCH */
 | 
						||
#ifdef CONFIG_FEATURE_VI_COLON
 | 
						||
static void Hit_Return(void);
 | 
						||
static Byte *get_one_address(Byte *, int *);	// get colon addr, if present
 | 
						||
static Byte *get_address(Byte *, int *, int *);	// get two colon addrs, if present
 | 
						||
static void colon(Byte *);	// execute the "colon" mode cmds
 | 
						||
#endif							/* CONFIG_FEATURE_VI_COLON */
 | 
						||
static Byte *get_input_line(Byte *);	// get input line- use "status line"
 | 
						||
#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
 | 
						||
static void winch_sig(int);	// catch window size changes
 | 
						||
static void suspend_sig(int);	// catch ctrl-Z
 | 
						||
static void alarm_sig(int);	// catch alarm time-outs
 | 
						||
static void catch_sig(int);	// catch ctrl-C
 | 
						||
static void core_sig(int);	// catch a core dump signal
 | 
						||
#endif							/* CONFIG_FEATURE_VI_USE_SIGNALS */
 | 
						||
#ifdef CONFIG_FEATURE_VI_DOT_CMD
 | 
						||
static void start_new_cmd_q(Byte);	// new queue for command
 | 
						||
static void end_cmd_q(void);	// stop saving input chars
 | 
						||
#else							/* CONFIG_FEATURE_VI_DOT_CMD */
 | 
						||
#define end_cmd_q()
 | 
						||
#endif							/* CONFIG_FEATURE_VI_DOT_CMD */
 | 
						||
#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
 | 
						||
static void window_size_get(int);	// find out what size the window is
 | 
						||
#endif							/* CONFIG_FEATURE_VI_WIN_RESIZE */
 | 
						||
#ifdef CONFIG_FEATURE_VI_SETOPTS
 | 
						||
static void showmatching(Byte *);	// show the matching pair ()  []  {}
 | 
						||
#endif							/* CONFIG_FEATURE_VI_SETOPTS */
 | 
						||
#if defined(CONFIG_FEATURE_VI_YANKMARK) || defined(CONFIG_FEATURE_VI_COLON) || defined(CONFIG_FEATURE_VI_CRASHME)
 | 
						||
static Byte *string_insert(Byte *, Byte *);	// insert the string at 'p'
 | 
						||
#endif							/* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_COLON || CONFIG_FEATURE_VI_CRASHME */
 | 
						||
#ifdef CONFIG_FEATURE_VI_YANKMARK
 | 
						||
static Byte *text_yank(Byte *, Byte *, int);	// save copy of "p" into a register
 | 
						||
static Byte what_reg(void);		// what is letter of current YDreg
 | 
						||
static void check_context(Byte);	// remember context for '' command
 | 
						||
static Byte *swap_context(Byte *);	// goto new context for '' command
 | 
						||
#endif							/* CONFIG_FEATURE_VI_YANKMARK */
 | 
						||
#ifdef CONFIG_FEATURE_VI_CRASHME
 | 
						||
static void crash_dummy();
 | 
						||
static void crash_test();
 | 
						||
static int crashme = 0;
 | 
						||
#endif							/* CONFIG_FEATURE_VI_CRASHME */
 | 
						||
 | 
						||
 | 
						||
extern int vi_main(int argc, char **argv)
 | 
						||
{
 | 
						||
	int c;
 | 
						||
 | 
						||
#ifdef CONFIG_FEATURE_VI_YANKMARK
 | 
						||
	int i;
 | 
						||
#endif							/* CONFIG_FEATURE_VI_YANKMARK */
 | 
						||
 | 
						||
	CMrc= "\033[%d;%dH";	// Terminal Crusor motion ESC sequence
 | 
						||
	CMup= "\033[A";		// move cursor up one line, same col
 | 
						||
	CMdown="\n";		// move cursor down one line, same col
 | 
						||
	Ceol= "\033[0K";	// Clear from cursor to end of line
 | 
						||
	Ceos= "\033[0J";	// Clear from cursor to end of screen
 | 
						||
	SOs = "\033[7m";	// Terminal standout mode on
 | 
						||
	SOn = "\033[0m";	// Terminal standout mode off
 | 
						||
	bell= "\007";		// Terminal bell sequence
 | 
						||
#ifdef CONFIG_FEATURE_VI_CRASHME
 | 
						||
	(void) srand((long) getpid());
 | 
						||
#endif							/* CONFIG_FEATURE_VI_CRASHME */
 | 
						||
	status_buffer = (Byte *) xmalloc(200);	// hold messages to user
 | 
						||
#ifdef CONFIG_FEATURE_VI_READONLY
 | 
						||
	vi_readonly = readonly = FALSE;
 | 
						||
	if (strncmp(argv[0], "view", 4) == 0) {
 | 
						||
		readonly = TRUE;
 | 
						||
		vi_readonly = TRUE;
 | 
						||
	}
 | 
						||
#endif							/* CONFIG_FEATURE_VI_READONLY */
 | 
						||
#ifdef CONFIG_FEATURE_VI_SETOPTS
 | 
						||
	autoindent = 1;
 | 
						||
	ignorecase = 1;
 | 
						||
	showmatch = 1;
 | 
						||
#endif							/* CONFIG_FEATURE_VI_SETOPTS */
 | 
						||
#ifdef CONFIG_FEATURE_VI_YANKMARK
 | 
						||
	for (i = 0; i < 28; i++) {
 | 
						||
		reg[i] = 0;
 | 
						||
	}					// init the yank regs
 | 
						||
#endif							/* CONFIG_FEATURE_VI_YANKMARK */
 | 
						||
#ifdef CONFIG_FEATURE_VI_DOT_CMD
 | 
						||
	modifying_cmds = (Byte *) "aAcCdDiIJoOpPrRsxX<>~";	// cmds modifying text[]
 | 
						||
#endif							/* CONFIG_FEATURE_VI_DOT_CMD */
 | 
						||
 | 
						||
	//  1-  process $HOME/.exrc file
 | 
						||
	//  2-  process EXINIT variable from environment
 | 
						||
	//  3-  process command line args
 | 
						||
	while ((c = getopt(argc, argv, "hCR")) != -1) {
 | 
						||
		switch (c) {
 | 
						||
#ifdef CONFIG_FEATURE_VI_CRASHME
 | 
						||
		case 'C':
 | 
						||
			crashme = 1;
 | 
						||
			break;
 | 
						||
#endif							/* CONFIG_FEATURE_VI_CRASHME */
 | 
						||
#ifdef CONFIG_FEATURE_VI_READONLY
 | 
						||
		case 'R':		// Read-only flag
 | 
						||
			readonly = TRUE;
 | 
						||
			break;
 | 
						||
#endif							/* CONFIG_FEATURE_VI_READONLY */
 | 
						||
			//case 'r':	// recover flag-  ignore- we don't use tmp file
 | 
						||
			//case 'x':	// encryption flag- ignore
 | 
						||
			//case 'c':	// execute command first
 | 
						||
			//case 'h':	// help -- just use default
 | 
						||
		default:
 | 
						||
			show_help();
 | 
						||
			return 1;
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	// The argv array can be used by the ":next"  and ":rewind" commands
 | 
						||
	// save optind.
 | 
						||
	fn_start = optind;	// remember first file name for :next and :rew
 | 
						||
	save_argc = argc;
 | 
						||
 | 
						||
	//----- This is the main file handling loop --------------
 | 
						||
	if (optind >= argc) {
 | 
						||
		editing = 1;	// 0= exit,  1= one file,  2= multiple files
 | 
						||
		edit_file(0);
 | 
						||
	} else {
 | 
						||
		for (; optind < argc; optind++) {
 | 
						||
			editing = 1;	// 0=exit, 1=one file, 2+ =many files
 | 
						||
			if (cfn != 0)
 | 
						||
				free(cfn);
 | 
						||
			cfn = (Byte *) xstrdup(argv[optind]);
 | 
						||
			edit_file(cfn);
 | 
						||
		}
 | 
						||
	}
 | 
						||
	//-----------------------------------------------------------
 | 
						||
 | 
						||
	return (0);
 | 
						||
}
 | 
						||
 | 
						||
static void edit_file(Byte * fn)
 | 
						||
{
 | 
						||
	char c;
 | 
						||
	int cnt, size, ch;
 | 
						||
 | 
						||
#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
 | 
						||
	char *msg;
 | 
						||
	int sig;
 | 
						||
#endif							/* CONFIG_FEATURE_VI_USE_SIGNALS */
 | 
						||
#ifdef CONFIG_FEATURE_VI_YANKMARK
 | 
						||
	static Byte *cur_line;
 | 
						||
#endif							/* CONFIG_FEATURE_VI_YANKMARK */
 | 
						||
 | 
						||
	rawmode();
 | 
						||
	rows = 24;
 | 
						||
	columns = 80;
 | 
						||
	ch= -1;
 | 
						||
#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
 | 
						||
	window_size_get(0);
 | 
						||
#endif							/* CONFIG_FEATURE_VI_WIN_RESIZE */
 | 
						||
	new_screen(rows, columns);	// get memory for virtual screen
 | 
						||
 | 
						||
	cnt = file_size(fn);	// file size
 | 
						||
	size = 2 * cnt;		// 200% of file size
 | 
						||
	new_text(size);		// get a text[] buffer
 | 
						||
	screenbegin = dot = end = text;
 | 
						||
	if (fn != 0) {
 | 
						||
		ch= file_insert(fn, text, cnt);
 | 
						||
	}
 | 
						||
	if (ch < 1) {
 | 
						||
		(void) char_insert(text, '\n');	// start empty buf with dummy line
 | 
						||
	}
 | 
						||
	file_modified = FALSE;
 | 
						||
#ifdef CONFIG_FEATURE_VI_YANKMARK
 | 
						||
	YDreg = 26;			// default Yank/Delete reg
 | 
						||
	Ureg = 27;			// hold orig line for "U" cmd
 | 
						||
	for (cnt = 0; cnt < 28; cnt++) {
 | 
						||
		mark[cnt] = 0;
 | 
						||
	}					// init the marks
 | 
						||
	mark[26] = mark[27] = text;	// init "previous context"
 | 
						||
#endif							/* CONFIG_FEATURE_VI_YANKMARK */
 | 
						||
 | 
						||
	err_method = 1;		// flash
 | 
						||
	last_forward_char = last_input_char = '\0';
 | 
						||
	crow = 0;
 | 
						||
	ccol = 0;
 | 
						||
	edit_status();
 | 
						||
 | 
						||
#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
 | 
						||
	signal(SIGHUP, catch_sig);
 | 
						||
	signal(SIGINT, catch_sig);
 | 
						||
	signal(SIGALRM, alarm_sig);
 | 
						||
	signal(SIGTERM, catch_sig);
 | 
						||
	signal(SIGQUIT, core_sig);
 | 
						||
	signal(SIGILL, core_sig);
 | 
						||
	signal(SIGTRAP, core_sig);
 | 
						||
	signal(SIGIOT, core_sig);
 | 
						||
	signal(SIGABRT, core_sig);
 | 
						||
	signal(SIGFPE, core_sig);
 | 
						||
	signal(SIGBUS, core_sig);
 | 
						||
	signal(SIGSEGV, core_sig);
 | 
						||
#ifdef SIGSYS
 | 
						||
	signal(SIGSYS, core_sig);
 | 
						||
#endif	
 | 
						||
	signal(SIGWINCH, winch_sig);
 | 
						||
	signal(SIGTSTP, suspend_sig);
 | 
						||
	sig = setjmp(restart);
 | 
						||
	if (sig != 0) {
 | 
						||
		msg = "";
 | 
						||
		if (sig == SIGWINCH)
 | 
						||
			msg = "(window resize)";
 | 
						||
		if (sig == SIGHUP)
 | 
						||
			msg = "(hangup)";
 | 
						||
		if (sig == SIGINT)
 | 
						||
			msg = "(interrupt)";
 | 
						||
		if (sig == SIGTERM)
 | 
						||
			msg = "(terminate)";
 | 
						||
		if (sig == SIGBUS)
 | 
						||
			msg = "(bus error)";
 | 
						||
		if (sig == SIGSEGV)
 | 
						||
			msg = "(I tried to touch invalid memory)";
 | 
						||
		if (sig == SIGALRM)
 | 
						||
			msg = "(alarm)";
 | 
						||
 | 
						||
		psbs("-- caught signal %d %s--", sig, msg);
 | 
						||
		screenbegin = dot = text;
 | 
						||
	}
 | 
						||
#endif							/* CONFIG_FEATURE_VI_USE_SIGNALS */
 | 
						||
 | 
						||
	editing = 1;
 | 
						||
	cmd_mode = 0;		// 0=command  1=insert  2='R'eplace
 | 
						||
	cmdcnt = 0;
 | 
						||
	tabstop = 8;
 | 
						||
	offset = 0;			// no horizontal offset
 | 
						||
	c = '\0';
 | 
						||
#ifdef CONFIG_FEATURE_VI_DOT_CMD
 | 
						||
	if (last_modifying_cmd != 0)
 | 
						||
		free(last_modifying_cmd);
 | 
						||
	if (ioq_start != NULL)
 | 
						||
		free(ioq_start);
 | 
						||
	ioq = ioq_start = last_modifying_cmd = 0;
 | 
						||
	adding2q = 0;
 | 
						||
#endif							/* CONFIG_FEATURE_VI_DOT_CMD */
 | 
						||
	redraw(FALSE);			// dont force every col re-draw
 | 
						||
	show_status_line();
 | 
						||
 | 
						||
	//------This is the main Vi cmd handling loop -----------------------
 | 
						||
	while (editing > 0) {
 | 
						||
#ifdef CONFIG_FEATURE_VI_CRASHME
 | 
						||
		if (crashme > 0) {
 | 
						||
			if ((end - text) > 1) {
 | 
						||
				crash_dummy();	// generate a random command
 | 
						||
			} else {
 | 
						||
				crashme = 0;
 | 
						||
				dot =
 | 
						||
					string_insert(text, (Byte *) "\n\n#####  Ran out of text to work on.  #####\n\n");	// insert the string
 | 
						||
				refresh(FALSE);
 | 
						||
			}
 | 
						||
		}
 | 
						||
#endif							/* CONFIG_FEATURE_VI_CRASHME */
 | 
						||
		last_input_char = c = get_one_char();	// get a cmd from user
 | 
						||
#ifdef CONFIG_FEATURE_VI_YANKMARK
 | 
						||
		// save a copy of the current line- for the 'U" command
 | 
						||
		if (begin_line(dot) != cur_line) {
 | 
						||
			cur_line = begin_line(dot);
 | 
						||
			text_yank(begin_line(dot), end_line(dot), Ureg);
 | 
						||
		}
 | 
						||
#endif							/* CONFIG_FEATURE_VI_YANKMARK */
 | 
						||
#ifdef CONFIG_FEATURE_VI_DOT_CMD
 | 
						||
		// These are commands that change text[].
 | 
						||
		// Remember the input for the "." command
 | 
						||
		if (!adding2q && ioq_start == 0
 | 
						||
			&& strchr((char *) modifying_cmds, c) != NULL) {
 | 
						||
			start_new_cmd_q(c);
 | 
						||
		}
 | 
						||
#endif							/* CONFIG_FEATURE_VI_DOT_CMD */
 | 
						||
		do_cmd(c);		// execute the user command
 | 
						||
		//
 | 
						||
		// poll to see if there is input already waiting. if we are
 | 
						||
		// not able to display output fast enough to keep up, skip
 | 
						||
		// the display update until we catch up with input.
 | 
						||
		if (mysleep(0) == 0) {
 | 
						||
			// no input pending- so update output
 | 
						||
			refresh(FALSE);
 | 
						||
			show_status_line();
 | 
						||
		}
 | 
						||
#ifdef CONFIG_FEATURE_VI_CRASHME
 | 
						||
		if (crashme > 0)
 | 
						||
			crash_test();	// test editor variables
 | 
						||
#endif							/* CONFIG_FEATURE_VI_CRASHME */
 | 
						||
	}
 | 
						||
	//-------------------------------------------------------------------
 | 
						||
 | 
						||
	place_cursor(rows, 0, FALSE);	// go to bottom of screen
 | 
						||
	clear_to_eol();		// Erase to end of line
 | 
						||
	cookmode();
 | 
						||
}
 | 
						||
 | 
						||
static Byte readbuffer[BUFSIZ];
 | 
						||
 | 
						||
#ifdef CONFIG_FEATURE_VI_CRASHME
 | 
						||
static int totalcmds = 0;
 | 
						||
static int Mp = 85;		// Movement command Probability
 | 
						||
static int Np = 90;		// Non-movement command Probability
 | 
						||
static int Dp = 96;		// Delete command Probability
 | 
						||
static int Ip = 97;		// Insert command Probability
 | 
						||
static int Yp = 98;		// Yank command Probability
 | 
						||
static int Pp = 99;		// Put command Probability
 | 
						||
static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
 | 
						||
char chars[20] = "\t012345 abcdABCD-=.$";
 | 
						||
char *words[20] = { "this", "is", "a", "test",
 | 
						||
	"broadcast", "the", "emergency", "of",
 | 
						||
	"system", "quick", "brown", "fox",
 | 
						||
	"jumped", "over", "lazy", "dogs",
 | 
						||
	"back", "January", "Febuary", "March"
 | 
						||
};
 | 
						||
char *lines[20] = {
 | 
						||
	"You should have received a copy of the GNU General Public License\n",
 | 
						||
	"char c, cm, *cmd, *cmd1;\n",
 | 
						||
	"generate a command by percentages\n",
 | 
						||
	"Numbers may be typed as a prefix to some commands.\n",
 | 
						||
	"Quit, discarding changes!\n",
 | 
						||
	"Forced write, if permission originally not valid.\n",
 | 
						||
	"In general, any ex or ed command (such as substitute or delete).\n",
 | 
						||
	"I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
 | 
						||
	"Please get w/ me and I will go over it with you.\n",
 | 
						||
	"The following is a list of scheduled, committed changes.\n",
 | 
						||
	"1.   Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
 | 
						||
	"Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
 | 
						||
	"Any question about transactions please contact Sterling Huxley.\n",
 | 
						||
	"I will try to get back to you by Friday, December 31.\n",
 | 
						||
	"This Change will be implemented on Friday.\n",
 | 
						||
	"Let me know if you have problems accessing this;\n",
 | 
						||
	"Sterling Huxley recently added you to the access list.\n",
 | 
						||
	"Would you like to go to lunch?\n",
 | 
						||
	"The last command will be automatically run.\n",
 | 
						||
	"This is too much english for a computer geek.\n",
 | 
						||
};
 | 
						||
char *multilines[20] = {
 | 
						||
	"You should have received a copy of the GNU General Public License\n",
 | 
						||
	"char c, cm, *cmd, *cmd1;\n",
 | 
						||
	"generate a command by percentages\n",
 | 
						||
	"Numbers may be typed as a prefix to some commands.\n",
 | 
						||
	"Quit, discarding changes!\n",
 | 
						||
	"Forced write, if permission originally not valid.\n",
 | 
						||
	"In general, any ex or ed command (such as substitute or delete).\n",
 | 
						||
	"I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
 | 
						||
	"Please get w/ me and I will go over it with you.\n",
 | 
						||
	"The following is a list of scheduled, committed changes.\n",
 | 
						||
	"1.   Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
 | 
						||
	"Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
 | 
						||
	"Any question about transactions please contact Sterling Huxley.\n",
 | 
						||
	"I will try to get back to you by Friday, December 31.\n",
 | 
						||
	"This Change will be implemented on Friday.\n",
 | 
						||
	"Let me know if you have problems accessing this;\n",
 | 
						||
	"Sterling Huxley recently added you to the access list.\n",
 | 
						||
	"Would you like to go to lunch?\n",
 | 
						||
	"The last command will be automatically run.\n",
 | 
						||
	"This is too much english for a computer geek.\n",
 | 
						||
};
 | 
						||
 | 
						||
// create a random command to execute
 | 
						||
static void crash_dummy()
 | 
						||
{
 | 
						||
	static int sleeptime;	// how long to pause between commands
 | 
						||
	char c, cm, *cmd, *cmd1;
 | 
						||
	int i, cnt, thing, rbi, startrbi, percent;
 | 
						||
 | 
						||
	// "dot" movement commands
 | 
						||
	cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
 | 
						||
 | 
						||
	// is there already a command running?
 | 
						||
	if (strlen((char *) readbuffer) > 0)
 | 
						||
		goto cd1;
 | 
						||
  cd0:
 | 
						||
	startrbi = rbi = 0;
 | 
						||
	sleeptime = 0;		// how long to pause between commands
 | 
						||
	memset(readbuffer, '\0', BUFSIZ - 1);	// clear the read buffer
 | 
						||
	// generate a command by percentages
 | 
						||
	percent = (int) lrand48() % 100;	// get a number from 0-99
 | 
						||
	if (percent < Mp) {	//  Movement commands
 | 
						||
		// available commands
 | 
						||
		cmd = cmd1;
 | 
						||
		M++;
 | 
						||
	} else if (percent < Np) {	//  non-movement commands
 | 
						||
		cmd = "mz<>\'\"";	// available commands
 | 
						||
		N++;
 | 
						||
	} else if (percent < Dp) {	//  Delete commands
 | 
						||
		cmd = "dx";		// available commands
 | 
						||
		D++;
 | 
						||
	} else if (percent < Ip) {	//  Inset commands
 | 
						||
		cmd = "iIaAsrJ";	// available commands
 | 
						||
		I++;
 | 
						||
	} else if (percent < Yp) {	//  Yank commands
 | 
						||
		cmd = "yY";		// available commands
 | 
						||
		Y++;
 | 
						||
	} else if (percent < Pp) {	//  Put commands
 | 
						||
		cmd = "pP";		// available commands
 | 
						||
		P++;
 | 
						||
	} else {
 | 
						||
		// We do not know how to handle this command, try again
 | 
						||
		U++;
 | 
						||
		goto cd0;
 | 
						||
	}
 | 
						||
	// randomly pick one of the available cmds from "cmd[]"
 | 
						||
	i = (int) lrand48() % strlen(cmd);
 | 
						||
	cm = cmd[i];
 | 
						||
	if (strchr(":\024", cm))
 | 
						||
		goto cd0;		// dont allow colon or ctrl-T commands
 | 
						||
	readbuffer[rbi++] = cm;	// put cmd into input buffer
 | 
						||
 | 
						||
	// now we have the command-
 | 
						||
	// there are 1, 2, and multi char commands
 | 
						||
	// find out which and generate the rest of command as necessary
 | 
						||
	if (strchr("dmryz<>\'\"", cm)) {	// 2-char commands
 | 
						||
		cmd1 = " \n\r0$^-+wWeEbBhjklHL";
 | 
						||
		if (cm == 'm' || cm == '\'' || cm == '\"') {	// pick a reg[]
 | 
						||
			cmd1 = "abcdefghijklmnopqrstuvwxyz";
 | 
						||
		}
 | 
						||
		thing = (int) lrand48() % strlen(cmd1);	// pick a movement command
 | 
						||
		c = cmd1[thing];
 | 
						||
		readbuffer[rbi++] = c;	// add movement to input buffer
 | 
						||
	}
 | 
						||
	if (strchr("iIaAsc", cm)) {	// multi-char commands
 | 
						||
		if (cm == 'c') {
 | 
						||
			// change some thing
 | 
						||
			thing = (int) lrand48() % strlen(cmd1);	// pick a movement command
 | 
						||
			c = cmd1[thing];
 | 
						||
			readbuffer[rbi++] = c;	// add movement to input buffer
 | 
						||
		}
 | 
						||
		thing = (int) lrand48() % 4;	// what thing to insert
 | 
						||
		cnt = (int) lrand48() % 10;	// how many to insert
 | 
						||
		for (i = 0; i < cnt; i++) {
 | 
						||
			if (thing == 0) {	// insert chars
 | 
						||
				readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
 | 
						||
			} else if (thing == 1) {	// insert words
 | 
						||
				strcat((char *) readbuffer, words[(int) lrand48() % 20]);
 | 
						||
				strcat((char *) readbuffer, " ");
 | 
						||
				sleeptime = 0;	// how fast to type
 | 
						||
			} else if (thing == 2) {	// insert lines
 | 
						||
				strcat((char *) readbuffer, lines[(int) lrand48() % 20]);
 | 
						||
				sleeptime = 0;	// how fast to type
 | 
						||
			} else {	// insert multi-lines
 | 
						||
				strcat((char *) readbuffer, multilines[(int) lrand48() % 20]);
 | 
						||
				sleeptime = 0;	// how fast to type
 | 
						||
			}
 | 
						||
		}
 | 
						||
		strcat((char *) readbuffer, "\033");
 | 
						||
	}
 | 
						||
  cd1:
 | 
						||
	totalcmds++;
 | 
						||
	if (sleeptime > 0)
 | 
						||
		(void) mysleep(sleeptime);	// sleep 1/100 sec
 | 
						||
}
 | 
						||
 | 
						||
// test to see if there are any errors
 | 
						||
static void crash_test()
 | 
						||
{
 | 
						||
	static time_t oldtim;
 | 
						||
	time_t tim;
 | 
						||
	char d[2], buf[BUFSIZ], msg[BUFSIZ];
 | 
						||
 | 
						||
	msg[0] = '\0';
 | 
						||
	if (end < text) {
 | 
						||
		strcat((char *) msg, "end<text ");
 | 
						||
	}
 | 
						||
	if (end > textend) {
 | 
						||
		strcat((char *) msg, "end>textend ");
 | 
						||
	}
 | 
						||
	if (dot < text) {
 | 
						||
		strcat((char *) msg, "dot<text ");
 | 
						||
	}
 | 
						||
	if (dot > end) {
 | 
						||
		strcat((char *) msg, "dot>end ");
 | 
						||
	}
 | 
						||
	if (screenbegin < text) {
 | 
						||
		strcat((char *) msg, "screenbegin<text ");
 | 
						||
	}
 | 
						||
	if (screenbegin > end - 1) {
 | 
						||
		strcat((char *) msg, "screenbegin>end-1 ");
 | 
						||
	}
 | 
						||
 | 
						||
	if (strlen(msg) > 0) {
 | 
						||
		alarm(0);
 | 
						||
		sprintf(buf, "\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
 | 
						||
			totalcmds, last_input_char, msg, SOs, SOn);
 | 
						||
		write(1, buf, strlen(buf));
 | 
						||
		while (read(0, d, 1) > 0) {
 | 
						||
			if (d[0] == '\n' || d[0] == '\r')
 | 
						||
				break;
 | 
						||
		}
 | 
						||
		alarm(3);
 | 
						||
	}
 | 
						||
	tim = (time_t) time((time_t *) 0);
 | 
						||
	if (tim >= (oldtim + 3)) {
 | 
						||
		sprintf((char *) status_buffer,
 | 
						||
				"Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
 | 
						||
				totalcmds, M, N, I, D, Y, P, U, end - text + 1);
 | 
						||
		oldtim = tim;
 | 
						||
	}
 | 
						||
	return;
 | 
						||
}
 | 
						||
#endif							/* CONFIG_FEATURE_VI_CRASHME */
 | 
						||
 | 
						||
//---------------------------------------------------------------------
 | 
						||
//----- the Ascii Chart -----------------------------------------------
 | 
						||
//
 | 
						||
//  00 nul   01 soh   02 stx   03 etx   04 eot   05 enq   06 ack   07 bel
 | 
						||
//  08 bs    09 ht    0a nl    0b vt    0c np    0d cr    0e so    0f si
 | 
						||
//  10 dle   11 dc1   12 dc2   13 dc3   14 dc4   15 nak   16 syn   17 etb
 | 
						||
//  18 can   19 em    1a sub   1b esc   1c fs    1d gs    1e rs    1f us
 | 
						||
//  20 sp    21 !     22 "     23 #     24 $     25 %     26 &     27 '
 | 
						||
//  28 (     29 )     2a *     2b +     2c ,     2d -     2e .     2f /
 | 
						||
//  30 0     31 1     32 2     33 3     34 4     35 5     36 6     37 7
 | 
						||
//  38 8     39 9     3a :     3b ;     3c <     3d =     3e >     3f ?
 | 
						||
//  40 @     41 A     42 B     43 C     44 D     45 E     46 F     47 G
 | 
						||
//  48 H     49 I     4a J     4b K     4c L     4d M     4e N     4f O
 | 
						||
//  50 P     51 Q     52 R     53 S     54 T     55 U     56 V     57 W
 | 
						||
//  58 X     59 Y     5a Z     5b [     5c \     5d ]     5e ^     5f _
 | 
						||
//  60 `     61 a     62 b     63 c     64 d     65 e     66 f     67 g
 | 
						||
//  68 h     69 i     6a j     6b k     6c l     6d m     6e n     6f o
 | 
						||
//  70 p     71 q     72 r     73 s     74 t     75 u     76 v     77 w
 | 
						||
//  78 x     79 y     7a z     7b {     7c |     7d }     7e ~     7f del
 | 
						||
//---------------------------------------------------------------------
 | 
						||
 | 
						||
//----- Execute a Vi Command -----------------------------------
 | 
						||
static void do_cmd(Byte c)
 | 
						||
{
 | 
						||
	Byte c1, *p, *q, *msg, buf[9], *save_dot;
 | 
						||
	int cnt, i, j, dir, yf;
 | 
						||
 | 
						||
	c1 = c;				// quiet the compiler
 | 
						||
	cnt = yf = dir = 0;	// quiet the compiler
 | 
						||
	p = q = save_dot = msg = buf;	// quiet the compiler
 | 
						||
	memset(buf, '\0', 9);	// clear buf
 | 
						||
 | 
						||
	/* if this is a cursor key, skip these checks */
 | 
						||
	switch (c) {
 | 
						||
		case VI_K_UP:
 | 
						||
		case VI_K_DOWN:
 | 
						||
		case VI_K_LEFT:
 | 
						||
		case VI_K_RIGHT:
 | 
						||
		case VI_K_HOME:
 | 
						||
		case VI_K_END:
 | 
						||
		case VI_K_PAGEUP:
 | 
						||
		case VI_K_PAGEDOWN:
 | 
						||
			goto key_cmd_mode;
 | 
						||
	}
 | 
						||
 | 
						||
	if (cmd_mode == 2) {
 | 
						||
		// we are 'R'eplacing the current *dot with new char
 | 
						||
		if (*dot == '\n') {
 | 
						||
			// don't Replace past E-o-l
 | 
						||
			cmd_mode = 1;	// convert to insert
 | 
						||
		} else {
 | 
						||
			if (1 <= c && c <= 127) {	// only ASCII chars
 | 
						||
				if (c != 27)
 | 
						||
					dot = yank_delete(dot, dot, 0, YANKDEL);	// delete char
 | 
						||
				dot = char_insert(dot, c);	// insert new char
 | 
						||
			}
 | 
						||
			goto dc1;
 | 
						||
		}
 | 
						||
	}
 | 
						||
	if (cmd_mode == 1) {
 | 
						||
		//  hitting "Insert" twice means "R" replace mode
 | 
						||
		if (c == VI_K_INSERT) goto dc5;
 | 
						||
		// insert the char c at "dot"
 | 
						||
		if (1 <= c && c <= 127) {
 | 
						||
			dot = char_insert(dot, c);	// only ASCII chars
 | 
						||
		}
 | 
						||
		goto dc1;
 | 
						||
	}
 | 
						||
 | 
						||
key_cmd_mode:
 | 
						||
	switch (c) {
 | 
						||
		//case 0x01:	// soh
 | 
						||
		//case 0x09:	// ht
 | 
						||
		//case 0x0b:	// vt
 | 
						||
		//case 0x0e:	// so
 | 
						||
		//case 0x0f:	// si
 | 
						||
		//case 0x10:	// dle
 | 
						||
		//case 0x11:	// dc1
 | 
						||
		//case 0x13:	// dc3
 | 
						||
#ifdef CONFIG_FEATURE_VI_CRASHME
 | 
						||
	case 0x14:			// dc4  ctrl-T
 | 
						||
		crashme = (crashme == 0) ? 1 : 0;
 | 
						||
		break;
 | 
						||
#endif							/* CONFIG_FEATURE_VI_CRASHME */
 | 
						||
		//case 0x16:	// syn
 | 
						||
		//case 0x17:	// etb
 | 
						||
		//case 0x18:	// can
 | 
						||
		//case 0x1c:	// fs
 | 
						||
		//case 0x1d:	// gs
 | 
						||
		//case 0x1e:	// rs
 | 
						||
		//case 0x1f:	// us
 | 
						||
		//case '!':	// !- 
 | 
						||
		//case '#':	// #- 
 | 
						||
		//case '&':	// &- 
 | 
						||
		//case '(':	// (- 
 | 
						||
		//case ')':	// )- 
 | 
						||
		//case '*':	// *- 
 | 
						||
		//case ',':	// ,- 
 | 
						||
		//case '=':	// =- 
 | 
						||
		//case '@':	// @- 
 | 
						||
		//case 'F':	// F- 
 | 
						||
		//case 'K':	// K- 
 | 
						||
		//case 'Q':	// Q- 
 | 
						||
		//case 'S':	// S- 
 | 
						||
		//case 'T':	// T- 
 | 
						||
		//case 'V':	// V- 
 | 
						||
		//case '[':	// [- 
 | 
						||
		//case '\\':	// \- 
 | 
						||
		//case ']':	// ]- 
 | 
						||
		//case '_':	// _- 
 | 
						||
		//case '`':	// `- 
 | 
						||
		//case 'g':	// g- 
 | 
						||
		//case 'u':	// u- FIXME- there is no undo
 | 
						||
		//case 'v':	// v- 
 | 
						||
	default:			// unrecognised command
 | 
						||
		buf[0] = c;
 | 
						||
		buf[1] = '\0';
 | 
						||
		if (c <= ' ') {
 | 
						||
			buf[0] = '^';
 | 
						||
			buf[1] = c + '@';
 | 
						||
			buf[2] = '\0';
 | 
						||
		}
 | 
						||
		ni((Byte *) buf);
 | 
						||
		end_cmd_q();	// stop adding to q
 | 
						||
	case 0x00:			// nul- ignore
 | 
						||
		break;
 | 
						||
	case 2:			// ctrl-B  scroll up   full screen
 | 
						||
	case VI_K_PAGEUP:	// Cursor Key Page Up
 | 
						||
		dot_scroll(rows - 2, -1);
 | 
						||
		break;
 | 
						||
#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
 | 
						||
	case 0x03:			// ctrl-C   interrupt
 | 
						||
		longjmp(restart, 1);
 | 
						||
		break;
 | 
						||
	case 26:			// ctrl-Z suspend
 | 
						||
		suspend_sig(SIGTSTP);
 | 
						||
		break;
 | 
						||
#endif							/* CONFIG_FEATURE_VI_USE_SIGNALS */
 | 
						||
	case 4:			// ctrl-D  scroll down half screen
 | 
						||
		dot_scroll((rows - 2) / 2, 1);
 | 
						||
		break;
 | 
						||
	case 5:			// ctrl-E  scroll down one line
 | 
						||
		dot_scroll(1, 1);
 | 
						||
		break;
 | 
						||
	case 6:			// ctrl-F  scroll down full screen
 | 
						||
	case VI_K_PAGEDOWN:	// Cursor Key Page Down
 | 
						||
		dot_scroll(rows - 2, 1);
 | 
						||
		break;
 | 
						||
	case 7:			// ctrl-G  show current status
 | 
						||
		edit_status();
 | 
						||
		break;
 | 
						||
	case 'h':			// h- move left
 | 
						||
	case VI_K_LEFT:	// cursor key Left
 | 
						||
	case 8:			// ctrl-H- move left    (This may be ERASE char)
 | 
						||
	case 127:			// DEL- move left   (This may be ERASE char)
 | 
						||
		if (cmdcnt-- > 1) {
 | 
						||
			do_cmd(c);
 | 
						||
		}				// repeat cnt
 | 
						||
		dot_left();
 | 
						||
		break;
 | 
						||
	case 10:			// Newline ^J
 | 
						||
	case 'j':			// j- goto next line, same col
 | 
						||
	case VI_K_DOWN:	// cursor key Down
 | 
						||
		if (cmdcnt-- > 1) {
 | 
						||
			do_cmd(c);
 | 
						||
		}				// repeat cnt
 | 
						||
		dot_next();		// go to next B-o-l
 | 
						||
		dot = move_to_col(dot, ccol + offset);	// try stay in same col
 | 
						||
		break;
 | 
						||
	case 12:			// ctrl-L  force redraw whole screen
 | 
						||
	case 18:			// ctrl-R  force redraw
 | 
						||
		place_cursor(0, 0, FALSE);	// put cursor in correct place
 | 
						||
		clear_to_eos();	// tel terminal to erase display
 | 
						||
		(void) mysleep(10);
 | 
						||
		screen_erase();	// erase the internal screen buffer
 | 
						||
		refresh(TRUE);	// this will redraw the entire display
 | 
						||
		break;
 | 
						||
	case 13:			// Carriage Return ^M
 | 
						||
	case '+':			// +- goto next line
 | 
						||
		if (cmdcnt-- > 1) {
 | 
						||
			do_cmd(c);
 | 
						||
		}				// repeat cnt
 | 
						||
		dot_next();
 | 
						||
		dot_skip_over_ws();
 | 
						||
		break;
 | 
						||
	case 21:			// ctrl-U  scroll up   half screen
 | 
						||
		dot_scroll((rows - 2) / 2, -1);
 | 
						||
		break;
 | 
						||
	case 25:			// ctrl-Y  scroll up one line
 | 
						||
		dot_scroll(1, -1);
 | 
						||
		break;
 | 
						||
	case 27:			// esc
 | 
						||
		if (cmd_mode == 0)
 | 
						||
			indicate_error(c);
 | 
						||
		cmd_mode = 0;	// stop insrting
 | 
						||
		end_cmd_q();
 | 
						||
		*status_buffer = '\0';	// clear status buffer
 | 
						||
		break;
 | 
						||
	case ' ':			// move right
 | 
						||
	case 'l':			// move right
 | 
						||
	case VI_K_RIGHT:	// Cursor Key Right
 | 
						||
		if (cmdcnt-- > 1) {
 | 
						||
			do_cmd(c);
 | 
						||
		}				// repeat cnt
 | 
						||
		dot_right();
 | 
						||
		break;
 | 
						||
#ifdef CONFIG_FEATURE_VI_YANKMARK
 | 
						||
	case '"':			// "- name a register to use for Delete/Yank
 | 
						||
		c1 = get_one_char();
 | 
						||
		c1 = tolower(c1);
 | 
						||
		if (islower(c1)) {
 | 
						||
			YDreg = c1 - 'a';
 | 
						||
		} else {
 | 
						||
			indicate_error(c);
 | 
						||
		}
 | 
						||
		break;
 | 
						||
	case '\'':			// '- goto a specific mark
 | 
						||
		c1 = get_one_char();
 | 
						||
		c1 = tolower(c1);
 | 
						||
		if (islower(c1)) {
 | 
						||
			c1 = c1 - 'a';
 | 
						||
			// get the b-o-l
 | 
						||
			q = mark[(int) c1];
 | 
						||
			if (text <= q && q < end) {
 | 
						||
				dot = q;
 | 
						||
				dot_begin();	// go to B-o-l
 | 
						||
				dot_skip_over_ws();
 | 
						||
			}
 | 
						||
		} else if (c1 == '\'') {	// goto previous context
 | 
						||
			dot = swap_context(dot);	// swap current and previous context
 | 
						||
			dot_begin();	// go to B-o-l
 | 
						||
			dot_skip_over_ws();
 | 
						||
		} else {
 | 
						||
			indicate_error(c);
 | 
						||
		}
 | 
						||
		break;
 | 
						||
	case 'm':			// m- Mark a line
 | 
						||
		// this is really stupid.  If there are any inserts or deletes
 | 
						||
		// between text[0] and dot then this mark will not point to the
 | 
						||
		// correct location! It could be off by many lines!
 | 
						||
		// Well..., at least its quick and dirty.
 | 
						||
		c1 = get_one_char();
 | 
						||
		c1 = tolower(c1);
 | 
						||
		if (islower(c1)) {
 | 
						||
			c1 = c1 - 'a';
 | 
						||
			// remember the line
 | 
						||
			mark[(int) c1] = dot;
 | 
						||
		} else {
 | 
						||
			indicate_error(c);
 | 
						||
		}
 | 
						||
		break;
 | 
						||
	case 'P':			// P- Put register before
 | 
						||
	case 'p':			// p- put register after
 | 
						||
		p = reg[YDreg];
 | 
						||
		if (p == 0) {
 | 
						||
			psbs("Nothing in register %c", what_reg());
 | 
						||
			break;
 | 
						||
		}
 | 
						||
		// are we putting whole lines or strings
 | 
						||
		if (strchr((char *) p, '\n') != NULL) {
 | 
						||
			if (c == 'P') {
 | 
						||
				dot_begin();	// putting lines- Put above
 | 
						||
			}
 | 
						||
			if (c == 'p') {
 | 
						||
				// are we putting after very last line?
 | 
						||
				if (end_line(dot) == (end - 1)) {
 | 
						||
					dot = end;	// force dot to end of text[]
 | 
						||
				} else {
 | 
						||
					dot_next();	// next line, then put before
 | 
						||
				}
 | 
						||
			}
 | 
						||
		} else {
 | 
						||
			if (c == 'p')
 | 
						||
				dot_right();	// move to right, can move to NL
 | 
						||
		}
 | 
						||
		dot = string_insert(dot, p);	// insert the string
 | 
						||
		end_cmd_q();	// stop adding to q
 | 
						||
		break;
 | 
						||
	case 'U':			// U- Undo; replace current line with original version
 | 
						||
		if (reg[Ureg] != 0) {
 | 
						||
			p = begin_line(dot);
 | 
						||
			q = end_line(dot);
 | 
						||
			p = text_hole_delete(p, q);	// delete cur line
 | 
						||
			p = string_insert(p, reg[Ureg]);	// insert orig line
 | 
						||
			dot = p;
 | 
						||
			dot_skip_over_ws();
 | 
						||
		}
 | 
						||
		break;
 | 
						||
#endif							/* CONFIG_FEATURE_VI_YANKMARK */
 | 
						||
	case '$':			// $- goto end of line
 | 
						||
	case VI_K_END:		// Cursor Key End
 | 
						||
		if (cmdcnt-- > 1) {
 | 
						||
			do_cmd(c);
 | 
						||
		}				// repeat cnt
 | 
						||
		dot = end_line(dot + 1);
 | 
						||
		break;
 | 
						||
	case '%':			// %- find matching char of pair () [] {}
 | 
						||
		for (q = dot; q < end && *q != '\n'; q++) {
 | 
						||
			if (strchr("()[]{}", *q) != NULL) {
 | 
						||
				// we found half of a pair
 | 
						||
				p = find_pair(q, *q);
 | 
						||
				if (p == NULL) {
 | 
						||
					indicate_error(c);
 | 
						||
				} else {
 | 
						||
					dot = p;
 | 
						||
				}
 | 
						||
				break;
 | 
						||
			}
 | 
						||
		}
 | 
						||
		if (*q == '\n')
 | 
						||
			indicate_error(c);
 | 
						||
		break;
 | 
						||
	case 'f':			// f- forward to a user specified char
 | 
						||
		last_forward_char = get_one_char();	// get the search char
 | 
						||
		//
 | 
						||
		// dont seperate these two commands. 'f' depends on ';'
 | 
						||
		//
 | 
						||
		//**** fall thru to ... 'i'
 | 
						||
	case ';':			// ;- look at rest of line for last forward char
 | 
						||
		if (cmdcnt-- > 1) {
 | 
						||
			do_cmd(';');
 | 
						||
		}				// repeat cnt
 | 
						||
		if (last_forward_char == 0) break;
 | 
						||
		q = dot + 1;
 | 
						||
		while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
 | 
						||
			q++;
 | 
						||
		}
 | 
						||
		if (*q == last_forward_char)
 | 
						||
			dot = q;
 | 
						||
		break;
 | 
						||
	case '-':			// -- goto prev line
 | 
						||
		if (cmdcnt-- > 1) {
 | 
						||
			do_cmd(c);
 | 
						||
		}				// repeat cnt
 | 
						||
		dot_prev();
 | 
						||
		dot_skip_over_ws();
 | 
						||
		break;
 | 
						||
#ifdef CONFIG_FEATURE_VI_DOT_CMD
 | 
						||
	case '.':			// .- repeat the last modifying command
 | 
						||
		// Stuff the last_modifying_cmd back into stdin
 | 
						||
		// and let it be re-executed.
 | 
						||
		if (last_modifying_cmd != 0) {
 | 
						||
			ioq = ioq_start = (Byte *) xstrdup((char *) last_modifying_cmd);
 | 
						||
		}
 | 
						||
		break;
 | 
						||
#endif							/* CONFIG_FEATURE_VI_DOT_CMD */
 | 
						||
#ifdef CONFIG_FEATURE_VI_SEARCH
 | 
						||
	case '?':			// /- search for a pattern
 | 
						||
	case '/':			// /- search for a pattern
 | 
						||
		buf[0] = c;
 | 
						||
		buf[1] = '\0';
 | 
						||
		q = get_input_line(buf);	// get input line- use "status line"
 | 
						||
		if (strlen((char *) q) == 1)
 | 
						||
			goto dc3;	// if no pat re-use old pat
 | 
						||
		if (strlen((char *) q) > 1) {	// new pat- save it and find
 | 
						||
			// there is a new pat
 | 
						||
			if (last_search_pattern != 0) {
 | 
						||
				free(last_search_pattern);
 | 
						||
			}
 | 
						||
			last_search_pattern = (Byte *) xstrdup((char *) q);
 | 
						||
			goto dc3;	// now find the pattern
 | 
						||
		}
 | 
						||
		// user changed mind and erased the "/"-  do nothing
 | 
						||
		break;
 | 
						||
	case 'N':			// N- backward search for last pattern
 | 
						||
		if (cmdcnt-- > 1) {
 | 
						||
			do_cmd(c);
 | 
						||
		}				// repeat cnt
 | 
						||
		dir = BACK;		// assume BACKWARD search
 | 
						||
		p = dot - 1;
 | 
						||
		if (last_search_pattern[0] == '?') {
 | 
						||
			dir = FORWARD;
 | 
						||
			p = dot + 1;
 | 
						||
		}
 | 
						||
		goto dc4;		// now search for pattern
 | 
						||
		break;
 | 
						||
	case 'n':			// n- repeat search for last pattern
 | 
						||
		// search rest of text[] starting at next char
 | 
						||
		// if search fails return orignal "p" not the "p+1" address
 | 
						||
		if (cmdcnt-- > 1) {
 | 
						||
			do_cmd(c);
 | 
						||
		}				// repeat cnt
 | 
						||
	  dc3:
 | 
						||
		if (last_search_pattern == 0) {
 | 
						||
			msg = (Byte *) "No previous regular expression";
 | 
						||
			goto dc2;
 | 
						||
		}
 | 
						||
		if (last_search_pattern[0] == '/') {
 | 
						||
			dir = FORWARD;	// assume FORWARD search
 | 
						||
			p = dot + 1;
 | 
						||
		}
 | 
						||
		if (last_search_pattern[0] == '?') {
 | 
						||
			dir = BACK;
 | 
						||
			p = dot - 1;
 | 
						||
		}
 | 
						||
	  dc4:
 | 
						||
		q = char_search(p, last_search_pattern + 1, dir, FULL);
 | 
						||
		if (q != NULL) {
 | 
						||
			dot = q;	// good search, update "dot"
 | 
						||
			msg = (Byte *) "";
 | 
						||
			goto dc2;
 | 
						||
		}
 | 
						||
		// no pattern found between "dot" and "end"- continue at top
 | 
						||
		p = text;
 | 
						||
		if (dir == BACK) {
 | 
						||
			p = end - 1;
 | 
						||
		}
 | 
						||
		q = char_search(p, last_search_pattern + 1, dir, FULL);
 | 
						||
		if (q != NULL) {	// found something
 | 
						||
			dot = q;	// found new pattern- goto it
 | 
						||
			msg = (Byte *) "search hit BOTTOM, continuing at TOP";
 | 
						||
			if (dir == BACK) {
 | 
						||
				msg = (Byte *) "search hit TOP, continuing at BOTTOM";
 | 
						||
			}
 | 
						||
		} else {
 | 
						||
			msg = (Byte *) "Pattern not found";
 | 
						||
		}
 | 
						||
	  dc2:
 | 
						||
		psbs("%s", msg);
 | 
						||
		break;
 | 
						||
	case '{':			// {- move backward paragraph
 | 
						||
		q = char_search(dot, (Byte *) "\n\n", BACK, FULL);
 | 
						||
		if (q != NULL) {	// found blank line
 | 
						||
			dot = next_line(q);	// move to next blank line
 | 
						||
		}
 | 
						||
		break;
 | 
						||
	case '}':			// }- move forward paragraph
 | 
						||
		q = char_search(dot, (Byte *) "\n\n", FORWARD, FULL);
 | 
						||
		if (q != NULL) {	// found blank line
 | 
						||
			dot = next_line(q);	// move to next blank line
 | 
						||
		}
 | 
						||
		break;
 | 
						||
#endif							/* CONFIG_FEATURE_VI_SEARCH */
 | 
						||
	case '0':			// 0- goto begining of line
 | 
						||
	case '1':			// 1- 
 | 
						||
	case '2':			// 2- 
 | 
						||
	case '3':			// 3- 
 | 
						||
	case '4':			// 4- 
 | 
						||
	case '5':			// 5- 
 | 
						||
	case '6':			// 6- 
 | 
						||
	case '7':			// 7- 
 | 
						||
	case '8':			// 8- 
 | 
						||
	case '9':			// 9- 
 | 
						||
		if (c == '0' && cmdcnt < 1) {
 | 
						||
			dot_begin();	// this was a standalone zero
 | 
						||
		} else {
 | 
						||
			cmdcnt = cmdcnt * 10 + (c - '0');	// this 0 is part of a number
 | 
						||
		}
 | 
						||
		break;
 | 
						||
	case ':':			// :- the colon mode commands
 | 
						||
		p = get_input_line((Byte *) ":");	// get input line- use "status line"
 | 
						||
#ifdef CONFIG_FEATURE_VI_COLON
 | 
						||
		colon(p);		// execute the command
 | 
						||
#else							/* CONFIG_FEATURE_VI_COLON */
 | 
						||
		if (*p == ':')
 | 
						||
			p++;				// move past the ':'
 | 
						||
		cnt = strlen((char *) p);
 | 
						||
		if (cnt <= 0)
 | 
						||
			break;
 | 
						||
		if (strncasecmp((char *) p, "quit", cnt) == 0 ||
 | 
						||
			strncasecmp((char *) p, "q!", cnt) == 0) {	// delete lines
 | 
						||
			if (file_modified && p[1] != '!') {
 | 
						||
				psbs("No write since last change (:quit! overrides)");
 | 
						||
			} else {
 | 
						||
				editing = 0;
 | 
						||
			}
 | 
						||
		} else if (strncasecmp((char *) p, "write", cnt) == 0 ||
 | 
						||
				   strncasecmp((char *) p, "wq", cnt) == 0) {
 | 
						||
			cnt = file_write(cfn, text, end - 1);
 | 
						||
			file_modified = FALSE;
 | 
						||
			psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt);
 | 
						||
			if (p[1] == 'q') {
 | 
						||
				editing = 0;
 | 
						||
			}
 | 
						||
		} else if (strncasecmp((char *) p, "file", cnt) == 0 ) {
 | 
						||
			edit_status();			// show current file status
 | 
						||
		} else if (sscanf((char *) p, "%d", &j) > 0) {
 | 
						||
			dot = find_line(j);		// go to line # j
 | 
						||
			dot_skip_over_ws();
 | 
						||
		} else {		// unrecognised cmd
 | 
						||
			ni((Byte *) p);
 | 
						||
		}
 | 
						||
#endif							/* CONFIG_FEATURE_VI_COLON */
 | 
						||
		break;
 | 
						||
	case '<':			// <- Left  shift something
 | 
						||
	case '>':			// >- Right shift something
 | 
						||
		cnt = count_lines(text, dot);	// remember what line we are on
 | 
						||
		c1 = get_one_char();	// get the type of thing to delete
 | 
						||
		find_range(&p, &q, c1);
 | 
						||
		(void) yank_delete(p, q, 1, YANKONLY);	// save copy before change
 | 
						||
		p = begin_line(p);
 | 
						||
		q = end_line(q);
 | 
						||
		i = count_lines(p, q);	// # of lines we are shifting
 | 
						||
		for ( ; i > 0; i--, p = next_line(p)) {
 | 
						||
			if (c == '<') {
 | 
						||
				// shift left- remove tab or 8 spaces
 | 
						||
				if (*p == '\t') {
 | 
						||
					// shrink buffer 1 char
 | 
						||
					(void) text_hole_delete(p, p);
 | 
						||
				} else if (*p == ' ') {
 | 
						||
					// we should be calculating columns, not just SPACE
 | 
						||
					for (j = 0; *p == ' ' && j < tabstop; j++) {
 | 
						||
						(void) text_hole_delete(p, p);
 | 
						||
					}
 | 
						||
				}
 | 
						||
			} else if (c == '>') {
 | 
						||
				// shift right -- add tab or 8 spaces
 | 
						||
				(void) char_insert(p, '\t');
 | 
						||
			}
 | 
						||
		}
 | 
						||
		dot = find_line(cnt);	// what line were we on
 | 
						||
		dot_skip_over_ws();
 | 
						||
		end_cmd_q();	// stop adding to q
 | 
						||
		break;
 | 
						||
	case 'A':			// A- append at e-o-l
 | 
						||
		dot_end();		// go to e-o-l
 | 
						||
		//**** fall thru to ... 'a'
 | 
						||
	case 'a':			// a- append after current char
 | 
						||
		if (*dot != '\n')
 | 
						||
			dot++;
 | 
						||
		goto dc_i;
 | 
						||
		break;
 | 
						||
	case 'B':			// B- back a blank-delimited Word
 | 
						||
	case 'E':			// E- end of a blank-delimited word
 | 
						||
	case 'W':			// W- forward a blank-delimited word
 | 
						||
		if (cmdcnt-- > 1) {
 | 
						||
			do_cmd(c);
 | 
						||
		}				// repeat cnt
 | 
						||
		dir = FORWARD;
 | 
						||
		if (c == 'B')
 | 
						||
			dir = BACK;
 | 
						||
		if (c == 'W' || isspace(dot[dir])) {
 | 
						||
			dot = skip_thing(dot, 1, dir, S_TO_WS);
 | 
						||
			dot = skip_thing(dot, 2, dir, S_OVER_WS);
 | 
						||
		}
 | 
						||
		if (c != 'W')
 | 
						||
			dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
 | 
						||
		break;
 | 
						||
	case 'C':			// C- Change to e-o-l
 | 
						||
	case 'D':			// D- delete to e-o-l
 | 
						||
		save_dot = dot;
 | 
						||
		dot = dollar_line(dot);	// move to before NL
 | 
						||
		// copy text into a register and delete
 | 
						||
		dot = yank_delete(save_dot, dot, 0, YANKDEL);	// delete to e-o-l
 | 
						||
		if (c == 'C')
 | 
						||
			goto dc_i;	// start inserting
 | 
						||
#ifdef CONFIG_FEATURE_VI_DOT_CMD
 | 
						||
		if (c == 'D')
 | 
						||
			end_cmd_q();	// stop adding to q
 | 
						||
#endif							/* CONFIG_FEATURE_VI_DOT_CMD */
 | 
						||
		break;
 | 
						||
	case 'G':		// G- goto to a line number (default= E-O-F)
 | 
						||
		dot = end - 1;				// assume E-O-F
 | 
						||
		if (cmdcnt > 0) {
 | 
						||
			dot = find_line(cmdcnt);	// what line is #cmdcnt
 | 
						||
		}
 | 
						||
		dot_skip_over_ws();
 | 
						||
		break;
 | 
						||
	case 'H':			// H- goto top line on screen
 | 
						||
		dot = screenbegin;
 | 
						||
		if (cmdcnt > (rows - 1)) {
 | 
						||
			cmdcnt = (rows - 1);
 | 
						||
		}
 | 
						||
		if (cmdcnt-- > 1) {
 | 
						||
			do_cmd('+');
 | 
						||
		}				// repeat cnt
 | 
						||
		dot_skip_over_ws();
 | 
						||
		break;
 | 
						||
	case 'I':			// I- insert before first non-blank
 | 
						||
		dot_begin();	// 0
 | 
						||
		dot_skip_over_ws();
 | 
						||
		//**** fall thru to ... 'i'
 | 
						||
	case 'i':			// i- insert before current char
 | 
						||
	case VI_K_INSERT:	// Cursor Key Insert
 | 
						||
	  dc_i:
 | 
						||
		cmd_mode = 1;	// start insrting
 | 
						||
		psb("-- Insert --");
 | 
						||
		break;
 | 
						||
	case 'J':			// J- join current and next lines together
 | 
						||
		if (cmdcnt-- > 2) {
 | 
						||
			do_cmd(c);
 | 
						||
		}				// repeat cnt
 | 
						||
		dot_end();		// move to NL
 | 
						||
		if (dot < end - 1) {	// make sure not last char in text[]
 | 
						||
			*dot++ = ' ';	// replace NL with space
 | 
						||
			while (isblnk(*dot)) {	// delete leading WS
 | 
						||
				dot_delete();
 | 
						||
			}
 | 
						||
		}
 | 
						||
		end_cmd_q();	// stop adding to q
 | 
						||
		break;
 | 
						||
	case 'L':			// L- goto bottom line on screen
 | 
						||
		dot = end_screen();
 | 
						||
		if (cmdcnt > (rows - 1)) {
 | 
						||
			cmdcnt = (rows - 1);
 | 
						||
		}
 | 
						||
		if (cmdcnt-- > 1) {
 | 
						||
			do_cmd('-');
 | 
						||
		}				// repeat cnt
 | 
						||
		dot_begin();
 | 
						||
		dot_skip_over_ws();
 | 
						||
		break;
 | 
						||
	case 'M':			// M- goto middle line on screen
 | 
						||
		dot = screenbegin;
 | 
						||
		for (cnt = 0; cnt < (rows-1) / 2; cnt++)
 | 
						||
			dot = next_line(dot);
 | 
						||
		break;
 | 
						||
	case 'O':			// O- open a empty line above
 | 
						||
		//    0i\n ESC -i
 | 
						||
		p = begin_line(dot);
 | 
						||
		if (p[-1] == '\n') {
 | 
						||
			dot_prev();
 | 
						||
	case 'o':			// o- open a empty line below; Yes, I know it is in the middle of the "if (..."
 | 
						||
			dot_end();
 | 
						||
			dot = char_insert(dot, '\n');
 | 
						||
		} else {
 | 
						||
			dot_begin();	// 0
 | 
						||
			dot = char_insert(dot, '\n');	// i\n ESC
 | 
						||
			dot_prev();	// -
 | 
						||
		}
 | 
						||
		goto dc_i;
 | 
						||
		break;
 | 
						||
	case 'R':			// R- continuous Replace char
 | 
						||
	  dc5:
 | 
						||
		cmd_mode = 2;
 | 
						||
		psb("-- Replace --");
 | 
						||
		break;
 | 
						||
	case 'X':			// X- delete char before dot
 | 
						||
	case 'x':			// x- delete the current char
 | 
						||
	case 's':			// s- substitute the current char
 | 
						||
		if (cmdcnt-- > 1) {
 | 
						||
			do_cmd(c);
 | 
						||
		}				// repeat cnt
 | 
						||
		dir = 0;
 | 
						||
		if (c == 'X')
 | 
						||
			dir = -1;
 | 
						||
		if (dot[dir] != '\n') {
 | 
						||
			if (c == 'X')
 | 
						||
				dot--;	// delete prev char
 | 
						||
			dot = yank_delete(dot, dot, 0, YANKDEL);	// delete char
 | 
						||
		}
 | 
						||
		if (c == 's')
 | 
						||
			goto dc_i;	// start insrting
 | 
						||
		end_cmd_q();	// stop adding to q
 | 
						||
		break;
 | 
						||
	case 'Z':			// Z- if modified, {write}; exit
 | 
						||
		// ZZ means to save file (if necessary), then exit
 | 
						||
		c1 = get_one_char();
 | 
						||
		if (c1 != 'Z') {
 | 
						||
			indicate_error(c);
 | 
						||
			break;
 | 
						||
		}
 | 
						||
		if (file_modified
 | 
						||
#ifdef CONFIG_FEATURE_VI_READONLY
 | 
						||
			&& ! vi_readonly
 | 
						||
			&& ! readonly
 | 
						||
#endif							/* CONFIG_FEATURE_VI_READONLY */
 | 
						||
			) {
 | 
						||
			cnt = file_write(cfn, text, end - 1);
 | 
						||
			if (cnt == (end - 1 - text + 1)) {
 | 
						||
				editing = 0;
 | 
						||
			}
 | 
						||
		} else {
 | 
						||
			editing = 0;
 | 
						||
		}
 | 
						||
		break;
 | 
						||
	case '^':			// ^- move to first non-blank on line
 | 
						||
		dot_begin();
 | 
						||
		dot_skip_over_ws();
 | 
						||
		break;
 | 
						||
	case 'b':			// b- back a word
 | 
						||
	case 'e':			// e- end of word
 | 
						||
		if (cmdcnt-- > 1) {
 | 
						||
			do_cmd(c);
 | 
						||
		}				// repeat cnt
 | 
						||
		dir = FORWARD;
 | 
						||
		if (c == 'b')
 | 
						||
			dir = BACK;
 | 
						||
		if ((dot + dir) < text || (dot + dir) > end - 1)
 | 
						||
			break;
 | 
						||
		dot += dir;
 | 
						||
		if (isspace(*dot)) {
 | 
						||
			dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
 | 
						||
		}
 | 
						||
		if (isalnum(*dot) || *dot == '_') {
 | 
						||
			dot = skip_thing(dot, 1, dir, S_END_ALNUM);
 | 
						||
		} else if (ispunct(*dot)) {
 | 
						||
			dot = skip_thing(dot, 1, dir, S_END_PUNCT);
 | 
						||
		}
 | 
						||
		break;
 | 
						||
	case 'c':			// c- change something
 | 
						||
	case 'd':			// d- delete something
 | 
						||
#ifdef CONFIG_FEATURE_VI_YANKMARK
 | 
						||
	case 'y':			// y- yank   something
 | 
						||
	case 'Y':			// Y- Yank a line
 | 
						||
#endif							/* CONFIG_FEATURE_VI_YANKMARK */
 | 
						||
		yf = YANKDEL;	// assume either "c" or "d"
 | 
						||
#ifdef CONFIG_FEATURE_VI_YANKMARK
 | 
						||
		if (c == 'y' || c == 'Y')
 | 
						||
			yf = YANKONLY;
 | 
						||
#endif							/* CONFIG_FEATURE_VI_YANKMARK */
 | 
						||
		c1 = 'y';
 | 
						||
		if (c != 'Y')
 | 
						||
			c1 = get_one_char();	// get the type of thing to delete
 | 
						||
		find_range(&p, &q, c1);
 | 
						||
		if (c1 == 27) {	// ESC- user changed mind and wants out
 | 
						||
			c = c1 = 27;	// Escape- do nothing
 | 
						||
		} else if (strchr("wW", c1)) {
 | 
						||
			if (c == 'c') {
 | 
						||
				// don't include trailing WS as part of word
 | 
						||
				while (isblnk(*q)) {
 | 
						||
					if (q <= text || q[-1] == '\n')
 | 
						||
						break;
 | 
						||
					q--;
 | 
						||
				}
 | 
						||
			}
 | 
						||
			dot = yank_delete(p, q, 0, yf);	// delete word
 | 
						||
		} else if (strchr("^0bBeEft$", c1)) {
 | 
						||
			// single line copy text into a register and delete
 | 
						||
			dot = yank_delete(p, q, 0, yf);	// delete word
 | 
						||
		} else if (strchr("cdykjHL%+-{}\r\n", c1)) {
 | 
						||
			// multiple line copy text into a register and delete
 | 
						||
			dot = yank_delete(p, q, 1, yf);	// delete lines
 | 
						||
			if (c == 'c') {
 | 
						||
				dot = char_insert(dot, '\n');
 | 
						||
				// on the last line of file don't move to prev line
 | 
						||
				if (dot != (end-1)) {
 | 
						||
					dot_prev();
 | 
						||
				}
 | 
						||
			} else if (c == 'd') {
 | 
						||
				dot_begin();
 | 
						||
				dot_skip_over_ws();
 | 
						||
			}
 | 
						||
		} else {
 | 
						||
			// could not recognize object
 | 
						||
			c = c1 = 27;	// error-
 | 
						||
			indicate_error(c);
 | 
						||
		}
 | 
						||
		if (c1 != 27) {
 | 
						||
			// if CHANGING, not deleting, start inserting after the delete
 | 
						||
			if (c == 'c') {
 | 
						||
				strcpy((char *) buf, "Change");
 | 
						||
				goto dc_i;	// start inserting
 | 
						||
			}
 | 
						||
			if (c == 'd') {
 | 
						||
				strcpy((char *) buf, "Delete");
 | 
						||
			}
 | 
						||
#ifdef CONFIG_FEATURE_VI_YANKMARK
 | 
						||
			if (c == 'y' || c == 'Y') {
 | 
						||
				strcpy((char *) buf, "Yank");
 | 
						||
			}
 | 
						||
			p = reg[YDreg];
 | 
						||
			q = p + strlen((char *) p);
 | 
						||
			for (cnt = 0; p <= q; p++) {
 | 
						||
				if (*p == '\n')
 | 
						||
					cnt++;
 | 
						||
			}
 | 
						||
			psb("%s %d lines (%d chars) using [%c]",
 | 
						||
				buf, cnt, strlen((char *) reg[YDreg]), what_reg());
 | 
						||
#endif							/* CONFIG_FEATURE_VI_YANKMARK */
 | 
						||
			end_cmd_q();	// stop adding to q
 | 
						||
		}
 | 
						||
		break;
 | 
						||
	case 'k':			// k- goto prev line, same col
 | 
						||
	case VI_K_UP:		// cursor key Up
 | 
						||
		if (cmdcnt-- > 1) {
 | 
						||
			do_cmd(c);
 | 
						||
		}				// repeat cnt
 | 
						||
		dot_prev();
 | 
						||
		dot = move_to_col(dot, ccol + offset);	// try stay in same col
 | 
						||
		break;
 | 
						||
	case 'r':			// r- replace the current char with user input
 | 
						||
		c1 = get_one_char();	// get the replacement char
 | 
						||
		if (*dot != '\n') {
 | 
						||
			*dot = c1;
 | 
						||
			file_modified = TRUE;	// has the file been modified
 | 
						||
		}
 | 
						||
		end_cmd_q();	// stop adding to q
 | 
						||
		break;
 | 
						||
	case 't':			// t- move to char prior to next x
 | 
						||
                last_forward_char = get_one_char();
 | 
						||
                do_cmd(';');
 | 
						||
                if (*dot == last_forward_char)
 | 
						||
                        dot_left();
 | 
						||
                last_forward_char= 0;
 | 
						||
		break;
 | 
						||
	case 'w':			// w- forward a word
 | 
						||
		if (cmdcnt-- > 1) {
 | 
						||
			do_cmd(c);
 | 
						||
		}				// repeat cnt
 | 
						||
		if (isalnum(*dot) || *dot == '_') {	// we are on ALNUM
 | 
						||
			dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
 | 
						||
		} else if (ispunct(*dot)) {	// we are on PUNCT
 | 
						||
			dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
 | 
						||
		}
 | 
						||
		if (dot < end - 1)
 | 
						||
			dot++;		// move over word
 | 
						||
		if (isspace(*dot)) {
 | 
						||
			dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
 | 
						||
		}
 | 
						||
		break;
 | 
						||
	case 'z':			// z-
 | 
						||
		c1 = get_one_char();	// get the replacement char
 | 
						||
		cnt = 0;
 | 
						||
		if (c1 == '.')
 | 
						||
			cnt = (rows - 2) / 2;	// put dot at center
 | 
						||
		if (c1 == '-')
 | 
						||
			cnt = rows - 2;	// put dot at bottom
 | 
						||
		screenbegin = begin_line(dot);	// start dot at top
 | 
						||
		dot_scroll(cnt, -1);
 | 
						||
		break;
 | 
						||
	case '|':			// |- move to column "cmdcnt"
 | 
						||
		dot = move_to_col(dot, cmdcnt - 1);	// try to move to column
 | 
						||
		break;
 | 
						||
	case '~':			// ~- flip the case of letters   a-z -> A-Z
 | 
						||
		if (cmdcnt-- > 1) {
 | 
						||
			do_cmd(c);
 | 
						||
		}				// repeat cnt
 | 
						||
		if (islower(*dot)) {
 | 
						||
			*dot = toupper(*dot);
 | 
						||
			file_modified = TRUE;	// has the file been modified
 | 
						||
		} else if (isupper(*dot)) {
 | 
						||
			*dot = tolower(*dot);
 | 
						||
			file_modified = TRUE;	// has the file been modified
 | 
						||
		}
 | 
						||
		dot_right();
 | 
						||
		end_cmd_q();	// stop adding to q
 | 
						||
		break;
 | 
						||
		//----- The Cursor and Function Keys -----------------------------
 | 
						||
	case VI_K_HOME:	// Cursor Key Home
 | 
						||
		dot_begin();
 | 
						||
		break;
 | 
						||
		// The Fn keys could point to do_macro which could translate them
 | 
						||
	case VI_K_FUN1:	// Function Key F1
 | 
						||
	case VI_K_FUN2:	// Function Key F2
 | 
						||
	case VI_K_FUN3:	// Function Key F3
 | 
						||
	case VI_K_FUN4:	// Function Key F4
 | 
						||
	case VI_K_FUN5:	// Function Key F5
 | 
						||
	case VI_K_FUN6:	// Function Key F6
 | 
						||
	case VI_K_FUN7:	// Function Key F7
 | 
						||
	case VI_K_FUN8:	// Function Key F8
 | 
						||
	case VI_K_FUN9:	// Function Key F9
 | 
						||
	case VI_K_FUN10:	// Function Key F10
 | 
						||
	case VI_K_FUN11:	// Function Key F11
 | 
						||
	case VI_K_FUN12:	// Function Key F12
 | 
						||
		break;
 | 
						||
	}
 | 
						||
 | 
						||
  dc1:
 | 
						||
	// if text[] just became empty, add back an empty line
 | 
						||
	if (end == text) {
 | 
						||
		(void) char_insert(text, '\n');	// start empty buf with dummy line
 | 
						||
		dot = text;
 | 
						||
	}
 | 
						||
	// it is OK for dot to exactly equal to end, otherwise check dot validity
 | 
						||
	if (dot != end) {
 | 
						||
		dot = bound_dot(dot);	// make sure "dot" is valid
 | 
						||
	}
 | 
						||
#ifdef CONFIG_FEATURE_VI_YANKMARK
 | 
						||
	check_context(c);	// update the current context
 | 
						||
#endif							/* CONFIG_FEATURE_VI_YANKMARK */
 | 
						||
 | 
						||
	if (!isdigit(c))
 | 
						||
		cmdcnt = 0;		// cmd was not a number, reset cmdcnt
 | 
						||
	cnt = dot - begin_line(dot);
 | 
						||
	// Try to stay off of the Newline
 | 
						||
	if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
 | 
						||
		dot--;
 | 
						||
}
 | 
						||
 | 
						||
//----- The Colon commands -------------------------------------
 | 
						||
#ifdef CONFIG_FEATURE_VI_COLON
 | 
						||
static Byte *get_one_address(Byte * p, int *addr)	// get colon addr, if present
 | 
						||
{
 | 
						||
	int st;
 | 
						||
	Byte *q;
 | 
						||
 | 
						||
#ifdef CONFIG_FEATURE_VI_YANKMARK
 | 
						||
	Byte c;
 | 
						||
#endif							/* CONFIG_FEATURE_VI_YANKMARK */
 | 
						||
#ifdef CONFIG_FEATURE_VI_SEARCH
 | 
						||
	Byte *pat, buf[BUFSIZ];
 | 
						||
#endif							/* CONFIG_FEATURE_VI_SEARCH */
 | 
						||
 | 
						||
	*addr = -1;			// assume no addr
 | 
						||
	if (*p == '.') {	// the current line
 | 
						||
		p++;
 | 
						||
		q = begin_line(dot);
 | 
						||
		*addr = count_lines(text, q);
 | 
						||
#ifdef CONFIG_FEATURE_VI_YANKMARK
 | 
						||
	} else if (*p == '\'') {	// is this a mark addr
 | 
						||
		p++;
 | 
						||
		c = tolower(*p);
 | 
						||
		p++;
 | 
						||
		if (c >= 'a' && c <= 'z') {
 | 
						||
			// we have a mark
 | 
						||
			c = c - 'a';
 | 
						||
			q = mark[(int) c];
 | 
						||
			if (q != NULL) {	// is mark valid
 | 
						||
				*addr = count_lines(text, q);	// count lines
 | 
						||
			}
 | 
						||
		}
 | 
						||
#endif							/* CONFIG_FEATURE_VI_YANKMARK */
 | 
						||
#ifdef CONFIG_FEATURE_VI_SEARCH
 | 
						||
	} else if (*p == '/') {	// a search pattern
 | 
						||
		q = buf;
 | 
						||
		for (p++; *p; p++) {
 | 
						||
			if (*p == '/')
 | 
						||
				break;
 | 
						||
			*q++ = *p;
 | 
						||
			*q = '\0';
 | 
						||
		}
 | 
						||
		pat = (Byte *) xstrdup((char *) buf);	// save copy of pattern
 | 
						||
		if (*p == '/')
 | 
						||
			p++;
 | 
						||
		q = char_search(dot, pat, FORWARD, FULL);
 | 
						||
		if (q != NULL) {
 | 
						||
			*addr = count_lines(text, q);
 | 
						||
		}
 | 
						||
		free(pat);
 | 
						||
#endif							/* CONFIG_FEATURE_VI_SEARCH */
 | 
						||
	} else if (*p == '$') {	// the last line in file
 | 
						||
		p++;
 | 
						||
		q = begin_line(end - 1);
 | 
						||
		*addr = count_lines(text, q);
 | 
						||
	} else if (isdigit(*p)) {	// specific line number
 | 
						||
		sscanf((char *) p, "%d%n", addr, &st);
 | 
						||
		p += st;
 | 
						||
	} else {			// I don't reconise this
 | 
						||
		// unrecognised address- assume -1
 | 
						||
		*addr = -1;
 | 
						||
	}
 | 
						||
	return (p);
 | 
						||
}
 | 
						||
 | 
						||
static Byte *get_address(Byte *p, int *b, int *e)	// get two colon addrs, if present
 | 
						||
{
 | 
						||
	//----- get the address' i.e., 1,3   'a,'b  -----
 | 
						||
	// get FIRST addr, if present
 | 
						||
	while (isblnk(*p))
 | 
						||
		p++;				// skip over leading spaces
 | 
						||
	if (*p == '%') {			// alias for 1,$
 | 
						||
		p++;
 | 
						||
		*b = 1;
 | 
						||
		*e = count_lines(text, end-1);
 | 
						||
		goto ga0;
 | 
						||
	}
 | 
						||
	p = get_one_address(p, b);
 | 
						||
	while (isblnk(*p))
 | 
						||
		p++;
 | 
						||
	if (*p == ',') {			// is there a address seperator
 | 
						||
		p++;
 | 
						||
		while (isblnk(*p))
 | 
						||
			p++;
 | 
						||
		// get SECOND addr, if present
 | 
						||
		p = get_one_address(p, e);
 | 
						||
	}
 | 
						||
ga0:
 | 
						||
	while (isblnk(*p))
 | 
						||
		p++;				// skip over trailing spaces
 | 
						||
	return (p);
 | 
						||
}
 | 
						||
 | 
						||
static void colon(Byte * buf)
 | 
						||
{
 | 
						||
	Byte c, *orig_buf, *buf1, *q, *r;
 | 
						||
	Byte *fn, cmd[BUFSIZ], args[BUFSIZ];
 | 
						||
	int i, l, li, ch, st, b, e;
 | 
						||
	int useforce, forced;
 | 
						||
	struct stat st_buf;
 | 
						||
 | 
						||
	// :3154	// if (-e line 3154) goto it  else stay put
 | 
						||
	// :4,33w! foo	// write a portion of buffer to file "foo"
 | 
						||
	// :w		// write all of buffer to current file
 | 
						||
	// :q		// quit
 | 
						||
	// :q!		// quit- dont care about modified file
 | 
						||
	// :'a,'z!sort -u   // filter block through sort
 | 
						||
	// :'f		// goto mark "f"
 | 
						||
	// :'fl		// list literal the mark "f" line
 | 
						||
	// :.r bar	// read file "bar" into buffer before dot
 | 
						||
	// :/123/,/abc/d    // delete lines from "123" line to "abc" line
 | 
						||
	// :/xyz/	// goto the "xyz" line
 | 
						||
	// :s/find/replace/ // substitute pattern "find" with "replace"
 | 
						||
	// :!<cmd>	// run <cmd> then return
 | 
						||
	//
 | 
						||
	if (strlen((char *) buf) <= 0)
 | 
						||
		goto vc1;
 | 
						||
	if (*buf == ':')
 | 
						||
		buf++;			// move past the ':'
 | 
						||
 | 
						||
	forced = useforce = FALSE;
 | 
						||
	li = st = ch = i = 0;
 | 
						||
	b = e = -1;
 | 
						||
	q = text;			// assume 1,$ for the range
 | 
						||
	r = end - 1;
 | 
						||
	li = count_lines(text, end - 1);
 | 
						||
	fn = cfn;			// default to current file
 | 
						||
	memset(cmd, '\0', BUFSIZ);	// clear cmd[]
 | 
						||
	memset(args, '\0', BUFSIZ);	// clear args[]
 | 
						||
 | 
						||
	// look for optional address(es)  :.  :1  :1,9   :'q,'a   :%
 | 
						||
	buf = get_address(buf, &b, &e);
 | 
						||
 | 
						||
	// remember orig command line
 | 
						||
	orig_buf = buf;
 | 
						||
 | 
						||
	// get the COMMAND into cmd[]
 | 
						||
	buf1 = cmd;
 | 
						||
	while (*buf != '\0') {
 | 
						||
		if (isspace(*buf))
 | 
						||
			break;
 | 
						||
		*buf1++ = *buf++;
 | 
						||
	}
 | 
						||
	// get any ARGuments
 | 
						||
	while (isblnk(*buf))
 | 
						||
		buf++;
 | 
						||
	strcpy((char *) args, (char *) buf);
 | 
						||
	buf1 = last_char_is((char *)cmd, '!');
 | 
						||
	if (buf1) {
 | 
						||
		useforce = TRUE;
 | 
						||
		*buf1 = '\0';   // get rid of !
 | 
						||
	}
 | 
						||
	if (b >= 0) {
 | 
						||
		// if there is only one addr, then the addr
 | 
						||
		// is the line number of the single line the
 | 
						||
		// user wants. So, reset the end
 | 
						||
		// pointer to point at end of the "b" line
 | 
						||
		q = find_line(b);	// what line is #b
 | 
						||
		r = end_line(q);
 | 
						||
		li = 1;
 | 
						||
	}
 | 
						||
	if (e >= 0) {
 | 
						||
		// we were given two addrs.  change the
 | 
						||
		// end pointer to the addr given by user.
 | 
						||
		r = find_line(e);	// what line is #e
 | 
						||
		r = end_line(r);
 | 
						||
		li = e - b + 1;
 | 
						||
	}
 | 
						||
	// ------------ now look for the command ------------
 | 
						||
	i = strlen((char *) cmd);
 | 
						||
	if (i == 0) {		// :123CR goto line #123
 | 
						||
		if (b >= 0) {
 | 
						||
			dot = find_line(b);	// what line is #b
 | 
						||
			dot_skip_over_ws();
 | 
						||
		}
 | 
						||
	} else if (strncmp((char *) cmd, "!", 1) == 0) {	// run a cmd
 | 
						||
		// :!ls   run the <cmd>
 | 
						||
		(void) alarm(0);		// wait for input- no alarms
 | 
						||
		place_cursor(rows - 1, 0, FALSE);	// go to Status line
 | 
						||
		clear_to_eol();			// clear the line
 | 
						||
		cookmode();
 | 
						||
		system(orig_buf+1);		// run the cmd
 | 
						||
		rawmode();
 | 
						||
		Hit_Return();			// let user see results
 | 
						||
		(void) alarm(3);		// done waiting for input
 | 
						||
	} else if (strncmp((char *) cmd, "=", i) == 0) {	// where is the address
 | 
						||
		if (b < 0) {	// no addr given- use defaults
 | 
						||
			b = e = count_lines(text, dot);
 | 
						||
		}
 | 
						||
		psb("%d", b);
 | 
						||
	} else if (strncasecmp((char *) cmd, "delete", i) == 0) {	// delete lines
 | 
						||
		if (b < 0) {	// no addr given- use defaults
 | 
						||
			q = begin_line(dot);	// assume .,. for the range
 | 
						||
			r = end_line(dot);
 | 
						||
		}
 | 
						||
		dot = yank_delete(q, r, 1, YANKDEL);	// save, then delete lines
 | 
						||
		dot_skip_over_ws();
 | 
						||
	} else if (strncasecmp((char *) cmd, "edit", i) == 0) {	// Edit a file
 | 
						||
		int sr;
 | 
						||
		sr= 0;
 | 
						||
		// don't edit, if the current file has been modified
 | 
						||
		if (file_modified && ! useforce) {
 | 
						||
			psbs("No write since last change (:edit! overrides)");
 | 
						||
			goto vc1;
 | 
						||
		}
 | 
						||
		if (strlen(args) > 0) {
 | 
						||
			// the user supplied a file name
 | 
						||
			fn= args;
 | 
						||
		} else if (cfn != 0 && strlen(cfn) > 0) {
 | 
						||
			// no user supplied name- use the current filename
 | 
						||
			fn= cfn;
 | 
						||
			goto vc5;
 | 
						||
		} else {
 | 
						||
			// no user file name, no current name- punt
 | 
						||
			psbs("No current filename");
 | 
						||
			goto vc1;
 | 
						||
		}
 | 
						||
 | 
						||
		// see if file exists- if not, its just a new file request
 | 
						||
		if ((sr=stat((char*)fn, &st_buf)) < 0) {
 | 
						||
			// This is just a request for a new file creation.
 | 
						||
			// The file_insert below will fail but we get
 | 
						||
			// an empty buffer with a file name.  Then the "write"
 | 
						||
			// command can do the create.
 | 
						||
		} else {
 | 
						||
			if ((st_buf.st_mode & (S_IFREG)) == 0) {
 | 
						||
				// This is not a regular file
 | 
						||
				psbs("\"%s\" is not a regular file", fn);
 | 
						||
				goto vc1;
 | 
						||
			}
 | 
						||
			if ((st_buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
 | 
						||
				// dont have any read permissions
 | 
						||
				psbs("\"%s\" is not readable", fn);
 | 
						||
				goto vc1;
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// There is a read-able regular file
 | 
						||
		// make this the current file
 | 
						||
		q = (Byte *) xstrdup((char *) fn);	// save the cfn
 | 
						||
		if (cfn != 0)
 | 
						||
			free(cfn);		// free the old name
 | 
						||
		cfn = q;			// remember new cfn
 | 
						||
 | 
						||
	  vc5:
 | 
						||
		// delete all the contents of text[]
 | 
						||
		new_text(2 * file_size(fn));
 | 
						||
		screenbegin = dot = end = text;
 | 
						||
 | 
						||
		// insert new file
 | 
						||
		ch = file_insert(fn, text, file_size(fn));
 | 
						||
 | 
						||
		if (ch < 1) {
 | 
						||
			// start empty buf with dummy line
 | 
						||
			(void) char_insert(text, '\n');
 | 
						||
			ch= 1;
 | 
						||
		}
 | 
						||
		file_modified = FALSE;
 | 
						||
#ifdef CONFIG_FEATURE_VI_YANKMARK
 | 
						||
		if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
 | 
						||
			free(reg[Ureg]);	//   free orig line reg- for 'U'
 | 
						||
			reg[Ureg]= 0;
 | 
						||
		}
 | 
						||
		if (YDreg >= 0 && YDreg < 28 && reg[YDreg] != 0) {
 | 
						||
			free(reg[YDreg]);	//   free default yank/delete register
 | 
						||
			reg[YDreg]= 0;
 | 
						||
		}
 | 
						||
		for (li = 0; li < 28; li++) {
 | 
						||
			mark[li] = 0;
 | 
						||
		}				// init the marks
 | 
						||
#endif							/* CONFIG_FEATURE_VI_YANKMARK */
 | 
						||
		// how many lines in text[]?
 | 
						||
		li = count_lines(text, end - 1);
 | 
						||
		psb("\"%s\"%s"
 | 
						||
#ifdef CONFIG_FEATURE_VI_READONLY
 | 
						||
			"%s"
 | 
						||
#endif							/* CONFIG_FEATURE_VI_READONLY */
 | 
						||
			" %dL, %dC", cfn,
 | 
						||
			(sr < 0 ? " [New file]" : ""),
 | 
						||
#ifdef CONFIG_FEATURE_VI_READONLY
 | 
						||
			((vi_readonly || readonly) ? " [Read only]" : ""),
 | 
						||
#endif							/* CONFIG_FEATURE_VI_READONLY */
 | 
						||
			li, ch);
 | 
						||
	} else if (strncasecmp((char *) cmd, "file", i) == 0) {	// what File is this
 | 
						||
		if (b != -1 || e != -1) {
 | 
						||
			ni((Byte *) "No address allowed on this command");
 | 
						||
			goto vc1;
 | 
						||
		}
 | 
						||
		if (strlen((char *) args) > 0) {
 | 
						||
			// user wants a new filename
 | 
						||
			if (cfn != NULL)
 | 
						||
				free(cfn);
 | 
						||
			cfn = (Byte *) xstrdup((char *) args);
 | 
						||
		} else {
 | 
						||
			// user wants file status info
 | 
						||
			edit_status();
 | 
						||
		}
 | 
						||
	} else if (strncasecmp((char *) cmd, "features", i) == 0) {	// what features are available
 | 
						||
		// print out values of all features
 | 
						||
		place_cursor(rows - 1, 0, FALSE);	// go to Status line, bottom of screen
 | 
						||
		clear_to_eol();	// clear the line
 | 
						||
		cookmode();
 | 
						||
		show_help();
 | 
						||
		rawmode();
 | 
						||
		Hit_Return();
 | 
						||
	} else if (strncasecmp((char *) cmd, "list", i) == 0) {	// literal print line
 | 
						||
		if (b < 0) {	// no addr given- use defaults
 | 
						||
			q = begin_line(dot);	// assume .,. for the range
 | 
						||
			r = end_line(dot);
 | 
						||
		}
 | 
						||
		place_cursor(rows - 1, 0, FALSE);	// go to Status line, bottom of screen
 | 
						||
		clear_to_eol();	// clear the line
 | 
						||
		write(1, "\r\n", 2);
 | 
						||
		for (; q <= r; q++) {
 | 
						||
			c = *q;
 | 
						||
			if (c > '~')
 | 
						||
				standout_start();
 | 
						||
			if (c == '\n') {
 | 
						||
				write(1, "$\r", 2);
 | 
						||
			} else if (*q < ' ') {
 | 
						||
				write(1, "^", 1);
 | 
						||
				c += '@';
 | 
						||
			}
 | 
						||
			write(1, &c, 1);
 | 
						||
			if (c > '~')
 | 
						||
				standout_end();
 | 
						||
		}
 | 
						||
#ifdef CONFIG_FEATURE_VI_SET
 | 
						||
	  vc2:
 | 
						||
#endif							/* CONFIG_FEATURE_VI_SET */
 | 
						||
		Hit_Return();
 | 
						||
	} else if ((strncasecmp((char *) cmd, "quit", i) == 0) ||	// Quit
 | 
						||
			   (strncasecmp((char *) cmd, "next", i) == 0)) {	// edit next file
 | 
						||
		if (useforce) {
 | 
						||
			// force end of argv list
 | 
						||
			if (*cmd == 'q') {
 | 
						||
				optind = save_argc;
 | 
						||
			}
 | 
						||
			editing = 0;
 | 
						||
			goto vc1;
 | 
						||
		}
 | 
						||
		// don't exit if the file been modified
 | 
						||
		if (file_modified) {
 | 
						||
			psbs("No write since last change (:%s! overrides)",
 | 
						||
				 (*cmd == 'q' ? "quit" : "next"));
 | 
						||
			goto vc1;
 | 
						||
		}
 | 
						||
		// are there other file to edit
 | 
						||
		if (*cmd == 'q' && optind < save_argc - 1) {
 | 
						||
			psbs("%d more file to edit", (save_argc - optind - 1));
 | 
						||
			goto vc1;
 | 
						||
		}
 | 
						||
		if (*cmd == 'n' && optind >= save_argc - 1) {
 | 
						||
			psbs("No more files to edit");
 | 
						||
			goto vc1;
 | 
						||
		}
 | 
						||
		editing = 0;
 | 
						||
	} else if (strncasecmp((char *) cmd, "read", i) == 0) {	// read file into text[]
 | 
						||
		fn = args;
 | 
						||
		if (strlen((char *) fn) <= 0) {
 | 
						||
			psbs("No filename given");
 | 
						||
			goto vc1;
 | 
						||
		}
 | 
						||
		if (b < 0) {	// no addr given- use defaults
 | 
						||
			q = begin_line(dot);	// assume "dot"
 | 
						||
		}
 | 
						||
		// read after current line- unless user said ":0r foo"
 | 
						||
		if (b != 0)
 | 
						||
			q = next_line(q);
 | 
						||
#ifdef CONFIG_FEATURE_VI_READONLY
 | 
						||
		l= readonly;			// remember current files' status
 | 
						||
#endif
 | 
						||
		ch = file_insert(fn, q, file_size(fn));
 | 
						||
#ifdef CONFIG_FEATURE_VI_READONLY
 | 
						||
		readonly= l;
 | 
						||
#endif
 | 
						||
		if (ch < 0)
 | 
						||
			goto vc1;	// nothing was inserted
 | 
						||
		// how many lines in text[]?
 | 
						||
		li = count_lines(q, q + ch - 1);
 | 
						||
		psb("\"%s\""
 | 
						||
#ifdef CONFIG_FEATURE_VI_READONLY
 | 
						||
			"%s"
 | 
						||
#endif							/* CONFIG_FEATURE_VI_READONLY */
 | 
						||
			" %dL, %dC", fn,
 | 
						||
#ifdef CONFIG_FEATURE_VI_READONLY
 | 
						||
			((vi_readonly || readonly) ? " [Read only]" : ""),
 | 
						||
#endif							/* CONFIG_FEATURE_VI_READONLY */
 | 
						||
			li, ch);
 | 
						||
		if (ch > 0) {
 | 
						||
			// if the insert is before "dot" then we need to update
 | 
						||
			if (q <= dot)
 | 
						||
				dot += ch;
 | 
						||
			file_modified = TRUE;
 | 
						||
		}
 | 
						||
	} else if (strncasecmp((char *) cmd, "rewind", i) == 0) {	// rewind cmd line args
 | 
						||
		if (file_modified && ! useforce) {
 | 
						||
			psbs("No write since last change (:rewind! overrides)");
 | 
						||
		} else {
 | 
						||
			// reset the filenames to edit
 | 
						||
			optind = fn_start - 1;
 | 
						||
			editing = 0;
 | 
						||
		}
 | 
						||
#ifdef CONFIG_FEATURE_VI_SET
 | 
						||
	} else if (strncasecmp((char *) cmd, "set", i) == 0) {	// set or clear features
 | 
						||
		i = 0;			// offset into args
 | 
						||
		if (strlen((char *) args) == 0) {
 | 
						||
			// print out values of all options
 | 
						||
			place_cursor(rows - 1, 0, FALSE);	// go to Status line, bottom of screen
 | 
						||
			clear_to_eol();	// clear the line
 | 
						||
			printf("----------------------------------------\r\n");
 | 
						||
#ifdef CONFIG_FEATURE_VI_SETOPTS
 | 
						||
			if (!autoindent)
 | 
						||
				printf("no");
 | 
						||
			printf("autoindent ");
 | 
						||
			if (!err_method)
 | 
						||
				printf("no");
 | 
						||
			printf("flash ");
 | 
						||
			if (!ignorecase)
 | 
						||
				printf("no");
 | 
						||
			printf("ignorecase ");
 | 
						||
			if (!showmatch)
 | 
						||
				printf("no");
 | 
						||
			printf("showmatch ");
 | 
						||
			printf("tabstop=%d ", tabstop);
 | 
						||
#endif							/* CONFIG_FEATURE_VI_SETOPTS */
 | 
						||
			printf("\r\n");
 | 
						||
			goto vc2;
 | 
						||
		}
 | 
						||
		if (strncasecmp((char *) args, "no", 2) == 0)
 | 
						||
			i = 2;		// ":set noautoindent"
 | 
						||
#ifdef CONFIG_FEATURE_VI_SETOPTS
 | 
						||
		if (strncasecmp((char *) args + i, "autoindent", 10) == 0 ||
 | 
						||
			strncasecmp((char *) args + i, "ai", 2) == 0) {
 | 
						||
			autoindent = (i == 2) ? 0 : 1;
 | 
						||
		}
 | 
						||
		if (strncasecmp((char *) args + i, "flash", 5) == 0 ||
 | 
						||
			strncasecmp((char *) args + i, "fl", 2) == 0) {
 | 
						||
			err_method = (i == 2) ? 0 : 1;
 | 
						||
		}
 | 
						||
		if (strncasecmp((char *) args + i, "ignorecase", 10) == 0 ||
 | 
						||
			strncasecmp((char *) args + i, "ic", 2) == 0) {
 | 
						||
			ignorecase = (i == 2) ? 0 : 1;
 | 
						||
		}
 | 
						||
		if (strncasecmp((char *) args + i, "showmatch", 9) == 0 ||
 | 
						||
			strncasecmp((char *) args + i, "sm", 2) == 0) {
 | 
						||
			showmatch = (i == 2) ? 0 : 1;
 | 
						||
		}
 | 
						||
		if (strncasecmp((char *) args + i, "tabstop", 7) == 0) {
 | 
						||
			sscanf(strchr((char *) args + i, '='), "=%d", &ch);
 | 
						||
			if (ch > 0 && ch < columns - 1)
 | 
						||
				tabstop = ch;
 | 
						||
		}
 | 
						||
#endif							/* CONFIG_FEATURE_VI_SETOPTS */
 | 
						||
#endif							/* CONFIG_FEATURE_VI_SET */
 | 
						||
#ifdef CONFIG_FEATURE_VI_SEARCH
 | 
						||
	} else if (strncasecmp((char *) cmd, "s", 1) == 0) {	// substitute a pattern with a replacement pattern
 | 
						||
		Byte *ls, *F, *R;
 | 
						||
		int gflag;
 | 
						||
 | 
						||
		// F points to the "find" pattern
 | 
						||
		// R points to the "replace" pattern
 | 
						||
		// replace the cmd line delimiters "/" with NULLs
 | 
						||
		gflag = 0;		// global replace flag
 | 
						||
		c = orig_buf[1];	// what is the delimiter
 | 
						||
		F = orig_buf + 2;	// start of "find"
 | 
						||
		R = (Byte *) strchr((char *) F, c);	// middle delimiter
 | 
						||
		if (!R) goto colon_s_fail;
 | 
						||
		*R++ = '\0';	// terminate "find"
 | 
						||
		buf1 = (Byte *) strchr((char *) R, c);
 | 
						||
		if (!buf1) goto colon_s_fail;
 | 
						||
		*buf1++ = '\0';	// terminate "replace"
 | 
						||
		if (*buf1 == 'g') {	// :s/foo/bar/g
 | 
						||
			buf1++;
 | 
						||
			gflag++;	// turn on gflag
 | 
						||
		}
 | 
						||
		q = begin_line(q);
 | 
						||
		if (b < 0) {	// maybe :s/foo/bar/
 | 
						||
			q = begin_line(dot);	// start with cur line
 | 
						||
			b = count_lines(text, q);	// cur line number
 | 
						||
		}
 | 
						||
		if (e < 0)
 | 
						||
			e = b;		// maybe :.s/foo/bar/
 | 
						||
		for (i = b; i <= e; i++) {	// so, :20,23 s \0 find \0 replace \0
 | 
						||
			ls = q;		// orig line start
 | 
						||
		  vc4:
 | 
						||
			buf1 = char_search(q, F, FORWARD, LIMITED);	// search cur line only for "find"
 | 
						||
			if (buf1 != NULL) {
 | 
						||
				// we found the "find" pattern- delete it
 | 
						||
				(void) text_hole_delete(buf1, buf1 + strlen((char *) F) - 1);
 | 
						||
				// inset the "replace" patern
 | 
						||
				(void) string_insert(buf1, R);	// insert the string
 | 
						||
				// check for "global"  :s/foo/bar/g
 | 
						||
				if (gflag == 1) {
 | 
						||
					if ((buf1 + strlen((char *) R)) < end_line(ls)) {
 | 
						||
						q = buf1 + strlen((char *) R);
 | 
						||
						goto vc4;	// don't let q move past cur line
 | 
						||
					}
 | 
						||
				}
 | 
						||
			}
 | 
						||
			q = next_line(ls);
 | 
						||
		}
 | 
						||
#endif							/* CONFIG_FEATURE_VI_SEARCH */
 | 
						||
	} else if (strncasecmp((char *) cmd, "version", i) == 0) {	// show software version
 | 
						||
		psb("%s", vi_Version);
 | 
						||
	} else if ((strncasecmp((char *) cmd, "write", i) == 0) ||	// write text to file
 | 
						||
			   (strncasecmp((char *) cmd, "wq", i) == 0)) {	// write text to file
 | 
						||
		// is there a file name to write to?
 | 
						||
		if (strlen((char *) args) > 0) {
 | 
						||
			fn = args;
 | 
						||
		}
 | 
						||
#ifdef CONFIG_FEATURE_VI_READONLY
 | 
						||
		if ((vi_readonly || readonly) && ! useforce) {
 | 
						||
			psbs("\"%s\" File is read only", fn);
 | 
						||
			goto vc3;
 | 
						||
		}
 | 
						||
#endif							/* CONFIG_FEATURE_VI_READONLY */
 | 
						||
		// how many lines in text[]?
 | 
						||
		li = count_lines(q, r);
 | 
						||
		ch = r - q + 1;
 | 
						||
		// see if file exists- if not, its just a new file request
 | 
						||
		if (useforce) {
 | 
						||
			// if "fn" is not write-able, chmod u+w
 | 
						||
			// sprintf(syscmd, "chmod u+w %s", fn);
 | 
						||
			// system(syscmd);
 | 
						||
			forced = TRUE;
 | 
						||
		}
 | 
						||
		l = file_write(fn, q, r);
 | 
						||
		if (useforce && forced) {
 | 
						||
			// chmod u-w
 | 
						||
			// sprintf(syscmd, "chmod u-w %s", fn);
 | 
						||
			// system(syscmd);
 | 
						||
			forced = FALSE;
 | 
						||
		}
 | 
						||
		psb("\"%s\" %dL, %dC", fn, li, l);
 | 
						||
		if (q == text && r == end - 1 && l == ch)
 | 
						||
			file_modified = FALSE;
 | 
						||
		if (cmd[1] == 'q' && l == ch) {
 | 
						||
			editing = 0;
 | 
						||
		}
 | 
						||
#ifdef CONFIG_FEATURE_VI_READONLY
 | 
						||
	  vc3:;
 | 
						||
#endif							/* CONFIG_FEATURE_VI_READONLY */
 | 
						||
#ifdef CONFIG_FEATURE_VI_YANKMARK
 | 
						||
	} else if (strncasecmp((char *) cmd, "yank", i) == 0) {	// yank lines
 | 
						||
		if (b < 0) {	// no addr given- use defaults
 | 
						||
			q = begin_line(dot);	// assume .,. for the range
 | 
						||
			r = end_line(dot);
 | 
						||
		}
 | 
						||
		text_yank(q, r, YDreg);
 | 
						||
		li = count_lines(q, r);
 | 
						||
		psb("Yank %d lines (%d chars) into [%c]",
 | 
						||
			li, strlen((char *) reg[YDreg]), what_reg());
 | 
						||
#endif							/* CONFIG_FEATURE_VI_YANKMARK */
 | 
						||
	} else {
 | 
						||
		// cmd unknown
 | 
						||
		ni((Byte *) cmd);
 | 
						||
	}
 | 
						||
  vc1:
 | 
						||
	dot = bound_dot(dot);	// make sure "dot" is valid
 | 
						||
	return;
 | 
						||
#ifdef CONFIG_FEATURE_VI_SEARCH
 | 
						||
colon_s_fail:
 | 
						||
	psb(":s expression missing delimiters");
 | 
						||
	return;
 | 
						||
#endif
 | 
						||
 | 
						||
}
 | 
						||
 | 
						||
static void Hit_Return(void)
 | 
						||
{
 | 
						||
	char c;
 | 
						||
 | 
						||
	standout_start();	// start reverse video
 | 
						||
	write(1, "[Hit return to continue]", 24);
 | 
						||
	standout_end();		// end reverse video
 | 
						||
	while ((c = get_one_char()) != '\n' && c != '\r')	/*do nothing */
 | 
						||
		;
 | 
						||
	redraw(TRUE);		// force redraw all
 | 
						||
}
 | 
						||
#endif							/* CONFIG_FEATURE_VI_COLON */
 | 
						||
 | 
						||
//----- Synchronize the cursor to Dot --------------------------
 | 
						||
static void sync_cursor(Byte * d, int *row, int *col)
 | 
						||
{
 | 
						||
	Byte *beg_cur, *end_cur;	// begin and end of "d" line
 | 
						||
	Byte *beg_scr, *end_scr;	// begin and end of screen
 | 
						||
	Byte *tp;
 | 
						||
	int cnt, ro, co;
 | 
						||
 | 
						||
	beg_cur = begin_line(d);	// first char of cur line
 | 
						||
	end_cur = end_line(d);	// last char of cur line
 | 
						||
 | 
						||
	beg_scr = end_scr = screenbegin;	// first char of screen
 | 
						||
	end_scr = end_screen();	// last char of screen
 | 
						||
 | 
						||
	if (beg_cur < screenbegin) {
 | 
						||
		// "d" is before  top line on screen
 | 
						||
		// how many lines do we have to move
 | 
						||
		cnt = count_lines(beg_cur, screenbegin);
 | 
						||
	  sc1:
 | 
						||
		screenbegin = beg_cur;
 | 
						||
		if (cnt > (rows - 1) / 2) {
 | 
						||
			// we moved too many lines. put "dot" in middle of screen
 | 
						||
			for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
 | 
						||
				screenbegin = prev_line(screenbegin);
 | 
						||
			}
 | 
						||
		}
 | 
						||
	} else if (beg_cur > end_scr) {
 | 
						||
		// "d" is after bottom line on screen
 | 
						||
		// how many lines do we have to move
 | 
						||
		cnt = count_lines(end_scr, beg_cur);
 | 
						||
		if (cnt > (rows - 1) / 2)
 | 
						||
			goto sc1;	// too many lines
 | 
						||
		for (ro = 0; ro < cnt - 1; ro++) {
 | 
						||
			// move screen begin the same amount
 | 
						||
			screenbegin = next_line(screenbegin);
 | 
						||
			// now, move the end of screen
 | 
						||
			end_scr = next_line(end_scr);
 | 
						||
			end_scr = end_line(end_scr);
 | 
						||
		}
 | 
						||
	}
 | 
						||
	// "d" is on screen- find out which row
 | 
						||
	tp = screenbegin;
 | 
						||
	for (ro = 0; ro < rows - 1; ro++) {	// drive "ro" to correct row
 | 
						||
		if (tp == beg_cur)
 | 
						||
			break;
 | 
						||
		tp = next_line(tp);
 | 
						||
	}
 | 
						||
 | 
						||
	// find out what col "d" is on
 | 
						||
	co = 0;
 | 
						||
	do {				// drive "co" to correct column
 | 
						||
		if (*tp == '\n' || *tp == '\0')
 | 
						||
			break;
 | 
						||
		if (*tp == '\t') {
 | 
						||
			//         7       - (co %    8  )
 | 
						||
			co += ((tabstop - 1) - (co % tabstop));
 | 
						||
		} else if (*tp < ' ') {
 | 
						||
			co++;		// display as ^X, use 2 columns
 | 
						||
		}
 | 
						||
	} while (tp++ < d && ++co);
 | 
						||
 | 
						||
	// "co" is the column where "dot" is.
 | 
						||
	// The screen has "columns" columns.
 | 
						||
	// The currently displayed columns are  0+offset -- columns+ofset
 | 
						||
	// |-------------------------------------------------------------|
 | 
						||
	//               ^ ^                                ^
 | 
						||
	//        offset | |------- columns ----------------|
 | 
						||
	//
 | 
						||
	// If "co" is already in this range then we do not have to adjust offset
 | 
						||
	//      but, we do have to subtract the "offset" bias from "co".
 | 
						||
	// If "co" is outside this range then we have to change "offset".
 | 
						||
	// If the first char of a line is a tab the cursor will try to stay
 | 
						||
	//  in column 7, but we have to set offset to 0.
 | 
						||
 | 
						||
	if (co < 0 + offset) {
 | 
						||
		offset = co;
 | 
						||
	}
 | 
						||
	if (co >= columns + offset) {
 | 
						||
		offset = co - columns + 1;
 | 
						||
	}
 | 
						||
	// if the first char of the line is a tab, and "dot" is sitting on it
 | 
						||
	//  force offset to 0.
 | 
						||
	if (d == beg_cur && *d == '\t') {
 | 
						||
		offset = 0;
 | 
						||
	}
 | 
						||
	co -= offset;
 | 
						||
 | 
						||
	*row = ro;
 | 
						||
	*col = co;
 | 
						||
}
 | 
						||
 | 
						||
//----- Text Movement Routines ---------------------------------
 | 
						||
static Byte *begin_line(Byte * p) // return pointer to first char cur line
 | 
						||
{
 | 
						||
	while (p > text && p[-1] != '\n')
 | 
						||
		p--;			// go to cur line B-o-l
 | 
						||
	return (p);
 | 
						||
}
 | 
						||
 | 
						||
static Byte *end_line(Byte * p) // return pointer to NL of cur line line
 | 
						||
{
 | 
						||
	while (p < end - 1 && *p != '\n')
 | 
						||
		p++;			// go to cur line E-o-l
 | 
						||
	return (p);
 | 
						||
}
 | 
						||
 | 
						||
static Byte *dollar_line(Byte * p) // return pointer to just before NL line
 | 
						||
{
 | 
						||
	while (p < end - 1 && *p != '\n')
 | 
						||
		p++;			// go to cur line E-o-l
 | 
						||
	// Try to stay off of the Newline
 | 
						||
	if (*p == '\n' && (p - begin_line(p)) > 0)
 | 
						||
		p--;
 | 
						||
	return (p);
 | 
						||
}
 | 
						||
 | 
						||
static Byte *prev_line(Byte * p) // return pointer first char prev line
 | 
						||
{
 | 
						||
	p = begin_line(p);	// goto begining of cur line
 | 
						||
	if (p[-1] == '\n' && p > text)
 | 
						||
		p--;			// step to prev line
 | 
						||
	p = begin_line(p);	// goto begining of prev line
 | 
						||
	return (p);
 | 
						||
}
 | 
						||
 | 
						||
static Byte *next_line(Byte * p) // return pointer first char next line
 | 
						||
{
 | 
						||
	p = end_line(p);
 | 
						||
	if (*p == '\n' && p < end - 1)
 | 
						||
		p++;			// step to next line
 | 
						||
	return (p);
 | 
						||
}
 | 
						||
 | 
						||
//----- Text Information Routines ------------------------------
 | 
						||
static Byte *end_screen(void)
 | 
						||
{
 | 
						||
	Byte *q;
 | 
						||
	int cnt;
 | 
						||
 | 
						||
	// find new bottom line
 | 
						||
	q = screenbegin;
 | 
						||
	for (cnt = 0; cnt < rows - 2; cnt++)
 | 
						||
		q = next_line(q);
 | 
						||
	q = end_line(q);
 | 
						||
	return (q);
 | 
						||
}
 | 
						||
 | 
						||
static int count_lines(Byte * start, Byte * stop) // count line from start to stop
 | 
						||
{
 | 
						||
	Byte *q;
 | 
						||
	int cnt;
 | 
						||
 | 
						||
	if (stop < start) {	// start and stop are backwards- reverse them
 | 
						||
		q = start;
 | 
						||
		start = stop;
 | 
						||
		stop = q;
 | 
						||
	}
 | 
						||
	cnt = 0;
 | 
						||
	stop = end_line(stop);	// get to end of this line
 | 
						||
	for (q = start; q <= stop && q <= end - 1; q++) {
 | 
						||
		if (*q == '\n')
 | 
						||
			cnt++;
 | 
						||
	}
 | 
						||
	return (cnt);
 | 
						||
}
 | 
						||
 | 
						||
static Byte *find_line(int li)	// find begining of line #li
 | 
						||
{
 | 
						||
	Byte *q;
 | 
						||
 | 
						||
	for (q = text; li > 1; li--) {
 | 
						||
		q = next_line(q);
 | 
						||
	}
 | 
						||
	return (q);
 | 
						||
}
 | 
						||
 | 
						||
//----- Dot Movement Routines ----------------------------------
 | 
						||
static void dot_left(void)
 | 
						||
{
 | 
						||
	if (dot > text && dot[-1] != '\n')
 | 
						||
		dot--;
 | 
						||
}
 | 
						||
 | 
						||
static void dot_right(void)
 | 
						||
{
 | 
						||
	if (dot < end - 1 && *dot != '\n')
 | 
						||
		dot++;
 | 
						||
}
 | 
						||
 | 
						||
static void dot_begin(void)
 | 
						||
{
 | 
						||
	dot = begin_line(dot);	// return pointer to first char cur line
 | 
						||
}
 | 
						||
 | 
						||
static void dot_end(void)
 | 
						||
{
 | 
						||
	dot = end_line(dot);	// return pointer to last char cur line
 | 
						||
}
 | 
						||
 | 
						||
static Byte *move_to_col(Byte * p, int l)
 | 
						||
{
 | 
						||
	int co;
 | 
						||
 | 
						||
	p = begin_line(p);
 | 
						||
	co = 0;
 | 
						||
	do {
 | 
						||
		if (*p == '\n' || *p == '\0')
 | 
						||
			break;
 | 
						||
		if (*p == '\t') {
 | 
						||
			//         7       - (co %    8  )
 | 
						||
			co += ((tabstop - 1) - (co % tabstop));
 | 
						||
		} else if (*p < ' ') {
 | 
						||
			co++;		// display as ^X, use 2 columns
 | 
						||
		}
 | 
						||
	} while (++co <= l && p++ < end);
 | 
						||
	return (p);
 | 
						||
}
 | 
						||
 | 
						||
static void dot_next(void)
 | 
						||
{
 | 
						||
	dot = next_line(dot);
 | 
						||
}
 | 
						||
 | 
						||
static void dot_prev(void)
 | 
						||
{
 | 
						||
	dot = prev_line(dot);
 | 
						||
}
 | 
						||
 | 
						||
static void dot_scroll(int cnt, int dir)
 | 
						||
{
 | 
						||
	Byte *q;
 | 
						||
 | 
						||
	for (; cnt > 0; cnt--) {
 | 
						||
		if (dir < 0) {
 | 
						||
			// scroll Backwards
 | 
						||
			// ctrl-Y  scroll up one line
 | 
						||
			screenbegin = prev_line(screenbegin);
 | 
						||
		} else {
 | 
						||
			// scroll Forwards
 | 
						||
			// ctrl-E  scroll down one line
 | 
						||
			screenbegin = next_line(screenbegin);
 | 
						||
		}
 | 
						||
	}
 | 
						||
	// make sure "dot" stays on the screen so we dont scroll off
 | 
						||
	if (dot < screenbegin)
 | 
						||
		dot = screenbegin;
 | 
						||
	q = end_screen();	// find new bottom line
 | 
						||
	if (dot > q)
 | 
						||
		dot = begin_line(q);	// is dot is below bottom line?
 | 
						||
	dot_skip_over_ws();
 | 
						||
}
 | 
						||
 | 
						||
static void dot_skip_over_ws(void)
 | 
						||
{
 | 
						||
	// skip WS
 | 
						||
	while (isspace(*dot) && *dot != '\n' && dot < end - 1)
 | 
						||
		dot++;
 | 
						||
}
 | 
						||
 | 
						||
static void dot_delete(void)	// delete the char at 'dot'
 | 
						||
{
 | 
						||
	(void) text_hole_delete(dot, dot);
 | 
						||
}
 | 
						||
 | 
						||
static Byte *bound_dot(Byte * p) // make sure  text[0] <= P < "end"
 | 
						||
{
 | 
						||
	if (p >= end && end > text) {
 | 
						||
		p = end - 1;
 | 
						||
		indicate_error('1');
 | 
						||
	}
 | 
						||
	if (p < text) {
 | 
						||
		p = text;
 | 
						||
		indicate_error('2');
 | 
						||
	}
 | 
						||
	return (p);
 | 
						||
}
 | 
						||
 | 
						||
//----- Helper Utility Routines --------------------------------
 | 
						||
 | 
						||
//----------------------------------------------------------------
 | 
						||
//----- Char Routines --------------------------------------------
 | 
						||
/* Chars that are part of a word-
 | 
						||
 *    0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
 | 
						||
 * Chars that are Not part of a word (stoppers)
 | 
						||
 *    !"#$%&'()*+,-./:;<=>?@[\]^`{|}~
 | 
						||
 * Chars that are WhiteSpace
 | 
						||
 *    TAB NEWLINE VT FF RETURN SPACE
 | 
						||
 * DO NOT COUNT NEWLINE AS WHITESPACE
 | 
						||
 */
 | 
						||
 | 
						||
static Byte *new_screen(int ro, int co)
 | 
						||
{
 | 
						||
	int li;
 | 
						||
 | 
						||
	if (screen != 0)
 | 
						||
		free(screen);
 | 
						||
	screensize = ro * co + 8;
 | 
						||
	screen = (Byte *) xmalloc(screensize);
 | 
						||
	// initialize the new screen. assume this will be a empty file.
 | 
						||
	screen_erase();
 | 
						||
	//   non-existant text[] lines start with a tilde (~).
 | 
						||
	for (li = 1; li < ro - 1; li++) {
 | 
						||
		screen[(li * co) + 0] = '~';
 | 
						||
	}
 | 
						||
	return (screen);
 | 
						||
}
 | 
						||
 | 
						||
static Byte *new_text(int size)
 | 
						||
{
 | 
						||
	if (size < 10240)
 | 
						||
		size = 10240;	// have a minimum size for new files
 | 
						||
	if (text != 0) {
 | 
						||
		//text -= 4;
 | 
						||
		free(text);
 | 
						||
	}
 | 
						||
	text = (Byte *) xmalloc(size + 8);
 | 
						||
	memset(text, '\0', size);	// clear new text[]
 | 
						||
	//text += 4;		// leave some room for "oops"
 | 
						||
	textend = text + size - 1;
 | 
						||
	//textend -= 4;		// leave some root for "oops"
 | 
						||
	return (text);
 | 
						||
}
 | 
						||
 | 
						||
#ifdef CONFIG_FEATURE_VI_SEARCH
 | 
						||
static int mycmp(Byte * s1, Byte * s2, int len)
 | 
						||
{
 | 
						||
	int i;
 | 
						||
 | 
						||
	i = strncmp((char *) s1, (char *) s2, len);
 | 
						||
#ifdef CONFIG_FEATURE_VI_SETOPTS
 | 
						||
	if (ignorecase) {
 | 
						||
		i = strncasecmp((char *) s1, (char *) s2, len);
 | 
						||
	}
 | 
						||
#endif							/* CONFIG_FEATURE_VI_SETOPTS */
 | 
						||
	return (i);
 | 
						||
}
 | 
						||
 | 
						||
static Byte *char_search(Byte * p, Byte * pat, int dir, int range)	// search for pattern starting at p
 | 
						||
{
 | 
						||
#ifndef REGEX_SEARCH
 | 
						||
	Byte *start, *stop;
 | 
						||
	int len;
 | 
						||
 | 
						||
	len = strlen((char *) pat);
 | 
						||
	if (dir == FORWARD) {
 | 
						||
		stop = end - 1;	// assume range is p - end-1
 | 
						||
		if (range == LIMITED)
 | 
						||
			stop = next_line(p);	// range is to next line
 | 
						||
		for (start = p; start < stop; start++) {
 | 
						||
			if (mycmp(start, pat, len) == 0) {
 | 
						||
				return (start);
 | 
						||
			}
 | 
						||
		}
 | 
						||
	} else if (dir == BACK) {
 | 
						||
		stop = text;	// assume range is text - p
 | 
						||
		if (range == LIMITED)
 | 
						||
			stop = prev_line(p);	// range is to prev line
 | 
						||
		for (start = p - len; start >= stop; start--) {
 | 
						||
			if (mycmp(start, pat, len) == 0) {
 | 
						||
				return (start);
 | 
						||
			}
 | 
						||
		}
 | 
						||
	}
 | 
						||
	// pattern not found
 | 
						||
	return (NULL);
 | 
						||
#else							/*REGEX_SEARCH */
 | 
						||
	char *q;
 | 
						||
	struct re_pattern_buffer preg;
 | 
						||
	int i;
 | 
						||
	int size, range;
 | 
						||
 | 
						||
	re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
 | 
						||
	preg.translate = 0;
 | 
						||
	preg.fastmap = 0;
 | 
						||
	preg.buffer = 0;
 | 
						||
	preg.allocated = 0;
 | 
						||
 | 
						||
	// assume a LIMITED forward search
 | 
						||
	q = next_line(p);
 | 
						||
	q = end_line(q);
 | 
						||
	q = end - 1;
 | 
						||
	if (dir == BACK) {
 | 
						||
		q = prev_line(p);
 | 
						||
		q = text;
 | 
						||
	}
 | 
						||
	// count the number of chars to search over, forward or backward
 | 
						||
	size = q - p;
 | 
						||
	if (size < 0)
 | 
						||
		size = p - q;
 | 
						||
	// RANGE could be negative if we are searching backwards
 | 
						||
	range = q - p;
 | 
						||
 | 
						||
	q = (char *) re_compile_pattern(pat, strlen((char *) pat), &preg);
 | 
						||
	if (q != 0) {
 | 
						||
		// The pattern was not compiled
 | 
						||
		psbs("bad search pattern: \"%s\": %s", pat, q);
 | 
						||
		i = 0;			// return p if pattern not compiled
 | 
						||
		goto cs1;
 | 
						||
	}
 | 
						||
 | 
						||
	q = p;
 | 
						||
	if (range < 0) {
 | 
						||
		q = p - size;
 | 
						||
		if (q < text)
 | 
						||
			q = text;
 | 
						||
	}
 | 
						||
	// search for the compiled pattern, preg, in p[]
 | 
						||
	// range < 0-  search backward
 | 
						||
	// range > 0-  search forward
 | 
						||
	// 0 < start < size
 | 
						||
	// re_search() < 0  not found or error
 | 
						||
	// re_search() > 0  index of found pattern
 | 
						||
	//            struct pattern    char     int    int    int     struct reg
 | 
						||
	// re_search (*pattern_buffer,  *string, size,  start, range,  *regs)
 | 
						||
	i = re_search(&preg, q, size, 0, range, 0);
 | 
						||
	if (i == -1) {
 | 
						||
		p = 0;
 | 
						||
		i = 0;			// return NULL if pattern not found
 | 
						||
	}
 | 
						||
  cs1:
 | 
						||
	if (dir == FORWARD) {
 | 
						||
		p = p + i;
 | 
						||
	} else {
 | 
						||
		p = p - i;
 | 
						||
	}
 | 
						||
	return (p);
 | 
						||
#endif							/*REGEX_SEARCH */
 | 
						||
}
 | 
						||
#endif							/* CONFIG_FEATURE_VI_SEARCH */
 | 
						||
 | 
						||
static Byte *char_insert(Byte * p, Byte c) // insert the char c at 'p'
 | 
						||
{
 | 
						||
	if (c == 22) {		// Is this an ctrl-V?
 | 
						||
		p = stupid_insert(p, '^');	// use ^ to indicate literal next
 | 
						||
		p--;			// backup onto ^
 | 
						||
		refresh(FALSE);	// show the ^
 | 
						||
		c = get_one_char();
 | 
						||
		*p = c;
 | 
						||
		p++;
 | 
						||
		file_modified = TRUE;	// has the file been modified
 | 
						||
	} else if (c == 27) {	// Is this an ESC?
 | 
						||
		cmd_mode = 0;
 | 
						||
		cmdcnt = 0;
 | 
						||
		end_cmd_q();	// stop adding to q
 | 
						||
		strcpy((char *) status_buffer, " ");	// clear the status buffer
 | 
						||
		if ((p[-1] != '\n') && (dot>text)) {
 | 
						||
			p--;
 | 
						||
		}
 | 
						||
	} else if (c == erase_char) {	// Is this a BS
 | 
						||
		//     123456789
 | 
						||
		if ((p[-1] != '\n') && (dot>text)) {
 | 
						||
			p--;
 | 
						||
			p = text_hole_delete(p, p);	// shrink buffer 1 char
 | 
						||
#ifdef CONFIG_FEATURE_VI_DOT_CMD
 | 
						||
			// also rmove char from last_modifying_cmd
 | 
						||
			if (strlen((char *) last_modifying_cmd) > 0) {
 | 
						||
				Byte *q;
 | 
						||
 | 
						||
				q = last_modifying_cmd;
 | 
						||
				q[strlen((char *) q) - 1] = '\0';	// erase BS
 | 
						||
				q[strlen((char *) q) - 1] = '\0';	// erase prev char
 | 
						||
			}
 | 
						||
#endif							/* CONFIG_FEATURE_VI_DOT_CMD */
 | 
						||
		}
 | 
						||
	} else {
 | 
						||
		// insert a char into text[]
 | 
						||
		Byte *sp;		// "save p"
 | 
						||
 | 
						||
		if (c == 13)
 | 
						||
			c = '\n';	// translate \r to \n
 | 
						||
		sp = p;			// remember addr of insert
 | 
						||
		p = stupid_insert(p, c);	// insert the char
 | 
						||
#ifdef CONFIG_FEATURE_VI_SETOPTS
 | 
						||
		if (showmatch && strchr(")]}", *sp) != NULL) {
 | 
						||
			showmatching(sp);
 | 
						||
		}
 | 
						||
		if (autoindent && c == '\n') {	// auto indent the new line
 | 
						||
			Byte *q;
 | 
						||
 | 
						||
			q = prev_line(p);	// use prev line as templet
 | 
						||
			for (; isblnk(*q); q++) {
 | 
						||
				p = stupid_insert(p, *q);	// insert the char
 | 
						||
			}
 | 
						||
		}
 | 
						||
#endif							/* CONFIG_FEATURE_VI_SETOPTS */
 | 
						||
	}
 | 
						||
	return (p);
 | 
						||
}
 | 
						||
 | 
						||
static Byte *stupid_insert(Byte * p, Byte c) // stupidly insert the char c at 'p'
 | 
						||
{
 | 
						||
	p = text_hole_make(p, 1);
 | 
						||
	if (p != 0) {
 | 
						||
		*p = c;
 | 
						||
		file_modified = TRUE;	// has the file been modified
 | 
						||
		p++;
 | 
						||
	}
 | 
						||
	return (p);
 | 
						||
}
 | 
						||
 | 
						||
static Byte find_range(Byte ** start, Byte ** stop, Byte c)
 | 
						||
{
 | 
						||
	Byte *save_dot, *p, *q;
 | 
						||
	int cnt;
 | 
						||
 | 
						||
	save_dot = dot;
 | 
						||
	p = q = dot;
 | 
						||
 | 
						||
	if (strchr("cdy><", c)) {
 | 
						||
		// these cmds operate on whole lines
 | 
						||
		p = q = begin_line(p);
 | 
						||
		for (cnt = 1; cnt < cmdcnt; cnt++) {
 | 
						||
			q = next_line(q);
 | 
						||
		}
 | 
						||
		q = end_line(q);
 | 
						||
	} else if (strchr("^%$0bBeEft", c)) {
 | 
						||
		// These cmds operate on char positions
 | 
						||
		do_cmd(c);		// execute movement cmd
 | 
						||
		q = dot;
 | 
						||
	} else if (strchr("wW", c)) {
 | 
						||
		do_cmd(c);		// execute movement cmd
 | 
						||
		if (dot > text)
 | 
						||
			dot--;		// move back off of next word
 | 
						||
		if (dot > text && *dot == '\n')
 | 
						||
			dot--;		// stay off NL
 | 
						||
		q = dot;
 | 
						||
	} else if (strchr("H-k{", c)) {
 | 
						||
		// these operate on multi-lines backwards
 | 
						||
		q = end_line(dot);	// find NL
 | 
						||
		do_cmd(c);		// execute movement cmd
 | 
						||
		dot_begin();
 | 
						||
		p = dot;
 | 
						||
	} else if (strchr("L+j}\r\n", c)) {
 | 
						||
		// these operate on multi-lines forwards
 | 
						||
		p = begin_line(dot);
 | 
						||
		do_cmd(c);		// execute movement cmd
 | 
						||
		dot_end();		// find NL
 | 
						||
		q = dot;
 | 
						||
	} else {
 | 
						||
		c = 27;			// error- return an ESC char
 | 
						||
		//break;
 | 
						||
	}
 | 
						||
	*start = p;
 | 
						||
	*stop = q;
 | 
						||
	if (q < p) {
 | 
						||
		*start = q;
 | 
						||
		*stop = p;
 | 
						||
	}
 | 
						||
	dot = save_dot;
 | 
						||
	return (c);
 | 
						||
}
 | 
						||
 | 
						||
static int st_test(Byte * p, int type, int dir, Byte * tested)
 | 
						||
{
 | 
						||
	Byte c, c0, ci;
 | 
						||
	int test, inc;
 | 
						||
 | 
						||
	inc = dir;
 | 
						||
	c = c0 = p[0];
 | 
						||
	ci = p[inc];
 | 
						||
	test = 0;
 | 
						||
 | 
						||
	if (type == S_BEFORE_WS) {
 | 
						||
		c = ci;
 | 
						||
		test = ((!isspace(c)) || c == '\n');
 | 
						||
	}
 | 
						||
	if (type == S_TO_WS) {
 | 
						||
		c = c0;
 | 
						||
		test = ((!isspace(c)) || c == '\n');
 | 
						||
	}
 | 
						||
	if (type == S_OVER_WS) {
 | 
						||
		c = c0;
 | 
						||
		test = ((isspace(c)));
 | 
						||
	}
 | 
						||
	if (type == S_END_PUNCT) {
 | 
						||
		c = ci;
 | 
						||
		test = ((ispunct(c)));
 | 
						||
	}
 | 
						||
	if (type == S_END_ALNUM) {
 | 
						||
		c = ci;
 | 
						||
		test = ((isalnum(c)) || c == '_');
 | 
						||
	}
 | 
						||
	*tested = c;
 | 
						||
	return (test);
 | 
						||
}
 | 
						||
 | 
						||
static Byte *skip_thing(Byte * p, int linecnt, int dir, int type)
 | 
						||
{
 | 
						||
	Byte c;
 | 
						||
 | 
						||
	while (st_test(p, type, dir, &c)) {
 | 
						||
		// make sure we limit search to correct number of lines
 | 
						||
		if (c == '\n' && --linecnt < 1)
 | 
						||
			break;
 | 
						||
		if (dir >= 0 && p >= end - 1)
 | 
						||
			break;
 | 
						||
		if (dir < 0 && p <= text)
 | 
						||
			break;
 | 
						||
		p += dir;		// move to next char
 | 
						||
	}
 | 
						||
	return (p);
 | 
						||
}
 | 
						||
 | 
						||
// find matching char of pair  ()  []  {}
 | 
						||
static Byte *find_pair(Byte * p, Byte c)
 | 
						||
{
 | 
						||
	Byte match, *q;
 | 
						||
	int dir, level;
 | 
						||
 | 
						||
	match = ')';
 | 
						||
	level = 1;
 | 
						||
	dir = 1;			// assume forward
 | 
						||
	switch (c) {
 | 
						||
	case '(':
 | 
						||
		match = ')';
 | 
						||
		break;
 | 
						||
	case '[':
 | 
						||
		match = ']';
 | 
						||
		break;
 | 
						||
	case '{':
 | 
						||
		match = '}';
 | 
						||
		break;
 | 
						||
	case ')':
 | 
						||
		match = '(';
 | 
						||
		dir = -1;
 | 
						||
		break;
 | 
						||
	case ']':
 | 
						||
		match = '[';
 | 
						||
		dir = -1;
 | 
						||
		break;
 | 
						||
	case '}':
 | 
						||
		match = '{';
 | 
						||
		dir = -1;
 | 
						||
		break;
 | 
						||
	}
 | 
						||
	for (q = p + dir; text <= q && q < end; q += dir) {
 | 
						||
		// look for match, count levels of pairs  (( ))
 | 
						||
		if (*q == c)
 | 
						||
			level++;	// increase pair levels
 | 
						||
		if (*q == match)
 | 
						||
			level--;	// reduce pair level
 | 
						||
		if (level == 0)
 | 
						||
			break;		// found matching pair
 | 
						||
	}
 | 
						||
	if (level != 0)
 | 
						||
		q = NULL;		// indicate no match
 | 
						||
	return (q);
 | 
						||
}
 | 
						||
 | 
						||
#ifdef CONFIG_FEATURE_VI_SETOPTS
 | 
						||
// show the matching char of a pair,  ()  []  {}
 | 
						||
static void showmatching(Byte * p)
 | 
						||
{
 | 
						||
	Byte *q, *save_dot;
 | 
						||
 | 
						||
	// we found half of a pair
 | 
						||
	q = find_pair(p, *p);	// get loc of matching char
 | 
						||
	if (q == NULL) {
 | 
						||
		indicate_error('3');	// no matching char
 | 
						||
	} else {
 | 
						||
		// "q" now points to matching pair
 | 
						||
		save_dot = dot;	// remember where we are
 | 
						||
		dot = q;		// go to new loc
 | 
						||
		refresh(FALSE);	// let the user see it
 | 
						||
		(void) mysleep(40);	// give user some time
 | 
						||
		dot = save_dot;	// go back to old loc
 | 
						||
		refresh(FALSE);
 | 
						||
	}
 | 
						||
}
 | 
						||
#endif							/* CONFIG_FEATURE_VI_SETOPTS */
 | 
						||
 | 
						||
//  open a hole in text[]
 | 
						||
static Byte *text_hole_make(Byte * p, int size)	// at "p", make a 'size' byte hole
 | 
						||
{
 | 
						||
	Byte *src, *dest;
 | 
						||
	int cnt;
 | 
						||
 | 
						||
	if (size <= 0)
 | 
						||
		goto thm0;
 | 
						||
	src = p;
 | 
						||
	dest = p + size;
 | 
						||
	cnt = end - src;	// the rest of buffer
 | 
						||
	if (memmove(dest, src, cnt) != dest) {
 | 
						||
		psbs("can't create room for new characters");
 | 
						||
	}
 | 
						||
	memset(p, ' ', size);	// clear new hole
 | 
						||
	end = end + size;	// adjust the new END
 | 
						||
	file_modified = TRUE;	// has the file been modified
 | 
						||
  thm0:
 | 
						||
	return (p);
 | 
						||
}
 | 
						||
 | 
						||
//  close a hole in text[]
 | 
						||
static Byte *text_hole_delete(Byte * p, Byte * q) // delete "p" thru "q", inclusive
 | 
						||
{
 | 
						||
	Byte *src, *dest;
 | 
						||
	int cnt, hole_size;
 | 
						||
 | 
						||
	// move forwards, from beginning
 | 
						||
	// assume p <= q
 | 
						||
	src = q + 1;
 | 
						||
	dest = p;
 | 
						||
	if (q < p) {		// they are backward- swap them
 | 
						||
		src = p + 1;
 | 
						||
		dest = q;
 | 
						||
	}
 | 
						||
	hole_size = q - p + 1;
 | 
						||
	cnt = end - src;
 | 
						||
	if (src < text || src > end)
 | 
						||
		goto thd0;
 | 
						||
	if (dest < text || dest >= end)
 | 
						||
		goto thd0;
 | 
						||
	if (src >= end)
 | 
						||
		goto thd_atend;	// just delete the end of the buffer
 | 
						||
	if (memmove(dest, src, cnt) != dest) {
 | 
						||
		psbs("can't delete the character");
 | 
						||
	}
 | 
						||
  thd_atend:
 | 
						||
	end = end - hole_size;	// adjust the new END
 | 
						||
	if (dest >= end)
 | 
						||
		dest = end - 1;	// make sure dest in below end-1
 | 
						||
	if (end <= text)
 | 
						||
		dest = end = text;	// keep pointers valid
 | 
						||
	file_modified = TRUE;	// has the file been modified
 | 
						||
  thd0:
 | 
						||
	return (dest);
 | 
						||
}
 | 
						||
 | 
						||
// copy text into register, then delete text.
 | 
						||
// if dist <= 0, do not include, or go past, a NewLine
 | 
						||
//
 | 
						||
static Byte *yank_delete(Byte * start, Byte * stop, int dist, int yf)
 | 
						||
{
 | 
						||
	Byte *p;
 | 
						||
 | 
						||
	// make sure start <= stop
 | 
						||
	if (start > stop) {
 | 
						||
		// they are backwards, reverse them
 | 
						||
		p = start;
 | 
						||
		start = stop;
 | 
						||
		stop = p;
 | 
						||
	}
 | 
						||
	if (dist <= 0) {
 | 
						||
		// we can not cross NL boundaries
 | 
						||
		p = start;
 | 
						||
		if (*p == '\n')
 | 
						||
			return (p);
 | 
						||
		// dont go past a NewLine
 | 
						||
		for (; p + 1 <= stop; p++) {
 | 
						||
			if (p[1] == '\n') {
 | 
						||
				stop = p;	// "stop" just before NewLine
 | 
						||
				break;
 | 
						||
			}
 | 
						||
		}
 | 
						||
	}
 | 
						||
	p = start;
 | 
						||
#ifdef CONFIG_FEATURE_VI_YANKMARK
 | 
						||
	text_yank(start, stop, YDreg);
 | 
						||
#endif							/* CONFIG_FEATURE_VI_YANKMARK */
 | 
						||
	if (yf == YANKDEL) {
 | 
						||
		p = text_hole_delete(start, stop);
 | 
						||
	}					// delete lines
 | 
						||
	return (p);
 | 
						||
}
 | 
						||
 | 
						||
static void show_help(void)
 | 
						||
{
 | 
						||
	puts("These features are available:"
 | 
						||
#ifdef CONFIG_FEATURE_VI_SEARCH
 | 
						||
	"\n\tPattern searches with / and ?"
 | 
						||
#endif							/* CONFIG_FEATURE_VI_SEARCH */
 | 
						||
#ifdef CONFIG_FEATURE_VI_DOT_CMD
 | 
						||
	"\n\tLast command repeat with \'.\'"
 | 
						||
#endif							/* CONFIG_FEATURE_VI_DOT_CMD */
 | 
						||
#ifdef CONFIG_FEATURE_VI_YANKMARK
 | 
						||
	"\n\tLine marking with  'x"
 | 
						||
	"\n\tNamed buffers with  \"x"
 | 
						||
#endif							/* CONFIG_FEATURE_VI_YANKMARK */
 | 
						||
#ifdef CONFIG_FEATURE_VI_READONLY
 | 
						||
	"\n\tReadonly if vi is called as \"view\""
 | 
						||
	"\n\tReadonly with -R command line arg"
 | 
						||
#endif							/* CONFIG_FEATURE_VI_READONLY */
 | 
						||
#ifdef CONFIG_FEATURE_VI_SET
 | 
						||
	"\n\tSome colon mode commands with \':\'"
 | 
						||
#endif							/* CONFIG_FEATURE_VI_SET */
 | 
						||
#ifdef CONFIG_FEATURE_VI_SETOPTS
 | 
						||
	"\n\tSettable options with \":set\""
 | 
						||
#endif							/* CONFIG_FEATURE_VI_SETOPTS */
 | 
						||
#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
 | 
						||
	"\n\tSignal catching- ^C"
 | 
						||
	"\n\tJob suspend and resume with ^Z"
 | 
						||
#endif							/* CONFIG_FEATURE_VI_USE_SIGNALS */
 | 
						||
#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
 | 
						||
	"\n\tAdapt to window re-sizes"
 | 
						||
#endif							/* CONFIG_FEATURE_VI_WIN_RESIZE */
 | 
						||
	);
 | 
						||
}
 | 
						||
 | 
						||
static void print_literal(Byte * buf, Byte * s) // copy s to buf, convert unprintable
 | 
						||
{
 | 
						||
	Byte c, b[2];
 | 
						||
 | 
						||
	b[1] = '\0';
 | 
						||
	strcpy((char *) buf, "");	// init buf
 | 
						||
	if (strlen((char *) s) <= 0)
 | 
						||
		s = (Byte *) "(NULL)";
 | 
						||
	for (; *s > '\0'; s++) {
 | 
						||
		c = *s;
 | 
						||
		if (*s > '~') {
 | 
						||
			strcat((char *) buf, SOs);
 | 
						||
			c = *s - 128;
 | 
						||
		}
 | 
						||
		if (*s < ' ') {
 | 
						||
			strcat((char *) buf, "^");
 | 
						||
			c += '@';
 | 
						||
		}
 | 
						||
		b[0] = c;
 | 
						||
		strcat((char *) buf, (char *) b);
 | 
						||
		if (*s > '~')
 | 
						||
			strcat((char *) buf, SOn);
 | 
						||
		if (*s == '\n') {
 | 
						||
			strcat((char *) buf, "$");
 | 
						||
		}
 | 
						||
	}
 | 
						||
}
 | 
						||
 | 
						||
#ifdef CONFIG_FEATURE_VI_DOT_CMD
 | 
						||
static void start_new_cmd_q(Byte c)
 | 
						||
{
 | 
						||
	// release old cmd
 | 
						||
	if (last_modifying_cmd != 0)
 | 
						||
		free(last_modifying_cmd);
 | 
						||
	// get buffer for new cmd
 | 
						||
	last_modifying_cmd = (Byte *) xmalloc(BUFSIZ);
 | 
						||
	memset(last_modifying_cmd, '\0', BUFSIZ);	// clear new cmd queue
 | 
						||
	// if there is a current cmd count put it in the buffer first
 | 
						||
	if (cmdcnt > 0)
 | 
						||
		sprintf((char *) last_modifying_cmd, "%d", cmdcnt);
 | 
						||
	// save char c onto queue
 | 
						||
	last_modifying_cmd[strlen((char *) last_modifying_cmd)] = c;
 | 
						||
	adding2q = 1;
 | 
						||
	return;
 | 
						||
}
 | 
						||
 | 
						||
static void end_cmd_q(void)
 | 
						||
{
 | 
						||
#ifdef CONFIG_FEATURE_VI_YANKMARK
 | 
						||
	YDreg = 26;			// go back to default Yank/Delete reg
 | 
						||
#endif							/* CONFIG_FEATURE_VI_YANKMARK */
 | 
						||
	adding2q = 0;
 | 
						||
	return;
 | 
						||
}
 | 
						||
#endif							/* CONFIG_FEATURE_VI_DOT_CMD */
 | 
						||
 | 
						||
#if defined(CONFIG_FEATURE_VI_YANKMARK) || defined(CONFIG_FEATURE_VI_COLON) || defined(CONFIG_FEATURE_VI_CRASHME)
 | 
						||
static Byte *string_insert(Byte * p, Byte * s) // insert the string at 'p'
 | 
						||
{
 | 
						||
	int cnt, i;
 | 
						||
 | 
						||
	i = strlen((char *) s);
 | 
						||
	p = text_hole_make(p, i);
 | 
						||
	strncpy((char *) p, (char *) s, i);
 | 
						||
	for (cnt = 0; *s != '\0'; s++) {
 | 
						||
		if (*s == '\n')
 | 
						||
			cnt++;
 | 
						||
	}
 | 
						||
#ifdef CONFIG_FEATURE_VI_YANKMARK
 | 
						||
	psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
 | 
						||
#endif							/* CONFIG_FEATURE_VI_YANKMARK */
 | 
						||
	return (p);
 | 
						||
}
 | 
						||
#endif							/* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_COLON || CONFIG_FEATURE_VI_CRASHME */
 | 
						||
 | 
						||
#ifdef CONFIG_FEATURE_VI_YANKMARK
 | 
						||
static Byte *text_yank(Byte * p, Byte * q, int dest)	// copy text into a register
 | 
						||
{
 | 
						||
	Byte *t;
 | 
						||
	int cnt;
 | 
						||
 | 
						||
	if (q < p) {		// they are backwards- reverse them
 | 
						||
		t = q;
 | 
						||
		q = p;
 | 
						||
		p = t;
 | 
						||
	}
 | 
						||
	cnt = q - p + 1;
 | 
						||
	t = reg[dest];
 | 
						||
	if (t != 0) {		// if already a yank register
 | 
						||
		free(t);		//   free it
 | 
						||
	}
 | 
						||
	t = (Byte *) xmalloc(cnt + 1);	// get a new register
 | 
						||
	memset(t, '\0', cnt + 1);	// clear new text[]
 | 
						||
	strncpy((char *) t, (char *) p, cnt);	// copy text[] into bufer
 | 
						||
	reg[dest] = t;
 | 
						||
	return (p);
 | 
						||
}
 | 
						||
 | 
						||
static Byte what_reg(void)
 | 
						||
{
 | 
						||
	Byte c;
 | 
						||
	int i;
 | 
						||
 | 
						||
	i = 0;
 | 
						||
	c = 'D';			// default to D-reg
 | 
						||
	if (0 <= YDreg && YDreg <= 25)
 | 
						||
		c = 'a' + (Byte) YDreg;
 | 
						||
	if (YDreg == 26)
 | 
						||
		c = 'D';
 | 
						||
	if (YDreg == 27)
 | 
						||
		c = 'U';
 | 
						||
	return (c);
 | 
						||
}
 | 
						||
 | 
						||
static void check_context(Byte cmd)
 | 
						||
{
 | 
						||
	// A context is defined to be "modifying text"
 | 
						||
	// Any modifying command establishes a new context.
 | 
						||
 | 
						||
	if (dot < context_start || dot > context_end) {
 | 
						||
		if (strchr((char *) modifying_cmds, cmd) != NULL) {
 | 
						||
			// we are trying to modify text[]- make this the current context
 | 
						||
			mark[27] = mark[26];	// move cur to prev
 | 
						||
			mark[26] = dot;	// move local to cur
 | 
						||
			context_start = prev_line(prev_line(dot));
 | 
						||
			context_end = next_line(next_line(dot));
 | 
						||
			//loiter= start_loiter= now;
 | 
						||
		}
 | 
						||
	}
 | 
						||
	return;
 | 
						||
}
 | 
						||
 | 
						||
static Byte *swap_context(Byte * p) // goto new context for '' command make this the current context
 | 
						||
{
 | 
						||
	Byte *tmp;
 | 
						||
 | 
						||
	// the current context is in mark[26]
 | 
						||
	// the previous context is in mark[27]
 | 
						||
	// only swap context if other context is valid
 | 
						||
	if (text <= mark[27] && mark[27] <= end - 1) {
 | 
						||
		tmp = mark[27];
 | 
						||
		mark[27] = mark[26];
 | 
						||
		mark[26] = tmp;
 | 
						||
		p = mark[26];	// where we are going- previous context
 | 
						||
		context_start = prev_line(prev_line(prev_line(p)));
 | 
						||
		context_end = next_line(next_line(next_line(p)));
 | 
						||
	}
 | 
						||
	return (p);
 | 
						||
}
 | 
						||
#endif							/* CONFIG_FEATURE_VI_YANKMARK */
 | 
						||
 | 
						||
static int isblnk(Byte c) // is the char a blank or tab
 | 
						||
{
 | 
						||
	return (c == ' ' || c == '\t');
 | 
						||
}
 | 
						||
 | 
						||
//----- Set terminal attributes --------------------------------
 | 
						||
static void rawmode(void)
 | 
						||
{
 | 
						||
	tcgetattr(0, &term_orig);
 | 
						||
	term_vi = term_orig;
 | 
						||
	term_vi.c_lflag &= (~ICANON & ~ECHO);	// leave ISIG ON- allow intr's
 | 
						||
	term_vi.c_iflag &= (~IXON & ~ICRNL);
 | 
						||
	term_vi.c_oflag &= (~ONLCR);
 | 
						||
#ifndef linux
 | 
						||
	term_vi.c_cc[VMIN] = 1;
 | 
						||
	term_vi.c_cc[VTIME] = 0;
 | 
						||
#endif
 | 
						||
	erase_char = term_vi.c_cc[VERASE];
 | 
						||
	tcsetattr(0, TCSANOW, &term_vi);
 | 
						||
}
 | 
						||
 | 
						||
static void cookmode(void)
 | 
						||
{
 | 
						||
	tcsetattr(0, TCSANOW, &term_orig);
 | 
						||
}
 | 
						||
 | 
						||
#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
 | 
						||
//----- See what the window size currently is --------------------
 | 
						||
static void window_size_get(int sig)
 | 
						||
{
 | 
						||
	int i;
 | 
						||
 | 
						||
	i = ioctl(0, TIOCGWINSZ, &winsize);
 | 
						||
	if (i != 0) {
 | 
						||
		// force 24x80
 | 
						||
		winsize.ws_row = 24;
 | 
						||
		winsize.ws_col = 80;
 | 
						||
	}
 | 
						||
	if (winsize.ws_row <= 1) {
 | 
						||
		winsize.ws_row = 24;
 | 
						||
	}
 | 
						||
	if (winsize.ws_col <= 1) {
 | 
						||
		winsize.ws_col = 80;
 | 
						||
	}
 | 
						||
	rows = (int) winsize.ws_row;
 | 
						||
	columns = (int) winsize.ws_col;
 | 
						||
}
 | 
						||
#endif							/* CONFIG_FEATURE_VI_WIN_RESIZE */
 | 
						||
 | 
						||
//----- Come here when we get a window resize signal ---------
 | 
						||
#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
 | 
						||
static void winch_sig(int sig)
 | 
						||
{
 | 
						||
	signal(SIGWINCH, winch_sig);
 | 
						||
#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
 | 
						||
	window_size_get(0);
 | 
						||
#endif							/* CONFIG_FEATURE_VI_WIN_RESIZE */
 | 
						||
	new_screen(rows, columns);	// get memory for virtual screen
 | 
						||
	redraw(TRUE);		// re-draw the screen
 | 
						||
}
 | 
						||
 | 
						||
//----- Come here when we get a continue signal -------------------
 | 
						||
static void cont_sig(int sig)
 | 
						||
{
 | 
						||
	rawmode();			// terminal to "raw"
 | 
						||
	*status_buffer = '\0';	// clear the status buffer
 | 
						||
	redraw(TRUE);		// re-draw the screen
 | 
						||
 | 
						||
	signal(SIGTSTP, suspend_sig);
 | 
						||
	signal(SIGCONT, SIG_DFL);
 | 
						||
	kill(getpid(), SIGCONT);
 | 
						||
}
 | 
						||
 | 
						||
//----- Come here when we get a Suspend signal -------------------
 | 
						||
static void suspend_sig(int sig)
 | 
						||
{
 | 
						||
	place_cursor(rows - 1, 0, FALSE);	// go to bottom of screen
 | 
						||
	clear_to_eol();		// Erase to end of line
 | 
						||
	cookmode();			// terminal to "cooked"
 | 
						||
 | 
						||
	signal(SIGCONT, cont_sig);
 | 
						||
	signal(SIGTSTP, SIG_DFL);
 | 
						||
	kill(getpid(), SIGTSTP);
 | 
						||
}
 | 
						||
 | 
						||
//----- Come here when we get a signal ---------------------------
 | 
						||
static void catch_sig(int sig)
 | 
						||
{
 | 
						||
	signal(SIGHUP, catch_sig);
 | 
						||
	signal(SIGINT, catch_sig);
 | 
						||
	signal(SIGTERM, catch_sig);
 | 
						||
	longjmp(restart, sig);
 | 
						||
}
 | 
						||
 | 
						||
static void alarm_sig(int sig)
 | 
						||
{
 | 
						||
	signal(SIGALRM, catch_sig);
 | 
						||
	longjmp(restart, sig);
 | 
						||
}
 | 
						||
 | 
						||
//----- Come here when we get a core dump signal -----------------
 | 
						||
static void core_sig(int sig)
 | 
						||
{
 | 
						||
	signal(SIGQUIT, core_sig);
 | 
						||
	signal(SIGILL, core_sig);
 | 
						||
	signal(SIGTRAP, core_sig);
 | 
						||
	signal(SIGIOT, core_sig);
 | 
						||
	signal(SIGABRT, core_sig);
 | 
						||
	signal(SIGFPE, core_sig);
 | 
						||
	signal(SIGBUS, core_sig);
 | 
						||
	signal(SIGSEGV, core_sig);
 | 
						||
#ifdef SIGSYS
 | 
						||
	signal(SIGSYS, core_sig);
 | 
						||
#endif
 | 
						||
 | 
						||
	dot = bound_dot(dot);	// make sure "dot" is valid
 | 
						||
 | 
						||
	longjmp(restart, sig);
 | 
						||
}
 | 
						||
#endif							/* CONFIG_FEATURE_VI_USE_SIGNALS */
 | 
						||
 | 
						||
static int mysleep(int hund)	// sleep for 'h' 1/100 seconds
 | 
						||
{
 | 
						||
	// Don't hang- Wait 5/100 seconds-  1 Sec= 1000000
 | 
						||
	FD_ZERO(&rfds);
 | 
						||
	FD_SET(0, &rfds);
 | 
						||
	tv.tv_sec = 0;
 | 
						||
	tv.tv_usec = hund * 10000;
 | 
						||
	select(1, &rfds, NULL, NULL, &tv);
 | 
						||
	return (FD_ISSET(0, &rfds));
 | 
						||
}
 | 
						||
 | 
						||
//----- IO Routines --------------------------------------------
 | 
						||
static Byte readit(void)	// read (maybe cursor) key from stdin
 | 
						||
{
 | 
						||
	Byte c;
 | 
						||
	int i, bufsiz, cnt, cmdindex;
 | 
						||
	struct esc_cmds {
 | 
						||
		Byte *seq;
 | 
						||
		Byte val;
 | 
						||
	};
 | 
						||
 | 
						||
	static struct esc_cmds esccmds[] = {
 | 
						||
		{(Byte *) "OA", (Byte) VI_K_UP},	// cursor key Up
 | 
						||
		{(Byte *) "OB", (Byte) VI_K_DOWN},	// cursor key Down
 | 
						||
		{(Byte *) "OC", (Byte) VI_K_RIGHT},	// Cursor Key Right
 | 
						||
		{(Byte *) "OD", (Byte) VI_K_LEFT},	// cursor key Left
 | 
						||
		{(Byte *) "OH", (Byte) VI_K_HOME},	// Cursor Key Home
 | 
						||
		{(Byte *) "OF", (Byte) VI_K_END},	// Cursor Key End
 | 
						||
		{(Byte *) "[A", (Byte) VI_K_UP},	// cursor key Up
 | 
						||
		{(Byte *) "[B", (Byte) VI_K_DOWN},	// cursor key Down
 | 
						||
		{(Byte *) "[C", (Byte) VI_K_RIGHT},	// Cursor Key Right
 | 
						||
		{(Byte *) "[D", (Byte) VI_K_LEFT},	// cursor key Left
 | 
						||
		{(Byte *) "[H", (Byte) VI_K_HOME},	// Cursor Key Home
 | 
						||
		{(Byte *) "[F", (Byte) VI_K_END},	// Cursor Key End
 | 
						||
		{(Byte *) "[2~", (Byte) VI_K_INSERT},	// Cursor Key Insert
 | 
						||
		{(Byte *) "[5~", (Byte) VI_K_PAGEUP},	// Cursor Key Page Up
 | 
						||
		{(Byte *) "[6~", (Byte) VI_K_PAGEDOWN},	// Cursor Key Page Down
 | 
						||
		{(Byte *) "OP", (Byte) VI_K_FUN1},	// Function Key F1
 | 
						||
		{(Byte *) "OQ", (Byte) VI_K_FUN2},	// Function Key F2
 | 
						||
		{(Byte *) "OR", (Byte) VI_K_FUN3},	// Function Key F3
 | 
						||
		{(Byte *) "OS", (Byte) VI_K_FUN4},	// Function Key F4
 | 
						||
		{(Byte *) "[15~", (Byte) VI_K_FUN5},	// Function Key F5
 | 
						||
		{(Byte *) "[17~", (Byte) VI_K_FUN6},	// Function Key F6
 | 
						||
		{(Byte *) "[18~", (Byte) VI_K_FUN7},	// Function Key F7
 | 
						||
		{(Byte *) "[19~", (Byte) VI_K_FUN8},	// Function Key F8
 | 
						||
		{(Byte *) "[20~", (Byte) VI_K_FUN9},	// Function Key F9
 | 
						||
		{(Byte *) "[21~", (Byte) VI_K_FUN10},	// Function Key F10
 | 
						||
		{(Byte *) "[23~", (Byte) VI_K_FUN11},	// Function Key F11
 | 
						||
		{(Byte *) "[24~", (Byte) VI_K_FUN12},	// Function Key F12
 | 
						||
		{(Byte *) "[11~", (Byte) VI_K_FUN1},	// Function Key F1
 | 
						||
		{(Byte *) "[12~", (Byte) VI_K_FUN2},	// Function Key F2
 | 
						||
		{(Byte *) "[13~", (Byte) VI_K_FUN3},	// Function Key F3
 | 
						||
		{(Byte *) "[14~", (Byte) VI_K_FUN4},	// Function Key F4
 | 
						||
	};
 | 
						||
 | 
						||
#define ESCCMDS_COUNT (sizeof(esccmds)/sizeof(struct esc_cmds))
 | 
						||
 | 
						||
	(void) alarm(0);	// turn alarm OFF while we wait for input
 | 
						||
	// get input from User- are there already input chars in Q?
 | 
						||
	bufsiz = strlen((char *) readbuffer);
 | 
						||
	if (bufsiz <= 0) {
 | 
						||
	  ri0:
 | 
						||
		// the Q is empty, wait for a typed char
 | 
						||
		bufsiz = read(0, readbuffer, BUFSIZ - 1);
 | 
						||
		if (bufsiz < 0) {
 | 
						||
			if (errno == EINTR)
 | 
						||
				goto ri0;	// interrupted sys call
 | 
						||
			if (errno == EBADF)
 | 
						||
				editing = 0;
 | 
						||
			if (errno == EFAULT)
 | 
						||
				editing = 0;
 | 
						||
			if (errno == EINVAL)
 | 
						||
				editing = 0;
 | 
						||
			if (errno == EIO)
 | 
						||
				editing = 0;
 | 
						||
			errno = 0;
 | 
						||
			bufsiz = 0;
 | 
						||
		}
 | 
						||
		readbuffer[bufsiz] = '\0';
 | 
						||
	}
 | 
						||
	// return char if it is not part of ESC sequence
 | 
						||
	if (readbuffer[0] != 27)
 | 
						||
		goto ri1;
 | 
						||
 | 
						||
	// This is an ESC char. Is this Esc sequence?
 | 
						||
	// Could be bare Esc key. See if there are any
 | 
						||
	// more chars to read after the ESC. This would
 | 
						||
	// be a Function or Cursor Key sequence.
 | 
						||
	FD_ZERO(&rfds);
 | 
						||
	FD_SET(0, &rfds);
 | 
						||
	tv.tv_sec = 0;
 | 
						||
	tv.tv_usec = 50000;	// Wait 5/100 seconds- 1 Sec=1000000
 | 
						||
 | 
						||
	// keep reading while there are input chars and room in buffer
 | 
						||
	while (select(1, &rfds, NULL, NULL, &tv) > 0 && bufsiz <= (BUFSIZ - 5)) {
 | 
						||
		// read the rest of the ESC string
 | 
						||
		i = read(0, (void *) (readbuffer + bufsiz), BUFSIZ - bufsiz);
 | 
						||
		if (i > 0) {
 | 
						||
			bufsiz += i;
 | 
						||
			readbuffer[bufsiz] = '\0';	// Terminate the string
 | 
						||
		}
 | 
						||
	}
 | 
						||
	// Maybe cursor or function key?
 | 
						||
	for (cmdindex = 0; cmdindex < ESCCMDS_COUNT; cmdindex++) {
 | 
						||
		cnt = strlen((char *) esccmds[cmdindex].seq);
 | 
						||
		i = strncmp((char *) esccmds[cmdindex].seq, (char *) readbuffer, cnt);
 | 
						||
		if (i == 0) {
 | 
						||
			// is a Cursor key- put derived value back into Q
 | 
						||
			readbuffer[0] = esccmds[cmdindex].val;
 | 
						||
			// squeeze out the ESC sequence
 | 
						||
			for (i = 1; i < cnt; i++) {
 | 
						||
				memmove(readbuffer + 1, readbuffer + 2, BUFSIZ - 2);
 | 
						||
				readbuffer[BUFSIZ - 1] = '\0';
 | 
						||
			}
 | 
						||
			break;
 | 
						||
		}
 | 
						||
	}
 | 
						||
  ri1:
 | 
						||
	c = readbuffer[0];
 | 
						||
	// remove one char from Q
 | 
						||
	memmove(readbuffer, readbuffer + 1, BUFSIZ - 1);
 | 
						||
	readbuffer[BUFSIZ - 1] = '\0';
 | 
						||
	(void) alarm(3);	// we are done waiting for input, turn alarm ON
 | 
						||
	return (c);
 | 
						||
}
 | 
						||
 | 
						||
//----- IO Routines --------------------------------------------
 | 
						||
static Byte get_one_char()
 | 
						||
{
 | 
						||
	static Byte c;
 | 
						||
 | 
						||
#ifdef CONFIG_FEATURE_VI_DOT_CMD
 | 
						||
	// ! adding2q  && ioq == 0  read()
 | 
						||
	// ! adding2q  && ioq != 0  *ioq
 | 
						||
	// adding2q         *last_modifying_cmd= read()
 | 
						||
	if (!adding2q) {
 | 
						||
		// we are not adding to the q.
 | 
						||
		// but, we may be reading from a q
 | 
						||
		if (ioq == 0) {
 | 
						||
			// there is no current q, read from STDIN
 | 
						||
			c = readit();	// get the users input
 | 
						||
		} else {
 | 
						||
			// there is a queue to get chars from first
 | 
						||
			c = *ioq++;
 | 
						||
			if (c == '\0') {
 | 
						||
				// the end of the q, read from STDIN
 | 
						||
				free(ioq_start);
 | 
						||
				ioq_start = ioq = 0;
 | 
						||
				c = readit();	// get the users input
 | 
						||
			}
 | 
						||
		}
 | 
						||
	} else {
 | 
						||
		// adding STDIN chars to q
 | 
						||
		c = readit();	// get the users input
 | 
						||
		if (last_modifying_cmd != 0) {
 | 
						||
			// add new char to q
 | 
						||
			last_modifying_cmd[strlen((char *) last_modifying_cmd)] = c;
 | 
						||
		}
 | 
						||
	}
 | 
						||
#else							/* CONFIG_FEATURE_VI_DOT_CMD */
 | 
						||
	c = readit();		// get the users input
 | 
						||
#endif							/* CONFIG_FEATURE_VI_DOT_CMD */
 | 
						||
	return (c);			// return the char, where ever it came from
 | 
						||
}
 | 
						||
 | 
						||
static Byte *get_input_line(Byte * prompt) // get input line- use "status line"
 | 
						||
{
 | 
						||
	Byte buf[BUFSIZ];
 | 
						||
	Byte c;
 | 
						||
	int i;
 | 
						||
	static Byte *obufp = NULL;
 | 
						||
 | 
						||
	strcpy((char *) buf, (char *) prompt);
 | 
						||
	*status_buffer = '\0';	// clear the status buffer
 | 
						||
	place_cursor(rows - 1, 0, FALSE);	// go to Status line, bottom of screen
 | 
						||
	clear_to_eol();		// clear the line
 | 
						||
	write(1, prompt, strlen((char *) prompt));	// write out the :, /, or ? prompt
 | 
						||
 | 
						||
	for (i = strlen((char *) buf); i < BUFSIZ;) {
 | 
						||
		c = get_one_char();	// read user input
 | 
						||
		if (c == '\n' || c == '\r' || c == 27)
 | 
						||
			break;		// is this end of input
 | 
						||
		if (c == erase_char) {	// user wants to erase prev char
 | 
						||
			i--;		// backup to prev char
 | 
						||
			buf[i] = '\0';	// erase the char
 | 
						||
			buf[i + 1] = '\0';	// null terminate buffer
 | 
						||
			write(1, " ", 3);	// erase char on screen
 | 
						||
			if (i <= 0) {	// user backs up before b-o-l, exit
 | 
						||
				break;
 | 
						||
			}
 | 
						||
		} else {
 | 
						||
			buf[i] = c;	// save char in buffer
 | 
						||
			buf[i + 1] = '\0';	// make sure buffer is null terminated
 | 
						||
			write(1, buf + i, 1);	// echo the char back to user
 | 
						||
			i++;
 | 
						||
		}
 | 
						||
	}
 | 
						||
	refresh(FALSE);
 | 
						||
	if (obufp != NULL)
 | 
						||
		free(obufp);
 | 
						||
	obufp = (Byte *) xstrdup((char *) buf);
 | 
						||
	return (obufp);
 | 
						||
}
 | 
						||
 | 
						||
static int file_size(Byte * fn) // what is the byte size of "fn"
 | 
						||
{
 | 
						||
	struct stat st_buf;
 | 
						||
	int cnt, sr;
 | 
						||
 | 
						||
	if (fn == 0 || strlen(fn) <= 0)
 | 
						||
		return (-1);
 | 
						||
	cnt = -1;
 | 
						||
	sr = stat((char *) fn, &st_buf);	// see if file exists
 | 
						||
	if (sr >= 0) {
 | 
						||
		cnt = (int) st_buf.st_size;
 | 
						||
	}
 | 
						||
	return (cnt);
 | 
						||
}
 | 
						||
 | 
						||
static int file_insert(Byte * fn, Byte * p, int size)
 | 
						||
{
 | 
						||
	int fd, cnt;
 | 
						||
 | 
						||
	cnt = -1;
 | 
						||
#ifdef CONFIG_FEATURE_VI_READONLY
 | 
						||
	readonly = FALSE;
 | 
						||
#endif							/* CONFIG_FEATURE_VI_READONLY */
 | 
						||
	if (fn == 0 || strlen((char*) fn) <= 0) {
 | 
						||
		psbs("No filename given");
 | 
						||
		goto fi0;
 | 
						||
	}
 | 
						||
	if (size == 0) {
 | 
						||
		// OK- this is just a no-op
 | 
						||
		cnt = 0;
 | 
						||
		goto fi0;
 | 
						||
	}
 | 
						||
	if (size < 0) {
 | 
						||
		psbs("Trying to insert a negative number (%d) of characters", size);
 | 
						||
		goto fi0;
 | 
						||
	}
 | 
						||
	if (p < text || p > end) {
 | 
						||
		psbs("Trying to insert file outside of memory");
 | 
						||
		goto fi0;
 | 
						||
	}
 | 
						||
 | 
						||
	// see if we can open the file
 | 
						||
#ifdef CONFIG_FEATURE_VI_READONLY
 | 
						||
	if (vi_readonly) goto fi1;		// do not try write-mode
 | 
						||
#endif
 | 
						||
	fd = open((char *) fn, O_RDWR);			// assume read & write
 | 
						||
	if (fd < 0) {
 | 
						||
		// could not open for writing- maybe file is read only
 | 
						||
#ifdef CONFIG_FEATURE_VI_READONLY
 | 
						||
  fi1:
 | 
						||
#endif
 | 
						||
		fd = open((char *) fn, O_RDONLY);	// try read-only
 | 
						||
		if (fd < 0) {
 | 
						||
			psbs("\"%s\" %s", fn, "could not open file");
 | 
						||
			goto fi0;
 | 
						||
		}
 | 
						||
#ifdef CONFIG_FEATURE_VI_READONLY
 | 
						||
		// got the file- read-only
 | 
						||
		readonly = TRUE;
 | 
						||
#endif							/* CONFIG_FEATURE_VI_READONLY */
 | 
						||
	}
 | 
						||
	p = text_hole_make(p, size);
 | 
						||
	cnt = read(fd, p, size);
 | 
						||
	close(fd);
 | 
						||
	if (cnt < 0) {
 | 
						||
		cnt = -1;
 | 
						||
		p = text_hole_delete(p, p + size - 1);	// un-do buffer insert
 | 
						||
		psbs("could not read file \"%s\"", fn);
 | 
						||
	} else if (cnt < size) {
 | 
						||
		// There was a partial read, shrink unused space text[]
 | 
						||
		p = text_hole_delete(p + cnt, p + (size - cnt) - 1);	// un-do buffer insert
 | 
						||
		psbs("could not read all of file \"%s\"", fn);
 | 
						||
	}
 | 
						||
	if (cnt >= size)
 | 
						||
		file_modified = TRUE;
 | 
						||
  fi0:
 | 
						||
	return (cnt);
 | 
						||
}
 | 
						||
 | 
						||
static int file_write(Byte * fn, Byte * first, Byte * last)
 | 
						||
{
 | 
						||
	int fd, cnt, charcnt;
 | 
						||
 | 
						||
	if (fn == 0) {
 | 
						||
		psbs("No current filename");
 | 
						||
		return (-1);
 | 
						||
	}
 | 
						||
	charcnt = 0;
 | 
						||
	// FIXIT- use the correct umask()
 | 
						||
	fd = open((char *) fn, (O_WRONLY | O_CREAT | O_TRUNC), 0664);
 | 
						||
	if (fd < 0)
 | 
						||
		return (-1);
 | 
						||
	cnt = last - first + 1;
 | 
						||
	charcnt = write(fd, first, cnt);
 | 
						||
	if (charcnt == cnt) {
 | 
						||
		// good write
 | 
						||
		//file_modified= FALSE; // the file has not been modified
 | 
						||
	} else {
 | 
						||
		charcnt = 0;
 | 
						||
	}
 | 
						||
	close(fd);
 | 
						||
	return (charcnt);
 | 
						||
}
 | 
						||
 | 
						||
//----- Terminal Drawing ---------------------------------------
 | 
						||
// The terminal is made up of 'rows' line of 'columns' columns.
 | 
						||
// classicly this would be 24 x 80.
 | 
						||
//  screen coordinates
 | 
						||
//  0,0     ...     0,79
 | 
						||
//  1,0     ...     1,79
 | 
						||
//  .       ...     .
 | 
						||
//  .       ...     .
 | 
						||
//  22,0    ...     22,79
 | 
						||
//  23,0    ...     23,79   status line
 | 
						||
//
 | 
						||
 | 
						||
//----- Move the cursor to row x col (count from 0, not 1) -------
 | 
						||
static void place_cursor(int row, int col, int opti)
 | 
						||
{
 | 
						||
	char cm1[BUFSIZ];
 | 
						||
	char *cm;
 | 
						||
	int l;
 | 
						||
#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
 | 
						||
	char cm2[BUFSIZ];
 | 
						||
	Byte *screenp;
 | 
						||
	// char cm3[BUFSIZ];
 | 
						||
	int Rrow= last_row;
 | 
						||
#endif							/* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
 | 
						||
	
 | 
						||
	memset(cm1, '\0', BUFSIZ - 1);  // clear the buffer
 | 
						||
 | 
						||
	if (row < 0) row = 0;
 | 
						||
	if (row >= rows) row = rows - 1;
 | 
						||
	if (col < 0) col = 0;
 | 
						||
	if (col >= columns) col = columns - 1;
 | 
						||
	
 | 
						||
	//----- 1.  Try the standard terminal ESC sequence
 | 
						||
	sprintf((char *) cm1, CMrc, row + 1, col + 1);
 | 
						||
	cm= cm1;
 | 
						||
	if (! opti) goto pc0;
 | 
						||
 | 
						||
#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
 | 
						||
	//----- find the minimum # of chars to move cursor -------------
 | 
						||
	//----- 2.  Try moving with discreet chars (Newline, [back]space, ...)
 | 
						||
	memset(cm2, '\0', BUFSIZ - 1);  // clear the buffer
 | 
						||
	
 | 
						||
	// move to the correct row
 | 
						||
	while (row < Rrow) {
 | 
						||
		// the cursor has to move up
 | 
						||
		strcat(cm2, CMup);
 | 
						||
		Rrow--;
 | 
						||
	}
 | 
						||
	while (row > Rrow) {
 | 
						||
		// the cursor has to move down
 | 
						||
		strcat(cm2, CMdown);
 | 
						||
		Rrow++;
 | 
						||
	}
 | 
						||
	
 | 
						||
	// now move to the correct column
 | 
						||
	strcat(cm2, "\r");			// start at col 0
 | 
						||
	// just send out orignal source char to get to correct place
 | 
						||
	screenp = &screen[row * columns];	// start of screen line
 | 
						||
	strncat(cm2, screenp, col);
 | 
						||
 | 
						||
	//----- 3.  Try some other way of moving cursor
 | 
						||
	//---------------------------------------------
 | 
						||
 | 
						||
	// pick the shortest cursor motion to send out
 | 
						||
	cm= cm1;
 | 
						||
	if (strlen(cm2) < strlen(cm)) {
 | 
						||
		cm= cm2;
 | 
						||
	}  /* else if (strlen(cm3) < strlen(cm)) {
 | 
						||
		cm= cm3;
 | 
						||
	} */
 | 
						||
#endif							/* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
 | 
						||
  pc0:
 | 
						||
	l= strlen(cm);
 | 
						||
	if (l) write(1, cm, l);			// move the cursor
 | 
						||
}
 | 
						||
 | 
						||
//----- Erase from cursor to end of line -----------------------
 | 
						||
static void clear_to_eol()
 | 
						||
{
 | 
						||
	write(1, Ceol, strlen(Ceol));	// Erase from cursor to end of line
 | 
						||
}
 | 
						||
 | 
						||
//----- Erase from cursor to end of screen -----------------------
 | 
						||
static void clear_to_eos()
 | 
						||
{
 | 
						||
	write(1, Ceos, strlen(Ceos));	// Erase from cursor to end of screen
 | 
						||
}
 | 
						||
 | 
						||
//----- Start standout mode ------------------------------------
 | 
						||
static void standout_start() // send "start reverse video" sequence
 | 
						||
{
 | 
						||
	write(1, SOs, strlen(SOs));	// Start reverse video mode
 | 
						||
}
 | 
						||
 | 
						||
//----- End standout mode --------------------------------------
 | 
						||
static void standout_end() // send "end reverse video" sequence
 | 
						||
{
 | 
						||
	write(1, SOn, strlen(SOn));	// End reverse video mode
 | 
						||
}
 | 
						||
 | 
						||
//----- Flash the screen  --------------------------------------
 | 
						||
static void flash(int h)
 | 
						||
{
 | 
						||
	standout_start();	// send "start reverse video" sequence
 | 
						||
	redraw(TRUE);
 | 
						||
	(void) mysleep(h);
 | 
						||
	standout_end();		// send "end reverse video" sequence
 | 
						||
	redraw(TRUE);
 | 
						||
}
 | 
						||
 | 
						||
static void beep()
 | 
						||
{
 | 
						||
	write(1, bell, strlen(bell));	// send out a bell character
 | 
						||
}
 | 
						||
 | 
						||
static void indicate_error(char c)
 | 
						||
{
 | 
						||
#ifdef CONFIG_FEATURE_VI_CRASHME
 | 
						||
	if (crashme > 0)
 | 
						||
		return;			// generate a random command
 | 
						||
#endif							/* CONFIG_FEATURE_VI_CRASHME */
 | 
						||
	if (err_method == 0) {
 | 
						||
		beep();
 | 
						||
	} else {
 | 
						||
		flash(10);
 | 
						||
	}
 | 
						||
}
 | 
						||
 | 
						||
//----- Screen[] Routines --------------------------------------
 | 
						||
//----- Erase the Screen[] memory ------------------------------
 | 
						||
static void screen_erase()
 | 
						||
{
 | 
						||
	memset(screen, ' ', screensize);	// clear new screen
 | 
						||
}
 | 
						||
 | 
						||
//----- Draw the status line at bottom of the screen -------------
 | 
						||
static void show_status_line(void)
 | 
						||
{
 | 
						||
	static int last_cksum;
 | 
						||
	int l, cnt, cksum;
 | 
						||
 | 
						||
	cnt = strlen((char *) status_buffer);
 | 
						||
	for (cksum= l= 0; l < cnt; l++) { cksum += (int)(status_buffer[l]); }
 | 
						||
	// don't write the status line unless it changes
 | 
						||
	if (cnt > 0 && last_cksum != cksum) {
 | 
						||
		last_cksum= cksum;		// remember if we have seen this line
 | 
						||
		place_cursor(rows - 1, 0, FALSE);	// put cursor on status line
 | 
						||
		write(1, status_buffer, cnt);
 | 
						||
		clear_to_eol();
 | 
						||
		place_cursor(crow, ccol, FALSE);	// put cursor back in correct place
 | 
						||
	}
 | 
						||
}
 | 
						||
 | 
						||
//----- format the status buffer, the bottom line of screen ------
 | 
						||
// print status buffer, with STANDOUT mode
 | 
						||
static void psbs(char *format, ...)
 | 
						||
{
 | 
						||
	va_list args;
 | 
						||
 | 
						||
	va_start(args, format);
 | 
						||
	strcpy((char *) status_buffer, SOs);	// Terminal standout mode on
 | 
						||
	vsprintf((char *) status_buffer + strlen((char *) status_buffer), format,
 | 
						||
			 args);
 | 
						||
	strcat((char *) status_buffer, SOn);	// Terminal standout mode off
 | 
						||
	va_end(args);
 | 
						||
 | 
						||
	return;
 | 
						||
}
 | 
						||
 | 
						||
// print status buffer
 | 
						||
static void psb(char *format, ...)
 | 
						||
{
 | 
						||
	va_list args;
 | 
						||
 | 
						||
	va_start(args, format);
 | 
						||
	vsprintf((char *) status_buffer, format, args);
 | 
						||
	va_end(args);
 | 
						||
	return;
 | 
						||
}
 | 
						||
 | 
						||
static void ni(Byte * s) // display messages
 | 
						||
{
 | 
						||
	Byte buf[BUFSIZ];
 | 
						||
 | 
						||
	print_literal(buf, s);
 | 
						||
	psbs("\'%s\' is not implemented", buf);
 | 
						||
}
 | 
						||
 | 
						||
static void edit_status(void)	// show file status on status line
 | 
						||
{
 | 
						||
	int cur, tot, percent;
 | 
						||
 | 
						||
	cur = count_lines(text, dot);
 | 
						||
	tot = count_lines(text, end - 1);
 | 
						||
	//    current line         percent
 | 
						||
	//   -------------    ~~ ----------
 | 
						||
	//    total lines            100
 | 
						||
	if (tot > 0) {
 | 
						||
		percent = (100 * cur) / tot;
 | 
						||
	} else {
 | 
						||
		cur = tot = 0;
 | 
						||
		percent = 100;
 | 
						||
	}
 | 
						||
	psb("\"%s\""
 | 
						||
#ifdef CONFIG_FEATURE_VI_READONLY
 | 
						||
		"%s"
 | 
						||
#endif							/* CONFIG_FEATURE_VI_READONLY */
 | 
						||
		"%s line %d of %d --%d%%--",
 | 
						||
		(cfn != 0 ? (char *) cfn : "No file"),
 | 
						||
#ifdef CONFIG_FEATURE_VI_READONLY
 | 
						||
		((vi_readonly || readonly) ? " [Read only]" : ""),
 | 
						||
#endif							/* CONFIG_FEATURE_VI_READONLY */
 | 
						||
		(file_modified ? " [modified]" : ""),
 | 
						||
		cur, tot, percent);
 | 
						||
}
 | 
						||
 | 
						||
//----- Force refresh of all Lines -----------------------------
 | 
						||
static void redraw(int full_screen)
 | 
						||
{
 | 
						||
	place_cursor(0, 0, FALSE);	// put cursor in correct place
 | 
						||
	clear_to_eos();		// tel terminal to erase display
 | 
						||
	screen_erase();		// erase the internal screen buffer
 | 
						||
	refresh(full_screen);	// this will redraw the entire display
 | 
						||
}
 | 
						||
 | 
						||
//----- Format a text[] line into a buffer ---------------------
 | 
						||
static void format_line(Byte *dest, Byte *src, int li)
 | 
						||
{
 | 
						||
	int co;
 | 
						||
	Byte c;
 | 
						||
	
 | 
						||
	for (co= 0; co < MAX_SCR_COLS; co++) {
 | 
						||
		c= ' ';		// assume blank
 | 
						||
		if (li > 0 && co == 0) {
 | 
						||
			c = '~';        // not first line, assume Tilde
 | 
						||
		}
 | 
						||
		// are there chars in text[] and have we gone past the end
 | 
						||
		if (text < end && src < end) {
 | 
						||
			c = *src++;
 | 
						||
		}
 | 
						||
		if (c == '\n')
 | 
						||
			break;
 | 
						||
		if (c < ' ' || c > '~') {
 | 
						||
			if (c == '\t') {
 | 
						||
				c = ' ';
 | 
						||
				//       co %    8     !=     7
 | 
						||
				for (; (co % tabstop) != (tabstop - 1); co++) {
 | 
						||
					dest[co] = c;
 | 
						||
				}
 | 
						||
			} else {
 | 
						||
				dest[co++] = '^';
 | 
						||
				c |= '@';       // make it visible
 | 
						||
				c &= 0x7f;      // get rid of hi bit
 | 
						||
			}
 | 
						||
		}
 | 
						||
		// the co++ is done here so that the column will
 | 
						||
		// not be overwritten when we blank-out the rest of line
 | 
						||
		dest[co] = c;
 | 
						||
		if (src >= end)
 | 
						||
			break;
 | 
						||
	}
 | 
						||
}
 | 
						||
 | 
						||
//----- Refresh the changed screen lines -----------------------
 | 
						||
// Copy the source line from text[] into the buffer and note
 | 
						||
// if the current screenline is different from the new buffer.
 | 
						||
// If they differ then that line needs redrawing on the terminal.
 | 
						||
//
 | 
						||
static void refresh(int full_screen)
 | 
						||
{
 | 
						||
	static int old_offset;
 | 
						||
	int li, changed;
 | 
						||
	Byte buf[MAX_SCR_COLS];
 | 
						||
	Byte *tp, *sp;		// pointer into text[] and screen[]
 | 
						||
#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
 | 
						||
	int last_li= -2;				// last line that changed- for optimizing cursor movement
 | 
						||
#endif							/* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
 | 
						||
 | 
						||
#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
 | 
						||
	window_size_get(0);
 | 
						||
#endif							/* CONFIG_FEATURE_VI_WIN_RESIZE */
 | 
						||
	sync_cursor(dot, &crow, &ccol);	// where cursor will be (on "dot")
 | 
						||
	tp = screenbegin;	// index into text[] of top line
 | 
						||
 | 
						||
	// compare text[] to screen[] and mark screen[] lines that need updating
 | 
						||
	for (li = 0; li < rows - 1; li++) {
 | 
						||
		int cs, ce;				// column start & end
 | 
						||
		memset(buf, ' ', MAX_SCR_COLS);		// blank-out the buffer
 | 
						||
		buf[MAX_SCR_COLS-1] = 0;		// NULL terminate the buffer
 | 
						||
		// format current text line into buf
 | 
						||
		format_line(buf, tp, li);
 | 
						||
 | 
						||
		// skip to the end of the current text[] line
 | 
						||
		while (tp < end && *tp++ != '\n') /*no-op*/ ;
 | 
						||
 | 
						||
		// see if there are any changes between vitual screen and buf
 | 
						||
		changed = FALSE;	// assume no change
 | 
						||
		cs= 0;
 | 
						||
		ce= columns-1;
 | 
						||
		sp = &screen[li * columns];	// start of screen line
 | 
						||
		if (full_screen) {
 | 
						||
			// force re-draw of every single column from 0 - columns-1
 | 
						||
			goto re0;
 | 
						||
		}
 | 
						||
		// compare newly formatted buffer with virtual screen
 | 
						||
		// look forward for first difference between buf and screen
 | 
						||
		for ( ; cs <= ce; cs++) {
 | 
						||
			if (buf[cs + offset] != sp[cs]) {
 | 
						||
				changed = TRUE;	// mark for redraw
 | 
						||
				break;
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// look backward for last difference between buf and screen
 | 
						||
		for ( ; ce >= cs; ce--) {
 | 
						||
			if (buf[ce + offset] != sp[ce]) {
 | 
						||
				changed = TRUE;	// mark for redraw
 | 
						||
				break;
 | 
						||
			}
 | 
						||
		}
 | 
						||
		// now, cs is index of first diff, and ce is index of last diff
 | 
						||
 | 
						||
		// if horz offset has changed, force a redraw
 | 
						||
		if (offset != old_offset) {
 | 
						||
  re0:
 | 
						||
			changed = TRUE;
 | 
						||
		}
 | 
						||
 | 
						||
		// make a sanity check of columns indexes
 | 
						||
		if (cs < 0) cs= 0;
 | 
						||
		if (ce > columns-1) ce= columns-1;
 | 
						||
		if (cs > ce) {  cs= 0;  ce= columns-1;  }
 | 
						||
		// is there a change between vitual screen and buf
 | 
						||
		if (changed) {
 | 
						||
			//  copy changed part of buffer to virtual screen
 | 
						||
			memmove(sp+cs, buf+(cs+offset), ce-cs+1);
 | 
						||
 | 
						||
			// move cursor to column of first change
 | 
						||
			if (offset != old_offset) {
 | 
						||
				// opti_cur_move is still too stupid
 | 
						||
				// to handle offsets correctly
 | 
						||
				place_cursor(li, cs, FALSE);
 | 
						||
			} else {
 | 
						||
#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
 | 
						||
				// if this just the next line
 | 
						||
				//  try to optimize cursor movement
 | 
						||
				//  otherwise, use standard ESC sequence
 | 
						||
				place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE);
 | 
						||
				last_li= li;
 | 
						||
#else							/* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
 | 
						||
				place_cursor(li, cs, FALSE);	// use standard ESC sequence
 | 
						||
#endif							/* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
 | 
						||
			}
 | 
						||
 | 
						||
			// write line out to terminal
 | 
						||
			write(1, sp+cs, ce-cs+1);
 | 
						||
#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
 | 
						||
			last_row = li;
 | 
						||
#endif							/* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
 | 
						||
	place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE);
 | 
						||
	last_row = crow;
 | 
						||
#else
 | 
						||
	place_cursor(crow, ccol, FALSE);
 | 
						||
#endif							/* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
 | 
						||
	
 | 
						||
	if (offset != old_offset)
 | 
						||
		old_offset = offset;
 | 
						||
}
 |