blob: 835adb62ee16b40fd8c9b3b17299559cb3ef6c6c [file] [log] [blame]
Eric Andersen3f980402001-04-04 17:31:15 +00001/* vi: set sw=8 ts=8: */
2/*
3 * tiny vi.c: A small 'vi' clone
4 * Copyright (C) 2000, 2001 Sterling Huxley <sterling@europa.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
Eric Andersen044228d2001-07-17 01:12:36 +000021static const char vi_Version[] =
Aaron Lehmanna170e1c2002-11-28 11:27:31 +000022 "$Id: vi.c,v 1.25 2002/11/28 11:27:23 aaronl Exp $";
Eric Andersen3f980402001-04-04 17:31:15 +000023
24/*
25 * To compile for standalone use:
Eric Andersend402edf2001-04-04 19:29:48 +000026 * gcc -Wall -Os -s -DSTANDALONE -o vi vi.c
Eric Andersen3f980402001-04-04 17:31:15 +000027 * or
Eric Andersenbdfd0d72001-10-24 05:00:29 +000028 * gcc -Wall -Os -s -DSTANDALONE -DCONFIG_FEATURE_VI_CRASHME -o vi vi.c # include testing features
Eric Andersen3f980402001-04-04 17:31:15 +000029 * strip vi
30 */
31
32/*
33 * Things To Do:
34 * EXINIT
Eric Andersen1c0d3112001-04-16 15:46:44 +000035 * $HOME/.exrc and ./.exrc
Eric Andersen3f980402001-04-04 17:31:15 +000036 * add magic to search /foo.*bar
37 * add :help command
38 * :map macros
39 * how about mode lines: vi: set sw=8 ts=8:
40 * if mark[] values were line numbers rather than pointers
41 * it would be easier to change the mark when add/delete lines
Eric Andersen1c0d3112001-04-16 15:46:44 +000042 * More intelligence in refresh()
43 * ":r !cmd" and "!cmd" to filter text through an external command
44 * A true "undo" facility
45 * An "ex" line oriented mode- maybe using "cmdedit"
Eric Andersen3f980402001-04-04 17:31:15 +000046 */
47
48//---- Feature -------------- Bytes to immplement
49#ifdef STANDALONE
Eric Andersen822c3832001-05-07 17:37:43 +000050#define vi_main main
Eric Andersenbdfd0d72001-10-24 05:00:29 +000051#define CONFIG_FEATURE_VI_COLON // 4288
52#define CONFIG_FEATURE_VI_YANKMARK // 1408
53#define CONFIG_FEATURE_VI_SEARCH // 1088
54#define CONFIG_FEATURE_VI_USE_SIGNALS // 1056
55#define CONFIG_FEATURE_VI_DOT_CMD // 576
56#define CONFIG_FEATURE_VI_READONLY // 128
57#define CONFIG_FEATURE_VI_SETOPTS // 576
58#define CONFIG_FEATURE_VI_SET // 224
59#define CONFIG_FEATURE_VI_WIN_RESIZE // 256 WIN_RESIZE
Eric Andersen3f980402001-04-04 17:31:15 +000060// To test editor using CRASHME:
61// vi -C filename
62// To stop testing, wait until all to text[] is deleted, or
63// Ctrl-Z and kill -9 %1
64// while in the editor Ctrl-T will toggle the crashme function on and off.
Eric Andersenbdfd0d72001-10-24 05:00:29 +000065//#define CONFIG_FEATURE_VI_CRASHME // randomly pick commands to execute
Eric Andersen3f980402001-04-04 17:31:15 +000066#endif /* STANDALONE */
67
Eric Andersen3f980402001-04-04 17:31:15 +000068#include <stdio.h>
69#include <stdlib.h>
70#include <string.h>
71#include <termios.h>
72#include <unistd.h>
73#include <sys/ioctl.h>
74#include <sys/time.h>
75#include <sys/types.h>
76#include <sys/stat.h>
77#include <time.h>
78#include <fcntl.h>
79#include <signal.h>
80#include <setjmp.h>
81#include <regex.h>
82#include <ctype.h>
83#include <assert.h>
84#include <errno.h>
85#include <stdarg.h>
Eric Andersene0c07572001-06-23 13:49:14 +000086#ifndef STANDALONE
87#include "busybox.h"
88#endif /* STANDALONE */
Eric Andersen3f980402001-04-04 17:31:15 +000089
90#ifndef TRUE
91#define TRUE ((int)1)
92#define FALSE ((int)0)
93#endif /* TRUE */
Eric Andersen1c0d3112001-04-16 15:46:44 +000094#define MAX_SCR_COLS BUFSIZ
Eric Andersen3f980402001-04-04 17:31:15 +000095
96// Misc. non-Ascii keys that report an escape sequence
97#define VI_K_UP 128 // cursor key Up
98#define VI_K_DOWN 129 // cursor key Down
99#define VI_K_RIGHT 130 // Cursor Key Right
100#define VI_K_LEFT 131 // cursor key Left
101#define VI_K_HOME 132 // Cursor Key Home
102#define VI_K_END 133 // Cursor Key End
103#define VI_K_INSERT 134 // Cursor Key Insert
104#define VI_K_PAGEUP 135 // Cursor Key Page Up
105#define VI_K_PAGEDOWN 136 // Cursor Key Page Down
106#define VI_K_FUN1 137 // Function Key F1
107#define VI_K_FUN2 138 // Function Key F2
108#define VI_K_FUN3 139 // Function Key F3
109#define VI_K_FUN4 140 // Function Key F4
110#define VI_K_FUN5 141 // Function Key F5
111#define VI_K_FUN6 142 // Function Key F6
112#define VI_K_FUN7 143 // Function Key F7
113#define VI_K_FUN8 144 // Function Key F8
114#define VI_K_FUN9 145 // Function Key F9
115#define VI_K_FUN10 146 // Function Key F10
116#define VI_K_FUN11 147 // Function Key F11
117#define VI_K_FUN12 148 // Function Key F12
118
119static const int YANKONLY = FALSE;
120static const int YANKDEL = TRUE;
121static const int FORWARD = 1; // code depends on "1" for array index
122static const int BACK = -1; // code depends on "-1" for array index
123static const int LIMITED = 0; // how much of text[] in char_search
124static const int FULL = 1; // how much of text[] in char_search
125
126static const int S_BEFORE_WS = 1; // used in skip_thing() for moving "dot"
127static const int S_TO_WS = 2; // used in skip_thing() for moving "dot"
128static const int S_OVER_WS = 3; // used in skip_thing() for moving "dot"
129static const int S_END_PUNCT = 4; // used in skip_thing() for moving "dot"
130static const int S_END_ALNUM = 5; // used in skip_thing() for moving "dot"
131
132typedef unsigned char Byte;
133
134
135static int editing; // >0 while we are editing a file
136static int cmd_mode; // 0=command 1=insert
137static int file_modified; // buffer contents changed
138static int err_method; // indicate error with beep or flash
139static int fn_start; // index of first cmd line file name
140static int save_argc; // how many file names on cmd line
141static int cmdcnt; // repetition count
142static fd_set rfds; // use select() for small sleeps
143static struct timeval tv; // use select() for small sleeps
144static char erase_char; // the users erase character
145static int rows, columns; // the terminal screen is this size
146static int crow, ccol, offset; // cursor is on Crow x Ccol with Horz Ofset
Eric Andersen822c3832001-05-07 17:37:43 +0000147static char *SOs, *SOn; // terminal standout start/normal ESC sequence
148static char *bell; // terminal bell sequence
149static char *Ceol, *Ceos; // Clear-end-of-line and Clear-end-of-screen ESC sequence
150static char *CMrc; // Cursor motion arbitrary destination ESC sequence
151static char *CMup, *CMdown; // Cursor motion up and down ESC sequence
Eric Andersen3f980402001-04-04 17:31:15 +0000152static Byte *status_buffer; // mesages to the user
153static Byte last_input_char; // last char read from user
154static Byte last_forward_char; // last char searched for with 'f'
155static Byte *cfn; // previous, current, and next file name
156static Byte *text, *end, *textend; // pointers to the user data in memory
157static Byte *screen; // pointer to the virtual screen buffer
158static int screensize; // and its size
159static Byte *screenbegin; // index into text[], of top line on the screen
160static Byte *dot; // where all the action takes place
161static int tabstop;
162static struct termios term_orig, term_vi; // remember what the cooked mode was
163
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000164#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
Eric Andersen822c3832001-05-07 17:37:43 +0000165static int last_row; // where the cursor was last moved to
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000166#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
167#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000168static jmp_buf restart; // catch_sig()
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000169#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
170#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
Eric Andersen3f980402001-04-04 17:31:15 +0000171static struct winsize winsize; // remember the window size
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000172#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
173#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000174static int adding2q; // are we currently adding user input to q
175static Byte *last_modifying_cmd; // last modifying cmd for "."
176static Byte *ioq, *ioq_start; // pointer to string for get_one_char to "read"
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000177#endif /* CONFIG_FEATURE_VI_DOT_CMD */
178#if defined(CONFIG_FEATURE_VI_DOT_CMD) || defined(CONFIG_FEATURE_VI_YANKMARK)
Eric Andersen3f980402001-04-04 17:31:15 +0000179static Byte *modifying_cmds; // cmds that modify text[]
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000180#endif /* CONFIG_FEATURE_VI_DOT_CMD || CONFIG_FEATURE_VI_YANKMARK */
181#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen822c3832001-05-07 17:37:43 +0000182static int vi_readonly, readonly;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000183#endif /* CONFIG_FEATURE_VI_READONLY */
184#ifdef CONFIG_FEATURE_VI_SETOPTS
Eric Andersen3f980402001-04-04 17:31:15 +0000185static int autoindent;
186static int showmatch;
187static int ignorecase;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000188#endif /* CONFIG_FEATURE_VI_SETOPTS */
189#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000190static Byte *reg[28]; // named register a-z, "D", and "U" 0-25,26,27
191static int YDreg, Ureg; // default delete register and orig line for "U"
192static Byte *mark[28]; // user marks points somewhere in text[]- a-z and previous context ''
193static Byte *context_start, *context_end;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000194#endif /* CONFIG_FEATURE_VI_YANKMARK */
195#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +0000196static Byte *last_search_pattern; // last pattern from a '/' or '?' search
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000197#endif /* CONFIG_FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +0000198
199
200static void edit_file(Byte *); // edit one file
201static void do_cmd(Byte); // execute a command
202static void sync_cursor(Byte *, int *, int *); // synchronize the screen cursor to dot
203static Byte *begin_line(Byte *); // return pointer to cur line B-o-l
204static Byte *end_line(Byte *); // return pointer to cur line E-o-l
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000205extern inline Byte *dollar_line(Byte *); // return pointer to just before NL
Eric Andersen3f980402001-04-04 17:31:15 +0000206static Byte *prev_line(Byte *); // return pointer to prev line B-o-l
207static Byte *next_line(Byte *); // return pointer to next line B-o-l
208static Byte *end_screen(void); // get pointer to last char on screen
209static int count_lines(Byte *, Byte *); // count line from start to stop
210static Byte *find_line(int); // find begining of line #li
211static Byte *move_to_col(Byte *, int); // move "p" to column l
212static int isblnk(Byte); // is the char a blank or tab
213static void dot_left(void); // move dot left- dont leave line
214static void dot_right(void); // move dot right- dont leave line
215static void dot_begin(void); // move dot to B-o-l
216static void dot_end(void); // move dot to E-o-l
217static void dot_next(void); // move dot to next line B-o-l
218static void dot_prev(void); // move dot to prev line B-o-l
219static void dot_scroll(int, int); // move the screen up or down
220static void dot_skip_over_ws(void); // move dot pat WS
221static void dot_delete(void); // delete the char at 'dot'
222static Byte *bound_dot(Byte *); // make sure text[0] <= P < "end"
223static Byte *new_screen(int, int); // malloc virtual screen memory
224static Byte *new_text(int); // malloc memory for text[] buffer
225static Byte *char_insert(Byte *, Byte); // insert the char c at 'p'
226static Byte *stupid_insert(Byte *, Byte); // stupidly insert the char c at 'p'
227static Byte find_range(Byte **, Byte **, Byte); // return pointers for an object
228static int st_test(Byte *, int, int, Byte *); // helper for skip_thing()
229static Byte *skip_thing(Byte *, int, int, int); // skip some object
230static Byte *find_pair(Byte *, Byte); // find matching pair () [] {}
231static Byte *text_hole_delete(Byte *, Byte *); // at "p", delete a 'size' byte hole
232static Byte *text_hole_make(Byte *, int); // at "p", make a 'size' byte hole
233static Byte *yank_delete(Byte *, Byte *, int, int); // yank text[] into register then delete
234static void show_help(void); // display some help info
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000235extern inline void print_literal(Byte *, Byte *); // copy s to buf, convert unprintable
Eric Andersen3f980402001-04-04 17:31:15 +0000236static void rawmode(void); // set "raw" mode on tty
237static void cookmode(void); // return to "cooked" mode on tty
238static int mysleep(int); // sleep for 'h' 1/100 seconds
239static Byte readit(void); // read (maybe cursor) key from stdin
240static Byte get_one_char(void); // read 1 char from stdin
241static int file_size(Byte *); // what is the byte size of "fn"
242static int file_insert(Byte *, Byte *, int);
243static int file_write(Byte *, Byte *, Byte *);
Eric Andersen822c3832001-05-07 17:37:43 +0000244static void place_cursor(int, int, int);
Eric Andersenbff7a602001-11-17 07:15:43 +0000245static void screen_erase(void);
Eric Andersen3f980402001-04-04 17:31:15 +0000246static void clear_to_eol(void);
247static void clear_to_eos(void);
248static void standout_start(void); // send "start reverse video" sequence
249static void standout_end(void); // send "end reverse video" sequence
250static void flash(int); // flash the terminal screen
251static void beep(void); // beep the terminal
252static void indicate_error(char); // use flash or beep to indicate error
253static void show_status_line(void); // put a message on the bottom line
254static void psb(char *, ...); // Print Status Buf
255static void psbs(char *, ...); // Print Status Buf in standout mode
256static void ni(Byte *); // display messages
257static void edit_status(void); // show file status on status line
258static void redraw(int); // force a full screen refresh
Eric Andersen822c3832001-05-07 17:37:43 +0000259static void format_line(Byte*, Byte*, int);
Eric Andersen3f980402001-04-04 17:31:15 +0000260static void refresh(int); // update the terminal from screen[]
261
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000262#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +0000263static Byte *char_search(Byte *, Byte *, int, int); // search for pattern starting at p
264static int mycmp(Byte *, Byte *, int); // string cmp based in "ignorecase"
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000265#endif /* CONFIG_FEATURE_VI_SEARCH */
266#ifdef CONFIG_FEATURE_VI_COLON
Eric Andersen3f980402001-04-04 17:31:15 +0000267static void Hit_Return(void);
Eric Andersen1c0d3112001-04-16 15:46:44 +0000268static Byte *get_one_address(Byte *, int *); // get colon addr, if present
269static Byte *get_address(Byte *, int *, int *); // get two colon addrs, if present
Eric Andersen3f980402001-04-04 17:31:15 +0000270static void colon(Byte *); // execute the "colon" mode cmds
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000271#endif /* CONFIG_FEATURE_VI_COLON */
Eric Andersen3f980402001-04-04 17:31:15 +0000272static Byte *get_input_line(Byte *); // get input line- use "status line"
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000273#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000274static void winch_sig(int); // catch window size changes
275static void suspend_sig(int); // catch ctrl-Z
276static void alarm_sig(int); // catch alarm time-outs
277static void catch_sig(int); // catch ctrl-C
278static void core_sig(int); // catch a core dump signal
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000279#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
280#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000281static void start_new_cmd_q(Byte); // new queue for command
Eric Andersenbff7a602001-11-17 07:15:43 +0000282static void end_cmd_q(void); // stop saving input chars
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000283#else /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +0000284#define end_cmd_q()
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000285#endif /* CONFIG_FEATURE_VI_DOT_CMD */
286#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
Eric Andersen3f980402001-04-04 17:31:15 +0000287static void window_size_get(int); // find out what size the window is
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000288#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
289#ifdef CONFIG_FEATURE_VI_SETOPTS
Eric Andersen3f980402001-04-04 17:31:15 +0000290static void showmatching(Byte *); // show the matching pair () [] {}
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000291#endif /* CONFIG_FEATURE_VI_SETOPTS */
292#if defined(CONFIG_FEATURE_VI_YANKMARK) || defined(CONFIG_FEATURE_VI_COLON) || defined(CONFIG_FEATURE_VI_CRASHME)
Eric Andersen3f980402001-04-04 17:31:15 +0000293static Byte *string_insert(Byte *, Byte *); // insert the string at 'p'
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000294#endif /* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_COLON || CONFIG_FEATURE_VI_CRASHME */
295#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000296static Byte *text_yank(Byte *, Byte *, int); // save copy of "p" into a register
297static Byte what_reg(void); // what is letter of current YDreg
298static void check_context(Byte); // remember context for '' command
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000299extern inline Byte *swap_context(Byte *); // goto new context for '' command
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000300#endif /* CONFIG_FEATURE_VI_YANKMARK */
301#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000302static void crash_dummy();
303static void crash_test();
304static int crashme = 0;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000305#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen3f980402001-04-04 17:31:15 +0000306
307
Eric Andersen3f980402001-04-04 17:31:15 +0000308extern int vi_main(int argc, char **argv)
Eric Andersen3f980402001-04-04 17:31:15 +0000309{
Eric Andersend402edf2001-04-04 19:29:48 +0000310 int c;
Eric Andersen3f980402001-04-04 17:31:15 +0000311
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000312#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000313 int i;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000314#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +0000315
Eric Andersen822c3832001-05-07 17:37:43 +0000316 CMrc= "\033[%d;%dH"; // Terminal Crusor motion ESC sequence
317 CMup= "\033[A"; // move cursor up one line, same col
318 CMdown="\n"; // move cursor down one line, same col
319 Ceol= "\033[0K"; // Clear from cursor to end of line
320 Ceos= "\033[0J"; // Clear from cursor to end of screen
Eric Andersen3f980402001-04-04 17:31:15 +0000321 SOs = "\033[7m"; // Terminal standout mode on
322 SOn = "\033[0m"; // Terminal standout mode off
Eric Andersen822c3832001-05-07 17:37:43 +0000323 bell= "\007"; // Terminal bell sequence
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000324#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000325 (void) srand((long) getpid());
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000326#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen80f5ac72001-11-17 06:57:42 +0000327 status_buffer = (Byte *) xmalloc(200); // hold messages to user
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000328#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen822c3832001-05-07 17:37:43 +0000329 vi_readonly = readonly = FALSE;
Eric Andersen3f980402001-04-04 17:31:15 +0000330 if (strncmp(argv[0], "view", 4) == 0) {
331 readonly = TRUE;
Eric Andersen822c3832001-05-07 17:37:43 +0000332 vi_readonly = TRUE;
Eric Andersen3f980402001-04-04 17:31:15 +0000333 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000334#endif /* CONFIG_FEATURE_VI_READONLY */
335#ifdef CONFIG_FEATURE_VI_SETOPTS
Eric Andersen3f980402001-04-04 17:31:15 +0000336 autoindent = 1;
337 ignorecase = 1;
338 showmatch = 1;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000339#endif /* CONFIG_FEATURE_VI_SETOPTS */
340#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000341 for (i = 0; i < 28; i++) {
342 reg[i] = 0;
343 } // init the yank regs
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000344#endif /* CONFIG_FEATURE_VI_YANKMARK */
Tim Riker86c76a92002-04-26 07:41:22 +0000345#if defined(CONFIG_FEATURE_VI_DOT_CMD) || defined(CONFIG_FEATURE_VI_YANKMARK)
Eric Andersen3f980402001-04-04 17:31:15 +0000346 modifying_cmds = (Byte *) "aAcCdDiIJoOpPrRsxX<>~"; // cmds modifying text[]
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000347#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +0000348
349 // 1- process $HOME/.exrc file
350 // 2- process EXINIT variable from environment
351 // 3- process command line args
352 while ((c = getopt(argc, argv, "hCR")) != -1) {
353 switch (c) {
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000354#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000355 case 'C':
356 crashme = 1;
357 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000358#endif /* CONFIG_FEATURE_VI_CRASHME */
359#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen3f980402001-04-04 17:31:15 +0000360 case 'R': // Read-only flag
361 readonly = TRUE;
362 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000363#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen822c3832001-05-07 17:37:43 +0000364 //case 'r': // recover flag- ignore- we don't use tmp file
365 //case 'x': // encryption flag- ignore
366 //case 'c': // execute command first
367 //case 'h': // help -- just use default
Eric Andersen3f980402001-04-04 17:31:15 +0000368 default:
369 show_help();
Eric Andersendd8500b2001-07-02 18:06:14 +0000370 return 1;
Eric Andersen3f980402001-04-04 17:31:15 +0000371 }
372 }
373
374 // The argv array can be used by the ":next" and ":rewind" commands
375 // save optind.
376 fn_start = optind; // remember first file name for :next and :rew
377 save_argc = argc;
378
379 //----- This is the main file handling loop --------------
380 if (optind >= argc) {
381 editing = 1; // 0= exit, 1= one file, 2= multiple files
382 edit_file(0);
383 } else {
384 for (; optind < argc; optind++) {
385 editing = 1; // 0=exit, 1=one file, 2+ =many files
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000386 free(cfn);
Matt Kraaic8227632001-11-12 16:57:27 +0000387 cfn = (Byte *) xstrdup(argv[optind]);
Eric Andersen3f980402001-04-04 17:31:15 +0000388 edit_file(cfn);
389 }
390 }
391 //-----------------------------------------------------------
392
393 return (0);
394}
395
396static void edit_file(Byte * fn)
397{
398 char c;
Eric Andersen1c0d3112001-04-16 15:46:44 +0000399 int cnt, size, ch;
Eric Andersen3f980402001-04-04 17:31:15 +0000400
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000401#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000402 char *msg;
403 int sig;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000404#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
405#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000406 static Byte *cur_line;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000407#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +0000408
409 rawmode();
410 rows = 24;
411 columns = 80;
Eric Andersen822c3832001-05-07 17:37:43 +0000412 ch= -1;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000413#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
Eric Andersen3f980402001-04-04 17:31:15 +0000414 window_size_get(0);
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000415#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
Eric Andersen3f980402001-04-04 17:31:15 +0000416 new_screen(rows, columns); // get memory for virtual screen
417
418 cnt = file_size(fn); // file size
419 size = 2 * cnt; // 200% of file size
420 new_text(size); // get a text[] buffer
421 screenbegin = dot = end = text;
422 if (fn != 0) {
Eric Andersen1c0d3112001-04-16 15:46:44 +0000423 ch= file_insert(fn, text, cnt);
424 }
425 if (ch < 1) {
Eric Andersen3f980402001-04-04 17:31:15 +0000426 (void) char_insert(text, '\n'); // start empty buf with dummy line
427 }
428 file_modified = FALSE;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000429#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000430 YDreg = 26; // default Yank/Delete reg
431 Ureg = 27; // hold orig line for "U" cmd
432 for (cnt = 0; cnt < 28; cnt++) {
433 mark[cnt] = 0;
434 } // init the marks
435 mark[26] = mark[27] = text; // init "previous context"
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000436#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +0000437
438 err_method = 1; // flash
439 last_forward_char = last_input_char = '\0';
440 crow = 0;
441 ccol = 0;
442 edit_status();
443
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000444#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000445 signal(SIGHUP, catch_sig);
446 signal(SIGINT, catch_sig);
447 signal(SIGALRM, alarm_sig);
448 signal(SIGTERM, catch_sig);
449 signal(SIGQUIT, core_sig);
450 signal(SIGILL, core_sig);
451 signal(SIGTRAP, core_sig);
452 signal(SIGIOT, core_sig);
453 signal(SIGABRT, core_sig);
454 signal(SIGFPE, core_sig);
455 signal(SIGBUS, core_sig);
456 signal(SIGSEGV, core_sig);
Eric Andersend402edf2001-04-04 19:29:48 +0000457#ifdef SIGSYS
Eric Andersen3f980402001-04-04 17:31:15 +0000458 signal(SIGSYS, core_sig);
Eric Andersend402edf2001-04-04 19:29:48 +0000459#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000460 signal(SIGWINCH, winch_sig);
461 signal(SIGTSTP, suspend_sig);
462 sig = setjmp(restart);
463 if (sig != 0) {
464 msg = "";
465 if (sig == SIGWINCH)
466 msg = "(window resize)";
467 if (sig == SIGHUP)
468 msg = "(hangup)";
469 if (sig == SIGINT)
470 msg = "(interrupt)";
471 if (sig == SIGTERM)
472 msg = "(terminate)";
473 if (sig == SIGBUS)
474 msg = "(bus error)";
475 if (sig == SIGSEGV)
476 msg = "(I tried to touch invalid memory)";
477 if (sig == SIGALRM)
478 msg = "(alarm)";
479
480 psbs("-- caught signal %d %s--", sig, msg);
Eric Andersen1c0d3112001-04-16 15:46:44 +0000481 screenbegin = dot = text;
Eric Andersen3f980402001-04-04 17:31:15 +0000482 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000483#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Eric Andersen3f980402001-04-04 17:31:15 +0000484
485 editing = 1;
486 cmd_mode = 0; // 0=command 1=insert 2='R'eplace
487 cmdcnt = 0;
488 tabstop = 8;
489 offset = 0; // no horizontal offset
490 c = '\0';
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000491#ifdef CONFIG_FEATURE_VI_DOT_CMD
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000492 free(last_modifying_cmd);
493 free(ioq_start);
Eric Andersen3f980402001-04-04 17:31:15 +0000494 ioq = ioq_start = last_modifying_cmd = 0;
495 adding2q = 0;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000496#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen822c3832001-05-07 17:37:43 +0000497 redraw(FALSE); // dont force every col re-draw
Eric Andersen3f980402001-04-04 17:31:15 +0000498 show_status_line();
499
500 //------This is the main Vi cmd handling loop -----------------------
501 while (editing > 0) {
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000502#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000503 if (crashme > 0) {
504 if ((end - text) > 1) {
505 crash_dummy(); // generate a random command
506 } else {
507 crashme = 0;
508 dot =
509 string_insert(text, (Byte *) "\n\n##### Ran out of text to work on. #####\n\n"); // insert the string
510 refresh(FALSE);
511 }
512 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000513#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen3f980402001-04-04 17:31:15 +0000514 last_input_char = c = get_one_char(); // get a cmd from user
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000515#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000516 // save a copy of the current line- for the 'U" command
517 if (begin_line(dot) != cur_line) {
518 cur_line = begin_line(dot);
519 text_yank(begin_line(dot), end_line(dot), Ureg);
520 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000521#endif /* CONFIG_FEATURE_VI_YANKMARK */
522#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000523 // These are commands that change text[].
524 // Remember the input for the "." command
525 if (!adding2q && ioq_start == 0
526 && strchr((char *) modifying_cmds, c) != NULL) {
527 start_new_cmd_q(c);
528 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000529#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +0000530 do_cmd(c); // execute the user command
531 //
532 // poll to see if there is input already waiting. if we are
533 // not able to display output fast enough to keep up, skip
534 // the display update until we catch up with input.
535 if (mysleep(0) == 0) {
536 // no input pending- so update output
537 refresh(FALSE);
538 show_status_line();
539 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000540#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000541 if (crashme > 0)
542 crash_test(); // test editor variables
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000543#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen3f980402001-04-04 17:31:15 +0000544 }
545 //-------------------------------------------------------------------
546
Eric Andersen822c3832001-05-07 17:37:43 +0000547 place_cursor(rows, 0, FALSE); // go to bottom of screen
Eric Andersen3f980402001-04-04 17:31:15 +0000548 clear_to_eol(); // Erase to end of line
549 cookmode();
550}
551
552static Byte readbuffer[BUFSIZ];
553
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000554#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000555static int totalcmds = 0;
556static int Mp = 85; // Movement command Probability
557static int Np = 90; // Non-movement command Probability
558static int Dp = 96; // Delete command Probability
559static int Ip = 97; // Insert command Probability
560static int Yp = 98; // Yank command Probability
561static int Pp = 99; // Put command Probability
562static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
563char chars[20] = "\t012345 abcdABCD-=.$";
564char *words[20] = { "this", "is", "a", "test",
565 "broadcast", "the", "emergency", "of",
566 "system", "quick", "brown", "fox",
567 "jumped", "over", "lazy", "dogs",
568 "back", "January", "Febuary", "March"
569};
570char *lines[20] = {
571 "You should have received a copy of the GNU General Public License\n",
572 "char c, cm, *cmd, *cmd1;\n",
573 "generate a command by percentages\n",
574 "Numbers may be typed as a prefix to some commands.\n",
575 "Quit, discarding changes!\n",
576 "Forced write, if permission originally not valid.\n",
577 "In general, any ex or ed command (such as substitute or delete).\n",
578 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
579 "Please get w/ me and I will go over it with you.\n",
580 "The following is a list of scheduled, committed changes.\n",
581 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
582 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
583 "Any question about transactions please contact Sterling Huxley.\n",
584 "I will try to get back to you by Friday, December 31.\n",
585 "This Change will be implemented on Friday.\n",
586 "Let me know if you have problems accessing this;\n",
587 "Sterling Huxley recently added you to the access list.\n",
588 "Would you like to go to lunch?\n",
589 "The last command will be automatically run.\n",
590 "This is too much english for a computer geek.\n",
591};
592char *multilines[20] = {
593 "You should have received a copy of the GNU General Public License\n",
594 "char c, cm, *cmd, *cmd1;\n",
595 "generate a command by percentages\n",
596 "Numbers may be typed as a prefix to some commands.\n",
597 "Quit, discarding changes!\n",
598 "Forced write, if permission originally not valid.\n",
599 "In general, any ex or ed command (such as substitute or delete).\n",
600 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
601 "Please get w/ me and I will go over it with you.\n",
602 "The following is a list of scheduled, committed changes.\n",
603 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
604 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
605 "Any question about transactions please contact Sterling Huxley.\n",
606 "I will try to get back to you by Friday, December 31.\n",
607 "This Change will be implemented on Friday.\n",
608 "Let me know if you have problems accessing this;\n",
609 "Sterling Huxley recently added you to the access list.\n",
610 "Would you like to go to lunch?\n",
611 "The last command will be automatically run.\n",
612 "This is too much english for a computer geek.\n",
613};
614
615// create a random command to execute
616static void crash_dummy()
617{
618 static int sleeptime; // how long to pause between commands
619 char c, cm, *cmd, *cmd1;
620 int i, cnt, thing, rbi, startrbi, percent;
621
622 // "dot" movement commands
623 cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
624
625 // is there already a command running?
626 if (strlen((char *) readbuffer) > 0)
627 goto cd1;
628 cd0:
629 startrbi = rbi = 0;
630 sleeptime = 0; // how long to pause between commands
631 memset(readbuffer, '\0', BUFSIZ - 1); // clear the read buffer
632 // generate a command by percentages
633 percent = (int) lrand48() % 100; // get a number from 0-99
634 if (percent < Mp) { // Movement commands
635 // available commands
636 cmd = cmd1;
637 M++;
638 } else if (percent < Np) { // non-movement commands
639 cmd = "mz<>\'\""; // available commands
640 N++;
641 } else if (percent < Dp) { // Delete commands
642 cmd = "dx"; // available commands
643 D++;
644 } else if (percent < Ip) { // Inset commands
645 cmd = "iIaAsrJ"; // available commands
646 I++;
647 } else if (percent < Yp) { // Yank commands
648 cmd = "yY"; // available commands
649 Y++;
650 } else if (percent < Pp) { // Put commands
651 cmd = "pP"; // available commands
652 P++;
653 } else {
654 // We do not know how to handle this command, try again
655 U++;
656 goto cd0;
657 }
658 // randomly pick one of the available cmds from "cmd[]"
659 i = (int) lrand48() % strlen(cmd);
660 cm = cmd[i];
661 if (strchr(":\024", cm))
Eric Andersen822c3832001-05-07 17:37:43 +0000662 goto cd0; // dont allow colon or ctrl-T commands
Eric Andersen3f980402001-04-04 17:31:15 +0000663 readbuffer[rbi++] = cm; // put cmd into input buffer
664
665 // now we have the command-
666 // there are 1, 2, and multi char commands
667 // find out which and generate the rest of command as necessary
668 if (strchr("dmryz<>\'\"", cm)) { // 2-char commands
669 cmd1 = " \n\r0$^-+wWeEbBhjklHL";
670 if (cm == 'm' || cm == '\'' || cm == '\"') { // pick a reg[]
671 cmd1 = "abcdefghijklmnopqrstuvwxyz";
672 }
673 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
674 c = cmd1[thing];
675 readbuffer[rbi++] = c; // add movement to input buffer
676 }
677 if (strchr("iIaAsc", cm)) { // multi-char commands
678 if (cm == 'c') {
679 // change some thing
680 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
681 c = cmd1[thing];
682 readbuffer[rbi++] = c; // add movement to input buffer
683 }
684 thing = (int) lrand48() % 4; // what thing to insert
685 cnt = (int) lrand48() % 10; // how many to insert
686 for (i = 0; i < cnt; i++) {
687 if (thing == 0) { // insert chars
688 readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
689 } else if (thing == 1) { // insert words
690 strcat((char *) readbuffer, words[(int) lrand48() % 20]);
691 strcat((char *) readbuffer, " ");
692 sleeptime = 0; // how fast to type
693 } else if (thing == 2) { // insert lines
694 strcat((char *) readbuffer, lines[(int) lrand48() % 20]);
695 sleeptime = 0; // how fast to type
696 } else { // insert multi-lines
697 strcat((char *) readbuffer, multilines[(int) lrand48() % 20]);
698 sleeptime = 0; // how fast to type
699 }
700 }
701 strcat((char *) readbuffer, "\033");
702 }
703 cd1:
704 totalcmds++;
705 if (sleeptime > 0)
706 (void) mysleep(sleeptime); // sleep 1/100 sec
707}
708
709// test to see if there are any errors
710static void crash_test()
711{
712 static time_t oldtim;
713 time_t tim;
Eric Andersen1c0d3112001-04-16 15:46:44 +0000714 char d[2], buf[BUFSIZ], msg[BUFSIZ];
Eric Andersen3f980402001-04-04 17:31:15 +0000715
716 msg[0] = '\0';
717 if (end < text) {
718 strcat((char *) msg, "end<text ");
719 }
720 if (end > textend) {
721 strcat((char *) msg, "end>textend ");
722 }
723 if (dot < text) {
724 strcat((char *) msg, "dot<text ");
725 }
726 if (dot > end) {
727 strcat((char *) msg, "dot>end ");
728 }
729 if (screenbegin < text) {
730 strcat((char *) msg, "screenbegin<text ");
731 }
732 if (screenbegin > end - 1) {
733 strcat((char *) msg, "screenbegin>end-1 ");
734 }
735
736 if (strlen(msg) > 0) {
737 alarm(0);
Eric Andersen822c3832001-05-07 17:37:43 +0000738 sprintf(buf, "\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
739 totalcmds, last_input_char, msg, SOs, SOn);
Eric Andersen3f980402001-04-04 17:31:15 +0000740 write(1, buf, strlen(buf));
Eric Andersen3f980402001-04-04 17:31:15 +0000741 while (read(0, d, 1) > 0) {
742 if (d[0] == '\n' || d[0] == '\r')
743 break;
744 }
745 alarm(3);
746 }
747 tim = (time_t) time((time_t *) 0);
748 if (tim >= (oldtim + 3)) {
749 sprintf((char *) status_buffer,
750 "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
751 totalcmds, M, N, I, D, Y, P, U, end - text + 1);
752 oldtim = tim;
753 }
754 return;
755}
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000756#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen3f980402001-04-04 17:31:15 +0000757
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000758//----- The Colon commands -------------------------------------
759#ifdef CONFIG_FEATURE_VI_COLON
760static Byte *get_one_address(Byte * p, int *addr) // get colon addr, if present
761{
762 int st;
763 Byte *q;
764
765#ifdef CONFIG_FEATURE_VI_YANKMARK
766 Byte c;
767#endif /* CONFIG_FEATURE_VI_YANKMARK */
768#ifdef CONFIG_FEATURE_VI_SEARCH
769 Byte *pat, buf[BUFSIZ];
770#endif /* CONFIG_FEATURE_VI_SEARCH */
771
772 *addr = -1; // assume no addr
773 if (*p == '.') { // the current line
774 p++;
775 q = begin_line(dot);
776 *addr = count_lines(text, q);
777#ifdef CONFIG_FEATURE_VI_YANKMARK
778 } else if (*p == '\'') { // is this a mark addr
779 p++;
780 c = tolower(*p);
781 p++;
782 if (c >= 'a' && c <= 'z') {
783 // we have a mark
784 c = c - 'a';
785 q = mark[(int) c];
786 if (q != NULL) { // is mark valid
787 *addr = count_lines(text, q); // count lines
788 }
789 }
790#endif /* CONFIG_FEATURE_VI_YANKMARK */
791#ifdef CONFIG_FEATURE_VI_SEARCH
792 } else if (*p == '/') { // a search pattern
793 q = buf;
794 for (p++; *p; p++) {
795 if (*p == '/')
796 break;
797 *q++ = *p;
798 *q = '\0';
799 }
800 pat = (Byte *) xstrdup((char *) buf); // save copy of pattern
801 if (*p == '/')
802 p++;
803 q = char_search(dot, pat, FORWARD, FULL);
804 if (q != NULL) {
805 *addr = count_lines(text, q);
806 }
807 free(pat);
808#endif /* CONFIG_FEATURE_VI_SEARCH */
809 } else if (*p == '$') { // the last line in file
810 p++;
811 q = begin_line(end - 1);
812 *addr = count_lines(text, q);
813 } else if (isdigit(*p)) { // specific line number
814 sscanf((char *) p, "%d%n", addr, &st);
815 p += st;
816 } else { // I don't reconise this
817 // unrecognised address- assume -1
818 *addr = -1;
819 }
820 return (p);
821}
822
823static Byte *get_address(Byte *p, int *b, int *e) // get two colon addrs, if present
824{
825 //----- get the address' i.e., 1,3 'a,'b -----
826 // get FIRST addr, if present
827 while (isblnk(*p))
828 p++; // skip over leading spaces
829 if (*p == '%') { // alias for 1,$
830 p++;
831 *b = 1;
832 *e = count_lines(text, end-1);
833 goto ga0;
834 }
835 p = get_one_address(p, b);
836 while (isblnk(*p))
837 p++;
838 if (*p == ',') { // is there a address seperator
839 p++;
840 while (isblnk(*p))
841 p++;
842 // get SECOND addr, if present
843 p = get_one_address(p, e);
844 }
845ga0:
846 while (isblnk(*p))
847 p++; // skip over trailing spaces
848 return (p);
849}
850
851static void colon(Byte * buf)
852{
853 Byte c, *orig_buf, *buf1, *q, *r;
854 Byte *fn, cmd[BUFSIZ], args[BUFSIZ];
855 int i, l, li, ch, st, b, e;
856 int useforce, forced;
857 struct stat st_buf;
858
859 // :3154 // if (-e line 3154) goto it else stay put
860 // :4,33w! foo // write a portion of buffer to file "foo"
861 // :w // write all of buffer to current file
862 // :q // quit
863 // :q! // quit- dont care about modified file
864 // :'a,'z!sort -u // filter block through sort
865 // :'f // goto mark "f"
866 // :'fl // list literal the mark "f" line
867 // :.r bar // read file "bar" into buffer before dot
868 // :/123/,/abc/d // delete lines from "123" line to "abc" line
869 // :/xyz/ // goto the "xyz" line
870 // :s/find/replace/ // substitute pattern "find" with "replace"
871 // :!<cmd> // run <cmd> then return
872 //
873 if (strlen((char *) buf) <= 0)
874 goto vc1;
875 if (*buf == ':')
876 buf++; // move past the ':'
877
878 forced = useforce = FALSE;
879 li = st = ch = i = 0;
880 b = e = -1;
881 q = text; // assume 1,$ for the range
882 r = end - 1;
883 li = count_lines(text, end - 1);
884 fn = cfn; // default to current file
885 memset(cmd, '\0', BUFSIZ); // clear cmd[]
886 memset(args, '\0', BUFSIZ); // clear args[]
887
888 // look for optional address(es) :. :1 :1,9 :'q,'a :%
889 buf = get_address(buf, &b, &e);
890
891 // remember orig command line
892 orig_buf = buf;
893
894 // get the COMMAND into cmd[]
895 buf1 = cmd;
896 while (*buf != '\0') {
897 if (isspace(*buf))
898 break;
899 *buf1++ = *buf++;
900 }
901 // get any ARGuments
902 while (isblnk(*buf))
903 buf++;
904 strcpy((char *) args, (char *) buf);
905 buf1 = last_char_is((char *)cmd, '!');
906 if (buf1) {
907 useforce = TRUE;
908 *buf1 = '\0'; // get rid of !
909 }
910 if (b >= 0) {
911 // if there is only one addr, then the addr
912 // is the line number of the single line the
913 // user wants. So, reset the end
914 // pointer to point at end of the "b" line
915 q = find_line(b); // what line is #b
916 r = end_line(q);
917 li = 1;
918 }
919 if (e >= 0) {
920 // we were given two addrs. change the
921 // end pointer to the addr given by user.
922 r = find_line(e); // what line is #e
923 r = end_line(r);
924 li = e - b + 1;
925 }
926 // ------------ now look for the command ------------
927 i = strlen((char *) cmd);
928 if (i == 0) { // :123CR goto line #123
929 if (b >= 0) {
930 dot = find_line(b); // what line is #b
931 dot_skip_over_ws();
932 }
933 } else if (strncmp((char *) cmd, "!", 1) == 0) { // run a cmd
934 // :!ls run the <cmd>
935 (void) alarm(0); // wait for input- no alarms
936 place_cursor(rows - 1, 0, FALSE); // go to Status line
937 clear_to_eol(); // clear the line
938 cookmode();
939 system(orig_buf+1); // run the cmd
940 rawmode();
941 Hit_Return(); // let user see results
942 (void) alarm(3); // done waiting for input
943 } else if (strncmp((char *) cmd, "=", i) == 0) { // where is the address
944 if (b < 0) { // no addr given- use defaults
945 b = e = count_lines(text, dot);
946 }
947 psb("%d", b);
948 } else if (strncasecmp((char *) cmd, "delete", i) == 0) { // delete lines
949 if (b < 0) { // no addr given- use defaults
950 q = begin_line(dot); // assume .,. for the range
951 r = end_line(dot);
952 }
953 dot = yank_delete(q, r, 1, YANKDEL); // save, then delete lines
954 dot_skip_over_ws();
955 } else if (strncasecmp((char *) cmd, "edit", i) == 0) { // Edit a file
956 int sr;
957 sr= 0;
958 // don't edit, if the current file has been modified
959 if (file_modified && ! useforce) {
960 psbs("No write since last change (:edit! overrides)");
961 goto vc1;
962 }
963 if (strlen(args) > 0) {
964 // the user supplied a file name
965 fn= args;
966 } else if (cfn != 0 && strlen(cfn) > 0) {
967 // no user supplied name- use the current filename
968 fn= cfn;
969 goto vc5;
970 } else {
971 // no user file name, no current name- punt
972 psbs("No current filename");
973 goto vc1;
974 }
975
976 // see if file exists- if not, its just a new file request
977 if ((sr=stat((char*)fn, &st_buf)) < 0) {
978 // This is just a request for a new file creation.
979 // The file_insert below will fail but we get
980 // an empty buffer with a file name. Then the "write"
981 // command can do the create.
982 } else {
983 if ((st_buf.st_mode & (S_IFREG)) == 0) {
984 // This is not a regular file
985 psbs("\"%s\" is not a regular file", fn);
986 goto vc1;
987 }
988 if ((st_buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
989 // dont have any read permissions
990 psbs("\"%s\" is not readable", fn);
991 goto vc1;
992 }
993 }
994
995 // There is a read-able regular file
996 // make this the current file
997 q = (Byte *) xstrdup((char *) fn); // save the cfn
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000998 free(cfn); // free the old name
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000999 cfn = q; // remember new cfn
1000
1001 vc5:
1002 // delete all the contents of text[]
1003 new_text(2 * file_size(fn));
1004 screenbegin = dot = end = text;
1005
1006 // insert new file
1007 ch = file_insert(fn, text, file_size(fn));
1008
1009 if (ch < 1) {
1010 // start empty buf with dummy line
1011 (void) char_insert(text, '\n');
1012 ch= 1;
1013 }
1014 file_modified = FALSE;
1015#ifdef CONFIG_FEATURE_VI_YANKMARK
1016 if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
1017 free(reg[Ureg]); // free orig line reg- for 'U'
1018 reg[Ureg]= 0;
1019 }
1020 if (YDreg >= 0 && YDreg < 28 && reg[YDreg] != 0) {
1021 free(reg[YDreg]); // free default yank/delete register
1022 reg[YDreg]= 0;
1023 }
1024 for (li = 0; li < 28; li++) {
1025 mark[li] = 0;
1026 } // init the marks
1027#endif /* CONFIG_FEATURE_VI_YANKMARK */
1028 // how many lines in text[]?
1029 li = count_lines(text, end - 1);
1030 psb("\"%s\"%s"
1031#ifdef CONFIG_FEATURE_VI_READONLY
1032 "%s"
1033#endif /* CONFIG_FEATURE_VI_READONLY */
1034 " %dL, %dC", cfn,
1035 (sr < 0 ? " [New file]" : ""),
1036#ifdef CONFIG_FEATURE_VI_READONLY
1037 ((vi_readonly || readonly) ? " [Read only]" : ""),
1038#endif /* CONFIG_FEATURE_VI_READONLY */
1039 li, ch);
1040 } else if (strncasecmp((char *) cmd, "file", i) == 0) { // what File is this
1041 if (b != -1 || e != -1) {
1042 ni((Byte *) "No address allowed on this command");
1043 goto vc1;
1044 }
1045 if (strlen((char *) args) > 0) {
1046 // user wants a new filename
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001047 free(cfn);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001048 cfn = (Byte *) xstrdup((char *) args);
1049 } else {
1050 // user wants file status info
1051 edit_status();
1052 }
1053 } else if (strncasecmp((char *) cmd, "features", i) == 0) { // what features are available
1054 // print out values of all features
1055 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
1056 clear_to_eol(); // clear the line
1057 cookmode();
1058 show_help();
1059 rawmode();
1060 Hit_Return();
1061 } else if (strncasecmp((char *) cmd, "list", i) == 0) { // literal print line
1062 if (b < 0) { // no addr given- use defaults
1063 q = begin_line(dot); // assume .,. for the range
1064 r = end_line(dot);
1065 }
1066 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
1067 clear_to_eol(); // clear the line
1068 write(1, "\r\n", 2);
1069 for (; q <= r; q++) {
1070 c = *q;
1071 if (c > '~')
1072 standout_start();
1073 if (c == '\n') {
1074 write(1, "$\r", 2);
1075 } else if (*q < ' ') {
1076 write(1, "^", 1);
1077 c += '@';
1078 }
1079 write(1, &c, 1);
1080 if (c > '~')
1081 standout_end();
1082 }
1083#ifdef CONFIG_FEATURE_VI_SET
1084 vc2:
1085#endif /* CONFIG_FEATURE_VI_SET */
1086 Hit_Return();
1087 } else if ((strncasecmp((char *) cmd, "quit", i) == 0) || // Quit
1088 (strncasecmp((char *) cmd, "next", i) == 0)) { // edit next file
1089 if (useforce) {
1090 // force end of argv list
1091 if (*cmd == 'q') {
1092 optind = save_argc;
1093 }
1094 editing = 0;
1095 goto vc1;
1096 }
1097 // don't exit if the file been modified
1098 if (file_modified) {
1099 psbs("No write since last change (:%s! overrides)",
1100 (*cmd == 'q' ? "quit" : "next"));
1101 goto vc1;
1102 }
1103 // are there other file to edit
1104 if (*cmd == 'q' && optind < save_argc - 1) {
1105 psbs("%d more file to edit", (save_argc - optind - 1));
1106 goto vc1;
1107 }
1108 if (*cmd == 'n' && optind >= save_argc - 1) {
1109 psbs("No more files to edit");
1110 goto vc1;
1111 }
1112 editing = 0;
1113 } else if (strncasecmp((char *) cmd, "read", i) == 0) { // read file into text[]
1114 fn = args;
1115 if (strlen((char *) fn) <= 0) {
1116 psbs("No filename given");
1117 goto vc1;
1118 }
1119 if (b < 0) { // no addr given- use defaults
1120 q = begin_line(dot); // assume "dot"
1121 }
1122 // read after current line- unless user said ":0r foo"
1123 if (b != 0)
1124 q = next_line(q);
1125#ifdef CONFIG_FEATURE_VI_READONLY
1126 l= readonly; // remember current files' status
1127#endif
1128 ch = file_insert(fn, q, file_size(fn));
1129#ifdef CONFIG_FEATURE_VI_READONLY
1130 readonly= l;
1131#endif
1132 if (ch < 0)
1133 goto vc1; // nothing was inserted
1134 // how many lines in text[]?
1135 li = count_lines(q, q + ch - 1);
1136 psb("\"%s\""
1137#ifdef CONFIG_FEATURE_VI_READONLY
1138 "%s"
1139#endif /* CONFIG_FEATURE_VI_READONLY */
1140 " %dL, %dC", fn,
1141#ifdef CONFIG_FEATURE_VI_READONLY
1142 ((vi_readonly || readonly) ? " [Read only]" : ""),
1143#endif /* CONFIG_FEATURE_VI_READONLY */
1144 li, ch);
1145 if (ch > 0) {
1146 // if the insert is before "dot" then we need to update
1147 if (q <= dot)
1148 dot += ch;
1149 file_modified = TRUE;
1150 }
1151 } else if (strncasecmp((char *) cmd, "rewind", i) == 0) { // rewind cmd line args
1152 if (file_modified && ! useforce) {
1153 psbs("No write since last change (:rewind! overrides)");
1154 } else {
1155 // reset the filenames to edit
1156 optind = fn_start - 1;
1157 editing = 0;
1158 }
1159#ifdef CONFIG_FEATURE_VI_SET
1160 } else if (strncasecmp((char *) cmd, "set", i) == 0) { // set or clear features
1161 i = 0; // offset into args
1162 if (strlen((char *) args) == 0) {
1163 // print out values of all options
1164 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
1165 clear_to_eol(); // clear the line
1166 printf("----------------------------------------\r\n");
1167#ifdef CONFIG_FEATURE_VI_SETOPTS
1168 if (!autoindent)
1169 printf("no");
1170 printf("autoindent ");
1171 if (!err_method)
1172 printf("no");
1173 printf("flash ");
1174 if (!ignorecase)
1175 printf("no");
1176 printf("ignorecase ");
1177 if (!showmatch)
1178 printf("no");
1179 printf("showmatch ");
1180 printf("tabstop=%d ", tabstop);
1181#endif /* CONFIG_FEATURE_VI_SETOPTS */
1182 printf("\r\n");
1183 goto vc2;
1184 }
1185 if (strncasecmp((char *) args, "no", 2) == 0)
1186 i = 2; // ":set noautoindent"
1187#ifdef CONFIG_FEATURE_VI_SETOPTS
1188 if (strncasecmp((char *) args + i, "autoindent", 10) == 0 ||
1189 strncasecmp((char *) args + i, "ai", 2) == 0) {
1190 autoindent = (i == 2) ? 0 : 1;
1191 }
1192 if (strncasecmp((char *) args + i, "flash", 5) == 0 ||
1193 strncasecmp((char *) args + i, "fl", 2) == 0) {
1194 err_method = (i == 2) ? 0 : 1;
1195 }
1196 if (strncasecmp((char *) args + i, "ignorecase", 10) == 0 ||
1197 strncasecmp((char *) args + i, "ic", 2) == 0) {
1198 ignorecase = (i == 2) ? 0 : 1;
1199 }
1200 if (strncasecmp((char *) args + i, "showmatch", 9) == 0 ||
1201 strncasecmp((char *) args + i, "sm", 2) == 0) {
1202 showmatch = (i == 2) ? 0 : 1;
1203 }
1204 if (strncasecmp((char *) args + i, "tabstop", 7) == 0) {
1205 sscanf(strchr((char *) args + i, '='), "=%d", &ch);
1206 if (ch > 0 && ch < columns - 1)
1207 tabstop = ch;
1208 }
1209#endif /* CONFIG_FEATURE_VI_SETOPTS */
1210#endif /* CONFIG_FEATURE_VI_SET */
1211#ifdef CONFIG_FEATURE_VI_SEARCH
1212 } else if (strncasecmp((char *) cmd, "s", 1) == 0) { // substitute a pattern with a replacement pattern
1213 Byte *ls, *F, *R;
1214 int gflag;
1215
1216 // F points to the "find" pattern
1217 // R points to the "replace" pattern
1218 // replace the cmd line delimiters "/" with NULLs
1219 gflag = 0; // global replace flag
1220 c = orig_buf[1]; // what is the delimiter
1221 F = orig_buf + 2; // start of "find"
1222 R = (Byte *) strchr((char *) F, c); // middle delimiter
1223 if (!R) goto colon_s_fail;
1224 *R++ = '\0'; // terminate "find"
1225 buf1 = (Byte *) strchr((char *) R, c);
1226 if (!buf1) goto colon_s_fail;
1227 *buf1++ = '\0'; // terminate "replace"
1228 if (*buf1 == 'g') { // :s/foo/bar/g
1229 buf1++;
1230 gflag++; // turn on gflag
1231 }
1232 q = begin_line(q);
1233 if (b < 0) { // maybe :s/foo/bar/
1234 q = begin_line(dot); // start with cur line
1235 b = count_lines(text, q); // cur line number
1236 }
1237 if (e < 0)
1238 e = b; // maybe :.s/foo/bar/
1239 for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0
1240 ls = q; // orig line start
1241 vc4:
1242 buf1 = char_search(q, F, FORWARD, LIMITED); // search cur line only for "find"
1243 if (buf1 != NULL) {
1244 // we found the "find" pattern- delete it
1245 (void) text_hole_delete(buf1, buf1 + strlen((char *) F) - 1);
1246 // inset the "replace" patern
1247 (void) string_insert(buf1, R); // insert the string
1248 // check for "global" :s/foo/bar/g
1249 if (gflag == 1) {
1250 if ((buf1 + strlen((char *) R)) < end_line(ls)) {
1251 q = buf1 + strlen((char *) R);
1252 goto vc4; // don't let q move past cur line
1253 }
1254 }
1255 }
1256 q = next_line(ls);
1257 }
1258#endif /* CONFIG_FEATURE_VI_SEARCH */
1259 } else if (strncasecmp((char *) cmd, "version", i) == 0) { // show software version
1260 psb("%s", vi_Version);
1261 } else if ((strncasecmp((char *) cmd, "write", i) == 0) || // write text to file
1262 (strncasecmp((char *) cmd, "wq", i) == 0) ||
1263 (strncasecmp((char *) cmd, "x", i) == 0)) {
1264 // is there a file name to write to?
1265 if (strlen((char *) args) > 0) {
1266 fn = args;
1267 }
1268#ifdef CONFIG_FEATURE_VI_READONLY
1269 if ((vi_readonly || readonly) && ! useforce) {
1270 psbs("\"%s\" File is read only", fn);
1271 goto vc3;
1272 }
1273#endif /* CONFIG_FEATURE_VI_READONLY */
1274 // how many lines in text[]?
1275 li = count_lines(q, r);
1276 ch = r - q + 1;
1277 // see if file exists- if not, its just a new file request
1278 if (useforce) {
1279 // if "fn" is not write-able, chmod u+w
1280 // sprintf(syscmd, "chmod u+w %s", fn);
1281 // system(syscmd);
1282 forced = TRUE;
1283 }
1284 l = file_write(fn, q, r);
1285 if (useforce && forced) {
1286 // chmod u-w
1287 // sprintf(syscmd, "chmod u-w %s", fn);
1288 // system(syscmd);
1289 forced = FALSE;
1290 }
1291 psb("\"%s\" %dL, %dC", fn, li, l);
1292 if (q == text && r == end - 1 && l == ch)
1293 file_modified = FALSE;
1294 if ((cmd[0] == 'x' || cmd[1] == 'q') && l == ch) {
1295 editing = 0;
1296 }
1297#ifdef CONFIG_FEATURE_VI_READONLY
1298 vc3:;
1299#endif /* CONFIG_FEATURE_VI_READONLY */
1300#ifdef CONFIG_FEATURE_VI_YANKMARK
1301 } else if (strncasecmp((char *) cmd, "yank", i) == 0) { // yank lines
1302 if (b < 0) { // no addr given- use defaults
1303 q = begin_line(dot); // assume .,. for the range
1304 r = end_line(dot);
1305 }
1306 text_yank(q, r, YDreg);
1307 li = count_lines(q, r);
1308 psb("Yank %d lines (%d chars) into [%c]",
1309 li, strlen((char *) reg[YDreg]), what_reg());
1310#endif /* CONFIG_FEATURE_VI_YANKMARK */
1311 } else {
1312 // cmd unknown
1313 ni((Byte *) cmd);
1314 }
1315 vc1:
1316 dot = bound_dot(dot); // make sure "dot" is valid
1317 return;
1318#ifdef CONFIG_FEATURE_VI_SEARCH
1319colon_s_fail:
1320 psb(":s expression missing delimiters");
1321 return;
1322#endif
1323
1324}
1325
1326static void Hit_Return(void)
1327{
1328 char c;
1329
1330 standout_start(); // start reverse video
1331 write(1, "[Hit return to continue]", 24);
1332 standout_end(); // end reverse video
1333 while ((c = get_one_char()) != '\n' && c != '\r') /*do nothing */
1334 ;
1335 redraw(TRUE); // force redraw all
1336}
1337#endif /* CONFIG_FEATURE_VI_COLON */
1338
1339//----- Synchronize the cursor to Dot --------------------------
1340static void sync_cursor(Byte * d, int *row, int *col)
1341{
1342 Byte *beg_cur, *end_cur; // begin and end of "d" line
1343 Byte *beg_scr, *end_scr; // begin and end of screen
1344 Byte *tp;
1345 int cnt, ro, co;
1346
1347 beg_cur = begin_line(d); // first char of cur line
1348 end_cur = end_line(d); // last char of cur line
1349
1350 beg_scr = end_scr = screenbegin; // first char of screen
1351 end_scr = end_screen(); // last char of screen
1352
1353 if (beg_cur < screenbegin) {
1354 // "d" is before top line on screen
1355 // how many lines do we have to move
1356 cnt = count_lines(beg_cur, screenbegin);
1357 sc1:
1358 screenbegin = beg_cur;
1359 if (cnt > (rows - 1) / 2) {
1360 // we moved too many lines. put "dot" in middle of screen
1361 for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
1362 screenbegin = prev_line(screenbegin);
1363 }
1364 }
1365 } else if (beg_cur > end_scr) {
1366 // "d" is after bottom line on screen
1367 // how many lines do we have to move
1368 cnt = count_lines(end_scr, beg_cur);
1369 if (cnt > (rows - 1) / 2)
1370 goto sc1; // too many lines
1371 for (ro = 0; ro < cnt - 1; ro++) {
1372 // move screen begin the same amount
1373 screenbegin = next_line(screenbegin);
1374 // now, move the end of screen
1375 end_scr = next_line(end_scr);
1376 end_scr = end_line(end_scr);
1377 }
1378 }
1379 // "d" is on screen- find out which row
1380 tp = screenbegin;
1381 for (ro = 0; ro < rows - 1; ro++) { // drive "ro" to correct row
1382 if (tp == beg_cur)
1383 break;
1384 tp = next_line(tp);
1385 }
1386
1387 // find out what col "d" is on
1388 co = 0;
1389 do { // drive "co" to correct column
1390 if (*tp == '\n' || *tp == '\0')
1391 break;
1392 if (*tp == '\t') {
1393 // 7 - (co % 8 )
1394 co += ((tabstop - 1) - (co % tabstop));
1395 } else if (*tp < ' ') {
1396 co++; // display as ^X, use 2 columns
1397 }
1398 } while (tp++ < d && ++co);
1399
1400 // "co" is the column where "dot" is.
1401 // The screen has "columns" columns.
1402 // The currently displayed columns are 0+offset -- columns+ofset
1403 // |-------------------------------------------------------------|
1404 // ^ ^ ^
1405 // offset | |------- columns ----------------|
1406 //
1407 // If "co" is already in this range then we do not have to adjust offset
1408 // but, we do have to subtract the "offset" bias from "co".
1409 // If "co" is outside this range then we have to change "offset".
1410 // If the first char of a line is a tab the cursor will try to stay
1411 // in column 7, but we have to set offset to 0.
1412
1413 if (co < 0 + offset) {
1414 offset = co;
1415 }
1416 if (co >= columns + offset) {
1417 offset = co - columns + 1;
1418 }
1419 // if the first char of the line is a tab, and "dot" is sitting on it
1420 // force offset to 0.
1421 if (d == beg_cur && *d == '\t') {
1422 offset = 0;
1423 }
1424 co -= offset;
1425
1426 *row = ro;
1427 *col = co;
1428}
1429
1430//----- Text Movement Routines ---------------------------------
1431static Byte *begin_line(Byte * p) // return pointer to first char cur line
1432{
1433 while (p > text && p[-1] != '\n')
1434 p--; // go to cur line B-o-l
1435 return (p);
1436}
1437
1438static Byte *end_line(Byte * p) // return pointer to NL of cur line line
1439{
1440 while (p < end - 1 && *p != '\n')
1441 p++; // go to cur line E-o-l
1442 return (p);
1443}
1444
1445extern inline Byte *dollar_line(Byte * p) // return pointer to just before NL line
1446{
1447 while (p < end - 1 && *p != '\n')
1448 p++; // go to cur line E-o-l
1449 // Try to stay off of the Newline
1450 if (*p == '\n' && (p - begin_line(p)) > 0)
1451 p--;
1452 return (p);
1453}
1454
1455static Byte *prev_line(Byte * p) // return pointer first char prev line
1456{
1457 p = begin_line(p); // goto begining of cur line
1458 if (p[-1] == '\n' && p > text)
1459 p--; // step to prev line
1460 p = begin_line(p); // goto begining of prev line
1461 return (p);
1462}
1463
1464static Byte *next_line(Byte * p) // return pointer first char next line
1465{
1466 p = end_line(p);
1467 if (*p == '\n' && p < end - 1)
1468 p++; // step to next line
1469 return (p);
1470}
1471
1472//----- Text Information Routines ------------------------------
1473static Byte *end_screen(void)
1474{
1475 Byte *q;
1476 int cnt;
1477
1478 // find new bottom line
1479 q = screenbegin;
1480 for (cnt = 0; cnt < rows - 2; cnt++)
1481 q = next_line(q);
1482 q = end_line(q);
1483 return (q);
1484}
1485
1486static int count_lines(Byte * start, Byte * stop) // count line from start to stop
1487{
1488 Byte *q;
1489 int cnt;
1490
1491 if (stop < start) { // start and stop are backwards- reverse them
1492 q = start;
1493 start = stop;
1494 stop = q;
1495 }
1496 cnt = 0;
1497 stop = end_line(stop); // get to end of this line
1498 for (q = start; q <= stop && q <= end - 1; q++) {
1499 if (*q == '\n')
1500 cnt++;
1501 }
1502 return (cnt);
1503}
1504
1505static Byte *find_line(int li) // find begining of line #li
1506{
1507 Byte *q;
1508
1509 for (q = text; li > 1; li--) {
1510 q = next_line(q);
1511 }
1512 return (q);
1513}
1514
1515//----- Dot Movement Routines ----------------------------------
1516static void dot_left(void)
1517{
1518 if (dot > text && dot[-1] != '\n')
1519 dot--;
1520}
1521
1522static void dot_right(void)
1523{
1524 if (dot < end - 1 && *dot != '\n')
1525 dot++;
1526}
1527
1528static void dot_begin(void)
1529{
1530 dot = begin_line(dot); // return pointer to first char cur line
1531}
1532
1533static void dot_end(void)
1534{
1535 dot = end_line(dot); // return pointer to last char cur line
1536}
1537
1538static Byte *move_to_col(Byte * p, int l)
1539{
1540 int co;
1541
1542 p = begin_line(p);
1543 co = 0;
1544 do {
1545 if (*p == '\n' || *p == '\0')
1546 break;
1547 if (*p == '\t') {
1548 // 7 - (co % 8 )
1549 co += ((tabstop - 1) - (co % tabstop));
1550 } else if (*p < ' ') {
1551 co++; // display as ^X, use 2 columns
1552 }
1553 } while (++co <= l && p++ < end);
1554 return (p);
1555}
1556
1557static void dot_next(void)
1558{
1559 dot = next_line(dot);
1560}
1561
1562static void dot_prev(void)
1563{
1564 dot = prev_line(dot);
1565}
1566
1567static void dot_scroll(int cnt, int dir)
1568{
1569 Byte *q;
1570
1571 for (; cnt > 0; cnt--) {
1572 if (dir < 0) {
1573 // scroll Backwards
1574 // ctrl-Y scroll up one line
1575 screenbegin = prev_line(screenbegin);
1576 } else {
1577 // scroll Forwards
1578 // ctrl-E scroll down one line
1579 screenbegin = next_line(screenbegin);
1580 }
1581 }
1582 // make sure "dot" stays on the screen so we dont scroll off
1583 if (dot < screenbegin)
1584 dot = screenbegin;
1585 q = end_screen(); // find new bottom line
1586 if (dot > q)
1587 dot = begin_line(q); // is dot is below bottom line?
1588 dot_skip_over_ws();
1589}
1590
1591static void dot_skip_over_ws(void)
1592{
1593 // skip WS
1594 while (isspace(*dot) && *dot != '\n' && dot < end - 1)
1595 dot++;
1596}
1597
1598static void dot_delete(void) // delete the char at 'dot'
1599{
1600 (void) text_hole_delete(dot, dot);
1601}
1602
1603static Byte *bound_dot(Byte * p) // make sure text[0] <= P < "end"
1604{
1605 if (p >= end && end > text) {
1606 p = end - 1;
1607 indicate_error('1');
1608 }
1609 if (p < text) {
1610 p = text;
1611 indicate_error('2');
1612 }
1613 return (p);
1614}
1615
1616//----- Helper Utility Routines --------------------------------
1617
1618//----------------------------------------------------------------
1619//----- Char Routines --------------------------------------------
1620/* Chars that are part of a word-
1621 * 0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
1622 * Chars that are Not part of a word (stoppers)
1623 * !"#$%&'()*+,-./:;<=>?@[\]^`{|}~
1624 * Chars that are WhiteSpace
1625 * TAB NEWLINE VT FF RETURN SPACE
1626 * DO NOT COUNT NEWLINE AS WHITESPACE
1627 */
1628
1629static Byte *new_screen(int ro, int co)
1630{
1631 int li;
1632
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001633 free(screen);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001634 screensize = ro * co + 8;
1635 screen = (Byte *) xmalloc(screensize);
1636 // initialize the new screen. assume this will be a empty file.
1637 screen_erase();
1638 // non-existant text[] lines start with a tilde (~).
1639 for (li = 1; li < ro - 1; li++) {
1640 screen[(li * co) + 0] = '~';
1641 }
1642 return (screen);
1643}
1644
1645static Byte *new_text(int size)
1646{
1647 if (size < 10240)
1648 size = 10240; // have a minimum size for new files
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001649 free(text);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001650 text = (Byte *) xmalloc(size + 8);
1651 memset(text, '\0', size); // clear new text[]
1652 //text += 4; // leave some room for "oops"
1653 textend = text + size - 1;
1654 //textend -= 4; // leave some root for "oops"
1655 return (text);
1656}
1657
1658#ifdef CONFIG_FEATURE_VI_SEARCH
1659static int mycmp(Byte * s1, Byte * s2, int len)
1660{
1661 int i;
1662
1663 i = strncmp((char *) s1, (char *) s2, len);
1664#ifdef CONFIG_FEATURE_VI_SETOPTS
1665 if (ignorecase) {
1666 i = strncasecmp((char *) s1, (char *) s2, len);
1667 }
1668#endif /* CONFIG_FEATURE_VI_SETOPTS */
1669 return (i);
1670}
1671
1672static Byte *char_search(Byte * p, Byte * pat, int dir, int range) // search for pattern starting at p
1673{
1674#ifndef REGEX_SEARCH
1675 Byte *start, *stop;
1676 int len;
1677
1678 len = strlen((char *) pat);
1679 if (dir == FORWARD) {
1680 stop = end - 1; // assume range is p - end-1
1681 if (range == LIMITED)
1682 stop = next_line(p); // range is to next line
1683 for (start = p; start < stop; start++) {
1684 if (mycmp(start, pat, len) == 0) {
1685 return (start);
1686 }
1687 }
1688 } else if (dir == BACK) {
1689 stop = text; // assume range is text - p
1690 if (range == LIMITED)
1691 stop = prev_line(p); // range is to prev line
1692 for (start = p - len; start >= stop; start--) {
1693 if (mycmp(start, pat, len) == 0) {
1694 return (start);
1695 }
1696 }
1697 }
1698 // pattern not found
1699 return (NULL);
1700#else /*REGEX_SEARCH */
1701 char *q;
1702 struct re_pattern_buffer preg;
1703 int i;
1704 int size, range;
1705
1706 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
1707 preg.translate = 0;
1708 preg.fastmap = 0;
1709 preg.buffer = 0;
1710 preg.allocated = 0;
1711
1712 // assume a LIMITED forward search
1713 q = next_line(p);
1714 q = end_line(q);
1715 q = end - 1;
1716 if (dir == BACK) {
1717 q = prev_line(p);
1718 q = text;
1719 }
1720 // count the number of chars to search over, forward or backward
1721 size = q - p;
1722 if (size < 0)
1723 size = p - q;
1724 // RANGE could be negative if we are searching backwards
1725 range = q - p;
1726
1727 q = (char *) re_compile_pattern(pat, strlen((char *) pat), &preg);
1728 if (q != 0) {
1729 // The pattern was not compiled
1730 psbs("bad search pattern: \"%s\": %s", pat, q);
1731 i = 0; // return p if pattern not compiled
1732 goto cs1;
1733 }
1734
1735 q = p;
1736 if (range < 0) {
1737 q = p - size;
1738 if (q < text)
1739 q = text;
1740 }
1741 // search for the compiled pattern, preg, in p[]
1742 // range < 0- search backward
1743 // range > 0- search forward
1744 // 0 < start < size
1745 // re_search() < 0 not found or error
1746 // re_search() > 0 index of found pattern
1747 // struct pattern char int int int struct reg
1748 // re_search (*pattern_buffer, *string, size, start, range, *regs)
1749 i = re_search(&preg, q, size, 0, range, 0);
1750 if (i == -1) {
1751 p = 0;
1752 i = 0; // return NULL if pattern not found
1753 }
1754 cs1:
1755 if (dir == FORWARD) {
1756 p = p + i;
1757 } else {
1758 p = p - i;
1759 }
1760 return (p);
1761#endif /*REGEX_SEARCH */
1762}
1763#endif /* CONFIG_FEATURE_VI_SEARCH */
1764
1765static Byte *char_insert(Byte * p, Byte c) // insert the char c at 'p'
1766{
1767 if (c == 22) { // Is this an ctrl-V?
1768 p = stupid_insert(p, '^'); // use ^ to indicate literal next
1769 p--; // backup onto ^
1770 refresh(FALSE); // show the ^
1771 c = get_one_char();
1772 *p = c;
1773 p++;
1774 file_modified = TRUE; // has the file been modified
1775 } else if (c == 27) { // Is this an ESC?
1776 cmd_mode = 0;
1777 cmdcnt = 0;
1778 end_cmd_q(); // stop adding to q
1779 strcpy((char *) status_buffer, " "); // clear the status buffer
1780 if ((p[-1] != '\n') && (dot>text)) {
1781 p--;
1782 }
1783 } else if (c == erase_char) { // Is this a BS
1784 // 123456789
1785 if ((p[-1] != '\n') && (dot>text)) {
1786 p--;
1787 p = text_hole_delete(p, p); // shrink buffer 1 char
1788#ifdef CONFIG_FEATURE_VI_DOT_CMD
1789 // also rmove char from last_modifying_cmd
1790 if (strlen((char *) last_modifying_cmd) > 0) {
1791 Byte *q;
1792
1793 q = last_modifying_cmd;
1794 q[strlen((char *) q) - 1] = '\0'; // erase BS
1795 q[strlen((char *) q) - 1] = '\0'; // erase prev char
1796 }
1797#endif /* CONFIG_FEATURE_VI_DOT_CMD */
1798 }
1799 } else {
1800 // insert a char into text[]
1801 Byte *sp; // "save p"
1802
1803 if (c == 13)
1804 c = '\n'; // translate \r to \n
1805 sp = p; // remember addr of insert
1806 p = stupid_insert(p, c); // insert the char
1807#ifdef CONFIG_FEATURE_VI_SETOPTS
1808 if (showmatch && strchr(")]}", *sp) != NULL) {
1809 showmatching(sp);
1810 }
1811 if (autoindent && c == '\n') { // auto indent the new line
1812 Byte *q;
1813
1814 q = prev_line(p); // use prev line as templet
1815 for (; isblnk(*q); q++) {
1816 p = stupid_insert(p, *q); // insert the char
1817 }
1818 }
1819#endif /* CONFIG_FEATURE_VI_SETOPTS */
1820 }
1821 return (p);
1822}
1823
1824static Byte *stupid_insert(Byte * p, Byte c) // stupidly insert the char c at 'p'
1825{
1826 p = text_hole_make(p, 1);
1827 if (p != 0) {
1828 *p = c;
1829 file_modified = TRUE; // has the file been modified
1830 p++;
1831 }
1832 return (p);
1833}
1834
1835static Byte find_range(Byte ** start, Byte ** stop, Byte c)
1836{
1837 Byte *save_dot, *p, *q;
1838 int cnt;
1839
1840 save_dot = dot;
1841 p = q = dot;
1842
1843 if (strchr("cdy><", c)) {
1844 // these cmds operate on whole lines
1845 p = q = begin_line(p);
1846 for (cnt = 1; cnt < cmdcnt; cnt++) {
1847 q = next_line(q);
1848 }
1849 q = end_line(q);
1850 } else if (strchr("^%$0bBeEft", c)) {
1851 // These cmds operate on char positions
1852 do_cmd(c); // execute movement cmd
1853 q = dot;
1854 } else if (strchr("wW", c)) {
1855 do_cmd(c); // execute movement cmd
1856 if (dot > text)
1857 dot--; // move back off of next word
1858 if (dot > text && *dot == '\n')
1859 dot--; // stay off NL
1860 q = dot;
1861 } else if (strchr("H-k{", c)) {
1862 // these operate on multi-lines backwards
1863 q = end_line(dot); // find NL
1864 do_cmd(c); // execute movement cmd
1865 dot_begin();
1866 p = dot;
1867 } else if (strchr("L+j}\r\n", c)) {
1868 // these operate on multi-lines forwards
1869 p = begin_line(dot);
1870 do_cmd(c); // execute movement cmd
1871 dot_end(); // find NL
1872 q = dot;
1873 } else {
1874 c = 27; // error- return an ESC char
1875 //break;
1876 }
1877 *start = p;
1878 *stop = q;
1879 if (q < p) {
1880 *start = q;
1881 *stop = p;
1882 }
1883 dot = save_dot;
1884 return (c);
1885}
1886
1887static int st_test(Byte * p, int type, int dir, Byte * tested)
1888{
1889 Byte c, c0, ci;
1890 int test, inc;
1891
1892 inc = dir;
1893 c = c0 = p[0];
1894 ci = p[inc];
1895 test = 0;
1896
1897 if (type == S_BEFORE_WS) {
1898 c = ci;
1899 test = ((!isspace(c)) || c == '\n');
1900 }
1901 if (type == S_TO_WS) {
1902 c = c0;
1903 test = ((!isspace(c)) || c == '\n');
1904 }
1905 if (type == S_OVER_WS) {
1906 c = c0;
1907 test = ((isspace(c)));
1908 }
1909 if (type == S_END_PUNCT) {
1910 c = ci;
1911 test = ((ispunct(c)));
1912 }
1913 if (type == S_END_ALNUM) {
1914 c = ci;
1915 test = ((isalnum(c)) || c == '_');
1916 }
1917 *tested = c;
1918 return (test);
1919}
1920
1921static Byte *skip_thing(Byte * p, int linecnt, int dir, int type)
1922{
1923 Byte c;
1924
1925 while (st_test(p, type, dir, &c)) {
1926 // make sure we limit search to correct number of lines
1927 if (c == '\n' && --linecnt < 1)
1928 break;
1929 if (dir >= 0 && p >= end - 1)
1930 break;
1931 if (dir < 0 && p <= text)
1932 break;
1933 p += dir; // move to next char
1934 }
1935 return (p);
1936}
1937
1938// find matching char of pair () [] {}
1939static Byte *find_pair(Byte * p, Byte c)
1940{
1941 Byte match, *q;
1942 int dir, level;
1943
1944 match = ')';
1945 level = 1;
1946 dir = 1; // assume forward
1947 switch (c) {
1948 case '(':
1949 match = ')';
1950 break;
1951 case '[':
1952 match = ']';
1953 break;
1954 case '{':
1955 match = '}';
1956 break;
1957 case ')':
1958 match = '(';
1959 dir = -1;
1960 break;
1961 case ']':
1962 match = '[';
1963 dir = -1;
1964 break;
1965 case '}':
1966 match = '{';
1967 dir = -1;
1968 break;
1969 }
1970 for (q = p + dir; text <= q && q < end; q += dir) {
1971 // look for match, count levels of pairs (( ))
1972 if (*q == c)
1973 level++; // increase pair levels
1974 if (*q == match)
1975 level--; // reduce pair level
1976 if (level == 0)
1977 break; // found matching pair
1978 }
1979 if (level != 0)
1980 q = NULL; // indicate no match
1981 return (q);
1982}
1983
1984#ifdef CONFIG_FEATURE_VI_SETOPTS
1985// show the matching char of a pair, () [] {}
1986static void showmatching(Byte * p)
1987{
1988 Byte *q, *save_dot;
1989
1990 // we found half of a pair
1991 q = find_pair(p, *p); // get loc of matching char
1992 if (q == NULL) {
1993 indicate_error('3'); // no matching char
1994 } else {
1995 // "q" now points to matching pair
1996 save_dot = dot; // remember where we are
1997 dot = q; // go to new loc
1998 refresh(FALSE); // let the user see it
1999 (void) mysleep(40); // give user some time
2000 dot = save_dot; // go back to old loc
2001 refresh(FALSE);
2002 }
2003}
2004#endif /* CONFIG_FEATURE_VI_SETOPTS */
2005
2006// open a hole in text[]
2007static Byte *text_hole_make(Byte * p, int size) // at "p", make a 'size' byte hole
2008{
2009 Byte *src, *dest;
2010 int cnt;
2011
2012 if (size <= 0)
2013 goto thm0;
2014 src = p;
2015 dest = p + size;
2016 cnt = end - src; // the rest of buffer
2017 if (memmove(dest, src, cnt) != dest) {
2018 psbs("can't create room for new characters");
2019 }
2020 memset(p, ' ', size); // clear new hole
2021 end = end + size; // adjust the new END
2022 file_modified = TRUE; // has the file been modified
2023 thm0:
2024 return (p);
2025}
2026
2027// close a hole in text[]
2028static Byte *text_hole_delete(Byte * p, Byte * q) // delete "p" thru "q", inclusive
2029{
2030 Byte *src, *dest;
2031 int cnt, hole_size;
2032
2033 // move forwards, from beginning
2034 // assume p <= q
2035 src = q + 1;
2036 dest = p;
2037 if (q < p) { // they are backward- swap them
2038 src = p + 1;
2039 dest = q;
2040 }
2041 hole_size = q - p + 1;
2042 cnt = end - src;
2043 if (src < text || src > end)
2044 goto thd0;
2045 if (dest < text || dest >= end)
2046 goto thd0;
2047 if (src >= end)
2048 goto thd_atend; // just delete the end of the buffer
2049 if (memmove(dest, src, cnt) != dest) {
2050 psbs("can't delete the character");
2051 }
2052 thd_atend:
2053 end = end - hole_size; // adjust the new END
2054 if (dest >= end)
2055 dest = end - 1; // make sure dest in below end-1
2056 if (end <= text)
2057 dest = end = text; // keep pointers valid
2058 file_modified = TRUE; // has the file been modified
2059 thd0:
2060 return (dest);
2061}
2062
2063// copy text into register, then delete text.
2064// if dist <= 0, do not include, or go past, a NewLine
2065//
2066static Byte *yank_delete(Byte * start, Byte * stop, int dist, int yf)
2067{
2068 Byte *p;
2069
2070 // make sure start <= stop
2071 if (start > stop) {
2072 // they are backwards, reverse them
2073 p = start;
2074 start = stop;
2075 stop = p;
2076 }
2077 if (dist <= 0) {
2078 // we can not cross NL boundaries
2079 p = start;
2080 if (*p == '\n')
2081 return (p);
2082 // dont go past a NewLine
2083 for (; p + 1 <= stop; p++) {
2084 if (p[1] == '\n') {
2085 stop = p; // "stop" just before NewLine
2086 break;
2087 }
2088 }
2089 }
2090 p = start;
2091#ifdef CONFIG_FEATURE_VI_YANKMARK
2092 text_yank(start, stop, YDreg);
2093#endif /* CONFIG_FEATURE_VI_YANKMARK */
2094 if (yf == YANKDEL) {
2095 p = text_hole_delete(start, stop);
2096 } // delete lines
2097 return (p);
2098}
2099
2100static void show_help(void)
2101{
2102 puts("These features are available:"
2103#ifdef CONFIG_FEATURE_VI_SEARCH
2104 "\n\tPattern searches with / and ?"
2105#endif /* CONFIG_FEATURE_VI_SEARCH */
2106#ifdef CONFIG_FEATURE_VI_DOT_CMD
2107 "\n\tLast command repeat with \'.\'"
2108#endif /* CONFIG_FEATURE_VI_DOT_CMD */
2109#ifdef CONFIG_FEATURE_VI_YANKMARK
2110 "\n\tLine marking with 'x"
2111 "\n\tNamed buffers with \"x"
2112#endif /* CONFIG_FEATURE_VI_YANKMARK */
2113#ifdef CONFIG_FEATURE_VI_READONLY
2114 "\n\tReadonly if vi is called as \"view\""
2115 "\n\tReadonly with -R command line arg"
2116#endif /* CONFIG_FEATURE_VI_READONLY */
2117#ifdef CONFIG_FEATURE_VI_SET
2118 "\n\tSome colon mode commands with \':\'"
2119#endif /* CONFIG_FEATURE_VI_SET */
2120#ifdef CONFIG_FEATURE_VI_SETOPTS
2121 "\n\tSettable options with \":set\""
2122#endif /* CONFIG_FEATURE_VI_SETOPTS */
2123#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
2124 "\n\tSignal catching- ^C"
2125 "\n\tJob suspend and resume with ^Z"
2126#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
2127#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
2128 "\n\tAdapt to window re-sizes"
2129#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
2130 );
2131}
2132
2133extern inline void print_literal(Byte * buf, Byte * s) // copy s to buf, convert unprintable
2134{
2135 Byte c, b[2];
2136
2137 b[1] = '\0';
2138 strcpy((char *) buf, ""); // init buf
2139 if (strlen((char *) s) <= 0)
2140 s = (Byte *) "(NULL)";
2141 for (; *s > '\0'; s++) {
2142 c = *s;
2143 if (*s > '~') {
2144 strcat((char *) buf, SOs);
2145 c = *s - 128;
2146 }
2147 if (*s < ' ') {
2148 strcat((char *) buf, "^");
2149 c += '@';
2150 }
2151 b[0] = c;
2152 strcat((char *) buf, (char *) b);
2153 if (*s > '~')
2154 strcat((char *) buf, SOn);
2155 if (*s == '\n') {
2156 strcat((char *) buf, "$");
2157 }
2158 }
2159}
2160
2161#ifdef CONFIG_FEATURE_VI_DOT_CMD
2162static void start_new_cmd_q(Byte c)
2163{
2164 // release old cmd
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002165 free(last_modifying_cmd);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002166 // get buffer for new cmd
2167 last_modifying_cmd = (Byte *) xmalloc(BUFSIZ);
2168 memset(last_modifying_cmd, '\0', BUFSIZ); // clear new cmd queue
2169 // if there is a current cmd count put it in the buffer first
2170 if (cmdcnt > 0)
2171 sprintf((char *) last_modifying_cmd, "%d", cmdcnt);
2172 // save char c onto queue
2173 last_modifying_cmd[strlen((char *) last_modifying_cmd)] = c;
2174 adding2q = 1;
2175 return;
2176}
2177
2178static void end_cmd_q(void)
2179{
2180#ifdef CONFIG_FEATURE_VI_YANKMARK
2181 YDreg = 26; // go back to default Yank/Delete reg
2182#endif /* CONFIG_FEATURE_VI_YANKMARK */
2183 adding2q = 0;
2184 return;
2185}
2186#endif /* CONFIG_FEATURE_VI_DOT_CMD */
2187
2188#if defined(CONFIG_FEATURE_VI_YANKMARK) || defined(CONFIG_FEATURE_VI_COLON) || defined(CONFIG_FEATURE_VI_CRASHME)
2189static Byte *string_insert(Byte * p, Byte * s) // insert the string at 'p'
2190{
2191 int cnt, i;
2192
2193 i = strlen((char *) s);
2194 p = text_hole_make(p, i);
2195 strncpy((char *) p, (char *) s, i);
2196 for (cnt = 0; *s != '\0'; s++) {
2197 if (*s == '\n')
2198 cnt++;
2199 }
2200#ifdef CONFIG_FEATURE_VI_YANKMARK
2201 psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
2202#endif /* CONFIG_FEATURE_VI_YANKMARK */
2203 return (p);
2204}
2205#endif /* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_COLON || CONFIG_FEATURE_VI_CRASHME */
2206
2207#ifdef CONFIG_FEATURE_VI_YANKMARK
2208static Byte *text_yank(Byte * p, Byte * q, int dest) // copy text into a register
2209{
2210 Byte *t;
2211 int cnt;
2212
2213 if (q < p) { // they are backwards- reverse them
2214 t = q;
2215 q = p;
2216 p = t;
2217 }
2218 cnt = q - p + 1;
2219 t = reg[dest];
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002220 free(t); // if already a yank register, free it
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002221 t = (Byte *) xmalloc(cnt + 1); // get a new register
2222 memset(t, '\0', cnt + 1); // clear new text[]
2223 strncpy((char *) t, (char *) p, cnt); // copy text[] into bufer
2224 reg[dest] = t;
2225 return (p);
2226}
2227
2228static Byte what_reg(void)
2229{
2230 Byte c;
2231 int i;
2232
2233 i = 0;
2234 c = 'D'; // default to D-reg
2235 if (0 <= YDreg && YDreg <= 25)
2236 c = 'a' + (Byte) YDreg;
2237 if (YDreg == 26)
2238 c = 'D';
2239 if (YDreg == 27)
2240 c = 'U';
2241 return (c);
2242}
2243
2244static void check_context(Byte cmd)
2245{
2246 // A context is defined to be "modifying text"
2247 // Any modifying command establishes a new context.
2248
2249 if (dot < context_start || dot > context_end) {
2250 if (strchr((char *) modifying_cmds, cmd) != NULL) {
2251 // we are trying to modify text[]- make this the current context
2252 mark[27] = mark[26]; // move cur to prev
2253 mark[26] = dot; // move local to cur
2254 context_start = prev_line(prev_line(dot));
2255 context_end = next_line(next_line(dot));
2256 //loiter= start_loiter= now;
2257 }
2258 }
2259 return;
2260}
2261
2262extern inline Byte *swap_context(Byte * p) // goto new context for '' command make this the current context
2263{
2264 Byte *tmp;
2265
2266 // the current context is in mark[26]
2267 // the previous context is in mark[27]
2268 // only swap context if other context is valid
2269 if (text <= mark[27] && mark[27] <= end - 1) {
2270 tmp = mark[27];
2271 mark[27] = mark[26];
2272 mark[26] = tmp;
2273 p = mark[26]; // where we are going- previous context
2274 context_start = prev_line(prev_line(prev_line(p)));
2275 context_end = next_line(next_line(next_line(p)));
2276 }
2277 return (p);
2278}
2279#endif /* CONFIG_FEATURE_VI_YANKMARK */
2280
2281static int isblnk(Byte c) // is the char a blank or tab
2282{
2283 return (c == ' ' || c == '\t');
2284}
2285
2286//----- Set terminal attributes --------------------------------
2287static void rawmode(void)
2288{
2289 tcgetattr(0, &term_orig);
2290 term_vi = term_orig;
2291 term_vi.c_lflag &= (~ICANON & ~ECHO); // leave ISIG ON- allow intr's
2292 term_vi.c_iflag &= (~IXON & ~ICRNL);
2293 term_vi.c_oflag &= (~ONLCR);
2294#ifndef linux
2295 term_vi.c_cc[VMIN] = 1;
2296 term_vi.c_cc[VTIME] = 0;
2297#endif
2298 erase_char = term_vi.c_cc[VERASE];
2299 tcsetattr(0, TCSANOW, &term_vi);
2300}
2301
2302static void cookmode(void)
2303{
2304 tcsetattr(0, TCSANOW, &term_orig);
2305}
2306
2307#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
2308//----- See what the window size currently is --------------------
2309static void window_size_get(int sig)
2310{
2311 int i;
2312
2313 i = ioctl(0, TIOCGWINSZ, &winsize);
2314 if (i != 0) {
2315 // force 24x80
2316 winsize.ws_row = 24;
2317 winsize.ws_col = 80;
2318 }
2319 if (winsize.ws_row <= 1) {
2320 winsize.ws_row = 24;
2321 }
2322 if (winsize.ws_col <= 1) {
2323 winsize.ws_col = 80;
2324 }
2325 rows = (int) winsize.ws_row;
2326 columns = (int) winsize.ws_col;
2327}
2328#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
2329
2330//----- Come here when we get a window resize signal ---------
2331#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
2332static void winch_sig(int sig)
2333{
2334 signal(SIGWINCH, winch_sig);
2335#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
2336 window_size_get(0);
2337#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
2338 new_screen(rows, columns); // get memory for virtual screen
2339 redraw(TRUE); // re-draw the screen
2340}
2341
2342//----- Come here when we get a continue signal -------------------
2343static void cont_sig(int sig)
2344{
2345 rawmode(); // terminal to "raw"
2346 *status_buffer = '\0'; // clear the status buffer
2347 redraw(TRUE); // re-draw the screen
2348
2349 signal(SIGTSTP, suspend_sig);
2350 signal(SIGCONT, SIG_DFL);
2351 kill(getpid(), SIGCONT);
2352}
2353
2354//----- Come here when we get a Suspend signal -------------------
2355static void suspend_sig(int sig)
2356{
2357 place_cursor(rows - 1, 0, FALSE); // go to bottom of screen
2358 clear_to_eol(); // Erase to end of line
2359 cookmode(); // terminal to "cooked"
2360
2361 signal(SIGCONT, cont_sig);
2362 signal(SIGTSTP, SIG_DFL);
2363 kill(getpid(), SIGTSTP);
2364}
2365
2366//----- Come here when we get a signal ---------------------------
2367static void catch_sig(int sig)
2368{
2369 signal(SIGHUP, catch_sig);
2370 signal(SIGINT, catch_sig);
2371 signal(SIGTERM, catch_sig);
2372 longjmp(restart, sig);
2373}
2374
2375static void alarm_sig(int sig)
2376{
2377 signal(SIGALRM, catch_sig);
2378 longjmp(restart, sig);
2379}
2380
2381//----- Come here when we get a core dump signal -----------------
2382static void core_sig(int sig)
2383{
2384 signal(SIGQUIT, core_sig);
2385 signal(SIGILL, core_sig);
2386 signal(SIGTRAP, core_sig);
2387 signal(SIGIOT, core_sig);
2388 signal(SIGABRT, core_sig);
2389 signal(SIGFPE, core_sig);
2390 signal(SIGBUS, core_sig);
2391 signal(SIGSEGV, core_sig);
2392#ifdef SIGSYS
2393 signal(SIGSYS, core_sig);
2394#endif
2395
2396 dot = bound_dot(dot); // make sure "dot" is valid
2397
2398 longjmp(restart, sig);
2399}
2400#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
2401
2402static int mysleep(int hund) // sleep for 'h' 1/100 seconds
2403{
2404 // Don't hang- Wait 5/100 seconds- 1 Sec= 1000000
2405 FD_ZERO(&rfds);
2406 FD_SET(0, &rfds);
2407 tv.tv_sec = 0;
2408 tv.tv_usec = hund * 10000;
2409 select(1, &rfds, NULL, NULL, &tv);
2410 return (FD_ISSET(0, &rfds));
2411}
2412
2413//----- IO Routines --------------------------------------------
2414static Byte readit(void) // read (maybe cursor) key from stdin
2415{
2416 Byte c;
2417 int i, bufsiz, cnt, cmdindex;
2418 struct esc_cmds {
2419 Byte *seq;
2420 Byte val;
2421 };
2422
2423 static struct esc_cmds esccmds[] = {
2424 {(Byte *) "OA", (Byte) VI_K_UP}, // cursor key Up
2425 {(Byte *) "OB", (Byte) VI_K_DOWN}, // cursor key Down
2426 {(Byte *) "OC", (Byte) VI_K_RIGHT}, // Cursor Key Right
2427 {(Byte *) "OD", (Byte) VI_K_LEFT}, // cursor key Left
2428 {(Byte *) "OH", (Byte) VI_K_HOME}, // Cursor Key Home
2429 {(Byte *) "OF", (Byte) VI_K_END}, // Cursor Key End
2430 {(Byte *) "", (Byte) VI_K_UP}, // cursor key Up
2431 {(Byte *) "", (Byte) VI_K_DOWN}, // cursor key Down
2432 {(Byte *) "", (Byte) VI_K_RIGHT}, // Cursor Key Right
2433 {(Byte *) "", (Byte) VI_K_LEFT}, // cursor key Left
2434 {(Byte *) "", (Byte) VI_K_HOME}, // Cursor Key Home
2435 {(Byte *) "", (Byte) VI_K_END}, // Cursor Key End
2436 {(Byte *) "[2~", (Byte) VI_K_INSERT}, // Cursor Key Insert
2437 {(Byte *) "[5~", (Byte) VI_K_PAGEUP}, // Cursor Key Page Up
2438 {(Byte *) "[6~", (Byte) VI_K_PAGEDOWN}, // Cursor Key Page Down
2439 {(Byte *) "OP", (Byte) VI_K_FUN1}, // Function Key F1
2440 {(Byte *) "OQ", (Byte) VI_K_FUN2}, // Function Key F2
2441 {(Byte *) "OR", (Byte) VI_K_FUN3}, // Function Key F3
2442 {(Byte *) "OS", (Byte) VI_K_FUN4}, // Function Key F4
2443 {(Byte *) "[15~", (Byte) VI_K_FUN5}, // Function Key F5
2444 {(Byte *) "[17~", (Byte) VI_K_FUN6}, // Function Key F6
2445 {(Byte *) "[18~", (Byte) VI_K_FUN7}, // Function Key F7
2446 {(Byte *) "[19~", (Byte) VI_K_FUN8}, // Function Key F8
2447 {(Byte *) "[20~", (Byte) VI_K_FUN9}, // Function Key F9
2448 {(Byte *) "[21~", (Byte) VI_K_FUN10}, // Function Key F10
2449 {(Byte *) "[23~", (Byte) VI_K_FUN11}, // Function Key F11
2450 {(Byte *) "[24~", (Byte) VI_K_FUN12}, // Function Key F12
2451 {(Byte *) "[11~", (Byte) VI_K_FUN1}, // Function Key F1
2452 {(Byte *) "[12~", (Byte) VI_K_FUN2}, // Function Key F2
2453 {(Byte *) "[13~", (Byte) VI_K_FUN3}, // Function Key F3
2454 {(Byte *) "[14~", (Byte) VI_K_FUN4}, // Function Key F4
2455 };
2456
2457#define ESCCMDS_COUNT (sizeof(esccmds)/sizeof(struct esc_cmds))
2458
2459 (void) alarm(0); // turn alarm OFF while we wait for input
2460 // get input from User- are there already input chars in Q?
2461 bufsiz = strlen((char *) readbuffer);
2462 if (bufsiz <= 0) {
2463 ri0:
2464 // the Q is empty, wait for a typed char
2465 bufsiz = read(0, readbuffer, BUFSIZ - 1);
2466 if (bufsiz < 0) {
2467 if (errno == EINTR)
2468 goto ri0; // interrupted sys call
2469 if (errno == EBADF)
2470 editing = 0;
2471 if (errno == EFAULT)
2472 editing = 0;
2473 if (errno == EINVAL)
2474 editing = 0;
2475 if (errno == EIO)
2476 editing = 0;
2477 errno = 0;
2478 bufsiz = 0;
2479 }
2480 readbuffer[bufsiz] = '\0';
2481 }
2482 // return char if it is not part of ESC sequence
2483 if (readbuffer[0] != 27)
2484 goto ri1;
2485
2486 // This is an ESC char. Is this Esc sequence?
2487 // Could be bare Esc key. See if there are any
2488 // more chars to read after the ESC. This would
2489 // be a Function or Cursor Key sequence.
2490 FD_ZERO(&rfds);
2491 FD_SET(0, &rfds);
2492 tv.tv_sec = 0;
2493 tv.tv_usec = 50000; // Wait 5/100 seconds- 1 Sec=1000000
2494
2495 // keep reading while there are input chars and room in buffer
2496 while (select(1, &rfds, NULL, NULL, &tv) > 0 && bufsiz <= (BUFSIZ - 5)) {
2497 // read the rest of the ESC string
2498 i = read(0, (void *) (readbuffer + bufsiz), BUFSIZ - bufsiz);
2499 if (i > 0) {
2500 bufsiz += i;
2501 readbuffer[bufsiz] = '\0'; // Terminate the string
2502 }
2503 }
2504 // Maybe cursor or function key?
2505 for (cmdindex = 0; cmdindex < ESCCMDS_COUNT; cmdindex++) {
2506 cnt = strlen((char *) esccmds[cmdindex].seq);
2507 i = strncmp((char *) esccmds[cmdindex].seq, (char *) readbuffer, cnt);
2508 if (i == 0) {
2509 // is a Cursor key- put derived value back into Q
2510 readbuffer[0] = esccmds[cmdindex].val;
2511 // squeeze out the ESC sequence
2512 for (i = 1; i < cnt; i++) {
2513 memmove(readbuffer + 1, readbuffer + 2, BUFSIZ - 2);
2514 readbuffer[BUFSIZ - 1] = '\0';
2515 }
2516 break;
2517 }
2518 }
2519 ri1:
2520 c = readbuffer[0];
2521 // remove one char from Q
2522 memmove(readbuffer, readbuffer + 1, BUFSIZ - 1);
2523 readbuffer[BUFSIZ - 1] = '\0';
2524 (void) alarm(3); // we are done waiting for input, turn alarm ON
2525 return (c);
2526}
2527
2528//----- IO Routines --------------------------------------------
2529static Byte get_one_char()
2530{
2531 static Byte c;
2532
2533#ifdef CONFIG_FEATURE_VI_DOT_CMD
2534 // ! adding2q && ioq == 0 read()
2535 // ! adding2q && ioq != 0 *ioq
2536 // adding2q *last_modifying_cmd= read()
2537 if (!adding2q) {
2538 // we are not adding to the q.
2539 // but, we may be reading from a q
2540 if (ioq == 0) {
2541 // there is no current q, read from STDIN
2542 c = readit(); // get the users input
2543 } else {
2544 // there is a queue to get chars from first
2545 c = *ioq++;
2546 if (c == '\0') {
2547 // the end of the q, read from STDIN
2548 free(ioq_start);
2549 ioq_start = ioq = 0;
2550 c = readit(); // get the users input
2551 }
2552 }
2553 } else {
2554 // adding STDIN chars to q
2555 c = readit(); // get the users input
2556 if (last_modifying_cmd != 0) {
Eric Andersenfda2b7f2002-10-26 10:19:19 +00002557 int len = strlen((char *) last_modifying_cmd);
2558 if (len + 1 >= BUFSIZ) {
2559 psbs("last_modifying_cmd overrun");
2560 } else {
2561 // add new char to q
2562 last_modifying_cmd[len] = c;
2563 }
2564
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002565 }
2566 }
2567#else /* CONFIG_FEATURE_VI_DOT_CMD */
2568 c = readit(); // get the users input
2569#endif /* CONFIG_FEATURE_VI_DOT_CMD */
2570 return (c); // return the char, where ever it came from
2571}
2572
2573static Byte *get_input_line(Byte * prompt) // get input line- use "status line"
2574{
2575 Byte buf[BUFSIZ];
2576 Byte c;
2577 int i;
2578 static Byte *obufp = NULL;
2579
2580 strcpy((char *) buf, (char *) prompt);
2581 *status_buffer = '\0'; // clear the status buffer
2582 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
2583 clear_to_eol(); // clear the line
2584 write(1, prompt, strlen((char *) prompt)); // write out the :, /, or ? prompt
2585
2586 for (i = strlen((char *) buf); i < BUFSIZ;) {
2587 c = get_one_char(); // read user input
2588 if (c == '\n' || c == '\r' || c == 27)
2589 break; // is this end of input
2590 if (c == erase_char) { // user wants to erase prev char
2591 i--; // backup to prev char
2592 buf[i] = '\0'; // erase the char
2593 buf[i + 1] = '\0'; // null terminate buffer
2594 write(1, " ", 3); // erase char on screen
2595 if (i <= 0) { // user backs up before b-o-l, exit
2596 break;
2597 }
2598 } else {
2599 buf[i] = c; // save char in buffer
2600 buf[i + 1] = '\0'; // make sure buffer is null terminated
2601 write(1, buf + i, 1); // echo the char back to user
2602 i++;
2603 }
2604 }
2605 refresh(FALSE);
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002606 free(obufp);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002607 obufp = (Byte *) xstrdup((char *) buf);
2608 return (obufp);
2609}
2610
2611static int file_size(Byte * fn) // what is the byte size of "fn"
2612{
2613 struct stat st_buf;
2614 int cnt, sr;
2615
2616 if (fn == 0 || strlen(fn) <= 0)
2617 return (-1);
2618 cnt = -1;
2619 sr = stat((char *) fn, &st_buf); // see if file exists
2620 if (sr >= 0) {
2621 cnt = (int) st_buf.st_size;
2622 }
2623 return (cnt);
2624}
2625
2626static int file_insert(Byte * fn, Byte * p, int size)
2627{
2628 int fd, cnt;
2629
2630 cnt = -1;
2631#ifdef CONFIG_FEATURE_VI_READONLY
2632 readonly = FALSE;
2633#endif /* CONFIG_FEATURE_VI_READONLY */
2634 if (fn == 0 || strlen((char*) fn) <= 0) {
2635 psbs("No filename given");
2636 goto fi0;
2637 }
2638 if (size == 0) {
2639 // OK- this is just a no-op
2640 cnt = 0;
2641 goto fi0;
2642 }
2643 if (size < 0) {
2644 psbs("Trying to insert a negative number (%d) of characters", size);
2645 goto fi0;
2646 }
2647 if (p < text || p > end) {
2648 psbs("Trying to insert file outside of memory");
2649 goto fi0;
2650 }
2651
2652 // see if we can open the file
2653#ifdef CONFIG_FEATURE_VI_READONLY
2654 if (vi_readonly) goto fi1; // do not try write-mode
2655#endif
2656 fd = open((char *) fn, O_RDWR); // assume read & write
2657 if (fd < 0) {
2658 // could not open for writing- maybe file is read only
2659#ifdef CONFIG_FEATURE_VI_READONLY
2660 fi1:
2661#endif
2662 fd = open((char *) fn, O_RDONLY); // try read-only
2663 if (fd < 0) {
2664 psbs("\"%s\" %s", fn, "could not open file");
2665 goto fi0;
2666 }
2667#ifdef CONFIG_FEATURE_VI_READONLY
2668 // got the file- read-only
2669 readonly = TRUE;
2670#endif /* CONFIG_FEATURE_VI_READONLY */
2671 }
2672 p = text_hole_make(p, size);
2673 cnt = read(fd, p, size);
2674 close(fd);
2675 if (cnt < 0) {
2676 cnt = -1;
2677 p = text_hole_delete(p, p + size - 1); // un-do buffer insert
2678 psbs("could not read file \"%s\"", fn);
2679 } else if (cnt < size) {
2680 // There was a partial read, shrink unused space text[]
2681 p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert
2682 psbs("could not read all of file \"%s\"", fn);
2683 }
2684 if (cnt >= size)
2685 file_modified = TRUE;
2686 fi0:
2687 return (cnt);
2688}
2689
2690static int file_write(Byte * fn, Byte * first, Byte * last)
2691{
2692 int fd, cnt, charcnt;
2693
2694 if (fn == 0) {
2695 psbs("No current filename");
2696 return (-1);
2697 }
2698 charcnt = 0;
2699 // FIXIT- use the correct umask()
2700 fd = open((char *) fn, (O_WRONLY | O_CREAT | O_TRUNC), 0664);
2701 if (fd < 0)
2702 return (-1);
2703 cnt = last - first + 1;
2704 charcnt = write(fd, first, cnt);
2705 if (charcnt == cnt) {
2706 // good write
2707 //file_modified= FALSE; // the file has not been modified
2708 } else {
2709 charcnt = 0;
2710 }
2711 close(fd);
2712 return (charcnt);
2713}
2714
2715//----- Terminal Drawing ---------------------------------------
2716// The terminal is made up of 'rows' line of 'columns' columns.
2717// classicly this would be 24 x 80.
2718// screen coordinates
2719// 0,0 ... 0,79
2720// 1,0 ... 1,79
2721// . ... .
2722// . ... .
2723// 22,0 ... 22,79
2724// 23,0 ... 23,79 status line
2725//
2726
2727//----- Move the cursor to row x col (count from 0, not 1) -------
2728static void place_cursor(int row, int col, int opti)
2729{
2730 char cm1[BUFSIZ];
2731 char *cm;
2732 int l;
2733#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2734 char cm2[BUFSIZ];
2735 Byte *screenp;
2736 // char cm3[BUFSIZ];
2737 int Rrow= last_row;
2738#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2739
2740 memset(cm1, '\0', BUFSIZ - 1); // clear the buffer
2741
2742 if (row < 0) row = 0;
2743 if (row >= rows) row = rows - 1;
2744 if (col < 0) col = 0;
2745 if (col >= columns) col = columns - 1;
2746
2747 //----- 1. Try the standard terminal ESC sequence
2748 sprintf((char *) cm1, CMrc, row + 1, col + 1);
2749 cm= cm1;
2750 if (! opti) goto pc0;
2751
2752#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2753 //----- find the minimum # of chars to move cursor -------------
2754 //----- 2. Try moving with discreet chars (Newline, [back]space, ...)
2755 memset(cm2, '\0', BUFSIZ - 1); // clear the buffer
2756
2757 // move to the correct row
2758 while (row < Rrow) {
2759 // the cursor has to move up
2760 strcat(cm2, CMup);
2761 Rrow--;
2762 }
2763 while (row > Rrow) {
2764 // the cursor has to move down
2765 strcat(cm2, CMdown);
2766 Rrow++;
2767 }
2768
2769 // now move to the correct column
2770 strcat(cm2, "\r"); // start at col 0
2771 // just send out orignal source char to get to correct place
2772 screenp = &screen[row * columns]; // start of screen line
2773 strncat(cm2, screenp, col);
2774
2775 //----- 3. Try some other way of moving cursor
2776 //---------------------------------------------
2777
2778 // pick the shortest cursor motion to send out
2779 cm= cm1;
2780 if (strlen(cm2) < strlen(cm)) {
2781 cm= cm2;
2782 } /* else if (strlen(cm3) < strlen(cm)) {
2783 cm= cm3;
2784 } */
2785#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2786 pc0:
2787 l= strlen(cm);
2788 if (l) write(1, cm, l); // move the cursor
2789}
2790
2791//----- Erase from cursor to end of line -----------------------
2792static void clear_to_eol()
2793{
2794 write(1, Ceol, strlen(Ceol)); // Erase from cursor to end of line
2795}
2796
2797//----- Erase from cursor to end of screen -----------------------
2798static void clear_to_eos()
2799{
2800 write(1, Ceos, strlen(Ceos)); // Erase from cursor to end of screen
2801}
2802
2803//----- Start standout mode ------------------------------------
2804static void standout_start() // send "start reverse video" sequence
2805{
2806 write(1, SOs, strlen(SOs)); // Start reverse video mode
2807}
2808
2809//----- End standout mode --------------------------------------
2810static void standout_end() // send "end reverse video" sequence
2811{
2812 write(1, SOn, strlen(SOn)); // End reverse video mode
2813}
2814
2815//----- Flash the screen --------------------------------------
2816static void flash(int h)
2817{
2818 standout_start(); // send "start reverse video" sequence
2819 redraw(TRUE);
2820 (void) mysleep(h);
2821 standout_end(); // send "end reverse video" sequence
2822 redraw(TRUE);
2823}
2824
2825static void beep()
2826{
2827 write(1, bell, strlen(bell)); // send out a bell character
2828}
2829
2830static void indicate_error(char c)
2831{
2832#ifdef CONFIG_FEATURE_VI_CRASHME
2833 if (crashme > 0)
2834 return; // generate a random command
2835#endif /* CONFIG_FEATURE_VI_CRASHME */
2836 if (err_method == 0) {
2837 beep();
2838 } else {
2839 flash(10);
2840 }
2841}
2842
2843//----- Screen[] Routines --------------------------------------
2844//----- Erase the Screen[] memory ------------------------------
2845static void screen_erase()
2846{
2847 memset(screen, ' ', screensize); // clear new screen
2848}
2849
2850//----- Draw the status line at bottom of the screen -------------
2851static void show_status_line(void)
2852{
2853 static int last_cksum;
2854 int l, cnt, cksum;
2855
2856 cnt = strlen((char *) status_buffer);
2857 for (cksum= l= 0; l < cnt; l++) { cksum += (int)(status_buffer[l]); }
2858 // don't write the status line unless it changes
2859 if (cnt > 0 && last_cksum != cksum) {
2860 last_cksum= cksum; // remember if we have seen this line
2861 place_cursor(rows - 1, 0, FALSE); // put cursor on status line
2862 write(1, status_buffer, cnt);
2863 clear_to_eol();
2864 place_cursor(crow, ccol, FALSE); // put cursor back in correct place
2865 }
2866}
2867
2868//----- format the status buffer, the bottom line of screen ------
2869// print status buffer, with STANDOUT mode
2870static void psbs(char *format, ...)
2871{
2872 va_list args;
2873
2874 va_start(args, format);
2875 strcpy((char *) status_buffer, SOs); // Terminal standout mode on
2876 vsprintf((char *) status_buffer + strlen((char *) status_buffer), format,
2877 args);
2878 strcat((char *) status_buffer, SOn); // Terminal standout mode off
2879 va_end(args);
2880
2881 return;
2882}
2883
2884// print status buffer
2885static void psb(char *format, ...)
2886{
2887 va_list args;
2888
2889 va_start(args, format);
2890 vsprintf((char *) status_buffer, format, args);
2891 va_end(args);
2892 return;
2893}
2894
2895static void ni(Byte * s) // display messages
2896{
2897 Byte buf[BUFSIZ];
2898
2899 print_literal(buf, s);
2900 psbs("\'%s\' is not implemented", buf);
2901}
2902
2903static void edit_status(void) // show file status on status line
2904{
2905 int cur, tot, percent;
2906
2907 cur = count_lines(text, dot);
2908 tot = count_lines(text, end - 1);
2909 // current line percent
2910 // ------------- ~~ ----------
2911 // total lines 100
2912 if (tot > 0) {
2913 percent = (100 * cur) / tot;
2914 } else {
2915 cur = tot = 0;
2916 percent = 100;
2917 }
2918 psb("\"%s\""
2919#ifdef CONFIG_FEATURE_VI_READONLY
2920 "%s"
2921#endif /* CONFIG_FEATURE_VI_READONLY */
2922 "%s line %d of %d --%d%%--",
2923 (cfn != 0 ? (char *) cfn : "No file"),
2924#ifdef CONFIG_FEATURE_VI_READONLY
2925 ((vi_readonly || readonly) ? " [Read only]" : ""),
2926#endif /* CONFIG_FEATURE_VI_READONLY */
2927 (file_modified ? " [modified]" : ""),
2928 cur, tot, percent);
2929}
2930
2931//----- Force refresh of all Lines -----------------------------
2932static void redraw(int full_screen)
2933{
2934 place_cursor(0, 0, FALSE); // put cursor in correct place
2935 clear_to_eos(); // tel terminal to erase display
2936 screen_erase(); // erase the internal screen buffer
2937 refresh(full_screen); // this will redraw the entire display
2938}
2939
2940//----- Format a text[] line into a buffer ---------------------
2941static void format_line(Byte *dest, Byte *src, int li)
2942{
2943 int co;
2944 Byte c;
2945
2946 for (co= 0; co < MAX_SCR_COLS; co++) {
2947 c= ' '; // assume blank
2948 if (li > 0 && co == 0) {
2949 c = '~'; // not first line, assume Tilde
2950 }
2951 // are there chars in text[] and have we gone past the end
2952 if (text < end && src < end) {
2953 c = *src++;
2954 }
2955 if (c == '\n')
2956 break;
2957 if (c < ' ' || c > '~') {
2958 if (c == '\t') {
2959 c = ' ';
2960 // co % 8 != 7
2961 for (; (co % tabstop) != (tabstop - 1); co++) {
2962 dest[co] = c;
2963 }
2964 } else {
2965 dest[co++] = '^';
2966 c |= '@'; // make it visible
2967 c &= 0x7f; // get rid of hi bit
2968 }
2969 }
2970 // the co++ is done here so that the column will
2971 // not be overwritten when we blank-out the rest of line
2972 dest[co] = c;
2973 if (src >= end)
2974 break;
2975 }
2976}
2977
2978//----- Refresh the changed screen lines -----------------------
2979// Copy the source line from text[] into the buffer and note
2980// if the current screenline is different from the new buffer.
2981// If they differ then that line needs redrawing on the terminal.
2982//
2983static void refresh(int full_screen)
2984{
2985 static int old_offset;
2986 int li, changed;
2987 Byte buf[MAX_SCR_COLS];
2988 Byte *tp, *sp; // pointer into text[] and screen[]
2989#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2990 int last_li= -2; // last line that changed- for optimizing cursor movement
2991#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2992
2993#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
2994 window_size_get(0);
2995#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
2996 sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
2997 tp = screenbegin; // index into text[] of top line
2998
2999 // compare text[] to screen[] and mark screen[] lines that need updating
3000 for (li = 0; li < rows - 1; li++) {
3001 int cs, ce; // column start & end
3002 memset(buf, ' ', MAX_SCR_COLS); // blank-out the buffer
3003 buf[MAX_SCR_COLS-1] = 0; // NULL terminate the buffer
3004 // format current text line into buf
3005 format_line(buf, tp, li);
3006
3007 // skip to the end of the current text[] line
3008 while (tp < end && *tp++ != '\n') /*no-op*/ ;
3009
3010 // see if there are any changes between vitual screen and buf
3011 changed = FALSE; // assume no change
3012 cs= 0;
3013 ce= columns-1;
3014 sp = &screen[li * columns]; // start of screen line
3015 if (full_screen) {
3016 // force re-draw of every single column from 0 - columns-1
3017 goto re0;
3018 }
3019 // compare newly formatted buffer with virtual screen
3020 // look forward for first difference between buf and screen
3021 for ( ; cs <= ce; cs++) {
3022 if (buf[cs + offset] != sp[cs]) {
3023 changed = TRUE; // mark for redraw
3024 break;
3025 }
3026 }
3027
3028 // look backward for last difference between buf and screen
3029 for ( ; ce >= cs; ce--) {
3030 if (buf[ce + offset] != sp[ce]) {
3031 changed = TRUE; // mark for redraw
3032 break;
3033 }
3034 }
3035 // now, cs is index of first diff, and ce is index of last diff
3036
3037 // if horz offset has changed, force a redraw
3038 if (offset != old_offset) {
3039 re0:
3040 changed = TRUE;
3041 }
3042
3043 // make a sanity check of columns indexes
3044 if (cs < 0) cs= 0;
3045 if (ce > columns-1) ce= columns-1;
3046 if (cs > ce) { cs= 0; ce= columns-1; }
3047 // is there a change between vitual screen and buf
3048 if (changed) {
3049 // copy changed part of buffer to virtual screen
3050 memmove(sp+cs, buf+(cs+offset), ce-cs+1);
3051
3052 // move cursor to column of first change
3053 if (offset != old_offset) {
3054 // opti_cur_move is still too stupid
3055 // to handle offsets correctly
3056 place_cursor(li, cs, FALSE);
3057 } else {
3058#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
3059 // if this just the next line
3060 // try to optimize cursor movement
3061 // otherwise, use standard ESC sequence
3062 place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE);
3063 last_li= li;
3064#else /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
3065 place_cursor(li, cs, FALSE); // use standard ESC sequence
3066#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
3067 }
3068
3069 // write line out to terminal
3070 write(1, sp+cs, ce-cs+1);
3071#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
3072 last_row = li;
3073#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
3074 }
3075 }
3076
3077#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
3078 place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE);
3079 last_row = crow;
3080#else
3081 place_cursor(crow, ccol, FALSE);
3082#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
3083
3084 if (offset != old_offset)
3085 old_offset = offset;
3086}
3087
Eric Andersen3f980402001-04-04 17:31:15 +00003088//---------------------------------------------------------------------
3089//----- the Ascii Chart -----------------------------------------------
3090//
3091// 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel
3092// 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si
3093// 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb
3094// 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us
3095// 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 '
3096// 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f /
3097// 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7
3098// 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ?
3099// 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G
3100// 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O
3101// 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W
3102// 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _
3103// 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g
3104// 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o
3105// 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w
3106// 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del
3107//---------------------------------------------------------------------
3108
3109//----- Execute a Vi Command -----------------------------------
3110static void do_cmd(Byte c)
3111{
3112 Byte c1, *p, *q, *msg, buf[9], *save_dot;
3113 int cnt, i, j, dir, yf;
3114
3115 c1 = c; // quiet the compiler
3116 cnt = yf = dir = 0; // quiet the compiler
3117 p = q = save_dot = msg = buf; // quiet the compiler
3118 memset(buf, '\0', 9); // clear buf
Eric Andersenbff7a602001-11-17 07:15:43 +00003119
3120 /* if this is a cursor key, skip these checks */
3121 switch (c) {
3122 case VI_K_UP:
3123 case VI_K_DOWN:
3124 case VI_K_LEFT:
3125 case VI_K_RIGHT:
3126 case VI_K_HOME:
3127 case VI_K_END:
3128 case VI_K_PAGEUP:
3129 case VI_K_PAGEDOWN:
3130 goto key_cmd_mode;
3131 }
3132
Eric Andersen3f980402001-04-04 17:31:15 +00003133 if (cmd_mode == 2) {
3134 // we are 'R'eplacing the current *dot with new char
3135 if (*dot == '\n') {
3136 // don't Replace past E-o-l
3137 cmd_mode = 1; // convert to insert
3138 } else {
3139 if (1 <= c && c <= 127) { // only ASCII chars
3140 if (c != 27)
3141 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3142 dot = char_insert(dot, c); // insert new char
3143 }
3144 goto dc1;
3145 }
3146 }
3147 if (cmd_mode == 1) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00003148 // hitting "Insert" twice means "R" replace mode
3149 if (c == VI_K_INSERT) goto dc5;
Eric Andersen3f980402001-04-04 17:31:15 +00003150 // insert the char c at "dot"
3151 if (1 <= c && c <= 127) {
3152 dot = char_insert(dot, c); // only ASCII chars
3153 }
3154 goto dc1;
3155 }
3156
Eric Andersenbff7a602001-11-17 07:15:43 +00003157key_cmd_mode:
Eric Andersen3f980402001-04-04 17:31:15 +00003158 switch (c) {
Eric Andersen822c3832001-05-07 17:37:43 +00003159 //case 0x01: // soh
3160 //case 0x09: // ht
3161 //case 0x0b: // vt
3162 //case 0x0e: // so
3163 //case 0x0f: // si
3164 //case 0x10: // dle
3165 //case 0x11: // dc1
3166 //case 0x13: // dc3
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003167#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen1c0d3112001-04-16 15:46:44 +00003168 case 0x14: // dc4 ctrl-T
Eric Andersen3f980402001-04-04 17:31:15 +00003169 crashme = (crashme == 0) ? 1 : 0;
Eric Andersen3f980402001-04-04 17:31:15 +00003170 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003171#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen822c3832001-05-07 17:37:43 +00003172 //case 0x16: // syn
3173 //case 0x17: // etb
3174 //case 0x18: // can
3175 //case 0x1c: // fs
3176 //case 0x1d: // gs
3177 //case 0x1e: // rs
3178 //case 0x1f: // us
3179 //case '!': // !-
3180 //case '#': // #-
3181 //case '&': // &-
3182 //case '(': // (-
3183 //case ')': // )-
3184 //case '*': // *-
3185 //case ',': // ,-
3186 //case '=': // =-
3187 //case '@': // @-
3188 //case 'F': // F-
3189 //case 'K': // K-
3190 //case 'Q': // Q-
3191 //case 'S': // S-
3192 //case 'T': // T-
3193 //case 'V': // V-
3194 //case '[': // [-
3195 //case '\\': // \-
3196 //case ']': // ]-
3197 //case '_': // _-
3198 //case '`': // `-
3199 //case 'g': // g-
Eric Andersen1c0d3112001-04-16 15:46:44 +00003200 //case 'u': // u- FIXME- there is no undo
Eric Andersen822c3832001-05-07 17:37:43 +00003201 //case 'v': // v-
Eric Andersen3f980402001-04-04 17:31:15 +00003202 default: // unrecognised command
3203 buf[0] = c;
3204 buf[1] = '\0';
3205 if (c <= ' ') {
3206 buf[0] = '^';
3207 buf[1] = c + '@';
3208 buf[2] = '\0';
3209 }
3210 ni((Byte *) buf);
3211 end_cmd_q(); // stop adding to q
3212 case 0x00: // nul- ignore
3213 break;
3214 case 2: // ctrl-B scroll up full screen
3215 case VI_K_PAGEUP: // Cursor Key Page Up
3216 dot_scroll(rows - 2, -1);
3217 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003218#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +00003219 case 0x03: // ctrl-C interrupt
3220 longjmp(restart, 1);
3221 break;
3222 case 26: // ctrl-Z suspend
3223 suspend_sig(SIGTSTP);
3224 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003225#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Eric Andersen3f980402001-04-04 17:31:15 +00003226 case 4: // ctrl-D scroll down half screen
3227 dot_scroll((rows - 2) / 2, 1);
3228 break;
3229 case 5: // ctrl-E scroll down one line
3230 dot_scroll(1, 1);
3231 break;
3232 case 6: // ctrl-F scroll down full screen
3233 case VI_K_PAGEDOWN: // Cursor Key Page Down
3234 dot_scroll(rows - 2, 1);
3235 break;
3236 case 7: // ctrl-G show current status
3237 edit_status();
3238 break;
3239 case 'h': // h- move left
3240 case VI_K_LEFT: // cursor key Left
Eric Andersen1c0d3112001-04-16 15:46:44 +00003241 case 8: // ctrl-H- move left (This may be ERASE char)
Eric Andersen3f980402001-04-04 17:31:15 +00003242 case 127: // DEL- move left (This may be ERASE char)
3243 if (cmdcnt-- > 1) {
3244 do_cmd(c);
3245 } // repeat cnt
3246 dot_left();
3247 break;
3248 case 10: // Newline ^J
3249 case 'j': // j- goto next line, same col
3250 case VI_K_DOWN: // cursor key Down
3251 if (cmdcnt-- > 1) {
3252 do_cmd(c);
3253 } // repeat cnt
3254 dot_next(); // go to next B-o-l
3255 dot = move_to_col(dot, ccol + offset); // try stay in same col
3256 break;
3257 case 12: // ctrl-L force redraw whole screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003258 case 18: // ctrl-R force redraw
Eric Andersen822c3832001-05-07 17:37:43 +00003259 place_cursor(0, 0, FALSE); // put cursor in correct place
Eric Andersen3f980402001-04-04 17:31:15 +00003260 clear_to_eos(); // tel terminal to erase display
3261 (void) mysleep(10);
3262 screen_erase(); // erase the internal screen buffer
3263 refresh(TRUE); // this will redraw the entire display
3264 break;
3265 case 13: // Carriage Return ^M
3266 case '+': // +- goto next line
3267 if (cmdcnt-- > 1) {
3268 do_cmd(c);
3269 } // repeat cnt
3270 dot_next();
3271 dot_skip_over_ws();
3272 break;
3273 case 21: // ctrl-U scroll up half screen
3274 dot_scroll((rows - 2) / 2, -1);
3275 break;
3276 case 25: // ctrl-Y scroll up one line
3277 dot_scroll(1, -1);
3278 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003279 case 27: // esc
Eric Andersen3f980402001-04-04 17:31:15 +00003280 if (cmd_mode == 0)
3281 indicate_error(c);
3282 cmd_mode = 0; // stop insrting
3283 end_cmd_q();
3284 *status_buffer = '\0'; // clear status buffer
3285 break;
3286 case ' ': // move right
3287 case 'l': // move right
3288 case VI_K_RIGHT: // Cursor Key Right
3289 if (cmdcnt-- > 1) {
3290 do_cmd(c);
3291 } // repeat cnt
3292 dot_right();
3293 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003294#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003295 case '"': // "- name a register to use for Delete/Yank
3296 c1 = get_one_char();
3297 c1 = tolower(c1);
3298 if (islower(c1)) {
3299 YDreg = c1 - 'a';
3300 } else {
3301 indicate_error(c);
3302 }
3303 break;
3304 case '\'': // '- goto a specific mark
3305 c1 = get_one_char();
3306 c1 = tolower(c1);
3307 if (islower(c1)) {
3308 c1 = c1 - 'a';
3309 // get the b-o-l
3310 q = mark[(int) c1];
3311 if (text <= q && q < end) {
3312 dot = q;
3313 dot_begin(); // go to B-o-l
3314 dot_skip_over_ws();
3315 }
3316 } else if (c1 == '\'') { // goto previous context
3317 dot = swap_context(dot); // swap current and previous context
3318 dot_begin(); // go to B-o-l
3319 dot_skip_over_ws();
3320 } else {
3321 indicate_error(c);
3322 }
3323 break;
3324 case 'm': // m- Mark a line
3325 // this is really stupid. If there are any inserts or deletes
3326 // between text[0] and dot then this mark will not point to the
3327 // correct location! It could be off by many lines!
3328 // Well..., at least its quick and dirty.
3329 c1 = get_one_char();
3330 c1 = tolower(c1);
3331 if (islower(c1)) {
3332 c1 = c1 - 'a';
3333 // remember the line
3334 mark[(int) c1] = dot;
3335 } else {
3336 indicate_error(c);
3337 }
3338 break;
3339 case 'P': // P- Put register before
3340 case 'p': // p- put register after
3341 p = reg[YDreg];
3342 if (p == 0) {
3343 psbs("Nothing in register %c", what_reg());
3344 break;
3345 }
3346 // are we putting whole lines or strings
3347 if (strchr((char *) p, '\n') != NULL) {
3348 if (c == 'P') {
3349 dot_begin(); // putting lines- Put above
3350 }
3351 if (c == 'p') {
3352 // are we putting after very last line?
3353 if (end_line(dot) == (end - 1)) {
3354 dot = end; // force dot to end of text[]
3355 } else {
3356 dot_next(); // next line, then put before
3357 }
3358 }
3359 } else {
3360 if (c == 'p')
3361 dot_right(); // move to right, can move to NL
3362 }
3363 dot = string_insert(dot, p); // insert the string
3364 end_cmd_q(); // stop adding to q
3365 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003366 case 'U': // U- Undo; replace current line with original version
3367 if (reg[Ureg] != 0) {
3368 p = begin_line(dot);
3369 q = end_line(dot);
3370 p = text_hole_delete(p, q); // delete cur line
3371 p = string_insert(p, reg[Ureg]); // insert orig line
3372 dot = p;
3373 dot_skip_over_ws();
3374 }
3375 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003376#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003377 case '$': // $- goto end of line
3378 case VI_K_END: // Cursor Key End
3379 if (cmdcnt-- > 1) {
3380 do_cmd(c);
3381 } // repeat cnt
3382 dot = end_line(dot + 1);
3383 break;
3384 case '%': // %- find matching char of pair () [] {}
3385 for (q = dot; q < end && *q != '\n'; q++) {
3386 if (strchr("()[]{}", *q) != NULL) {
3387 // we found half of a pair
3388 p = find_pair(q, *q);
3389 if (p == NULL) {
3390 indicate_error(c);
3391 } else {
3392 dot = p;
3393 }
3394 break;
3395 }
3396 }
3397 if (*q == '\n')
3398 indicate_error(c);
3399 break;
3400 case 'f': // f- forward to a user specified char
3401 last_forward_char = get_one_char(); // get the search char
3402 //
3403 // dont seperate these two commands. 'f' depends on ';'
3404 //
3405 //**** fall thru to ... 'i'
3406 case ';': // ;- look at rest of line for last forward char
3407 if (cmdcnt-- > 1) {
Eric Andersen822c3832001-05-07 17:37:43 +00003408 do_cmd(';');
Eric Andersen3f980402001-04-04 17:31:15 +00003409 } // repeat cnt
Eric Andersen822c3832001-05-07 17:37:43 +00003410 if (last_forward_char == 0) break;
Eric Andersen3f980402001-04-04 17:31:15 +00003411 q = dot + 1;
3412 while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
3413 q++;
3414 }
3415 if (*q == last_forward_char)
3416 dot = q;
3417 break;
3418 case '-': // -- goto prev line
3419 if (cmdcnt-- > 1) {
3420 do_cmd(c);
3421 } // repeat cnt
3422 dot_prev();
3423 dot_skip_over_ws();
3424 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003425#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003426 case '.': // .- repeat the last modifying command
3427 // Stuff the last_modifying_cmd back into stdin
3428 // and let it be re-executed.
3429 if (last_modifying_cmd != 0) {
Matt Kraaic8227632001-11-12 16:57:27 +00003430 ioq = ioq_start = (Byte *) xstrdup((char *) last_modifying_cmd);
Eric Andersen3f980402001-04-04 17:31:15 +00003431 }
3432 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003433#endif /* CONFIG_FEATURE_VI_DOT_CMD */
3434#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +00003435 case '?': // /- search for a pattern
3436 case '/': // /- search for a pattern
3437 buf[0] = c;
3438 buf[1] = '\0';
3439 q = get_input_line(buf); // get input line- use "status line"
3440 if (strlen((char *) q) == 1)
3441 goto dc3; // if no pat re-use old pat
3442 if (strlen((char *) q) > 1) { // new pat- save it and find
3443 // there is a new pat
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00003444 free(last_search_pattern);
Matt Kraaic8227632001-11-12 16:57:27 +00003445 last_search_pattern = (Byte *) xstrdup((char *) q);
Eric Andersen3f980402001-04-04 17:31:15 +00003446 goto dc3; // now find the pattern
3447 }
3448 // user changed mind and erased the "/"- do nothing
3449 break;
3450 case 'N': // N- backward search for last pattern
3451 if (cmdcnt-- > 1) {
3452 do_cmd(c);
3453 } // repeat cnt
3454 dir = BACK; // assume BACKWARD search
3455 p = dot - 1;
3456 if (last_search_pattern[0] == '?') {
3457 dir = FORWARD;
3458 p = dot + 1;
3459 }
3460 goto dc4; // now search for pattern
3461 break;
3462 case 'n': // n- repeat search for last pattern
3463 // search rest of text[] starting at next char
3464 // if search fails return orignal "p" not the "p+1" address
3465 if (cmdcnt-- > 1) {
3466 do_cmd(c);
3467 } // repeat cnt
3468 dc3:
3469 if (last_search_pattern == 0) {
3470 msg = (Byte *) "No previous regular expression";
3471 goto dc2;
3472 }
3473 if (last_search_pattern[0] == '/') {
3474 dir = FORWARD; // assume FORWARD search
3475 p = dot + 1;
3476 }
3477 if (last_search_pattern[0] == '?') {
3478 dir = BACK;
3479 p = dot - 1;
3480 }
3481 dc4:
3482 q = char_search(p, last_search_pattern + 1, dir, FULL);
3483 if (q != NULL) {
3484 dot = q; // good search, update "dot"
3485 msg = (Byte *) "";
3486 goto dc2;
3487 }
3488 // no pattern found between "dot" and "end"- continue at top
3489 p = text;
3490 if (dir == BACK) {
3491 p = end - 1;
3492 }
3493 q = char_search(p, last_search_pattern + 1, dir, FULL);
3494 if (q != NULL) { // found something
3495 dot = q; // found new pattern- goto it
3496 msg = (Byte *) "search hit BOTTOM, continuing at TOP";
3497 if (dir == BACK) {
3498 msg = (Byte *) "search hit TOP, continuing at BOTTOM";
3499 }
3500 } else {
3501 msg = (Byte *) "Pattern not found";
3502 }
3503 dc2:
3504 psbs("%s", msg);
3505 break;
3506 case '{': // {- move backward paragraph
3507 q = char_search(dot, (Byte *) "\n\n", BACK, FULL);
3508 if (q != NULL) { // found blank line
3509 dot = next_line(q); // move to next blank line
3510 }
3511 break;
3512 case '}': // }- move forward paragraph
3513 q = char_search(dot, (Byte *) "\n\n", FORWARD, FULL);
3514 if (q != NULL) { // found blank line
3515 dot = next_line(q); // move to next blank line
3516 }
3517 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003518#endif /* CONFIG_FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +00003519 case '0': // 0- goto begining of line
3520 case '1': // 1-
3521 case '2': // 2-
3522 case '3': // 3-
3523 case '4': // 4-
3524 case '5': // 5-
3525 case '6': // 6-
3526 case '7': // 7-
3527 case '8': // 8-
3528 case '9': // 9-
3529 if (c == '0' && cmdcnt < 1) {
3530 dot_begin(); // this was a standalone zero
3531 } else {
3532 cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number
3533 }
3534 break;
3535 case ':': // :- the colon mode commands
Eric Andersen3f980402001-04-04 17:31:15 +00003536 p = get_input_line((Byte *) ":"); // get input line- use "status line"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003537#ifdef CONFIG_FEATURE_VI_COLON
Eric Andersen3f980402001-04-04 17:31:15 +00003538 colon(p); // execute the command
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003539#else /* CONFIG_FEATURE_VI_COLON */
Eric Andersen822c3832001-05-07 17:37:43 +00003540 if (*p == ':')
3541 p++; // move past the ':'
3542 cnt = strlen((char *) p);
3543 if (cnt <= 0)
3544 break;
3545 if (strncasecmp((char *) p, "quit", cnt) == 0 ||
3546 strncasecmp((char *) p, "q!", cnt) == 0) { // delete lines
Matt Kraai1f0c4362001-12-20 23:13:26 +00003547 if (file_modified && p[1] != '!') {
Eric Andersen3f980402001-04-04 17:31:15 +00003548 psbs("No write since last change (:quit! overrides)");
3549 } else {
3550 editing = 0;
3551 }
Eric Andersen822c3832001-05-07 17:37:43 +00003552 } else if (strncasecmp((char *) p, "write", cnt) == 0 ||
Robert Griebla71389b2002-07-31 21:22:21 +00003553 strncasecmp((char *) p, "wq", cnt) == 0 ||
3554 strncasecmp((char *) p, "x", cnt) == 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003555 cnt = file_write(cfn, text, end - 1);
3556 file_modified = FALSE;
3557 psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt);
Robert Griebla71389b2002-07-31 21:22:21 +00003558 if (p[0] == 'x' || p[1] == 'q') {
Eric Andersen3f980402001-04-04 17:31:15 +00003559 editing = 0;
3560 }
Eric Andersen822c3832001-05-07 17:37:43 +00003561 } else if (strncasecmp((char *) p, "file", cnt) == 0 ) {
3562 edit_status(); // show current file status
3563 } else if (sscanf((char *) p, "%d", &j) > 0) {
3564 dot = find_line(j); // go to line # j
3565 dot_skip_over_ws();
Eric Andersen3f980402001-04-04 17:31:15 +00003566 } else { // unrecognised cmd
Eric Andersen822c3832001-05-07 17:37:43 +00003567 ni((Byte *) p);
Eric Andersen3f980402001-04-04 17:31:15 +00003568 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003569#endif /* CONFIG_FEATURE_VI_COLON */
Eric Andersen3f980402001-04-04 17:31:15 +00003570 break;
3571 case '<': // <- Left shift something
3572 case '>': // >- Right shift something
3573 cnt = count_lines(text, dot); // remember what line we are on
3574 c1 = get_one_char(); // get the type of thing to delete
3575 find_range(&p, &q, c1);
3576 (void) yank_delete(p, q, 1, YANKONLY); // save copy before change
3577 p = begin_line(p);
3578 q = end_line(q);
3579 i = count_lines(p, q); // # of lines we are shifting
3580 for ( ; i > 0; i--, p = next_line(p)) {
3581 if (c == '<') {
3582 // shift left- remove tab or 8 spaces
3583 if (*p == '\t') {
3584 // shrink buffer 1 char
3585 (void) text_hole_delete(p, p);
3586 } else if (*p == ' ') {
3587 // we should be calculating columns, not just SPACE
3588 for (j = 0; *p == ' ' && j < tabstop; j++) {
3589 (void) text_hole_delete(p, p);
3590 }
3591 }
3592 } else if (c == '>') {
3593 // shift right -- add tab or 8 spaces
3594 (void) char_insert(p, '\t');
3595 }
3596 }
3597 dot = find_line(cnt); // what line were we on
3598 dot_skip_over_ws();
3599 end_cmd_q(); // stop adding to q
3600 break;
3601 case 'A': // A- append at e-o-l
3602 dot_end(); // go to e-o-l
3603 //**** fall thru to ... 'a'
3604 case 'a': // a- append after current char
3605 if (*dot != '\n')
3606 dot++;
3607 goto dc_i;
3608 break;
3609 case 'B': // B- back a blank-delimited Word
3610 case 'E': // E- end of a blank-delimited word
3611 case 'W': // W- forward a blank-delimited word
3612 if (cmdcnt-- > 1) {
3613 do_cmd(c);
3614 } // repeat cnt
3615 dir = FORWARD;
3616 if (c == 'B')
3617 dir = BACK;
3618 if (c == 'W' || isspace(dot[dir])) {
3619 dot = skip_thing(dot, 1, dir, S_TO_WS);
3620 dot = skip_thing(dot, 2, dir, S_OVER_WS);
3621 }
3622 if (c != 'W')
3623 dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
3624 break;
3625 case 'C': // C- Change to e-o-l
3626 case 'D': // D- delete to e-o-l
3627 save_dot = dot;
3628 dot = dollar_line(dot); // move to before NL
3629 // copy text into a register and delete
3630 dot = yank_delete(save_dot, dot, 0, YANKDEL); // delete to e-o-l
3631 if (c == 'C')
3632 goto dc_i; // start inserting
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003633#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003634 if (c == 'D')
3635 end_cmd_q(); // stop adding to q
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003636#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +00003637 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003638 case 'G': // G- goto to a line number (default= E-O-F)
3639 dot = end - 1; // assume E-O-F
Eric Andersen1c0d3112001-04-16 15:46:44 +00003640 if (cmdcnt > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00003641 dot = find_line(cmdcnt); // what line is #cmdcnt
Eric Andersen1c0d3112001-04-16 15:46:44 +00003642 }
3643 dot_skip_over_ws();
3644 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003645 case 'H': // H- goto top line on screen
3646 dot = screenbegin;
3647 if (cmdcnt > (rows - 1)) {
3648 cmdcnt = (rows - 1);
3649 }
3650 if (cmdcnt-- > 1) {
3651 do_cmd('+');
3652 } // repeat cnt
3653 dot_skip_over_ws();
3654 break;
3655 case 'I': // I- insert before first non-blank
3656 dot_begin(); // 0
3657 dot_skip_over_ws();
3658 //**** fall thru to ... 'i'
3659 case 'i': // i- insert before current char
3660 case VI_K_INSERT: // Cursor Key Insert
3661 dc_i:
3662 cmd_mode = 1; // start insrting
3663 psb("-- Insert --");
3664 break;
3665 case 'J': // J- join current and next lines together
3666 if (cmdcnt-- > 2) {
3667 do_cmd(c);
3668 } // repeat cnt
3669 dot_end(); // move to NL
3670 if (dot < end - 1) { // make sure not last char in text[]
3671 *dot++ = ' '; // replace NL with space
3672 while (isblnk(*dot)) { // delete leading WS
3673 dot_delete();
3674 }
3675 }
3676 end_cmd_q(); // stop adding to q
3677 break;
3678 case 'L': // L- goto bottom line on screen
3679 dot = end_screen();
3680 if (cmdcnt > (rows - 1)) {
3681 cmdcnt = (rows - 1);
3682 }
3683 if (cmdcnt-- > 1) {
3684 do_cmd('-');
3685 } // repeat cnt
3686 dot_begin();
3687 dot_skip_over_ws();
3688 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003689 case 'M': // M- goto middle line on screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003690 dot = screenbegin;
3691 for (cnt = 0; cnt < (rows-1) / 2; cnt++)
3692 dot = next_line(dot);
3693 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003694 case 'O': // O- open a empty line above
Eric Andersen822c3832001-05-07 17:37:43 +00003695 // 0i\n ESC -i
Eric Andersen3f980402001-04-04 17:31:15 +00003696 p = begin_line(dot);
3697 if (p[-1] == '\n') {
3698 dot_prev();
3699 case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
3700 dot_end();
3701 dot = char_insert(dot, '\n');
3702 } else {
3703 dot_begin(); // 0
Eric Andersen822c3832001-05-07 17:37:43 +00003704 dot = char_insert(dot, '\n'); // i\n ESC
Eric Andersen3f980402001-04-04 17:31:15 +00003705 dot_prev(); // -
3706 }
3707 goto dc_i;
3708 break;
3709 case 'R': // R- continuous Replace char
Eric Andersen1c0d3112001-04-16 15:46:44 +00003710 dc5:
Eric Andersen3f980402001-04-04 17:31:15 +00003711 cmd_mode = 2;
3712 psb("-- Replace --");
3713 break;
3714 case 'X': // X- delete char before dot
3715 case 'x': // x- delete the current char
3716 case 's': // s- substitute the current char
3717 if (cmdcnt-- > 1) {
3718 do_cmd(c);
3719 } // repeat cnt
3720 dir = 0;
3721 if (c == 'X')
3722 dir = -1;
3723 if (dot[dir] != '\n') {
3724 if (c == 'X')
3725 dot--; // delete prev char
3726 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3727 }
3728 if (c == 's')
3729 goto dc_i; // start insrting
3730 end_cmd_q(); // stop adding to q
3731 break;
3732 case 'Z': // Z- if modified, {write}; exit
3733 // ZZ means to save file (if necessary), then exit
3734 c1 = get_one_char();
3735 if (c1 != 'Z') {
3736 indicate_error(c);
3737 break;
3738 }
Matt Kraai1f0c4362001-12-20 23:13:26 +00003739 if (file_modified
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003740#ifdef CONFIG_FEATURE_VI_READONLY
Matt Kraai1f0c4362001-12-20 23:13:26 +00003741 && ! vi_readonly
3742 && ! readonly
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003743#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen3f980402001-04-04 17:31:15 +00003744 ) {
3745 cnt = file_write(cfn, text, end - 1);
3746 if (cnt == (end - 1 - text + 1)) {
3747 editing = 0;
3748 }
3749 } else {
3750 editing = 0;
3751 }
3752 break;
3753 case '^': // ^- move to first non-blank on line
3754 dot_begin();
3755 dot_skip_over_ws();
3756 break;
3757 case 'b': // b- back a word
3758 case 'e': // e- end of word
3759 if (cmdcnt-- > 1) {
3760 do_cmd(c);
3761 } // repeat cnt
3762 dir = FORWARD;
3763 if (c == 'b')
3764 dir = BACK;
3765 if ((dot + dir) < text || (dot + dir) > end - 1)
3766 break;
3767 dot += dir;
3768 if (isspace(*dot)) {
3769 dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
3770 }
3771 if (isalnum(*dot) || *dot == '_') {
3772 dot = skip_thing(dot, 1, dir, S_END_ALNUM);
3773 } else if (ispunct(*dot)) {
3774 dot = skip_thing(dot, 1, dir, S_END_PUNCT);
3775 }
3776 break;
3777 case 'c': // c- change something
3778 case 'd': // d- delete something
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003779#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003780 case 'y': // y- yank something
3781 case 'Y': // Y- Yank a line
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003782#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003783 yf = YANKDEL; // assume either "c" or "d"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003784#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003785 if (c == 'y' || c == 'Y')
3786 yf = YANKONLY;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003787#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003788 c1 = 'y';
3789 if (c != 'Y')
3790 c1 = get_one_char(); // get the type of thing to delete
3791 find_range(&p, &q, c1);
3792 if (c1 == 27) { // ESC- user changed mind and wants out
3793 c = c1 = 27; // Escape- do nothing
3794 } else if (strchr("wW", c1)) {
3795 if (c == 'c') {
3796 // don't include trailing WS as part of word
3797 while (isblnk(*q)) {
3798 if (q <= text || q[-1] == '\n')
3799 break;
3800 q--;
3801 }
3802 }
3803 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen822c3832001-05-07 17:37:43 +00003804 } else if (strchr("^0bBeEft$", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003805 // single line copy text into a register and delete
3806 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen1c0d3112001-04-16 15:46:44 +00003807 } else if (strchr("cdykjHL%+-{}\r\n", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003808 // multiple line copy text into a register and delete
3809 dot = yank_delete(p, q, 1, yf); // delete lines
Eric Andersen1c0d3112001-04-16 15:46:44 +00003810 if (c == 'c') {
3811 dot = char_insert(dot, '\n');
3812 // on the last line of file don't move to prev line
3813 if (dot != (end-1)) {
3814 dot_prev();
3815 }
3816 } else if (c == 'd') {
Eric Andersen3f980402001-04-04 17:31:15 +00003817 dot_begin();
3818 dot_skip_over_ws();
3819 }
3820 } else {
3821 // could not recognize object
3822 c = c1 = 27; // error-
3823 indicate_error(c);
3824 }
3825 if (c1 != 27) {
3826 // if CHANGING, not deleting, start inserting after the delete
3827 if (c == 'c') {
3828 strcpy((char *) buf, "Change");
3829 goto dc_i; // start inserting
3830 }
3831 if (c == 'd') {
3832 strcpy((char *) buf, "Delete");
3833 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003834#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003835 if (c == 'y' || c == 'Y') {
3836 strcpy((char *) buf, "Yank");
3837 }
3838 p = reg[YDreg];
3839 q = p + strlen((char *) p);
3840 for (cnt = 0; p <= q; p++) {
3841 if (*p == '\n')
3842 cnt++;
3843 }
3844 psb("%s %d lines (%d chars) using [%c]",
3845 buf, cnt, strlen((char *) reg[YDreg]), what_reg());
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003846#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003847 end_cmd_q(); // stop adding to q
3848 }
3849 break;
3850 case 'k': // k- goto prev line, same col
3851 case VI_K_UP: // cursor key Up
3852 if (cmdcnt-- > 1) {
3853 do_cmd(c);
3854 } // repeat cnt
3855 dot_prev();
3856 dot = move_to_col(dot, ccol + offset); // try stay in same col
3857 break;
3858 case 'r': // r- replace the current char with user input
3859 c1 = get_one_char(); // get the replacement char
3860 if (*dot != '\n') {
3861 *dot = c1;
3862 file_modified = TRUE; // has the file been modified
3863 }
3864 end_cmd_q(); // stop adding to q
3865 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003866 case 't': // t- move to char prior to next x
3867 last_forward_char = get_one_char();
3868 do_cmd(';');
3869 if (*dot == last_forward_char)
3870 dot_left();
3871 last_forward_char= 0;
3872 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003873 case 'w': // w- forward a word
3874 if (cmdcnt-- > 1) {
3875 do_cmd(c);
3876 } // repeat cnt
3877 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
3878 dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
3879 } else if (ispunct(*dot)) { // we are on PUNCT
3880 dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
3881 }
3882 if (dot < end - 1)
3883 dot++; // move over word
3884 if (isspace(*dot)) {
3885 dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
3886 }
3887 break;
3888 case 'z': // z-
3889 c1 = get_one_char(); // get the replacement char
3890 cnt = 0;
3891 if (c1 == '.')
3892 cnt = (rows - 2) / 2; // put dot at center
3893 if (c1 == '-')
3894 cnt = rows - 2; // put dot at bottom
3895 screenbegin = begin_line(dot); // start dot at top
3896 dot_scroll(cnt, -1);
3897 break;
3898 case '|': // |- move to column "cmdcnt"
3899 dot = move_to_col(dot, cmdcnt - 1); // try to move to column
3900 break;
3901 case '~': // ~- flip the case of letters a-z -> A-Z
3902 if (cmdcnt-- > 1) {
3903 do_cmd(c);
3904 } // repeat cnt
3905 if (islower(*dot)) {
3906 *dot = toupper(*dot);
3907 file_modified = TRUE; // has the file been modified
3908 } else if (isupper(*dot)) {
3909 *dot = tolower(*dot);
3910 file_modified = TRUE; // has the file been modified
3911 }
3912 dot_right();
3913 end_cmd_q(); // stop adding to q
3914 break;
3915 //----- The Cursor and Function Keys -----------------------------
3916 case VI_K_HOME: // Cursor Key Home
3917 dot_begin();
3918 break;
3919 // The Fn keys could point to do_macro which could translate them
3920 case VI_K_FUN1: // Function Key F1
3921 case VI_K_FUN2: // Function Key F2
3922 case VI_K_FUN3: // Function Key F3
3923 case VI_K_FUN4: // Function Key F4
3924 case VI_K_FUN5: // Function Key F5
3925 case VI_K_FUN6: // Function Key F6
3926 case VI_K_FUN7: // Function Key F7
3927 case VI_K_FUN8: // Function Key F8
3928 case VI_K_FUN9: // Function Key F9
3929 case VI_K_FUN10: // Function Key F10
3930 case VI_K_FUN11: // Function Key F11
3931 case VI_K_FUN12: // Function Key F12
3932 break;
3933 }
3934
3935 dc1:
3936 // if text[] just became empty, add back an empty line
3937 if (end == text) {
3938 (void) char_insert(text, '\n'); // start empty buf with dummy line
3939 dot = text;
3940 }
3941 // it is OK for dot to exactly equal to end, otherwise check dot validity
3942 if (dot != end) {
3943 dot = bound_dot(dot); // make sure "dot" is valid
3944 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003945#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003946 check_context(c); // update the current context
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003947#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003948
3949 if (!isdigit(c))
3950 cmdcnt = 0; // cmd was not a number, reset cmdcnt
3951 cnt = dot - begin_line(dot);
3952 // Try to stay off of the Newline
3953 if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
3954 dot--;
3955}