blob: a8f5203c872819931c885c4ccbcfea39b008ef28 [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[] =
Tim Riker86c76a92002-04-26 07:41:22 +000022 "$Id: vi.c,v 1.21 2002/04/26 07:41:22 timr 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
205static Byte *dollar_line(Byte *); // return pointer to just before NL
206static 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
235static void print_literal(Byte *, Byte *); // copy s to buf, convert unprintable
236static 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
299static 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
386 if (cfn != 0)
387 free(cfn);
Matt Kraaic8227632001-11-12 16:57:27 +0000388 cfn = (Byte *) xstrdup(argv[optind]);
Eric Andersen3f980402001-04-04 17:31:15 +0000389 edit_file(cfn);
390 }
391 }
392 //-----------------------------------------------------------
393
394 return (0);
395}
396
397static void edit_file(Byte * fn)
398{
399 char c;
Eric Andersen1c0d3112001-04-16 15:46:44 +0000400 int cnt, size, ch;
Eric Andersen3f980402001-04-04 17:31:15 +0000401
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000402#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000403 char *msg;
404 int sig;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000405#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
406#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000407 static Byte *cur_line;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000408#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +0000409
410 rawmode();
411 rows = 24;
412 columns = 80;
Eric Andersen822c3832001-05-07 17:37:43 +0000413 ch= -1;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000414#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
Eric Andersen3f980402001-04-04 17:31:15 +0000415 window_size_get(0);
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000416#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
Eric Andersen3f980402001-04-04 17:31:15 +0000417 new_screen(rows, columns); // get memory for virtual screen
418
419 cnt = file_size(fn); // file size
420 size = 2 * cnt; // 200% of file size
421 new_text(size); // get a text[] buffer
422 screenbegin = dot = end = text;
423 if (fn != 0) {
Eric Andersen1c0d3112001-04-16 15:46:44 +0000424 ch= file_insert(fn, text, cnt);
425 }
426 if (ch < 1) {
Eric Andersen3f980402001-04-04 17:31:15 +0000427 (void) char_insert(text, '\n'); // start empty buf with dummy line
428 }
429 file_modified = FALSE;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000430#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000431 YDreg = 26; // default Yank/Delete reg
432 Ureg = 27; // hold orig line for "U" cmd
433 for (cnt = 0; cnt < 28; cnt++) {
434 mark[cnt] = 0;
435 } // init the marks
436 mark[26] = mark[27] = text; // init "previous context"
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000437#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +0000438
439 err_method = 1; // flash
440 last_forward_char = last_input_char = '\0';
441 crow = 0;
442 ccol = 0;
443 edit_status();
444
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000445#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000446 signal(SIGHUP, catch_sig);
447 signal(SIGINT, catch_sig);
448 signal(SIGALRM, alarm_sig);
449 signal(SIGTERM, catch_sig);
450 signal(SIGQUIT, core_sig);
451 signal(SIGILL, core_sig);
452 signal(SIGTRAP, core_sig);
453 signal(SIGIOT, core_sig);
454 signal(SIGABRT, core_sig);
455 signal(SIGFPE, core_sig);
456 signal(SIGBUS, core_sig);
457 signal(SIGSEGV, core_sig);
Eric Andersend402edf2001-04-04 19:29:48 +0000458#ifdef SIGSYS
Eric Andersen3f980402001-04-04 17:31:15 +0000459 signal(SIGSYS, core_sig);
Eric Andersend402edf2001-04-04 19:29:48 +0000460#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000461 signal(SIGWINCH, winch_sig);
462 signal(SIGTSTP, suspend_sig);
463 sig = setjmp(restart);
464 if (sig != 0) {
465 msg = "";
466 if (sig == SIGWINCH)
467 msg = "(window resize)";
468 if (sig == SIGHUP)
469 msg = "(hangup)";
470 if (sig == SIGINT)
471 msg = "(interrupt)";
472 if (sig == SIGTERM)
473 msg = "(terminate)";
474 if (sig == SIGBUS)
475 msg = "(bus error)";
476 if (sig == SIGSEGV)
477 msg = "(I tried to touch invalid memory)";
478 if (sig == SIGALRM)
479 msg = "(alarm)";
480
481 psbs("-- caught signal %d %s--", sig, msg);
Eric Andersen1c0d3112001-04-16 15:46:44 +0000482 screenbegin = dot = text;
Eric Andersen3f980402001-04-04 17:31:15 +0000483 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000484#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Eric Andersen3f980402001-04-04 17:31:15 +0000485
486 editing = 1;
487 cmd_mode = 0; // 0=command 1=insert 2='R'eplace
488 cmdcnt = 0;
489 tabstop = 8;
490 offset = 0; // no horizontal offset
491 c = '\0';
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000492#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000493 if (last_modifying_cmd != 0)
494 free(last_modifying_cmd);
495 if (ioq_start != NULL)
496 free(ioq_start);
497 ioq = ioq_start = last_modifying_cmd = 0;
498 adding2q = 0;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000499#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen822c3832001-05-07 17:37:43 +0000500 redraw(FALSE); // dont force every col re-draw
Eric Andersen3f980402001-04-04 17:31:15 +0000501 show_status_line();
502
503 //------This is the main Vi cmd handling loop -----------------------
504 while (editing > 0) {
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000505#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000506 if (crashme > 0) {
507 if ((end - text) > 1) {
508 crash_dummy(); // generate a random command
509 } else {
510 crashme = 0;
511 dot =
512 string_insert(text, (Byte *) "\n\n##### Ran out of text to work on. #####\n\n"); // insert the string
513 refresh(FALSE);
514 }
515 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000516#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen3f980402001-04-04 17:31:15 +0000517 last_input_char = c = get_one_char(); // get a cmd from user
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000518#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000519 // save a copy of the current line- for the 'U" command
520 if (begin_line(dot) != cur_line) {
521 cur_line = begin_line(dot);
522 text_yank(begin_line(dot), end_line(dot), Ureg);
523 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000524#endif /* CONFIG_FEATURE_VI_YANKMARK */
525#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000526 // These are commands that change text[].
527 // Remember the input for the "." command
528 if (!adding2q && ioq_start == 0
529 && strchr((char *) modifying_cmds, c) != NULL) {
530 start_new_cmd_q(c);
531 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000532#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +0000533 do_cmd(c); // execute the user command
534 //
535 // poll to see if there is input already waiting. if we are
536 // not able to display output fast enough to keep up, skip
537 // the display update until we catch up with input.
538 if (mysleep(0) == 0) {
539 // no input pending- so update output
540 refresh(FALSE);
541 show_status_line();
542 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000543#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000544 if (crashme > 0)
545 crash_test(); // test editor variables
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000546#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen3f980402001-04-04 17:31:15 +0000547 }
548 //-------------------------------------------------------------------
549
Eric Andersen822c3832001-05-07 17:37:43 +0000550 place_cursor(rows, 0, FALSE); // go to bottom of screen
Eric Andersen3f980402001-04-04 17:31:15 +0000551 clear_to_eol(); // Erase to end of line
552 cookmode();
553}
554
555static Byte readbuffer[BUFSIZ];
556
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000557#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000558static int totalcmds = 0;
559static int Mp = 85; // Movement command Probability
560static int Np = 90; // Non-movement command Probability
561static int Dp = 96; // Delete command Probability
562static int Ip = 97; // Insert command Probability
563static int Yp = 98; // Yank command Probability
564static int Pp = 99; // Put command Probability
565static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
566char chars[20] = "\t012345 abcdABCD-=.$";
567char *words[20] = { "this", "is", "a", "test",
568 "broadcast", "the", "emergency", "of",
569 "system", "quick", "brown", "fox",
570 "jumped", "over", "lazy", "dogs",
571 "back", "January", "Febuary", "March"
572};
573char *lines[20] = {
574 "You should have received a copy of the GNU General Public License\n",
575 "char c, cm, *cmd, *cmd1;\n",
576 "generate a command by percentages\n",
577 "Numbers may be typed as a prefix to some commands.\n",
578 "Quit, discarding changes!\n",
579 "Forced write, if permission originally not valid.\n",
580 "In general, any ex or ed command (such as substitute or delete).\n",
581 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
582 "Please get w/ me and I will go over it with you.\n",
583 "The following is a list of scheduled, committed changes.\n",
584 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
585 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
586 "Any question about transactions please contact Sterling Huxley.\n",
587 "I will try to get back to you by Friday, December 31.\n",
588 "This Change will be implemented on Friday.\n",
589 "Let me know if you have problems accessing this;\n",
590 "Sterling Huxley recently added you to the access list.\n",
591 "Would you like to go to lunch?\n",
592 "The last command will be automatically run.\n",
593 "This is too much english for a computer geek.\n",
594};
595char *multilines[20] = {
596 "You should have received a copy of the GNU General Public License\n",
597 "char c, cm, *cmd, *cmd1;\n",
598 "generate a command by percentages\n",
599 "Numbers may be typed as a prefix to some commands.\n",
600 "Quit, discarding changes!\n",
601 "Forced write, if permission originally not valid.\n",
602 "In general, any ex or ed command (such as substitute or delete).\n",
603 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
604 "Please get w/ me and I will go over it with you.\n",
605 "The following is a list of scheduled, committed changes.\n",
606 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
607 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
608 "Any question about transactions please contact Sterling Huxley.\n",
609 "I will try to get back to you by Friday, December 31.\n",
610 "This Change will be implemented on Friday.\n",
611 "Let me know if you have problems accessing this;\n",
612 "Sterling Huxley recently added you to the access list.\n",
613 "Would you like to go to lunch?\n",
614 "The last command will be automatically run.\n",
615 "This is too much english for a computer geek.\n",
616};
617
618// create a random command to execute
619static void crash_dummy()
620{
621 static int sleeptime; // how long to pause between commands
622 char c, cm, *cmd, *cmd1;
623 int i, cnt, thing, rbi, startrbi, percent;
624
625 // "dot" movement commands
626 cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
627
628 // is there already a command running?
629 if (strlen((char *) readbuffer) > 0)
630 goto cd1;
631 cd0:
632 startrbi = rbi = 0;
633 sleeptime = 0; // how long to pause between commands
634 memset(readbuffer, '\0', BUFSIZ - 1); // clear the read buffer
635 // generate a command by percentages
636 percent = (int) lrand48() % 100; // get a number from 0-99
637 if (percent < Mp) { // Movement commands
638 // available commands
639 cmd = cmd1;
640 M++;
641 } else if (percent < Np) { // non-movement commands
642 cmd = "mz<>\'\""; // available commands
643 N++;
644 } else if (percent < Dp) { // Delete commands
645 cmd = "dx"; // available commands
646 D++;
647 } else if (percent < Ip) { // Inset commands
648 cmd = "iIaAsrJ"; // available commands
649 I++;
650 } else if (percent < Yp) { // Yank commands
651 cmd = "yY"; // available commands
652 Y++;
653 } else if (percent < Pp) { // Put commands
654 cmd = "pP"; // available commands
655 P++;
656 } else {
657 // We do not know how to handle this command, try again
658 U++;
659 goto cd0;
660 }
661 // randomly pick one of the available cmds from "cmd[]"
662 i = (int) lrand48() % strlen(cmd);
663 cm = cmd[i];
664 if (strchr(":\024", cm))
Eric Andersen822c3832001-05-07 17:37:43 +0000665 goto cd0; // dont allow colon or ctrl-T commands
Eric Andersen3f980402001-04-04 17:31:15 +0000666 readbuffer[rbi++] = cm; // put cmd into input buffer
667
668 // now we have the command-
669 // there are 1, 2, and multi char commands
670 // find out which and generate the rest of command as necessary
671 if (strchr("dmryz<>\'\"", cm)) { // 2-char commands
672 cmd1 = " \n\r0$^-+wWeEbBhjklHL";
673 if (cm == 'm' || cm == '\'' || cm == '\"') { // pick a reg[]
674 cmd1 = "abcdefghijklmnopqrstuvwxyz";
675 }
676 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
677 c = cmd1[thing];
678 readbuffer[rbi++] = c; // add movement to input buffer
679 }
680 if (strchr("iIaAsc", cm)) { // multi-char commands
681 if (cm == 'c') {
682 // change some thing
683 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
684 c = cmd1[thing];
685 readbuffer[rbi++] = c; // add movement to input buffer
686 }
687 thing = (int) lrand48() % 4; // what thing to insert
688 cnt = (int) lrand48() % 10; // how many to insert
689 for (i = 0; i < cnt; i++) {
690 if (thing == 0) { // insert chars
691 readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
692 } else if (thing == 1) { // insert words
693 strcat((char *) readbuffer, words[(int) lrand48() % 20]);
694 strcat((char *) readbuffer, " ");
695 sleeptime = 0; // how fast to type
696 } else if (thing == 2) { // insert lines
697 strcat((char *) readbuffer, lines[(int) lrand48() % 20]);
698 sleeptime = 0; // how fast to type
699 } else { // insert multi-lines
700 strcat((char *) readbuffer, multilines[(int) lrand48() % 20]);
701 sleeptime = 0; // how fast to type
702 }
703 }
704 strcat((char *) readbuffer, "\033");
705 }
706 cd1:
707 totalcmds++;
708 if (sleeptime > 0)
709 (void) mysleep(sleeptime); // sleep 1/100 sec
710}
711
712// test to see if there are any errors
713static void crash_test()
714{
715 static time_t oldtim;
716 time_t tim;
Eric Andersen1c0d3112001-04-16 15:46:44 +0000717 char d[2], buf[BUFSIZ], msg[BUFSIZ];
Eric Andersen3f980402001-04-04 17:31:15 +0000718
719 msg[0] = '\0';
720 if (end < text) {
721 strcat((char *) msg, "end<text ");
722 }
723 if (end > textend) {
724 strcat((char *) msg, "end>textend ");
725 }
726 if (dot < text) {
727 strcat((char *) msg, "dot<text ");
728 }
729 if (dot > end) {
730 strcat((char *) msg, "dot>end ");
731 }
732 if (screenbegin < text) {
733 strcat((char *) msg, "screenbegin<text ");
734 }
735 if (screenbegin > end - 1) {
736 strcat((char *) msg, "screenbegin>end-1 ");
737 }
738
739 if (strlen(msg) > 0) {
740 alarm(0);
Eric Andersen822c3832001-05-07 17:37:43 +0000741 sprintf(buf, "\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
742 totalcmds, last_input_char, msg, SOs, SOn);
Eric Andersen3f980402001-04-04 17:31:15 +0000743 write(1, buf, strlen(buf));
Eric Andersen3f980402001-04-04 17:31:15 +0000744 while (read(0, d, 1) > 0) {
745 if (d[0] == '\n' || d[0] == '\r')
746 break;
747 }
748 alarm(3);
749 }
750 tim = (time_t) time((time_t *) 0);
751 if (tim >= (oldtim + 3)) {
752 sprintf((char *) status_buffer,
753 "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
754 totalcmds, M, N, I, D, Y, P, U, end - text + 1);
755 oldtim = tim;
756 }
757 return;
758}
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000759#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen3f980402001-04-04 17:31:15 +0000760
761//---------------------------------------------------------------------
762//----- the Ascii Chart -----------------------------------------------
763//
764// 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel
765// 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si
766// 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb
767// 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us
768// 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 '
769// 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f /
770// 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7
771// 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ?
772// 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G
773// 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O
774// 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W
775// 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _
776// 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g
777// 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o
778// 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w
779// 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del
780//---------------------------------------------------------------------
781
782//----- Execute a Vi Command -----------------------------------
783static void do_cmd(Byte c)
784{
785 Byte c1, *p, *q, *msg, buf[9], *save_dot;
786 int cnt, i, j, dir, yf;
787
788 c1 = c; // quiet the compiler
789 cnt = yf = dir = 0; // quiet the compiler
790 p = q = save_dot = msg = buf; // quiet the compiler
791 memset(buf, '\0', 9); // clear buf
Eric Andersenbff7a602001-11-17 07:15:43 +0000792
793 /* if this is a cursor key, skip these checks */
794 switch (c) {
795 case VI_K_UP:
796 case VI_K_DOWN:
797 case VI_K_LEFT:
798 case VI_K_RIGHT:
799 case VI_K_HOME:
800 case VI_K_END:
801 case VI_K_PAGEUP:
802 case VI_K_PAGEDOWN:
803 goto key_cmd_mode;
804 }
805
Eric Andersen3f980402001-04-04 17:31:15 +0000806 if (cmd_mode == 2) {
807 // we are 'R'eplacing the current *dot with new char
808 if (*dot == '\n') {
809 // don't Replace past E-o-l
810 cmd_mode = 1; // convert to insert
811 } else {
812 if (1 <= c && c <= 127) { // only ASCII chars
813 if (c != 27)
814 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
815 dot = char_insert(dot, c); // insert new char
816 }
817 goto dc1;
818 }
819 }
820 if (cmd_mode == 1) {
Eric Andersen1c0d3112001-04-16 15:46:44 +0000821 // hitting "Insert" twice means "R" replace mode
822 if (c == VI_K_INSERT) goto dc5;
Eric Andersen3f980402001-04-04 17:31:15 +0000823 // insert the char c at "dot"
824 if (1 <= c && c <= 127) {
825 dot = char_insert(dot, c); // only ASCII chars
826 }
827 goto dc1;
828 }
829
Eric Andersenbff7a602001-11-17 07:15:43 +0000830key_cmd_mode:
Eric Andersen3f980402001-04-04 17:31:15 +0000831 switch (c) {
Eric Andersen822c3832001-05-07 17:37:43 +0000832 //case 0x01: // soh
833 //case 0x09: // ht
834 //case 0x0b: // vt
835 //case 0x0e: // so
836 //case 0x0f: // si
837 //case 0x10: // dle
838 //case 0x11: // dc1
839 //case 0x13: // dc3
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000840#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen1c0d3112001-04-16 15:46:44 +0000841 case 0x14: // dc4 ctrl-T
Eric Andersen3f980402001-04-04 17:31:15 +0000842 crashme = (crashme == 0) ? 1 : 0;
Eric Andersen3f980402001-04-04 17:31:15 +0000843 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000844#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen822c3832001-05-07 17:37:43 +0000845 //case 0x16: // syn
846 //case 0x17: // etb
847 //case 0x18: // can
848 //case 0x1c: // fs
849 //case 0x1d: // gs
850 //case 0x1e: // rs
851 //case 0x1f: // us
852 //case '!': // !-
853 //case '#': // #-
854 //case '&': // &-
855 //case '(': // (-
856 //case ')': // )-
857 //case '*': // *-
858 //case ',': // ,-
859 //case '=': // =-
860 //case '@': // @-
861 //case 'F': // F-
862 //case 'K': // K-
863 //case 'Q': // Q-
864 //case 'S': // S-
865 //case 'T': // T-
866 //case 'V': // V-
867 //case '[': // [-
868 //case '\\': // \-
869 //case ']': // ]-
870 //case '_': // _-
871 //case '`': // `-
872 //case 'g': // g-
Eric Andersen1c0d3112001-04-16 15:46:44 +0000873 //case 'u': // u- FIXME- there is no undo
Eric Andersen822c3832001-05-07 17:37:43 +0000874 //case 'v': // v-
Eric Andersen3f980402001-04-04 17:31:15 +0000875 default: // unrecognised command
876 buf[0] = c;
877 buf[1] = '\0';
878 if (c <= ' ') {
879 buf[0] = '^';
880 buf[1] = c + '@';
881 buf[2] = '\0';
882 }
883 ni((Byte *) buf);
884 end_cmd_q(); // stop adding to q
885 case 0x00: // nul- ignore
886 break;
887 case 2: // ctrl-B scroll up full screen
888 case VI_K_PAGEUP: // Cursor Key Page Up
889 dot_scroll(rows - 2, -1);
890 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000891#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000892 case 0x03: // ctrl-C interrupt
893 longjmp(restart, 1);
894 break;
895 case 26: // ctrl-Z suspend
896 suspend_sig(SIGTSTP);
897 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000898#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Eric Andersen3f980402001-04-04 17:31:15 +0000899 case 4: // ctrl-D scroll down half screen
900 dot_scroll((rows - 2) / 2, 1);
901 break;
902 case 5: // ctrl-E scroll down one line
903 dot_scroll(1, 1);
904 break;
905 case 6: // ctrl-F scroll down full screen
906 case VI_K_PAGEDOWN: // Cursor Key Page Down
907 dot_scroll(rows - 2, 1);
908 break;
909 case 7: // ctrl-G show current status
910 edit_status();
911 break;
912 case 'h': // h- move left
913 case VI_K_LEFT: // cursor key Left
Eric Andersen1c0d3112001-04-16 15:46:44 +0000914 case 8: // ctrl-H- move left (This may be ERASE char)
Eric Andersen3f980402001-04-04 17:31:15 +0000915 case 127: // DEL- move left (This may be ERASE char)
916 if (cmdcnt-- > 1) {
917 do_cmd(c);
918 } // repeat cnt
919 dot_left();
920 break;
921 case 10: // Newline ^J
922 case 'j': // j- goto next line, same col
923 case VI_K_DOWN: // cursor key Down
924 if (cmdcnt-- > 1) {
925 do_cmd(c);
926 } // repeat cnt
927 dot_next(); // go to next B-o-l
928 dot = move_to_col(dot, ccol + offset); // try stay in same col
929 break;
930 case 12: // ctrl-L force redraw whole screen
Eric Andersen1c0d3112001-04-16 15:46:44 +0000931 case 18: // ctrl-R force redraw
Eric Andersen822c3832001-05-07 17:37:43 +0000932 place_cursor(0, 0, FALSE); // put cursor in correct place
Eric Andersen3f980402001-04-04 17:31:15 +0000933 clear_to_eos(); // tel terminal to erase display
934 (void) mysleep(10);
935 screen_erase(); // erase the internal screen buffer
936 refresh(TRUE); // this will redraw the entire display
937 break;
938 case 13: // Carriage Return ^M
939 case '+': // +- goto next line
940 if (cmdcnt-- > 1) {
941 do_cmd(c);
942 } // repeat cnt
943 dot_next();
944 dot_skip_over_ws();
945 break;
946 case 21: // ctrl-U scroll up half screen
947 dot_scroll((rows - 2) / 2, -1);
948 break;
949 case 25: // ctrl-Y scroll up one line
950 dot_scroll(1, -1);
951 break;
Eric Andersen822c3832001-05-07 17:37:43 +0000952 case 27: // esc
Eric Andersen3f980402001-04-04 17:31:15 +0000953 if (cmd_mode == 0)
954 indicate_error(c);
955 cmd_mode = 0; // stop insrting
956 end_cmd_q();
957 *status_buffer = '\0'; // clear status buffer
958 break;
959 case ' ': // move right
960 case 'l': // move right
961 case VI_K_RIGHT: // Cursor Key Right
962 if (cmdcnt-- > 1) {
963 do_cmd(c);
964 } // repeat cnt
965 dot_right();
966 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000967#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000968 case '"': // "- name a register to use for Delete/Yank
969 c1 = get_one_char();
970 c1 = tolower(c1);
971 if (islower(c1)) {
972 YDreg = c1 - 'a';
973 } else {
974 indicate_error(c);
975 }
976 break;
977 case '\'': // '- goto a specific mark
978 c1 = get_one_char();
979 c1 = tolower(c1);
980 if (islower(c1)) {
981 c1 = c1 - 'a';
982 // get the b-o-l
983 q = mark[(int) c1];
984 if (text <= q && q < end) {
985 dot = q;
986 dot_begin(); // go to B-o-l
987 dot_skip_over_ws();
988 }
989 } else if (c1 == '\'') { // goto previous context
990 dot = swap_context(dot); // swap current and previous context
991 dot_begin(); // go to B-o-l
992 dot_skip_over_ws();
993 } else {
994 indicate_error(c);
995 }
996 break;
997 case 'm': // m- Mark a line
998 // this is really stupid. If there are any inserts or deletes
999 // between text[0] and dot then this mark will not point to the
1000 // correct location! It could be off by many lines!
1001 // Well..., at least its quick and dirty.
1002 c1 = get_one_char();
1003 c1 = tolower(c1);
1004 if (islower(c1)) {
1005 c1 = c1 - 'a';
1006 // remember the line
1007 mark[(int) c1] = dot;
1008 } else {
1009 indicate_error(c);
1010 }
1011 break;
1012 case 'P': // P- Put register before
1013 case 'p': // p- put register after
1014 p = reg[YDreg];
1015 if (p == 0) {
1016 psbs("Nothing in register %c", what_reg());
1017 break;
1018 }
1019 // are we putting whole lines or strings
1020 if (strchr((char *) p, '\n') != NULL) {
1021 if (c == 'P') {
1022 dot_begin(); // putting lines- Put above
1023 }
1024 if (c == 'p') {
1025 // are we putting after very last line?
1026 if (end_line(dot) == (end - 1)) {
1027 dot = end; // force dot to end of text[]
1028 } else {
1029 dot_next(); // next line, then put before
1030 }
1031 }
1032 } else {
1033 if (c == 'p')
1034 dot_right(); // move to right, can move to NL
1035 }
1036 dot = string_insert(dot, p); // insert the string
1037 end_cmd_q(); // stop adding to q
1038 break;
Eric Andersen3f980402001-04-04 17:31:15 +00001039 case 'U': // U- Undo; replace current line with original version
1040 if (reg[Ureg] != 0) {
1041 p = begin_line(dot);
1042 q = end_line(dot);
1043 p = text_hole_delete(p, q); // delete cur line
1044 p = string_insert(p, reg[Ureg]); // insert orig line
1045 dot = p;
1046 dot_skip_over_ws();
1047 }
1048 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001049#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00001050 case '$': // $- goto end of line
1051 case VI_K_END: // Cursor Key End
1052 if (cmdcnt-- > 1) {
1053 do_cmd(c);
1054 } // repeat cnt
1055 dot = end_line(dot + 1);
1056 break;
1057 case '%': // %- find matching char of pair () [] {}
1058 for (q = dot; q < end && *q != '\n'; q++) {
1059 if (strchr("()[]{}", *q) != NULL) {
1060 // we found half of a pair
1061 p = find_pair(q, *q);
1062 if (p == NULL) {
1063 indicate_error(c);
1064 } else {
1065 dot = p;
1066 }
1067 break;
1068 }
1069 }
1070 if (*q == '\n')
1071 indicate_error(c);
1072 break;
1073 case 'f': // f- forward to a user specified char
1074 last_forward_char = get_one_char(); // get the search char
1075 //
1076 // dont seperate these two commands. 'f' depends on ';'
1077 //
1078 //**** fall thru to ... 'i'
1079 case ';': // ;- look at rest of line for last forward char
1080 if (cmdcnt-- > 1) {
Eric Andersen822c3832001-05-07 17:37:43 +00001081 do_cmd(';');
Eric Andersen3f980402001-04-04 17:31:15 +00001082 } // repeat cnt
Eric Andersen822c3832001-05-07 17:37:43 +00001083 if (last_forward_char == 0) break;
Eric Andersen3f980402001-04-04 17:31:15 +00001084 q = dot + 1;
1085 while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
1086 q++;
1087 }
1088 if (*q == last_forward_char)
1089 dot = q;
1090 break;
1091 case '-': // -- goto prev line
1092 if (cmdcnt-- > 1) {
1093 do_cmd(c);
1094 } // repeat cnt
1095 dot_prev();
1096 dot_skip_over_ws();
1097 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001098#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00001099 case '.': // .- repeat the last modifying command
1100 // Stuff the last_modifying_cmd back into stdin
1101 // and let it be re-executed.
1102 if (last_modifying_cmd != 0) {
Matt Kraaic8227632001-11-12 16:57:27 +00001103 ioq = ioq_start = (Byte *) xstrdup((char *) last_modifying_cmd);
Eric Andersen3f980402001-04-04 17:31:15 +00001104 }
1105 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001106#endif /* CONFIG_FEATURE_VI_DOT_CMD */
1107#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +00001108 case '?': // /- search for a pattern
1109 case '/': // /- search for a pattern
1110 buf[0] = c;
1111 buf[1] = '\0';
1112 q = get_input_line(buf); // get input line- use "status line"
1113 if (strlen((char *) q) == 1)
1114 goto dc3; // if no pat re-use old pat
1115 if (strlen((char *) q) > 1) { // new pat- save it and find
1116 // there is a new pat
1117 if (last_search_pattern != 0) {
1118 free(last_search_pattern);
1119 }
Matt Kraaic8227632001-11-12 16:57:27 +00001120 last_search_pattern = (Byte *) xstrdup((char *) q);
Eric Andersen3f980402001-04-04 17:31:15 +00001121 goto dc3; // now find the pattern
1122 }
1123 // user changed mind and erased the "/"- do nothing
1124 break;
1125 case 'N': // N- backward search for last pattern
1126 if (cmdcnt-- > 1) {
1127 do_cmd(c);
1128 } // repeat cnt
1129 dir = BACK; // assume BACKWARD search
1130 p = dot - 1;
1131 if (last_search_pattern[0] == '?') {
1132 dir = FORWARD;
1133 p = dot + 1;
1134 }
1135 goto dc4; // now search for pattern
1136 break;
1137 case 'n': // n- repeat search for last pattern
1138 // search rest of text[] starting at next char
1139 // if search fails return orignal "p" not the "p+1" address
1140 if (cmdcnt-- > 1) {
1141 do_cmd(c);
1142 } // repeat cnt
1143 dc3:
1144 if (last_search_pattern == 0) {
1145 msg = (Byte *) "No previous regular expression";
1146 goto dc2;
1147 }
1148 if (last_search_pattern[0] == '/') {
1149 dir = FORWARD; // assume FORWARD search
1150 p = dot + 1;
1151 }
1152 if (last_search_pattern[0] == '?') {
1153 dir = BACK;
1154 p = dot - 1;
1155 }
1156 dc4:
1157 q = char_search(p, last_search_pattern + 1, dir, FULL);
1158 if (q != NULL) {
1159 dot = q; // good search, update "dot"
1160 msg = (Byte *) "";
1161 goto dc2;
1162 }
1163 // no pattern found between "dot" and "end"- continue at top
1164 p = text;
1165 if (dir == BACK) {
1166 p = end - 1;
1167 }
1168 q = char_search(p, last_search_pattern + 1, dir, FULL);
1169 if (q != NULL) { // found something
1170 dot = q; // found new pattern- goto it
1171 msg = (Byte *) "search hit BOTTOM, continuing at TOP";
1172 if (dir == BACK) {
1173 msg = (Byte *) "search hit TOP, continuing at BOTTOM";
1174 }
1175 } else {
1176 msg = (Byte *) "Pattern not found";
1177 }
1178 dc2:
1179 psbs("%s", msg);
1180 break;
1181 case '{': // {- move backward paragraph
1182 q = char_search(dot, (Byte *) "\n\n", BACK, FULL);
1183 if (q != NULL) { // found blank line
1184 dot = next_line(q); // move to next blank line
1185 }
1186 break;
1187 case '}': // }- move forward paragraph
1188 q = char_search(dot, (Byte *) "\n\n", FORWARD, FULL);
1189 if (q != NULL) { // found blank line
1190 dot = next_line(q); // move to next blank line
1191 }
1192 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001193#endif /* CONFIG_FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +00001194 case '0': // 0- goto begining of line
1195 case '1': // 1-
1196 case '2': // 2-
1197 case '3': // 3-
1198 case '4': // 4-
1199 case '5': // 5-
1200 case '6': // 6-
1201 case '7': // 7-
1202 case '8': // 8-
1203 case '9': // 9-
1204 if (c == '0' && cmdcnt < 1) {
1205 dot_begin(); // this was a standalone zero
1206 } else {
1207 cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number
1208 }
1209 break;
1210 case ':': // :- the colon mode commands
Eric Andersen3f980402001-04-04 17:31:15 +00001211 p = get_input_line((Byte *) ":"); // get input line- use "status line"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001212#ifdef CONFIG_FEATURE_VI_COLON
Eric Andersen3f980402001-04-04 17:31:15 +00001213 colon(p); // execute the command
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001214#else /* CONFIG_FEATURE_VI_COLON */
Eric Andersen822c3832001-05-07 17:37:43 +00001215 if (*p == ':')
1216 p++; // move past the ':'
1217 cnt = strlen((char *) p);
1218 if (cnt <= 0)
1219 break;
1220 if (strncasecmp((char *) p, "quit", cnt) == 0 ||
1221 strncasecmp((char *) p, "q!", cnt) == 0) { // delete lines
Matt Kraai1f0c4362001-12-20 23:13:26 +00001222 if (file_modified && p[1] != '!') {
Eric Andersen3f980402001-04-04 17:31:15 +00001223 psbs("No write since last change (:quit! overrides)");
1224 } else {
1225 editing = 0;
1226 }
Eric Andersen822c3832001-05-07 17:37:43 +00001227 } else if (strncasecmp((char *) p, "write", cnt) == 0 ||
1228 strncasecmp((char *) p, "wq", cnt) == 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00001229 cnt = file_write(cfn, text, end - 1);
1230 file_modified = FALSE;
1231 psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt);
Eric Andersen822c3832001-05-07 17:37:43 +00001232 if (p[1] == 'q') {
Eric Andersen3f980402001-04-04 17:31:15 +00001233 editing = 0;
1234 }
Eric Andersen822c3832001-05-07 17:37:43 +00001235 } else if (strncasecmp((char *) p, "file", cnt) == 0 ) {
1236 edit_status(); // show current file status
1237 } else if (sscanf((char *) p, "%d", &j) > 0) {
1238 dot = find_line(j); // go to line # j
1239 dot_skip_over_ws();
Eric Andersen3f980402001-04-04 17:31:15 +00001240 } else { // unrecognised cmd
Eric Andersen822c3832001-05-07 17:37:43 +00001241 ni((Byte *) p);
Eric Andersen3f980402001-04-04 17:31:15 +00001242 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001243#endif /* CONFIG_FEATURE_VI_COLON */
Eric Andersen3f980402001-04-04 17:31:15 +00001244 break;
1245 case '<': // <- Left shift something
1246 case '>': // >- Right shift something
1247 cnt = count_lines(text, dot); // remember what line we are on
1248 c1 = get_one_char(); // get the type of thing to delete
1249 find_range(&p, &q, c1);
1250 (void) yank_delete(p, q, 1, YANKONLY); // save copy before change
1251 p = begin_line(p);
1252 q = end_line(q);
1253 i = count_lines(p, q); // # of lines we are shifting
1254 for ( ; i > 0; i--, p = next_line(p)) {
1255 if (c == '<') {
1256 // shift left- remove tab or 8 spaces
1257 if (*p == '\t') {
1258 // shrink buffer 1 char
1259 (void) text_hole_delete(p, p);
1260 } else if (*p == ' ') {
1261 // we should be calculating columns, not just SPACE
1262 for (j = 0; *p == ' ' && j < tabstop; j++) {
1263 (void) text_hole_delete(p, p);
1264 }
1265 }
1266 } else if (c == '>') {
1267 // shift right -- add tab or 8 spaces
1268 (void) char_insert(p, '\t');
1269 }
1270 }
1271 dot = find_line(cnt); // what line were we on
1272 dot_skip_over_ws();
1273 end_cmd_q(); // stop adding to q
1274 break;
1275 case 'A': // A- append at e-o-l
1276 dot_end(); // go to e-o-l
1277 //**** fall thru to ... 'a'
1278 case 'a': // a- append after current char
1279 if (*dot != '\n')
1280 dot++;
1281 goto dc_i;
1282 break;
1283 case 'B': // B- back a blank-delimited Word
1284 case 'E': // E- end of a blank-delimited word
1285 case 'W': // W- forward a blank-delimited word
1286 if (cmdcnt-- > 1) {
1287 do_cmd(c);
1288 } // repeat cnt
1289 dir = FORWARD;
1290 if (c == 'B')
1291 dir = BACK;
1292 if (c == 'W' || isspace(dot[dir])) {
1293 dot = skip_thing(dot, 1, dir, S_TO_WS);
1294 dot = skip_thing(dot, 2, dir, S_OVER_WS);
1295 }
1296 if (c != 'W')
1297 dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
1298 break;
1299 case 'C': // C- Change to e-o-l
1300 case 'D': // D- delete to e-o-l
1301 save_dot = dot;
1302 dot = dollar_line(dot); // move to before NL
1303 // copy text into a register and delete
1304 dot = yank_delete(save_dot, dot, 0, YANKDEL); // delete to e-o-l
1305 if (c == 'C')
1306 goto dc_i; // start inserting
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001307#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00001308 if (c == 'D')
1309 end_cmd_q(); // stop adding to q
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001310#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +00001311 break;
Eric Andersen822c3832001-05-07 17:37:43 +00001312 case 'G': // G- goto to a line number (default= E-O-F)
1313 dot = end - 1; // assume E-O-F
Eric Andersen1c0d3112001-04-16 15:46:44 +00001314 if (cmdcnt > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00001315 dot = find_line(cmdcnt); // what line is #cmdcnt
Eric Andersen1c0d3112001-04-16 15:46:44 +00001316 }
1317 dot_skip_over_ws();
1318 break;
Eric Andersen3f980402001-04-04 17:31:15 +00001319 case 'H': // H- goto top line on screen
1320 dot = screenbegin;
1321 if (cmdcnt > (rows - 1)) {
1322 cmdcnt = (rows - 1);
1323 }
1324 if (cmdcnt-- > 1) {
1325 do_cmd('+');
1326 } // repeat cnt
1327 dot_skip_over_ws();
1328 break;
1329 case 'I': // I- insert before first non-blank
1330 dot_begin(); // 0
1331 dot_skip_over_ws();
1332 //**** fall thru to ... 'i'
1333 case 'i': // i- insert before current char
1334 case VI_K_INSERT: // Cursor Key Insert
1335 dc_i:
1336 cmd_mode = 1; // start insrting
1337 psb("-- Insert --");
1338 break;
1339 case 'J': // J- join current and next lines together
1340 if (cmdcnt-- > 2) {
1341 do_cmd(c);
1342 } // repeat cnt
1343 dot_end(); // move to NL
1344 if (dot < end - 1) { // make sure not last char in text[]
1345 *dot++ = ' '; // replace NL with space
1346 while (isblnk(*dot)) { // delete leading WS
1347 dot_delete();
1348 }
1349 }
1350 end_cmd_q(); // stop adding to q
1351 break;
1352 case 'L': // L- goto bottom line on screen
1353 dot = end_screen();
1354 if (cmdcnt > (rows - 1)) {
1355 cmdcnt = (rows - 1);
1356 }
1357 if (cmdcnt-- > 1) {
1358 do_cmd('-');
1359 } // repeat cnt
1360 dot_begin();
1361 dot_skip_over_ws();
1362 break;
Eric Andersen822c3832001-05-07 17:37:43 +00001363 case 'M': // M- goto middle line on screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00001364 dot = screenbegin;
1365 for (cnt = 0; cnt < (rows-1) / 2; cnt++)
1366 dot = next_line(dot);
1367 break;
Eric Andersen3f980402001-04-04 17:31:15 +00001368 case 'O': // O- open a empty line above
Eric Andersen822c3832001-05-07 17:37:43 +00001369 // 0i\n ESC -i
Eric Andersen3f980402001-04-04 17:31:15 +00001370 p = begin_line(dot);
1371 if (p[-1] == '\n') {
1372 dot_prev();
1373 case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
1374 dot_end();
1375 dot = char_insert(dot, '\n');
1376 } else {
1377 dot_begin(); // 0
Eric Andersen822c3832001-05-07 17:37:43 +00001378 dot = char_insert(dot, '\n'); // i\n ESC
Eric Andersen3f980402001-04-04 17:31:15 +00001379 dot_prev(); // -
1380 }
1381 goto dc_i;
1382 break;
1383 case 'R': // R- continuous Replace char
Eric Andersen1c0d3112001-04-16 15:46:44 +00001384 dc5:
Eric Andersen3f980402001-04-04 17:31:15 +00001385 cmd_mode = 2;
1386 psb("-- Replace --");
1387 break;
1388 case 'X': // X- delete char before dot
1389 case 'x': // x- delete the current char
1390 case 's': // s- substitute the current char
1391 if (cmdcnt-- > 1) {
1392 do_cmd(c);
1393 } // repeat cnt
1394 dir = 0;
1395 if (c == 'X')
1396 dir = -1;
1397 if (dot[dir] != '\n') {
1398 if (c == 'X')
1399 dot--; // delete prev char
1400 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
1401 }
1402 if (c == 's')
1403 goto dc_i; // start insrting
1404 end_cmd_q(); // stop adding to q
1405 break;
1406 case 'Z': // Z- if modified, {write}; exit
1407 // ZZ means to save file (if necessary), then exit
1408 c1 = get_one_char();
1409 if (c1 != 'Z') {
1410 indicate_error(c);
1411 break;
1412 }
Matt Kraai1f0c4362001-12-20 23:13:26 +00001413 if (file_modified
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001414#ifdef CONFIG_FEATURE_VI_READONLY
Matt Kraai1f0c4362001-12-20 23:13:26 +00001415 && ! vi_readonly
1416 && ! readonly
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001417#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen3f980402001-04-04 17:31:15 +00001418 ) {
1419 cnt = file_write(cfn, text, end - 1);
1420 if (cnt == (end - 1 - text + 1)) {
1421 editing = 0;
1422 }
1423 } else {
1424 editing = 0;
1425 }
1426 break;
1427 case '^': // ^- move to first non-blank on line
1428 dot_begin();
1429 dot_skip_over_ws();
1430 break;
1431 case 'b': // b- back a word
1432 case 'e': // e- end of word
1433 if (cmdcnt-- > 1) {
1434 do_cmd(c);
1435 } // repeat cnt
1436 dir = FORWARD;
1437 if (c == 'b')
1438 dir = BACK;
1439 if ((dot + dir) < text || (dot + dir) > end - 1)
1440 break;
1441 dot += dir;
1442 if (isspace(*dot)) {
1443 dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
1444 }
1445 if (isalnum(*dot) || *dot == '_') {
1446 dot = skip_thing(dot, 1, dir, S_END_ALNUM);
1447 } else if (ispunct(*dot)) {
1448 dot = skip_thing(dot, 1, dir, S_END_PUNCT);
1449 }
1450 break;
1451 case 'c': // c- change something
1452 case 'd': // d- delete something
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001453#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00001454 case 'y': // y- yank something
1455 case 'Y': // Y- Yank a line
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001456#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00001457 yf = YANKDEL; // assume either "c" or "d"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001458#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00001459 if (c == 'y' || c == 'Y')
1460 yf = YANKONLY;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001461#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00001462 c1 = 'y';
1463 if (c != 'Y')
1464 c1 = get_one_char(); // get the type of thing to delete
1465 find_range(&p, &q, c1);
1466 if (c1 == 27) { // ESC- user changed mind and wants out
1467 c = c1 = 27; // Escape- do nothing
1468 } else if (strchr("wW", c1)) {
1469 if (c == 'c') {
1470 // don't include trailing WS as part of word
1471 while (isblnk(*q)) {
1472 if (q <= text || q[-1] == '\n')
1473 break;
1474 q--;
1475 }
1476 }
1477 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen822c3832001-05-07 17:37:43 +00001478 } else if (strchr("^0bBeEft$", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00001479 // single line copy text into a register and delete
1480 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen1c0d3112001-04-16 15:46:44 +00001481 } else if (strchr("cdykjHL%+-{}\r\n", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00001482 // multiple line copy text into a register and delete
1483 dot = yank_delete(p, q, 1, yf); // delete lines
Eric Andersen1c0d3112001-04-16 15:46:44 +00001484 if (c == 'c') {
1485 dot = char_insert(dot, '\n');
1486 // on the last line of file don't move to prev line
1487 if (dot != (end-1)) {
1488 dot_prev();
1489 }
1490 } else if (c == 'd') {
Eric Andersen3f980402001-04-04 17:31:15 +00001491 dot_begin();
1492 dot_skip_over_ws();
1493 }
1494 } else {
1495 // could not recognize object
1496 c = c1 = 27; // error-
1497 indicate_error(c);
1498 }
1499 if (c1 != 27) {
1500 // if CHANGING, not deleting, start inserting after the delete
1501 if (c == 'c') {
1502 strcpy((char *) buf, "Change");
1503 goto dc_i; // start inserting
1504 }
1505 if (c == 'd') {
1506 strcpy((char *) buf, "Delete");
1507 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001508#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00001509 if (c == 'y' || c == 'Y') {
1510 strcpy((char *) buf, "Yank");
1511 }
1512 p = reg[YDreg];
1513 q = p + strlen((char *) p);
1514 for (cnt = 0; p <= q; p++) {
1515 if (*p == '\n')
1516 cnt++;
1517 }
1518 psb("%s %d lines (%d chars) using [%c]",
1519 buf, cnt, strlen((char *) reg[YDreg]), what_reg());
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001520#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00001521 end_cmd_q(); // stop adding to q
1522 }
1523 break;
1524 case 'k': // k- goto prev line, same col
1525 case VI_K_UP: // cursor key Up
1526 if (cmdcnt-- > 1) {
1527 do_cmd(c);
1528 } // repeat cnt
1529 dot_prev();
1530 dot = move_to_col(dot, ccol + offset); // try stay in same col
1531 break;
1532 case 'r': // r- replace the current char with user input
1533 c1 = get_one_char(); // get the replacement char
1534 if (*dot != '\n') {
1535 *dot = c1;
1536 file_modified = TRUE; // has the file been modified
1537 }
1538 end_cmd_q(); // stop adding to q
1539 break;
Eric Andersen822c3832001-05-07 17:37:43 +00001540 case 't': // t- move to char prior to next x
1541 last_forward_char = get_one_char();
1542 do_cmd(';');
1543 if (*dot == last_forward_char)
1544 dot_left();
1545 last_forward_char= 0;
1546 break;
Eric Andersen3f980402001-04-04 17:31:15 +00001547 case 'w': // w- forward a word
1548 if (cmdcnt-- > 1) {
1549 do_cmd(c);
1550 } // repeat cnt
1551 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
1552 dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
1553 } else if (ispunct(*dot)) { // we are on PUNCT
1554 dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
1555 }
1556 if (dot < end - 1)
1557 dot++; // move over word
1558 if (isspace(*dot)) {
1559 dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
1560 }
1561 break;
1562 case 'z': // z-
1563 c1 = get_one_char(); // get the replacement char
1564 cnt = 0;
1565 if (c1 == '.')
1566 cnt = (rows - 2) / 2; // put dot at center
1567 if (c1 == '-')
1568 cnt = rows - 2; // put dot at bottom
1569 screenbegin = begin_line(dot); // start dot at top
1570 dot_scroll(cnt, -1);
1571 break;
1572 case '|': // |- move to column "cmdcnt"
1573 dot = move_to_col(dot, cmdcnt - 1); // try to move to column
1574 break;
1575 case '~': // ~- flip the case of letters a-z -> A-Z
1576 if (cmdcnt-- > 1) {
1577 do_cmd(c);
1578 } // repeat cnt
1579 if (islower(*dot)) {
1580 *dot = toupper(*dot);
1581 file_modified = TRUE; // has the file been modified
1582 } else if (isupper(*dot)) {
1583 *dot = tolower(*dot);
1584 file_modified = TRUE; // has the file been modified
1585 }
1586 dot_right();
1587 end_cmd_q(); // stop adding to q
1588 break;
1589 //----- The Cursor and Function Keys -----------------------------
1590 case VI_K_HOME: // Cursor Key Home
1591 dot_begin();
1592 break;
1593 // The Fn keys could point to do_macro which could translate them
1594 case VI_K_FUN1: // Function Key F1
1595 case VI_K_FUN2: // Function Key F2
1596 case VI_K_FUN3: // Function Key F3
1597 case VI_K_FUN4: // Function Key F4
1598 case VI_K_FUN5: // Function Key F5
1599 case VI_K_FUN6: // Function Key F6
1600 case VI_K_FUN7: // Function Key F7
1601 case VI_K_FUN8: // Function Key F8
1602 case VI_K_FUN9: // Function Key F9
1603 case VI_K_FUN10: // Function Key F10
1604 case VI_K_FUN11: // Function Key F11
1605 case VI_K_FUN12: // Function Key F12
1606 break;
1607 }
1608
1609 dc1:
1610 // if text[] just became empty, add back an empty line
1611 if (end == text) {
1612 (void) char_insert(text, '\n'); // start empty buf with dummy line
1613 dot = text;
1614 }
1615 // it is OK for dot to exactly equal to end, otherwise check dot validity
1616 if (dot != end) {
1617 dot = bound_dot(dot); // make sure "dot" is valid
1618 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001619#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00001620 check_context(c); // update the current context
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001621#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00001622
1623 if (!isdigit(c))
1624 cmdcnt = 0; // cmd was not a number, reset cmdcnt
1625 cnt = dot - begin_line(dot);
1626 // Try to stay off of the Newline
1627 if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
1628 dot--;
1629}
1630
1631//----- The Colon commands -------------------------------------
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001632#ifdef CONFIG_FEATURE_VI_COLON
Eric Andersen1c0d3112001-04-16 15:46:44 +00001633static Byte *get_one_address(Byte * p, int *addr) // get colon addr, if present
Eric Andersen3f980402001-04-04 17:31:15 +00001634{
1635 int st;
1636 Byte *q;
1637
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001638#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00001639 Byte c;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001640#endif /* CONFIG_FEATURE_VI_YANKMARK */
1641#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen1c0d3112001-04-16 15:46:44 +00001642 Byte *pat, buf[BUFSIZ];
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001643#endif /* CONFIG_FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +00001644
1645 *addr = -1; // assume no addr
1646 if (*p == '.') { // the current line
1647 p++;
1648 q = begin_line(dot);
1649 *addr = count_lines(text, q);
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001650#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00001651 } else if (*p == '\'') { // is this a mark addr
1652 p++;
1653 c = tolower(*p);
1654 p++;
1655 if (c >= 'a' && c <= 'z') {
1656 // we have a mark
1657 c = c - 'a';
1658 q = mark[(int) c];
1659 if (q != NULL) { // is mark valid
1660 *addr = count_lines(text, q); // count lines
1661 }
1662 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001663#endif /* CONFIG_FEATURE_VI_YANKMARK */
1664#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +00001665 } else if (*p == '/') { // a search pattern
1666 q = buf;
1667 for (p++; *p; p++) {
1668 if (*p == '/')
1669 break;
1670 *q++ = *p;
1671 *q = '\0';
1672 }
Matt Kraaic8227632001-11-12 16:57:27 +00001673 pat = (Byte *) xstrdup((char *) buf); // save copy of pattern
Eric Andersen3f980402001-04-04 17:31:15 +00001674 if (*p == '/')
1675 p++;
1676 q = char_search(dot, pat, FORWARD, FULL);
1677 if (q != NULL) {
1678 *addr = count_lines(text, q);
1679 }
1680 free(pat);
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001681#endif /* CONFIG_FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +00001682 } else if (*p == '$') { // the last line in file
1683 p++;
1684 q = begin_line(end - 1);
1685 *addr = count_lines(text, q);
1686 } else if (isdigit(*p)) { // specific line number
1687 sscanf((char *) p, "%d%n", addr, &st);
1688 p += st;
1689 } else { // I don't reconise this
1690 // unrecognised address- assume -1
1691 *addr = -1;
1692 }
1693 return (p);
1694}
1695
Eric Andersen1c0d3112001-04-16 15:46:44 +00001696static Byte *get_address(Byte *p, int *b, int *e) // get two colon addrs, if present
1697{
1698 //----- get the address' i.e., 1,3 'a,'b -----
1699 // get FIRST addr, if present
1700 while (isblnk(*p))
1701 p++; // skip over leading spaces
1702 if (*p == '%') { // alias for 1,$
1703 p++;
1704 *b = 1;
1705 *e = count_lines(text, end-1);
1706 goto ga0;
1707 }
1708 p = get_one_address(p, b);
1709 while (isblnk(*p))
1710 p++;
1711 if (*p == ',') { // is there a address seperator
1712 p++;
1713 while (isblnk(*p))
1714 p++;
1715 // get SECOND addr, if present
1716 p = get_one_address(p, e);
1717 }
1718ga0:
1719 while (isblnk(*p))
1720 p++; // skip over trailing spaces
1721 return (p);
1722}
1723
Eric Andersen3f980402001-04-04 17:31:15 +00001724static void colon(Byte * buf)
1725{
1726 Byte c, *orig_buf, *buf1, *q, *r;
Eric Andersen1c0d3112001-04-16 15:46:44 +00001727 Byte *fn, cmd[BUFSIZ], args[BUFSIZ];
Eric Andersen3f980402001-04-04 17:31:15 +00001728 int i, l, li, ch, st, b, e;
1729 int useforce, forced;
Eric Andersen1c0d3112001-04-16 15:46:44 +00001730 struct stat st_buf;
Eric Andersen3f980402001-04-04 17:31:15 +00001731
Eric Andersen822c3832001-05-07 17:37:43 +00001732 // :3154 // if (-e line 3154) goto it else stay put
1733 // :4,33w! foo // write a portion of buffer to file "foo"
1734 // :w // write all of buffer to current file
1735 // :q // quit
1736 // :q! // quit- dont care about modified file
Eric Andersen3f980402001-04-04 17:31:15 +00001737 // :'a,'z!sort -u // filter block through sort
Eric Andersen822c3832001-05-07 17:37:43 +00001738 // :'f // goto mark "f"
1739 // :'fl // list literal the mark "f" line
1740 // :.r bar // read file "bar" into buffer before dot
Eric Andersen3f980402001-04-04 17:31:15 +00001741 // :/123/,/abc/d // delete lines from "123" line to "abc" line
Eric Andersen822c3832001-05-07 17:37:43 +00001742 // :/xyz/ // goto the "xyz" line
Eric Andersen3f980402001-04-04 17:31:15 +00001743 // :s/find/replace/ // substitute pattern "find" with "replace"
Eric Andersen822c3832001-05-07 17:37:43 +00001744 // :!<cmd> // run <cmd> then return
Eric Andersen3f980402001-04-04 17:31:15 +00001745 //
1746 if (strlen((char *) buf) <= 0)
1747 goto vc1;
1748 if (*buf == ':')
1749 buf++; // move past the ':'
1750
1751 forced = useforce = FALSE;
1752 li = st = ch = i = 0;
1753 b = e = -1;
1754 q = text; // assume 1,$ for the range
1755 r = end - 1;
1756 li = count_lines(text, end - 1);
1757 fn = cfn; // default to current file
Eric Andersen822c3832001-05-07 17:37:43 +00001758 memset(cmd, '\0', BUFSIZ); // clear cmd[]
1759 memset(args, '\0', BUFSIZ); // clear args[]
Eric Andersen3f980402001-04-04 17:31:15 +00001760
Eric Andersen1c0d3112001-04-16 15:46:44 +00001761 // look for optional address(es) :. :1 :1,9 :'q,'a :%
1762 buf = get_address(buf, &b, &e);
Eric Andersen3f980402001-04-04 17:31:15 +00001763
1764 // remember orig command line
1765 orig_buf = buf;
1766
1767 // get the COMMAND into cmd[]
1768 buf1 = cmd;
Eric Andersen3f980402001-04-04 17:31:15 +00001769 while (*buf != '\0') {
1770 if (isspace(*buf))
1771 break;
1772 *buf1++ = *buf++;
Eric Andersen3f980402001-04-04 17:31:15 +00001773 }
1774 // get any ARGuments
1775 while (isblnk(*buf))
1776 buf++;
1777 strcpy((char *) args, (char *) buf);
Eric Andersenc33ebc92001-05-07 22:57:47 +00001778 buf1 = last_char_is((char *)cmd, '!');
1779 if (buf1) {
Eric Andersen3f980402001-04-04 17:31:15 +00001780 useforce = TRUE;
Eric Andersenc33ebc92001-05-07 22:57:47 +00001781 *buf1 = '\0'; // get rid of !
Eric Andersen3f980402001-04-04 17:31:15 +00001782 }
1783 if (b >= 0) {
1784 // if there is only one addr, then the addr
1785 // is the line number of the single line the
1786 // user wants. So, reset the end
1787 // pointer to point at end of the "b" line
1788 q = find_line(b); // what line is #b
1789 r = end_line(q);
1790 li = 1;
1791 }
1792 if (e >= 0) {
1793 // we were given two addrs. change the
1794 // end pointer to the addr given by user.
1795 r = find_line(e); // what line is #e
1796 r = end_line(r);
1797 li = e - b + 1;
1798 }
1799 // ------------ now look for the command ------------
1800 i = strlen((char *) cmd);
1801 if (i == 0) { // :123CR goto line #123
1802 if (b >= 0) {
1803 dot = find_line(b); // what line is #b
1804 dot_skip_over_ws();
1805 }
Eric Andersen1c0d3112001-04-16 15:46:44 +00001806 } else if (strncmp((char *) cmd, "!", 1) == 0) { // run a cmd
1807 // :!ls run the <cmd>
1808 (void) alarm(0); // wait for input- no alarms
Eric Andersen822c3832001-05-07 17:37:43 +00001809 place_cursor(rows - 1, 0, FALSE); // go to Status line
Eric Andersen1c0d3112001-04-16 15:46:44 +00001810 clear_to_eol(); // clear the line
1811 cookmode();
1812 system(orig_buf+1); // run the cmd
1813 rawmode();
1814 Hit_Return(); // let user see results
1815 (void) alarm(3); // done waiting for input
Eric Andersen3f980402001-04-04 17:31:15 +00001816 } else if (strncmp((char *) cmd, "=", i) == 0) { // where is the address
1817 if (b < 0) { // no addr given- use defaults
1818 b = e = count_lines(text, dot);
1819 }
1820 psb("%d", b);
1821 } else if (strncasecmp((char *) cmd, "delete", i) == 0) { // delete lines
1822 if (b < 0) { // no addr given- use defaults
1823 q = begin_line(dot); // assume .,. for the range
1824 r = end_line(dot);
1825 }
1826 dot = yank_delete(q, r, 1, YANKDEL); // save, then delete lines
1827 dot_skip_over_ws();
1828 } else if (strncasecmp((char *) cmd, "edit", i) == 0) { // Edit a file
Eric Andersen1c0d3112001-04-16 15:46:44 +00001829 int sr;
1830 sr= 0;
1831 // don't edit, if the current file has been modified
Matt Kraai1f0c4362001-12-20 23:13:26 +00001832 if (file_modified && ! useforce) {
Eric Andersen3f980402001-04-04 17:31:15 +00001833 psbs("No write since last change (:edit! overrides)");
1834 goto vc1;
1835 }
Eric Andersen1c0d3112001-04-16 15:46:44 +00001836 if (strlen(args) > 0) {
1837 // the user supplied a file name
1838 fn= args;
1839 } else if (cfn != 0 && strlen(cfn) > 0) {
1840 // no user supplied name- use the current filename
1841 fn= cfn;
1842 goto vc5;
1843 } else {
1844 // no user file name, no current name- punt
1845 psbs("No current filename");
1846 goto vc1;
Eric Andersen3f980402001-04-04 17:31:15 +00001847 }
Eric Andersen1c0d3112001-04-16 15:46:44 +00001848
1849 // see if file exists- if not, its just a new file request
1850 if ((sr=stat((char*)fn, &st_buf)) < 0) {
1851 // This is just a request for a new file creation.
1852 // The file_insert below will fail but we get
1853 // an empty buffer with a file name. Then the "write"
1854 // command can do the create.
1855 } else {
1856 if ((st_buf.st_mode & (S_IFREG)) == 0) {
1857 // This is not a regular file
1858 psbs("\"%s\" is not a regular file", fn);
1859 goto vc1;
1860 }
1861 if ((st_buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
1862 // dont have any read permissions
1863 psbs("\"%s\" is not readable", fn);
1864 goto vc1;
1865 }
1866 }
1867
1868 // There is a read-able regular file
1869 // make this the current file
Matt Kraaic8227632001-11-12 16:57:27 +00001870 q = (Byte *) xstrdup((char *) fn); // save the cfn
Eric Andersen3f980402001-04-04 17:31:15 +00001871 if (cfn != 0)
Eric Andersen1c0d3112001-04-16 15:46:44 +00001872 free(cfn); // free the old name
1873 cfn = q; // remember new cfn
1874
1875 vc5:
Eric Andersen3f980402001-04-04 17:31:15 +00001876 // delete all the contents of text[]
1877 new_text(2 * file_size(fn));
1878 screenbegin = dot = end = text;
Eric Andersen1c0d3112001-04-16 15:46:44 +00001879
Eric Andersen3f980402001-04-04 17:31:15 +00001880 // insert new file
Eric Andersen1c0d3112001-04-16 15:46:44 +00001881 ch = file_insert(fn, text, file_size(fn));
1882
1883 if (ch < 1) {
1884 // start empty buf with dummy line
1885 (void) char_insert(text, '\n');
1886 ch= 1;
Eric Andersen3f980402001-04-04 17:31:15 +00001887 }
1888 file_modified = FALSE;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001889#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen1c0d3112001-04-16 15:46:44 +00001890 if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00001891 free(reg[Ureg]); // free orig line reg- for 'U'
Eric Andersen1c0d3112001-04-16 15:46:44 +00001892 reg[Ureg]= 0;
1893 }
1894 if (YDreg >= 0 && YDreg < 28 && reg[YDreg] != 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00001895 free(reg[YDreg]); // free default yank/delete register
Eric Andersen1c0d3112001-04-16 15:46:44 +00001896 reg[YDreg]= 0;
1897 }
Eric Andersen3f980402001-04-04 17:31:15 +00001898 for (li = 0; li < 28; li++) {
1899 mark[li] = 0;
1900 } // init the marks
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001901#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00001902 // how many lines in text[]?
1903 li = count_lines(text, end - 1);
Eric Andersen822c3832001-05-07 17:37:43 +00001904 psb("\"%s\"%s"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001905#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen1c0d3112001-04-16 15:46:44 +00001906 "%s"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001907#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen1c0d3112001-04-16 15:46:44 +00001908 " %dL, %dC", cfn,
1909 (sr < 0 ? " [New file]" : ""),
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001910#ifdef CONFIG_FEATURE_VI_READONLY
Matt Kraai1f0c4362001-12-20 23:13:26 +00001911 ((vi_readonly || readonly) ? " [Read only]" : ""),
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001912#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen1c0d3112001-04-16 15:46:44 +00001913 li, ch);
Eric Andersen3f980402001-04-04 17:31:15 +00001914 } else if (strncasecmp((char *) cmd, "file", i) == 0) { // what File is this
1915 if (b != -1 || e != -1) {
1916 ni((Byte *) "No address allowed on this command");
1917 goto vc1;
1918 }
1919 if (strlen((char *) args) > 0) {
1920 // user wants a new filename
1921 if (cfn != NULL)
1922 free(cfn);
Matt Kraaic8227632001-11-12 16:57:27 +00001923 cfn = (Byte *) xstrdup((char *) args);
Eric Andersen3f980402001-04-04 17:31:15 +00001924 } else {
1925 // user wants file status info
1926 edit_status();
1927 }
1928 } else if (strncasecmp((char *) cmd, "features", i) == 0) { // what features are available
1929 // print out values of all features
Eric Andersen822c3832001-05-07 17:37:43 +00001930 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
Eric Andersen3f980402001-04-04 17:31:15 +00001931 clear_to_eol(); // clear the line
1932 cookmode();
1933 show_help();
1934 rawmode();
1935 Hit_Return();
1936 } else if (strncasecmp((char *) cmd, "list", i) == 0) { // literal print line
1937 if (b < 0) { // no addr given- use defaults
1938 q = begin_line(dot); // assume .,. for the range
1939 r = end_line(dot);
1940 }
Eric Andersen822c3832001-05-07 17:37:43 +00001941 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
Eric Andersen3f980402001-04-04 17:31:15 +00001942 clear_to_eol(); // clear the line
1943 write(1, "\r\n", 2);
1944 for (; q <= r; q++) {
1945 c = *q;
1946 if (c > '~')
1947 standout_start();
1948 if (c == '\n') {
1949 write(1, "$\r", 2);
1950 } else if (*q < ' ') {
1951 write(1, "^", 1);
1952 c += '@';
1953 }
1954 write(1, &c, 1);
1955 if (c > '~')
1956 standout_end();
1957 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001958#ifdef CONFIG_FEATURE_VI_SET
Eric Andersen3f980402001-04-04 17:31:15 +00001959 vc2:
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001960#endif /* CONFIG_FEATURE_VI_SET */
Eric Andersen3f980402001-04-04 17:31:15 +00001961 Hit_Return();
1962 } else if ((strncasecmp((char *) cmd, "quit", i) == 0) || // Quit
1963 (strncasecmp((char *) cmd, "next", i) == 0)) { // edit next file
Matt Kraai1f0c4362001-12-20 23:13:26 +00001964 if (useforce) {
Eric Andersen3f980402001-04-04 17:31:15 +00001965 // force end of argv list
1966 if (*cmd == 'q') {
1967 optind = save_argc;
1968 }
1969 editing = 0;
1970 goto vc1;
1971 }
1972 // don't exit if the file been modified
Matt Kraai1f0c4362001-12-20 23:13:26 +00001973 if (file_modified) {
Eric Andersen3f980402001-04-04 17:31:15 +00001974 psbs("No write since last change (:%s! overrides)",
1975 (*cmd == 'q' ? "quit" : "next"));
1976 goto vc1;
1977 }
1978 // are there other file to edit
1979 if (*cmd == 'q' && optind < save_argc - 1) {
1980 psbs("%d more file to edit", (save_argc - optind - 1));
1981 goto vc1;
1982 }
1983 if (*cmd == 'n' && optind >= save_argc - 1) {
1984 psbs("No more files to edit");
1985 goto vc1;
1986 }
1987 editing = 0;
1988 } else if (strncasecmp((char *) cmd, "read", i) == 0) { // read file into text[]
1989 fn = args;
1990 if (strlen((char *) fn) <= 0) {
1991 psbs("No filename given");
1992 goto vc1;
1993 }
1994 if (b < 0) { // no addr given- use defaults
1995 q = begin_line(dot); // assume "dot"
1996 }
1997 // read after current line- unless user said ":0r foo"
1998 if (b != 0)
1999 q = next_line(q);
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002000#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen1c0d3112001-04-16 15:46:44 +00002001 l= readonly; // remember current files' status
Matt Kraai51038a32001-07-31 15:01:12 +00002002#endif
Eric Andersen3f980402001-04-04 17:31:15 +00002003 ch = file_insert(fn, q, file_size(fn));
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002004#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen1c0d3112001-04-16 15:46:44 +00002005 readonly= l;
Matt Kraai51038a32001-07-31 15:01:12 +00002006#endif
Eric Andersen3f980402001-04-04 17:31:15 +00002007 if (ch < 0)
2008 goto vc1; // nothing was inserted
2009 // how many lines in text[]?
2010 li = count_lines(q, q + ch - 1);
Eric Andersen822c3832001-05-07 17:37:43 +00002011 psb("\"%s\""
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002012#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen1c0d3112001-04-16 15:46:44 +00002013 "%s"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002014#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen1c0d3112001-04-16 15:46:44 +00002015 " %dL, %dC", fn,
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002016#ifdef CONFIG_FEATURE_VI_READONLY
Matt Kraai1f0c4362001-12-20 23:13:26 +00002017 ((vi_readonly || readonly) ? " [Read only]" : ""),
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002018#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen1c0d3112001-04-16 15:46:44 +00002019 li, ch);
Eric Andersen3f980402001-04-04 17:31:15 +00002020 if (ch > 0) {
2021 // if the insert is before "dot" then we need to update
2022 if (q <= dot)
2023 dot += ch;
2024 file_modified = TRUE;
2025 }
2026 } else if (strncasecmp((char *) cmd, "rewind", i) == 0) { // rewind cmd line args
Matt Kraai1f0c4362001-12-20 23:13:26 +00002027 if (file_modified && ! useforce) {
Eric Andersen3f980402001-04-04 17:31:15 +00002028 psbs("No write since last change (:rewind! overrides)");
2029 } else {
2030 // reset the filenames to edit
2031 optind = fn_start - 1;
2032 editing = 0;
2033 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002034#ifdef CONFIG_FEATURE_VI_SET
Eric Andersen3f980402001-04-04 17:31:15 +00002035 } else if (strncasecmp((char *) cmd, "set", i) == 0) { // set or clear features
2036 i = 0; // offset into args
2037 if (strlen((char *) args) == 0) {
2038 // print out values of all options
Eric Andersen822c3832001-05-07 17:37:43 +00002039 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
Eric Andersen3f980402001-04-04 17:31:15 +00002040 clear_to_eol(); // clear the line
2041 printf("----------------------------------------\r\n");
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002042#ifdef CONFIG_FEATURE_VI_SETOPTS
Eric Andersen3f980402001-04-04 17:31:15 +00002043 if (!autoindent)
2044 printf("no");
2045 printf("autoindent ");
Eric Andersen822c3832001-05-07 17:37:43 +00002046 if (!err_method)
2047 printf("no");
2048 printf("flash ");
Eric Andersen3f980402001-04-04 17:31:15 +00002049 if (!ignorecase)
2050 printf("no");
2051 printf("ignorecase ");
2052 if (!showmatch)
2053 printf("no");
2054 printf("showmatch ");
2055 printf("tabstop=%d ", tabstop);
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002056#endif /* CONFIG_FEATURE_VI_SETOPTS */
Eric Andersen3f980402001-04-04 17:31:15 +00002057 printf("\r\n");
2058 goto vc2;
2059 }
2060 if (strncasecmp((char *) args, "no", 2) == 0)
2061 i = 2; // ":set noautoindent"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002062#ifdef CONFIG_FEATURE_VI_SETOPTS
Eric Andersen3f980402001-04-04 17:31:15 +00002063 if (strncasecmp((char *) args + i, "autoindent", 10) == 0 ||
2064 strncasecmp((char *) args + i, "ai", 2) == 0) {
2065 autoindent = (i == 2) ? 0 : 1;
2066 }
Eric Andersen822c3832001-05-07 17:37:43 +00002067 if (strncasecmp((char *) args + i, "flash", 5) == 0 ||
2068 strncasecmp((char *) args + i, "fl", 2) == 0) {
2069 err_method = (i == 2) ? 0 : 1;
2070 }
Eric Andersen3f980402001-04-04 17:31:15 +00002071 if (strncasecmp((char *) args + i, "ignorecase", 10) == 0 ||
2072 strncasecmp((char *) args + i, "ic", 2) == 0) {
2073 ignorecase = (i == 2) ? 0 : 1;
2074 }
2075 if (strncasecmp((char *) args + i, "showmatch", 9) == 0 ||
2076 strncasecmp((char *) args + i, "sm", 2) == 0) {
2077 showmatch = (i == 2) ? 0 : 1;
2078 }
2079 if (strncasecmp((char *) args + i, "tabstop", 7) == 0) {
2080 sscanf(strchr((char *) args + i, '='), "=%d", &ch);
2081 if (ch > 0 && ch < columns - 1)
2082 tabstop = ch;
2083 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002084#endif /* CONFIG_FEATURE_VI_SETOPTS */
2085#endif /* CONFIG_FEATURE_VI_SET */
2086#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +00002087 } else if (strncasecmp((char *) cmd, "s", 1) == 0) { // substitute a pattern with a replacement pattern
2088 Byte *ls, *F, *R;
2089 int gflag;
2090
2091 // F points to the "find" pattern
2092 // R points to the "replace" pattern
2093 // replace the cmd line delimiters "/" with NULLs
2094 gflag = 0; // global replace flag
2095 c = orig_buf[1]; // what is the delimiter
2096 F = orig_buf + 2; // start of "find"
2097 R = (Byte *) strchr((char *) F, c); // middle delimiter
Eric Andersenddb00542001-05-13 00:48:09 +00002098 if (!R) goto colon_s_fail;
Eric Andersen3f980402001-04-04 17:31:15 +00002099 *R++ = '\0'; // terminate "find"
2100 buf1 = (Byte *) strchr((char *) R, c);
Eric Andersenddb00542001-05-13 00:48:09 +00002101 if (!buf1) goto colon_s_fail;
Eric Andersen3f980402001-04-04 17:31:15 +00002102 *buf1++ = '\0'; // terminate "replace"
2103 if (*buf1 == 'g') { // :s/foo/bar/g
2104 buf1++;
2105 gflag++; // turn on gflag
2106 }
2107 q = begin_line(q);
2108 if (b < 0) { // maybe :s/foo/bar/
2109 q = begin_line(dot); // start with cur line
2110 b = count_lines(text, q); // cur line number
2111 }
2112 if (e < 0)
2113 e = b; // maybe :.s/foo/bar/
2114 for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0
2115 ls = q; // orig line start
2116 vc4:
2117 buf1 = char_search(q, F, FORWARD, LIMITED); // search cur line only for "find"
2118 if (buf1 != NULL) {
2119 // we found the "find" pattern- delete it
2120 (void) text_hole_delete(buf1, buf1 + strlen((char *) F) - 1);
2121 // inset the "replace" patern
2122 (void) string_insert(buf1, R); // insert the string
2123 // check for "global" :s/foo/bar/g
2124 if (gflag == 1) {
2125 if ((buf1 + strlen((char *) R)) < end_line(ls)) {
2126 q = buf1 + strlen((char *) R);
2127 goto vc4; // don't let q move past cur line
2128 }
2129 }
2130 }
2131 q = next_line(ls);
2132 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002133#endif /* CONFIG_FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +00002134 } else if (strncasecmp((char *) cmd, "version", i) == 0) { // show software version
2135 psb("%s", vi_Version);
2136 } else if ((strncasecmp((char *) cmd, "write", i) == 0) || // write text to file
2137 (strncasecmp((char *) cmd, "wq", i) == 0)) { // write text to file
2138 // is there a file name to write to?
2139 if (strlen((char *) args) > 0) {
2140 fn = args;
2141 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002142#ifdef CONFIG_FEATURE_VI_READONLY
Matt Kraai1f0c4362001-12-20 23:13:26 +00002143 if ((vi_readonly || readonly) && ! useforce) {
Eric Andersen3f980402001-04-04 17:31:15 +00002144 psbs("\"%s\" File is read only", fn);
2145 goto vc3;
2146 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002147#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen3f980402001-04-04 17:31:15 +00002148 // how many lines in text[]?
2149 li = count_lines(q, r);
2150 ch = r - q + 1;
Eric Andersen1c0d3112001-04-16 15:46:44 +00002151 // see if file exists- if not, its just a new file request
Matt Kraai1f0c4362001-12-20 23:13:26 +00002152 if (useforce) {
Eric Andersen3f980402001-04-04 17:31:15 +00002153 // if "fn" is not write-able, chmod u+w
2154 // sprintf(syscmd, "chmod u+w %s", fn);
2155 // system(syscmd);
2156 forced = TRUE;
2157 }
2158 l = file_write(fn, q, r);
Matt Kraai1f0c4362001-12-20 23:13:26 +00002159 if (useforce && forced) {
Eric Andersen3f980402001-04-04 17:31:15 +00002160 // chmod u-w
2161 // sprintf(syscmd, "chmod u-w %s", fn);
2162 // system(syscmd);
2163 forced = FALSE;
2164 }
2165 psb("\"%s\" %dL, %dC", fn, li, l);
2166 if (q == text && r == end - 1 && l == ch)
2167 file_modified = FALSE;
2168 if (cmd[1] == 'q' && l == ch) {
2169 editing = 0;
2170 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002171#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen3f980402001-04-04 17:31:15 +00002172 vc3:;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002173#endif /* CONFIG_FEATURE_VI_READONLY */
2174#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00002175 } else if (strncasecmp((char *) cmd, "yank", i) == 0) { // yank lines
2176 if (b < 0) { // no addr given- use defaults
2177 q = begin_line(dot); // assume .,. for the range
2178 r = end_line(dot);
2179 }
2180 text_yank(q, r, YDreg);
2181 li = count_lines(q, r);
2182 psb("Yank %d lines (%d chars) into [%c]",
2183 li, strlen((char *) reg[YDreg]), what_reg());
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002184#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00002185 } else {
2186 // cmd unknown
2187 ni((Byte *) cmd);
2188 }
2189 vc1:
2190 dot = bound_dot(dot); // make sure "dot" is valid
2191 return;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002192#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersenddb00542001-05-13 00:48:09 +00002193colon_s_fail:
2194 psb(":s expression missing delimiters");
2195 return;
2196#endif
2197
Eric Andersen3f980402001-04-04 17:31:15 +00002198}
2199
2200static void Hit_Return(void)
2201{
2202 char c;
2203
2204 standout_start(); // start reverse video
2205 write(1, "[Hit return to continue]", 24);
2206 standout_end(); // end reverse video
2207 while ((c = get_one_char()) != '\n' && c != '\r') /*do nothing */
2208 ;
2209 redraw(TRUE); // force redraw all
2210}
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002211#endif /* CONFIG_FEATURE_VI_COLON */
Eric Andersen3f980402001-04-04 17:31:15 +00002212
2213//----- Synchronize the cursor to Dot --------------------------
2214static void sync_cursor(Byte * d, int *row, int *col)
2215{
2216 Byte *beg_cur, *end_cur; // begin and end of "d" line
2217 Byte *beg_scr, *end_scr; // begin and end of screen
2218 Byte *tp;
2219 int cnt, ro, co;
2220
2221 beg_cur = begin_line(d); // first char of cur line
2222 end_cur = end_line(d); // last char of cur line
2223
2224 beg_scr = end_scr = screenbegin; // first char of screen
2225 end_scr = end_screen(); // last char of screen
2226
2227 if (beg_cur < screenbegin) {
2228 // "d" is before top line on screen
2229 // how many lines do we have to move
2230 cnt = count_lines(beg_cur, screenbegin);
2231 sc1:
2232 screenbegin = beg_cur;
2233 if (cnt > (rows - 1) / 2) {
2234 // we moved too many lines. put "dot" in middle of screen
2235 for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
2236 screenbegin = prev_line(screenbegin);
2237 }
2238 }
2239 } else if (beg_cur > end_scr) {
2240 // "d" is after bottom line on screen
2241 // how many lines do we have to move
2242 cnt = count_lines(end_scr, beg_cur);
2243 if (cnt > (rows - 1) / 2)
2244 goto sc1; // too many lines
2245 for (ro = 0; ro < cnt - 1; ro++) {
2246 // move screen begin the same amount
2247 screenbegin = next_line(screenbegin);
2248 // now, move the end of screen
2249 end_scr = next_line(end_scr);
2250 end_scr = end_line(end_scr);
2251 }
2252 }
2253 // "d" is on screen- find out which row
2254 tp = screenbegin;
2255 for (ro = 0; ro < rows - 1; ro++) { // drive "ro" to correct row
2256 if (tp == beg_cur)
2257 break;
2258 tp = next_line(tp);
2259 }
2260
2261 // find out what col "d" is on
2262 co = 0;
2263 do { // drive "co" to correct column
2264 if (*tp == '\n' || *tp == '\0')
2265 break;
2266 if (*tp == '\t') {
2267 // 7 - (co % 8 )
2268 co += ((tabstop - 1) - (co % tabstop));
2269 } else if (*tp < ' ') {
2270 co++; // display as ^X, use 2 columns
2271 }
2272 } while (tp++ < d && ++co);
2273
2274 // "co" is the column where "dot" is.
2275 // The screen has "columns" columns.
2276 // The currently displayed columns are 0+offset -- columns+ofset
2277 // |-------------------------------------------------------------|
2278 // ^ ^ ^
2279 // offset | |------- columns ----------------|
2280 //
2281 // If "co" is already in this range then we do not have to adjust offset
2282 // but, we do have to subtract the "offset" bias from "co".
2283 // If "co" is outside this range then we have to change "offset".
2284 // If the first char of a line is a tab the cursor will try to stay
2285 // in column 7, but we have to set offset to 0.
2286
2287 if (co < 0 + offset) {
2288 offset = co;
2289 }
2290 if (co >= columns + offset) {
2291 offset = co - columns + 1;
2292 }
2293 // if the first char of the line is a tab, and "dot" is sitting on it
2294 // force offset to 0.
2295 if (d == beg_cur && *d == '\t') {
2296 offset = 0;
2297 }
2298 co -= offset;
2299
2300 *row = ro;
2301 *col = co;
2302}
2303
2304//----- Text Movement Routines ---------------------------------
2305static Byte *begin_line(Byte * p) // return pointer to first char cur line
2306{
2307 while (p > text && p[-1] != '\n')
2308 p--; // go to cur line B-o-l
2309 return (p);
2310}
2311
2312static Byte *end_line(Byte * p) // return pointer to NL of cur line line
2313{
2314 while (p < end - 1 && *p != '\n')
2315 p++; // go to cur line E-o-l
2316 return (p);
2317}
2318
2319static Byte *dollar_line(Byte * p) // return pointer to just before NL line
2320{
2321 while (p < end - 1 && *p != '\n')
2322 p++; // go to cur line E-o-l
2323 // Try to stay off of the Newline
2324 if (*p == '\n' && (p - begin_line(p)) > 0)
2325 p--;
2326 return (p);
2327}
2328
2329static Byte *prev_line(Byte * p) // return pointer first char prev line
2330{
2331 p = begin_line(p); // goto begining of cur line
2332 if (p[-1] == '\n' && p > text)
2333 p--; // step to prev line
2334 p = begin_line(p); // goto begining of prev line
2335 return (p);
2336}
2337
2338static Byte *next_line(Byte * p) // return pointer first char next line
2339{
2340 p = end_line(p);
2341 if (*p == '\n' && p < end - 1)
2342 p++; // step to next line
2343 return (p);
2344}
2345
2346//----- Text Information Routines ------------------------------
2347static Byte *end_screen(void)
2348{
2349 Byte *q;
2350 int cnt;
2351
2352 // find new bottom line
2353 q = screenbegin;
2354 for (cnt = 0; cnt < rows - 2; cnt++)
2355 q = next_line(q);
2356 q = end_line(q);
2357 return (q);
2358}
2359
2360static int count_lines(Byte * start, Byte * stop) // count line from start to stop
2361{
2362 Byte *q;
2363 int cnt;
2364
2365 if (stop < start) { // start and stop are backwards- reverse them
2366 q = start;
2367 start = stop;
2368 stop = q;
2369 }
2370 cnt = 0;
2371 stop = end_line(stop); // get to end of this line
2372 for (q = start; q <= stop && q <= end - 1; q++) {
2373 if (*q == '\n')
2374 cnt++;
2375 }
2376 return (cnt);
2377}
2378
2379static Byte *find_line(int li) // find begining of line #li
2380{
2381 Byte *q;
2382
2383 for (q = text; li > 1; li--) {
2384 q = next_line(q);
2385 }
2386 return (q);
2387}
2388
2389//----- Dot Movement Routines ----------------------------------
2390static void dot_left(void)
2391{
2392 if (dot > text && dot[-1] != '\n')
2393 dot--;
2394}
2395
2396static void dot_right(void)
2397{
2398 if (dot < end - 1 && *dot != '\n')
2399 dot++;
2400}
2401
2402static void dot_begin(void)
2403{
2404 dot = begin_line(dot); // return pointer to first char cur line
2405}
2406
2407static void dot_end(void)
2408{
2409 dot = end_line(dot); // return pointer to last char cur line
2410}
2411
2412static Byte *move_to_col(Byte * p, int l)
2413{
2414 int co;
2415
2416 p = begin_line(p);
2417 co = 0;
2418 do {
2419 if (*p == '\n' || *p == '\0')
2420 break;
2421 if (*p == '\t') {
2422 // 7 - (co % 8 )
2423 co += ((tabstop - 1) - (co % tabstop));
2424 } else if (*p < ' ') {
2425 co++; // display as ^X, use 2 columns
2426 }
2427 } while (++co <= l && p++ < end);
2428 return (p);
2429}
2430
2431static void dot_next(void)
2432{
2433 dot = next_line(dot);
2434}
2435
2436static void dot_prev(void)
2437{
2438 dot = prev_line(dot);
2439}
2440
2441static void dot_scroll(int cnt, int dir)
2442{
2443 Byte *q;
2444
2445 for (; cnt > 0; cnt--) {
2446 if (dir < 0) {
2447 // scroll Backwards
2448 // ctrl-Y scroll up one line
2449 screenbegin = prev_line(screenbegin);
2450 } else {
2451 // scroll Forwards
2452 // ctrl-E scroll down one line
2453 screenbegin = next_line(screenbegin);
2454 }
2455 }
2456 // make sure "dot" stays on the screen so we dont scroll off
2457 if (dot < screenbegin)
2458 dot = screenbegin;
2459 q = end_screen(); // find new bottom line
2460 if (dot > q)
2461 dot = begin_line(q); // is dot is below bottom line?
2462 dot_skip_over_ws();
2463}
2464
2465static void dot_skip_over_ws(void)
2466{
2467 // skip WS
2468 while (isspace(*dot) && *dot != '\n' && dot < end - 1)
2469 dot++;
2470}
2471
2472static void dot_delete(void) // delete the char at 'dot'
2473{
2474 (void) text_hole_delete(dot, dot);
2475}
2476
2477static Byte *bound_dot(Byte * p) // make sure text[0] <= P < "end"
2478{
2479 if (p >= end && end > text) {
2480 p = end - 1;
2481 indicate_error('1');
2482 }
2483 if (p < text) {
2484 p = text;
2485 indicate_error('2');
2486 }
2487 return (p);
2488}
2489
2490//----- Helper Utility Routines --------------------------------
2491
2492//----------------------------------------------------------------
2493//----- Char Routines --------------------------------------------
2494/* Chars that are part of a word-
2495 * 0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
2496 * Chars that are Not part of a word (stoppers)
2497 * !"#$%&'()*+,-./:;<=>?@[\]^`{|}~
2498 * Chars that are WhiteSpace
2499 * TAB NEWLINE VT FF RETURN SPACE
2500 * DO NOT COUNT NEWLINE AS WHITESPACE
2501 */
2502
2503static Byte *new_screen(int ro, int co)
2504{
Eric Andersen822c3832001-05-07 17:37:43 +00002505 int li;
2506
Eric Andersen3f980402001-04-04 17:31:15 +00002507 if (screen != 0)
2508 free(screen);
2509 screensize = ro * co + 8;
Eric Andersen80f5ac72001-11-17 06:57:42 +00002510 screen = (Byte *) xmalloc(screensize);
Eric Andersen822c3832001-05-07 17:37:43 +00002511 // initialize the new screen. assume this will be a empty file.
2512 screen_erase();
2513 // non-existant text[] lines start with a tilde (~).
2514 for (li = 1; li < ro - 1; li++) {
2515 screen[(li * co) + 0] = '~';
2516 }
Eric Andersen3f980402001-04-04 17:31:15 +00002517 return (screen);
2518}
2519
2520static Byte *new_text(int size)
2521{
2522 if (size < 10240)
2523 size = 10240; // have a minimum size for new files
2524 if (text != 0) {
2525 //text -= 4;
2526 free(text);
2527 }
Eric Andersen80f5ac72001-11-17 06:57:42 +00002528 text = (Byte *) xmalloc(size + 8);
Eric Andersen3f980402001-04-04 17:31:15 +00002529 memset(text, '\0', size); // clear new text[]
Eric Andersen822c3832001-05-07 17:37:43 +00002530 //text += 4; // leave some room for "oops"
Eric Andersen3f980402001-04-04 17:31:15 +00002531 textend = text + size - 1;
Eric Andersen822c3832001-05-07 17:37:43 +00002532 //textend -= 4; // leave some root for "oops"
Eric Andersen3f980402001-04-04 17:31:15 +00002533 return (text);
2534}
2535
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002536#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +00002537static int mycmp(Byte * s1, Byte * s2, int len)
2538{
2539 int i;
2540
2541 i = strncmp((char *) s1, (char *) s2, len);
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002542#ifdef CONFIG_FEATURE_VI_SETOPTS
Eric Andersen3f980402001-04-04 17:31:15 +00002543 if (ignorecase) {
2544 i = strncasecmp((char *) s1, (char *) s2, len);
2545 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002546#endif /* CONFIG_FEATURE_VI_SETOPTS */
Eric Andersen3f980402001-04-04 17:31:15 +00002547 return (i);
2548}
2549
2550static Byte *char_search(Byte * p, Byte * pat, int dir, int range) // search for pattern starting at p
2551{
2552#ifndef REGEX_SEARCH
2553 Byte *start, *stop;
2554 int len;
2555
2556 len = strlen((char *) pat);
2557 if (dir == FORWARD) {
2558 stop = end - 1; // assume range is p - end-1
2559 if (range == LIMITED)
2560 stop = next_line(p); // range is to next line
2561 for (start = p; start < stop; start++) {
2562 if (mycmp(start, pat, len) == 0) {
2563 return (start);
2564 }
2565 }
2566 } else if (dir == BACK) {
2567 stop = text; // assume range is text - p
2568 if (range == LIMITED)
2569 stop = prev_line(p); // range is to prev line
2570 for (start = p - len; start >= stop; start--) {
2571 if (mycmp(start, pat, len) == 0) {
2572 return (start);
2573 }
2574 }
2575 }
2576 // pattern not found
2577 return (NULL);
2578#else /*REGEX_SEARCH */
2579 char *q;
2580 struct re_pattern_buffer preg;
2581 int i;
2582 int size, range;
2583
2584 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
2585 preg.translate = 0;
2586 preg.fastmap = 0;
2587 preg.buffer = 0;
2588 preg.allocated = 0;
2589
2590 // assume a LIMITED forward search
2591 q = next_line(p);
2592 q = end_line(q);
2593 q = end - 1;
2594 if (dir == BACK) {
2595 q = prev_line(p);
2596 q = text;
2597 }
2598 // count the number of chars to search over, forward or backward
2599 size = q - p;
2600 if (size < 0)
2601 size = p - q;
2602 // RANGE could be negative if we are searching backwards
2603 range = q - p;
2604
2605 q = (char *) re_compile_pattern(pat, strlen((char *) pat), &preg);
2606 if (q != 0) {
2607 // The pattern was not compiled
2608 psbs("bad search pattern: \"%s\": %s", pat, q);
2609 i = 0; // return p if pattern not compiled
2610 goto cs1;
2611 }
2612
2613 q = p;
2614 if (range < 0) {
2615 q = p - size;
2616 if (q < text)
2617 q = text;
2618 }
2619 // search for the compiled pattern, preg, in p[]
2620 // range < 0- search backward
2621 // range > 0- search forward
2622 // 0 < start < size
2623 // re_search() < 0 not found or error
2624 // re_search() > 0 index of found pattern
2625 // struct pattern char int int int struct reg
2626 // re_search (*pattern_buffer, *string, size, start, range, *regs)
2627 i = re_search(&preg, q, size, 0, range, 0);
2628 if (i == -1) {
2629 p = 0;
2630 i = 0; // return NULL if pattern not found
2631 }
2632 cs1:
2633 if (dir == FORWARD) {
2634 p = p + i;
2635 } else {
2636 p = p - i;
2637 }
2638 return (p);
2639#endif /*REGEX_SEARCH */
2640}
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002641#endif /* CONFIG_FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +00002642
2643static Byte *char_insert(Byte * p, Byte c) // insert the char c at 'p'
2644{
2645 if (c == 22) { // Is this an ctrl-V?
2646 p = stupid_insert(p, '^'); // use ^ to indicate literal next
2647 p--; // backup onto ^
2648 refresh(FALSE); // show the ^
2649 c = get_one_char();
2650 *p = c;
2651 p++;
2652 file_modified = TRUE; // has the file been modified
2653 } else if (c == 27) { // Is this an ESC?
2654 cmd_mode = 0;
2655 cmdcnt = 0;
2656 end_cmd_q(); // stop adding to q
Eric Andersen822c3832001-05-07 17:37:43 +00002657 strcpy((char *) status_buffer, " "); // clear the status buffer
Eric Andersenfdbac352001-08-02 05:26:41 +00002658 if ((p[-1] != '\n') && (dot>text)) {
Eric Andersen3f980402001-04-04 17:31:15 +00002659 p--;
2660 }
2661 } else if (c == erase_char) { // Is this a BS
2662 // 123456789
Eric Andersenfdbac352001-08-02 05:26:41 +00002663 if ((p[-1] != '\n') && (dot>text)) {
Eric Andersen3f980402001-04-04 17:31:15 +00002664 p--;
2665 p = text_hole_delete(p, p); // shrink buffer 1 char
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002666#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00002667 // also rmove char from last_modifying_cmd
2668 if (strlen((char *) last_modifying_cmd) > 0) {
2669 Byte *q;
2670
2671 q = last_modifying_cmd;
2672 q[strlen((char *) q) - 1] = '\0'; // erase BS
2673 q[strlen((char *) q) - 1] = '\0'; // erase prev char
2674 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002675#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +00002676 }
2677 } else {
2678 // insert a char into text[]
2679 Byte *sp; // "save p"
2680
2681 if (c == 13)
2682 c = '\n'; // translate \r to \n
2683 sp = p; // remember addr of insert
2684 p = stupid_insert(p, c); // insert the char
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002685#ifdef CONFIG_FEATURE_VI_SETOPTS
Eric Andersen3f980402001-04-04 17:31:15 +00002686 if (showmatch && strchr(")]}", *sp) != NULL) {
2687 showmatching(sp);
2688 }
2689 if (autoindent && c == '\n') { // auto indent the new line
2690 Byte *q;
2691
2692 q = prev_line(p); // use prev line as templet
2693 for (; isblnk(*q); q++) {
2694 p = stupid_insert(p, *q); // insert the char
2695 }
2696 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002697#endif /* CONFIG_FEATURE_VI_SETOPTS */
Eric Andersen3f980402001-04-04 17:31:15 +00002698 }
2699 return (p);
2700}
2701
2702static Byte *stupid_insert(Byte * p, Byte c) // stupidly insert the char c at 'p'
2703{
2704 p = text_hole_make(p, 1);
2705 if (p != 0) {
2706 *p = c;
2707 file_modified = TRUE; // has the file been modified
2708 p++;
2709 }
2710 return (p);
2711}
2712
2713static Byte find_range(Byte ** start, Byte ** stop, Byte c)
2714{
2715 Byte *save_dot, *p, *q;
2716 int cnt;
2717
2718 save_dot = dot;
2719 p = q = dot;
2720
2721 if (strchr("cdy><", c)) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00002722 // these cmds operate on whole lines
Eric Andersen3f980402001-04-04 17:31:15 +00002723 p = q = begin_line(p);
2724 for (cnt = 1; cnt < cmdcnt; cnt++) {
2725 q = next_line(q);
2726 }
2727 q = end_line(q);
Eric Andersen822c3832001-05-07 17:37:43 +00002728 } else if (strchr("^%$0bBeEft", c)) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00002729 // These cmds operate on char positions
Eric Andersen3f980402001-04-04 17:31:15 +00002730 do_cmd(c); // execute movement cmd
2731 q = dot;
2732 } else if (strchr("wW", c)) {
2733 do_cmd(c); // execute movement cmd
2734 if (dot > text)
2735 dot--; // move back off of next word
2736 if (dot > text && *dot == '\n')
2737 dot--; // stay off NL
2738 q = dot;
2739 } else if (strchr("H-k{", c)) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00002740 // these operate on multi-lines backwards
Eric Andersen3f980402001-04-04 17:31:15 +00002741 q = end_line(dot); // find NL
2742 do_cmd(c); // execute movement cmd
2743 dot_begin();
2744 p = dot;
2745 } else if (strchr("L+j}\r\n", c)) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00002746 // these operate on multi-lines forwards
Eric Andersen3f980402001-04-04 17:31:15 +00002747 p = begin_line(dot);
2748 do_cmd(c); // execute movement cmd
2749 dot_end(); // find NL
2750 q = dot;
2751 } else {
2752 c = 27; // error- return an ESC char
2753 //break;
2754 }
2755 *start = p;
2756 *stop = q;
2757 if (q < p) {
2758 *start = q;
2759 *stop = p;
2760 }
2761 dot = save_dot;
2762 return (c);
2763}
2764
2765static int st_test(Byte * p, int type, int dir, Byte * tested)
2766{
2767 Byte c, c0, ci;
2768 int test, inc;
2769
2770 inc = dir;
2771 c = c0 = p[0];
2772 ci = p[inc];
2773 test = 0;
2774
2775 if (type == S_BEFORE_WS) {
2776 c = ci;
2777 test = ((!isspace(c)) || c == '\n');
2778 }
2779 if (type == S_TO_WS) {
2780 c = c0;
2781 test = ((!isspace(c)) || c == '\n');
2782 }
2783 if (type == S_OVER_WS) {
2784 c = c0;
2785 test = ((isspace(c)));
2786 }
2787 if (type == S_END_PUNCT) {
2788 c = ci;
2789 test = ((ispunct(c)));
2790 }
2791 if (type == S_END_ALNUM) {
2792 c = ci;
2793 test = ((isalnum(c)) || c == '_');
2794 }
2795 *tested = c;
2796 return (test);
2797}
2798
2799static Byte *skip_thing(Byte * p, int linecnt, int dir, int type)
2800{
2801 Byte c;
2802
2803 while (st_test(p, type, dir, &c)) {
2804 // make sure we limit search to correct number of lines
2805 if (c == '\n' && --linecnt < 1)
2806 break;
2807 if (dir >= 0 && p >= end - 1)
2808 break;
2809 if (dir < 0 && p <= text)
2810 break;
2811 p += dir; // move to next char
2812 }
2813 return (p);
2814}
2815
2816// find matching char of pair () [] {}
2817static Byte *find_pair(Byte * p, Byte c)
2818{
2819 Byte match, *q;
2820 int dir, level;
2821
2822 match = ')';
2823 level = 1;
2824 dir = 1; // assume forward
2825 switch (c) {
2826 case '(':
2827 match = ')';
2828 break;
2829 case '[':
2830 match = ']';
2831 break;
2832 case '{':
2833 match = '}';
2834 break;
2835 case ')':
2836 match = '(';
2837 dir = -1;
2838 break;
2839 case ']':
2840 match = '[';
2841 dir = -1;
2842 break;
2843 case '}':
2844 match = '{';
2845 dir = -1;
2846 break;
2847 }
2848 for (q = p + dir; text <= q && q < end; q += dir) {
2849 // look for match, count levels of pairs (( ))
2850 if (*q == c)
2851 level++; // increase pair levels
2852 if (*q == match)
2853 level--; // reduce pair level
2854 if (level == 0)
2855 break; // found matching pair
2856 }
2857 if (level != 0)
2858 q = NULL; // indicate no match
2859 return (q);
2860}
2861
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002862#ifdef CONFIG_FEATURE_VI_SETOPTS
Eric Andersen3f980402001-04-04 17:31:15 +00002863// show the matching char of a pair, () [] {}
2864static void showmatching(Byte * p)
2865{
2866 Byte *q, *save_dot;
2867
2868 // we found half of a pair
2869 q = find_pair(p, *p); // get loc of matching char
2870 if (q == NULL) {
2871 indicate_error('3'); // no matching char
2872 } else {
2873 // "q" now points to matching pair
2874 save_dot = dot; // remember where we are
2875 dot = q; // go to new loc
2876 refresh(FALSE); // let the user see it
2877 (void) mysleep(40); // give user some time
2878 dot = save_dot; // go back to old loc
2879 refresh(FALSE);
2880 }
2881}
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002882#endif /* CONFIG_FEATURE_VI_SETOPTS */
Eric Andersen3f980402001-04-04 17:31:15 +00002883
2884// open a hole in text[]
2885static Byte *text_hole_make(Byte * p, int size) // at "p", make a 'size' byte hole
2886{
2887 Byte *src, *dest;
2888 int cnt;
2889
2890 if (size <= 0)
2891 goto thm0;
2892 src = p;
2893 dest = p + size;
2894 cnt = end - src; // the rest of buffer
2895 if (memmove(dest, src, cnt) != dest) {
2896 psbs("can't create room for new characters");
2897 }
2898 memset(p, ' ', size); // clear new hole
2899 end = end + size; // adjust the new END
2900 file_modified = TRUE; // has the file been modified
2901 thm0:
2902 return (p);
2903}
2904
2905// close a hole in text[]
2906static Byte *text_hole_delete(Byte * p, Byte * q) // delete "p" thru "q", inclusive
2907{
2908 Byte *src, *dest;
2909 int cnt, hole_size;
2910
2911 // move forwards, from beginning
2912 // assume p <= q
2913 src = q + 1;
2914 dest = p;
2915 if (q < p) { // they are backward- swap them
2916 src = p + 1;
2917 dest = q;
2918 }
2919 hole_size = q - p + 1;
2920 cnt = end - src;
2921 if (src < text || src > end)
2922 goto thd0;
2923 if (dest < text || dest >= end)
2924 goto thd0;
2925 if (src >= end)
2926 goto thd_atend; // just delete the end of the buffer
2927 if (memmove(dest, src, cnt) != dest) {
2928 psbs("can't delete the character");
2929 }
2930 thd_atend:
2931 end = end - hole_size; // adjust the new END
2932 if (dest >= end)
2933 dest = end - 1; // make sure dest in below end-1
2934 if (end <= text)
2935 dest = end = text; // keep pointers valid
2936 file_modified = TRUE; // has the file been modified
2937 thd0:
2938 return (dest);
2939}
2940
2941// copy text into register, then delete text.
2942// if dist <= 0, do not include, or go past, a NewLine
2943//
2944static Byte *yank_delete(Byte * start, Byte * stop, int dist, int yf)
2945{
2946 Byte *p;
2947
2948 // make sure start <= stop
2949 if (start > stop) {
2950 // they are backwards, reverse them
2951 p = start;
2952 start = stop;
2953 stop = p;
2954 }
2955 if (dist <= 0) {
2956 // we can not cross NL boundaries
2957 p = start;
2958 if (*p == '\n')
2959 return (p);
2960 // dont go past a NewLine
2961 for (; p + 1 <= stop; p++) {
2962 if (p[1] == '\n') {
2963 stop = p; // "stop" just before NewLine
2964 break;
2965 }
2966 }
2967 }
2968 p = start;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002969#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00002970 text_yank(start, stop, YDreg);
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002971#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00002972 if (yf == YANKDEL) {
2973 p = text_hole_delete(start, stop);
2974 } // delete lines
2975 return (p);
2976}
2977
2978static void show_help(void)
2979{
Eric Andersendd8500b2001-07-02 18:06:14 +00002980 puts("These features are available:"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002981#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersendd8500b2001-07-02 18:06:14 +00002982 "\n\tPattern searches with / and ?"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002983#endif /* CONFIG_FEATURE_VI_SEARCH */
2984#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersendd8500b2001-07-02 18:06:14 +00002985 "\n\tLast command repeat with \'.\'"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002986#endif /* CONFIG_FEATURE_VI_DOT_CMD */
2987#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersendd8500b2001-07-02 18:06:14 +00002988 "\n\tLine marking with 'x"
2989 "\n\tNamed buffers with \"x"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002990#endif /* CONFIG_FEATURE_VI_YANKMARK */
2991#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersendd8500b2001-07-02 18:06:14 +00002992 "\n\tReadonly if vi is called as \"view\""
2993 "\n\tReadonly with -R command line arg"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002994#endif /* CONFIG_FEATURE_VI_READONLY */
2995#ifdef CONFIG_FEATURE_VI_SET
Eric Andersendd8500b2001-07-02 18:06:14 +00002996 "\n\tSome colon mode commands with \':\'"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002997#endif /* CONFIG_FEATURE_VI_SET */
2998#ifdef CONFIG_FEATURE_VI_SETOPTS
Eric Andersendd8500b2001-07-02 18:06:14 +00002999 "\n\tSettable options with \":set\""
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003000#endif /* CONFIG_FEATURE_VI_SETOPTS */
3001#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersendd8500b2001-07-02 18:06:14 +00003002 "\n\tSignal catching- ^C"
3003 "\n\tJob suspend and resume with ^Z"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003004#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
3005#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
Eric Andersendd8500b2001-07-02 18:06:14 +00003006 "\n\tAdapt to window re-sizes"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003007#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
Eric Andersendd8500b2001-07-02 18:06:14 +00003008 );
Eric Andersen3f980402001-04-04 17:31:15 +00003009}
3010
3011static void print_literal(Byte * buf, Byte * s) // copy s to buf, convert unprintable
3012{
3013 Byte c, b[2];
3014
3015 b[1] = '\0';
3016 strcpy((char *) buf, ""); // init buf
3017 if (strlen((char *) s) <= 0)
3018 s = (Byte *) "(NULL)";
3019 for (; *s > '\0'; s++) {
3020 c = *s;
3021 if (*s > '~') {
3022 strcat((char *) buf, SOs);
3023 c = *s - 128;
3024 }
3025 if (*s < ' ') {
3026 strcat((char *) buf, "^");
3027 c += '@';
3028 }
3029 b[0] = c;
3030 strcat((char *) buf, (char *) b);
3031 if (*s > '~')
3032 strcat((char *) buf, SOn);
3033 if (*s == '\n') {
3034 strcat((char *) buf, "$");
3035 }
3036 }
3037}
3038
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003039#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003040static void start_new_cmd_q(Byte c)
3041{
3042 // release old cmd
3043 if (last_modifying_cmd != 0)
3044 free(last_modifying_cmd);
3045 // get buffer for new cmd
Eric Andersen80f5ac72001-11-17 06:57:42 +00003046 last_modifying_cmd = (Byte *) xmalloc(BUFSIZ);
Eric Andersen3f980402001-04-04 17:31:15 +00003047 memset(last_modifying_cmd, '\0', BUFSIZ); // clear new cmd queue
3048 // if there is a current cmd count put it in the buffer first
3049 if (cmdcnt > 0)
3050 sprintf((char *) last_modifying_cmd, "%d", cmdcnt);
3051 // save char c onto queue
3052 last_modifying_cmd[strlen((char *) last_modifying_cmd)] = c;
3053 adding2q = 1;
3054 return;
3055}
3056
Eric Andersenbff7a602001-11-17 07:15:43 +00003057static void end_cmd_q(void)
Eric Andersen3f980402001-04-04 17:31:15 +00003058{
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003059#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003060 YDreg = 26; // go back to default Yank/Delete reg
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003061#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003062 adding2q = 0;
3063 return;
3064}
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003065#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +00003066
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003067#if defined(CONFIG_FEATURE_VI_YANKMARK) || defined(CONFIG_FEATURE_VI_COLON) || defined(CONFIG_FEATURE_VI_CRASHME)
Eric Andersen3f980402001-04-04 17:31:15 +00003068static Byte *string_insert(Byte * p, Byte * s) // insert the string at 'p'
3069{
3070 int cnt, i;
3071
3072 i = strlen((char *) s);
3073 p = text_hole_make(p, i);
3074 strncpy((char *) p, (char *) s, i);
3075 for (cnt = 0; *s != '\0'; s++) {
3076 if (*s == '\n')
3077 cnt++;
3078 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003079#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003080 psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003081#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003082 return (p);
3083}
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003084#endif /* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_COLON || CONFIG_FEATURE_VI_CRASHME */
Eric Andersen3f980402001-04-04 17:31:15 +00003085
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003086#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003087static Byte *text_yank(Byte * p, Byte * q, int dest) // copy text into a register
3088{
3089 Byte *t;
3090 int cnt;
3091
3092 if (q < p) { // they are backwards- reverse them
3093 t = q;
3094 q = p;
3095 p = t;
3096 }
3097 cnt = q - p + 1;
3098 t = reg[dest];
3099 if (t != 0) { // if already a yank register
3100 free(t); // free it
3101 }
Eric Andersen80f5ac72001-11-17 06:57:42 +00003102 t = (Byte *) xmalloc(cnt + 1); // get a new register
Eric Andersen3f980402001-04-04 17:31:15 +00003103 memset(t, '\0', cnt + 1); // clear new text[]
3104 strncpy((char *) t, (char *) p, cnt); // copy text[] into bufer
3105 reg[dest] = t;
3106 return (p);
3107}
3108
3109static Byte what_reg(void)
3110{
3111 Byte c;
3112 int i;
3113
3114 i = 0;
3115 c = 'D'; // default to D-reg
3116 if (0 <= YDreg && YDreg <= 25)
3117 c = 'a' + (Byte) YDreg;
3118 if (YDreg == 26)
3119 c = 'D';
3120 if (YDreg == 27)
3121 c = 'U';
3122 return (c);
3123}
3124
3125static void check_context(Byte cmd)
3126{
3127 // A context is defined to be "modifying text"
3128 // Any modifying command establishes a new context.
3129
3130 if (dot < context_start || dot > context_end) {
3131 if (strchr((char *) modifying_cmds, cmd) != NULL) {
3132 // we are trying to modify text[]- make this the current context
3133 mark[27] = mark[26]; // move cur to prev
3134 mark[26] = dot; // move local to cur
3135 context_start = prev_line(prev_line(dot));
3136 context_end = next_line(next_line(dot));
3137 //loiter= start_loiter= now;
3138 }
3139 }
3140 return;
3141}
3142
3143static Byte *swap_context(Byte * p) // goto new context for '' command make this the current context
3144{
3145 Byte *tmp;
3146
3147 // the current context is in mark[26]
3148 // the previous context is in mark[27]
3149 // only swap context if other context is valid
3150 if (text <= mark[27] && mark[27] <= end - 1) {
3151 tmp = mark[27];
3152 mark[27] = mark[26];
3153 mark[26] = tmp;
3154 p = mark[26]; // where we are going- previous context
3155 context_start = prev_line(prev_line(prev_line(p)));
3156 context_end = next_line(next_line(next_line(p)));
3157 }
3158 return (p);
3159}
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003160#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003161
3162static int isblnk(Byte c) // is the char a blank or tab
3163{
3164 return (c == ' ' || c == '\t');
3165}
3166
3167//----- Set terminal attributes --------------------------------
3168static void rawmode(void)
3169{
3170 tcgetattr(0, &term_orig);
3171 term_vi = term_orig;
3172 term_vi.c_lflag &= (~ICANON & ~ECHO); // leave ISIG ON- allow intr's
3173 term_vi.c_iflag &= (~IXON & ~ICRNL);
Eric Andersen822c3832001-05-07 17:37:43 +00003174 term_vi.c_oflag &= (~ONLCR);
Glenn L McGrath78b0e372001-06-26 02:06:08 +00003175#ifndef linux
Eric Andersen3f980402001-04-04 17:31:15 +00003176 term_vi.c_cc[VMIN] = 1;
3177 term_vi.c_cc[VTIME] = 0;
Glenn L McGrath78b0e372001-06-26 02:06:08 +00003178#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003179 erase_char = term_vi.c_cc[VERASE];
3180 tcsetattr(0, TCSANOW, &term_vi);
3181}
3182
3183static void cookmode(void)
3184{
3185 tcsetattr(0, TCSANOW, &term_orig);
3186}
3187
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003188#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
Eric Andersen3f980402001-04-04 17:31:15 +00003189//----- See what the window size currently is --------------------
3190static void window_size_get(int sig)
3191{
3192 int i;
3193
3194 i = ioctl(0, TIOCGWINSZ, &winsize);
3195 if (i != 0) {
3196 // force 24x80
3197 winsize.ws_row = 24;
3198 winsize.ws_col = 80;
3199 }
3200 if (winsize.ws_row <= 1) {
3201 winsize.ws_row = 24;
3202 }
3203 if (winsize.ws_col <= 1) {
3204 winsize.ws_col = 80;
3205 }
3206 rows = (int) winsize.ws_row;
3207 columns = (int) winsize.ws_col;
3208}
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003209#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
Eric Andersen3f980402001-04-04 17:31:15 +00003210
3211//----- Come here when we get a window resize signal ---------
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003212#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +00003213static void winch_sig(int sig)
3214{
3215 signal(SIGWINCH, winch_sig);
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003216#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
Eric Andersen3f980402001-04-04 17:31:15 +00003217 window_size_get(0);
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003218#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
Eric Andersen3f980402001-04-04 17:31:15 +00003219 new_screen(rows, columns); // get memory for virtual screen
3220 redraw(TRUE); // re-draw the screen
3221}
3222
3223//----- Come here when we get a continue signal -------------------
3224static void cont_sig(int sig)
3225{
3226 rawmode(); // terminal to "raw"
3227 *status_buffer = '\0'; // clear the status buffer
3228 redraw(TRUE); // re-draw the screen
3229
3230 signal(SIGTSTP, suspend_sig);
3231 signal(SIGCONT, SIG_DFL);
3232 kill(getpid(), SIGCONT);
3233}
3234
3235//----- Come here when we get a Suspend signal -------------------
3236static void suspend_sig(int sig)
3237{
Eric Andersen822c3832001-05-07 17:37:43 +00003238 place_cursor(rows - 1, 0, FALSE); // go to bottom of screen
Eric Andersen3f980402001-04-04 17:31:15 +00003239 clear_to_eol(); // Erase to end of line
3240 cookmode(); // terminal to "cooked"
3241
3242 signal(SIGCONT, cont_sig);
3243 signal(SIGTSTP, SIG_DFL);
3244 kill(getpid(), SIGTSTP);
3245}
3246
Eric Andersen822c3832001-05-07 17:37:43 +00003247//----- Come here when we get a signal ---------------------------
Eric Andersen3f980402001-04-04 17:31:15 +00003248static void catch_sig(int sig)
3249{
3250 signal(SIGHUP, catch_sig);
3251 signal(SIGINT, catch_sig);
3252 signal(SIGTERM, catch_sig);
3253 longjmp(restart, sig);
3254}
3255
3256static void alarm_sig(int sig)
3257{
3258 signal(SIGALRM, catch_sig);
3259 longjmp(restart, sig);
3260}
3261
3262//----- Come here when we get a core dump signal -----------------
3263static void core_sig(int sig)
3264{
3265 signal(SIGQUIT, core_sig);
3266 signal(SIGILL, core_sig);
3267 signal(SIGTRAP, core_sig);
3268 signal(SIGIOT, core_sig);
3269 signal(SIGABRT, core_sig);
3270 signal(SIGFPE, core_sig);
3271 signal(SIGBUS, core_sig);
3272 signal(SIGSEGV, core_sig);
Eric Andersend402edf2001-04-04 19:29:48 +00003273#ifdef SIGSYS
Eric Andersen3f980402001-04-04 17:31:15 +00003274 signal(SIGSYS, core_sig);
Eric Andersend402edf2001-04-04 19:29:48 +00003275#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003276
3277 dot = bound_dot(dot); // make sure "dot" is valid
3278
3279 longjmp(restart, sig);
3280}
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003281#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Eric Andersen3f980402001-04-04 17:31:15 +00003282
3283static int mysleep(int hund) // sleep for 'h' 1/100 seconds
3284{
3285 // Don't hang- Wait 5/100 seconds- 1 Sec= 1000000
3286 FD_ZERO(&rfds);
3287 FD_SET(0, &rfds);
3288 tv.tv_sec = 0;
3289 tv.tv_usec = hund * 10000;
3290 select(1, &rfds, NULL, NULL, &tv);
3291 return (FD_ISSET(0, &rfds));
3292}
3293
3294//----- IO Routines --------------------------------------------
3295static Byte readit(void) // read (maybe cursor) key from stdin
3296{
3297 Byte c;
3298 int i, bufsiz, cnt, cmdindex;
3299 struct esc_cmds {
3300 Byte *seq;
3301 Byte val;
3302 };
3303
3304 static struct esc_cmds esccmds[] = {
3305 {(Byte *) "OA", (Byte) VI_K_UP}, // cursor key Up
3306 {(Byte *) "OB", (Byte) VI_K_DOWN}, // cursor key Down
3307 {(Byte *) "OC", (Byte) VI_K_RIGHT}, // Cursor Key Right
3308 {(Byte *) "OD", (Byte) VI_K_LEFT}, // cursor key Left
3309 {(Byte *) "OH", (Byte) VI_K_HOME}, // Cursor Key Home
3310 {(Byte *) "OF", (Byte) VI_K_END}, // Cursor Key End
3311 {(Byte *) "", (Byte) VI_K_UP}, // cursor key Up
3312 {(Byte *) "", (Byte) VI_K_DOWN}, // cursor key Down
3313 {(Byte *) "", (Byte) VI_K_RIGHT}, // Cursor Key Right
3314 {(Byte *) "", (Byte) VI_K_LEFT}, // cursor key Left
3315 {(Byte *) "", (Byte) VI_K_HOME}, // Cursor Key Home
3316 {(Byte *) "", (Byte) VI_K_END}, // Cursor Key End
3317 {(Byte *) "[2~", (Byte) VI_K_INSERT}, // Cursor Key Insert
3318 {(Byte *) "[5~", (Byte) VI_K_PAGEUP}, // Cursor Key Page Up
3319 {(Byte *) "[6~", (Byte) VI_K_PAGEDOWN}, // Cursor Key Page Down
3320 {(Byte *) "OP", (Byte) VI_K_FUN1}, // Function Key F1
3321 {(Byte *) "OQ", (Byte) VI_K_FUN2}, // Function Key F2
3322 {(Byte *) "OR", (Byte) VI_K_FUN3}, // Function Key F3
3323 {(Byte *) "OS", (Byte) VI_K_FUN4}, // Function Key F4
3324 {(Byte *) "[15~", (Byte) VI_K_FUN5}, // Function Key F5
3325 {(Byte *) "[17~", (Byte) VI_K_FUN6}, // Function Key F6
3326 {(Byte *) "[18~", (Byte) VI_K_FUN7}, // Function Key F7
3327 {(Byte *) "[19~", (Byte) VI_K_FUN8}, // Function Key F8
3328 {(Byte *) "[20~", (Byte) VI_K_FUN9}, // Function Key F9
3329 {(Byte *) "[21~", (Byte) VI_K_FUN10}, // Function Key F10
3330 {(Byte *) "[23~", (Byte) VI_K_FUN11}, // Function Key F11
3331 {(Byte *) "[24~", (Byte) VI_K_FUN12}, // Function Key F12
3332 {(Byte *) "[11~", (Byte) VI_K_FUN1}, // Function Key F1
3333 {(Byte *) "[12~", (Byte) VI_K_FUN2}, // Function Key F2
3334 {(Byte *) "[13~", (Byte) VI_K_FUN3}, // Function Key F3
3335 {(Byte *) "[14~", (Byte) VI_K_FUN4}, // Function Key F4
3336 };
3337
3338#define ESCCMDS_COUNT (sizeof(esccmds)/sizeof(struct esc_cmds))
3339
3340 (void) alarm(0); // turn alarm OFF while we wait for input
3341 // get input from User- are there already input chars in Q?
3342 bufsiz = strlen((char *) readbuffer);
3343 if (bufsiz <= 0) {
3344 ri0:
3345 // the Q is empty, wait for a typed char
3346 bufsiz = read(0, readbuffer, BUFSIZ - 1);
3347 if (bufsiz < 0) {
3348 if (errno == EINTR)
3349 goto ri0; // interrupted sys call
3350 if (errno == EBADF)
3351 editing = 0;
3352 if (errno == EFAULT)
3353 editing = 0;
3354 if (errno == EINVAL)
3355 editing = 0;
3356 if (errno == EIO)
3357 editing = 0;
3358 errno = 0;
3359 bufsiz = 0;
3360 }
3361 readbuffer[bufsiz] = '\0';
3362 }
3363 // return char if it is not part of ESC sequence
3364 if (readbuffer[0] != 27)
3365 goto ri1;
3366
3367 // This is an ESC char. Is this Esc sequence?
3368 // Could be bare Esc key. See if there are any
3369 // more chars to read after the ESC. This would
3370 // be a Function or Cursor Key sequence.
3371 FD_ZERO(&rfds);
3372 FD_SET(0, &rfds);
3373 tv.tv_sec = 0;
Eric Andersen1c0d3112001-04-16 15:46:44 +00003374 tv.tv_usec = 50000; // Wait 5/100 seconds- 1 Sec=1000000
Eric Andersen3f980402001-04-04 17:31:15 +00003375
3376 // keep reading while there are input chars and room in buffer
3377 while (select(1, &rfds, NULL, NULL, &tv) > 0 && bufsiz <= (BUFSIZ - 5)) {
3378 // read the rest of the ESC string
3379 i = read(0, (void *) (readbuffer + bufsiz), BUFSIZ - bufsiz);
3380 if (i > 0) {
3381 bufsiz += i;
3382 readbuffer[bufsiz] = '\0'; // Terminate the string
3383 }
3384 }
3385 // Maybe cursor or function key?
3386 for (cmdindex = 0; cmdindex < ESCCMDS_COUNT; cmdindex++) {
3387 cnt = strlen((char *) esccmds[cmdindex].seq);
3388 i = strncmp((char *) esccmds[cmdindex].seq, (char *) readbuffer, cnt);
3389 if (i == 0) {
3390 // is a Cursor key- put derived value back into Q
3391 readbuffer[0] = esccmds[cmdindex].val;
3392 // squeeze out the ESC sequence
3393 for (i = 1; i < cnt; i++) {
3394 memmove(readbuffer + 1, readbuffer + 2, BUFSIZ - 2);
3395 readbuffer[BUFSIZ - 1] = '\0';
3396 }
3397 break;
3398 }
3399 }
3400 ri1:
3401 c = readbuffer[0];
3402 // remove one char from Q
3403 memmove(readbuffer, readbuffer + 1, BUFSIZ - 1);
3404 readbuffer[BUFSIZ - 1] = '\0';
3405 (void) alarm(3); // we are done waiting for input, turn alarm ON
3406 return (c);
3407}
3408
3409//----- IO Routines --------------------------------------------
3410static Byte get_one_char()
3411{
3412 static Byte c;
3413
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003414#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003415 // ! adding2q && ioq == 0 read()
3416 // ! adding2q && ioq != 0 *ioq
3417 // adding2q *last_modifying_cmd= read()
3418 if (!adding2q) {
3419 // we are not adding to the q.
3420 // but, we may be reading from a q
3421 if (ioq == 0) {
3422 // there is no current q, read from STDIN
3423 c = readit(); // get the users input
3424 } else {
3425 // there is a queue to get chars from first
3426 c = *ioq++;
3427 if (c == '\0') {
3428 // the end of the q, read from STDIN
3429 free(ioq_start);
3430 ioq_start = ioq = 0;
3431 c = readit(); // get the users input
3432 }
3433 }
3434 } else {
3435 // adding STDIN chars to q
3436 c = readit(); // get the users input
3437 if (last_modifying_cmd != 0) {
3438 // add new char to q
3439 last_modifying_cmd[strlen((char *) last_modifying_cmd)] = c;
3440 }
3441 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003442#else /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +00003443 c = readit(); // get the users input
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003444#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +00003445 return (c); // return the char, where ever it came from
3446}
3447
Eric Andersen3f980402001-04-04 17:31:15 +00003448static Byte *get_input_line(Byte * prompt) // get input line- use "status line"
3449{
Eric Andersen1c0d3112001-04-16 15:46:44 +00003450 Byte buf[BUFSIZ];
Eric Andersen3f980402001-04-04 17:31:15 +00003451 Byte c;
3452 int i;
3453 static Byte *obufp = NULL;
3454
3455 strcpy((char *) buf, (char *) prompt);
3456 *status_buffer = '\0'; // clear the status buffer
Eric Andersen822c3832001-05-07 17:37:43 +00003457 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
Eric Andersen3f980402001-04-04 17:31:15 +00003458 clear_to_eol(); // clear the line
3459 write(1, prompt, strlen((char *) prompt)); // write out the :, /, or ? prompt
3460
Eric Andersen1c0d3112001-04-16 15:46:44 +00003461 for (i = strlen((char *) buf); i < BUFSIZ;) {
Eric Andersen3f980402001-04-04 17:31:15 +00003462 c = get_one_char(); // read user input
Eric Andersen1c0d3112001-04-16 15:46:44 +00003463 if (c == '\n' || c == '\r' || c == 27)
Eric Andersen3f980402001-04-04 17:31:15 +00003464 break; // is this end of input
3465 if (c == erase_char) { // user wants to erase prev char
3466 i--; // backup to prev char
3467 buf[i] = '\0'; // erase the char
3468 buf[i + 1] = '\0'; // null terminate buffer
3469 write(1, " ", 3); // erase char on screen
3470 if (i <= 0) { // user backs up before b-o-l, exit
3471 break;
3472 }
3473 } else {
3474 buf[i] = c; // save char in buffer
3475 buf[i + 1] = '\0'; // make sure buffer is null terminated
3476 write(1, buf + i, 1); // echo the char back to user
3477 i++;
3478 }
3479 }
3480 refresh(FALSE);
3481 if (obufp != NULL)
3482 free(obufp);
Matt Kraaic8227632001-11-12 16:57:27 +00003483 obufp = (Byte *) xstrdup((char *) buf);
Eric Andersen3f980402001-04-04 17:31:15 +00003484 return (obufp);
3485}
Eric Andersen3f980402001-04-04 17:31:15 +00003486
3487static int file_size(Byte * fn) // what is the byte size of "fn"
3488{
3489 struct stat st_buf;
3490 int cnt, sr;
3491
Eric Andersen1c0d3112001-04-16 15:46:44 +00003492 if (fn == 0 || strlen(fn) <= 0)
Eric Andersen3f980402001-04-04 17:31:15 +00003493 return (-1);
3494 cnt = -1;
3495 sr = stat((char *) fn, &st_buf); // see if file exists
3496 if (sr >= 0) {
3497 cnt = (int) st_buf.st_size;
3498 }
3499 return (cnt);
3500}
3501
3502static int file_insert(Byte * fn, Byte * p, int size)
3503{
Eric Andersen1c0d3112001-04-16 15:46:44 +00003504 int fd, cnt;
Eric Andersen3f980402001-04-04 17:31:15 +00003505
3506 cnt = -1;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003507#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen1c0d3112001-04-16 15:46:44 +00003508 readonly = FALSE;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003509#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen1c0d3112001-04-16 15:46:44 +00003510 if (fn == 0 || strlen((char*) fn) <= 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003511 psbs("No filename given");
3512 goto fi0;
3513 }
Eric Andersen1c0d3112001-04-16 15:46:44 +00003514 if (size == 0) {
3515 // OK- this is just a no-op
3516 cnt = 0;
Eric Andersen3f980402001-04-04 17:31:15 +00003517 goto fi0;
3518 }
Eric Andersen1c0d3112001-04-16 15:46:44 +00003519 if (size < 0) {
3520 psbs("Trying to insert a negative number (%d) of characters", size);
Eric Andersen3f980402001-04-04 17:31:15 +00003521 goto fi0;
3522 }
Eric Andersen1c0d3112001-04-16 15:46:44 +00003523 if (p < text || p > end) {
3524 psbs("Trying to insert file outside of memory");
3525 goto fi0;
3526 }
3527
3528 // see if we can open the file
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003529#ifdef CONFIG_FEATURE_VI_READONLY
Matt Kraai1f0c4362001-12-20 23:13:26 +00003530 if (vi_readonly) goto fi1; // do not try write-mode
Eric Andersen822c3832001-05-07 17:37:43 +00003531#endif
Eric Andersen1c0d3112001-04-16 15:46:44 +00003532 fd = open((char *) fn, O_RDWR); // assume read & write
Eric Andersen3f980402001-04-04 17:31:15 +00003533 if (fd < 0) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00003534 // could not open for writing- maybe file is read only
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003535#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen822c3832001-05-07 17:37:43 +00003536 fi1:
3537#endif
Eric Andersen1c0d3112001-04-16 15:46:44 +00003538 fd = open((char *) fn, O_RDONLY); // try read-only
3539 if (fd < 0) {
3540 psbs("\"%s\" %s", fn, "could not open file");
3541 goto fi0;
3542 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003543#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen1c0d3112001-04-16 15:46:44 +00003544 // got the file- read-only
3545 readonly = TRUE;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003546#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen3f980402001-04-04 17:31:15 +00003547 }
3548 p = text_hole_make(p, size);
3549 cnt = read(fd, p, size);
3550 close(fd);
3551 if (cnt < 0) {
3552 cnt = -1;
3553 p = text_hole_delete(p, p + size - 1); // un-do buffer insert
3554 psbs("could not read file \"%s\"", fn);
3555 } else if (cnt < size) {
3556 // There was a partial read, shrink unused space text[]
3557 p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert
3558 psbs("could not read all of file \"%s\"", fn);
3559 }
3560 if (cnt >= size)
3561 file_modified = TRUE;
3562 fi0:
3563 return (cnt);
3564}
3565
3566static int file_write(Byte * fn, Byte * first, Byte * last)
3567{
3568 int fd, cnt, charcnt;
3569
3570 if (fn == 0) {
3571 psbs("No current filename");
3572 return (-1);
3573 }
3574 charcnt = 0;
3575 // FIXIT- use the correct umask()
Eric Andersen20aab262001-07-19 22:28:02 +00003576 fd = open((char *) fn, (O_WRONLY | O_CREAT | O_TRUNC), 0664);
Eric Andersen3f980402001-04-04 17:31:15 +00003577 if (fd < 0)
3578 return (-1);
3579 cnt = last - first + 1;
3580 charcnt = write(fd, first, cnt);
3581 if (charcnt == cnt) {
3582 // good write
3583 //file_modified= FALSE; // the file has not been modified
3584 } else {
3585 charcnt = 0;
3586 }
3587 close(fd);
3588 return (charcnt);
3589}
3590
3591//----- Terminal Drawing ---------------------------------------
3592// The terminal is made up of 'rows' line of 'columns' columns.
3593// classicly this would be 24 x 80.
3594// screen coordinates
3595// 0,0 ... 0,79
3596// 1,0 ... 1,79
3597// . ... .
3598// . ... .
Eric Andersen822c3832001-05-07 17:37:43 +00003599// 22,0 ... 22,79
3600// 23,0 ... 23,79 status line
Eric Andersen3f980402001-04-04 17:31:15 +00003601//
3602
3603//----- Move the cursor to row x col (count from 0, not 1) -------
Eric Andersen822c3832001-05-07 17:37:43 +00003604static void place_cursor(int row, int col, int opti)
Eric Andersen3f980402001-04-04 17:31:15 +00003605{
Eric Andersen822c3832001-05-07 17:37:43 +00003606 char cm1[BUFSIZ];
3607 char *cm;
Eric Andersen3f980402001-04-04 17:31:15 +00003608 int l;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003609#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
Eric Andersen822c3832001-05-07 17:37:43 +00003610 char cm2[BUFSIZ];
3611 Byte *screenp;
3612 // char cm3[BUFSIZ];
3613 int Rrow= last_row;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003614#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
Eric Andersen822c3832001-05-07 17:37:43 +00003615
3616 memset(cm1, '\0', BUFSIZ - 1); // clear the buffer
Eric Andersen3f980402001-04-04 17:31:15 +00003617
Eric Andersen822c3832001-05-07 17:37:43 +00003618 if (row < 0) row = 0;
3619 if (row >= rows) row = rows - 1;
3620 if (col < 0) col = 0;
3621 if (col >= columns) col = columns - 1;
3622
3623 //----- 1. Try the standard terminal ESC sequence
3624 sprintf((char *) cm1, CMrc, row + 1, col + 1);
3625 cm= cm1;
Matt Kraai1f0c4362001-12-20 23:13:26 +00003626 if (! opti) goto pc0;
Eric Andersen822c3832001-05-07 17:37:43 +00003627
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003628#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
Eric Andersen822c3832001-05-07 17:37:43 +00003629 //----- find the minimum # of chars to move cursor -------------
3630 //----- 2. Try moving with discreet chars (Newline, [back]space, ...)
3631 memset(cm2, '\0', BUFSIZ - 1); // clear the buffer
3632
3633 // move to the correct row
3634 while (row < Rrow) {
3635 // the cursor has to move up
3636 strcat(cm2, CMup);
3637 Rrow--;
3638 }
3639 while (row > Rrow) {
3640 // the cursor has to move down
3641 strcat(cm2, CMdown);
3642 Rrow++;
3643 }
3644
3645 // now move to the correct column
3646 strcat(cm2, "\r"); // start at col 0
3647 // just send out orignal source char to get to correct place
3648 screenp = &screen[row * columns]; // start of screen line
3649 strncat(cm2, screenp, col);
3650
3651 //----- 3. Try some other way of moving cursor
3652 //---------------------------------------------
3653
3654 // pick the shortest cursor motion to send out
3655 cm= cm1;
3656 if (strlen(cm2) < strlen(cm)) {
3657 cm= cm2;
3658 } /* else if (strlen(cm3) < strlen(cm)) {
3659 cm= cm3;
3660 } */
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003661#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
Eric Andersen822c3832001-05-07 17:37:43 +00003662 pc0:
3663 l= strlen(cm);
3664 if (l) write(1, cm, l); // move the cursor
Eric Andersen3f980402001-04-04 17:31:15 +00003665}
3666
3667//----- Erase from cursor to end of line -----------------------
3668static void clear_to_eol()
3669{
Eric Andersen822c3832001-05-07 17:37:43 +00003670 write(1, Ceol, strlen(Ceol)); // Erase from cursor to end of line
Eric Andersen3f980402001-04-04 17:31:15 +00003671}
3672
3673//----- Erase from cursor to end of screen -----------------------
3674static void clear_to_eos()
3675{
Eric Andersen822c3832001-05-07 17:37:43 +00003676 write(1, Ceos, strlen(Ceos)); // Erase from cursor to end of screen
Eric Andersen3f980402001-04-04 17:31:15 +00003677}
3678
3679//----- Start standout mode ------------------------------------
3680static void standout_start() // send "start reverse video" sequence
3681{
Eric Andersen822c3832001-05-07 17:37:43 +00003682 write(1, SOs, strlen(SOs)); // Start reverse video mode
Eric Andersen3f980402001-04-04 17:31:15 +00003683}
3684
3685//----- End standout mode --------------------------------------
3686static void standout_end() // send "end reverse video" sequence
3687{
Eric Andersen822c3832001-05-07 17:37:43 +00003688 write(1, SOn, strlen(SOn)); // End reverse video mode
Eric Andersen3f980402001-04-04 17:31:15 +00003689}
3690
3691//----- Flash the screen --------------------------------------
3692static void flash(int h)
3693{
3694 standout_start(); // send "start reverse video" sequence
3695 redraw(TRUE);
3696 (void) mysleep(h);
3697 standout_end(); // send "end reverse video" sequence
3698 redraw(TRUE);
3699}
3700
3701static void beep()
3702{
Eric Andersen822c3832001-05-07 17:37:43 +00003703 write(1, bell, strlen(bell)); // send out a bell character
Eric Andersen3f980402001-04-04 17:31:15 +00003704}
3705
3706static void indicate_error(char c)
3707{
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003708#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +00003709 if (crashme > 0)
3710 return; // generate a random command
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003711#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen3f980402001-04-04 17:31:15 +00003712 if (err_method == 0) {
3713 beep();
3714 } else {
3715 flash(10);
3716 }
3717}
3718
3719//----- Screen[] Routines --------------------------------------
3720//----- Erase the Screen[] memory ------------------------------
3721static void screen_erase()
3722{
Eric Andersen822c3832001-05-07 17:37:43 +00003723 memset(screen, ' ', screensize); // clear new screen
Eric Andersen3f980402001-04-04 17:31:15 +00003724}
3725
3726//----- Draw the status line at bottom of the screen -------------
3727static void show_status_line(void)
3728{
Eric Andersen822c3832001-05-07 17:37:43 +00003729 static int last_cksum;
3730 int l, cnt, cksum;
Eric Andersen3f980402001-04-04 17:31:15 +00003731
3732 cnt = strlen((char *) status_buffer);
Eric Andersen822c3832001-05-07 17:37:43 +00003733 for (cksum= l= 0; l < cnt; l++) { cksum += (int)(status_buffer[l]); }
3734 // don't write the status line unless it changes
3735 if (cnt > 0 && last_cksum != cksum) {
3736 last_cksum= cksum; // remember if we have seen this line
3737 place_cursor(rows - 1, 0, FALSE); // put cursor on status line
Eric Andersen3f980402001-04-04 17:31:15 +00003738 write(1, status_buffer, cnt);
Eric Andersen822c3832001-05-07 17:37:43 +00003739 clear_to_eol();
3740 place_cursor(crow, ccol, FALSE); // put cursor back in correct place
Eric Andersen3f980402001-04-04 17:31:15 +00003741 }
Eric Andersen3f980402001-04-04 17:31:15 +00003742}
3743
3744//----- format the status buffer, the bottom line of screen ------
3745// print status buffer, with STANDOUT mode
3746static void psbs(char *format, ...)
3747{
3748 va_list args;
3749
3750 va_start(args, format);
Eric Andersen822c3832001-05-07 17:37:43 +00003751 strcpy((char *) status_buffer, SOs); // Terminal standout mode on
Eric Andersen3f980402001-04-04 17:31:15 +00003752 vsprintf((char *) status_buffer + strlen((char *) status_buffer), format,
3753 args);
Eric Andersen822c3832001-05-07 17:37:43 +00003754 strcat((char *) status_buffer, SOn); // Terminal standout mode off
Eric Andersen3f980402001-04-04 17:31:15 +00003755 va_end(args);
3756
3757 return;
3758}
3759
3760// print status buffer
3761static void psb(char *format, ...)
3762{
3763 va_list args;
3764
3765 va_start(args, format);
3766 vsprintf((char *) status_buffer, format, args);
3767 va_end(args);
3768 return;
3769}
3770
3771static void ni(Byte * s) // display messages
3772{
Eric Andersen1c0d3112001-04-16 15:46:44 +00003773 Byte buf[BUFSIZ];
Eric Andersen3f980402001-04-04 17:31:15 +00003774
3775 print_literal(buf, s);
3776 psbs("\'%s\' is not implemented", buf);
3777}
3778
3779static void edit_status(void) // show file status on status line
3780{
3781 int cur, tot, percent;
3782
3783 cur = count_lines(text, dot);
3784 tot = count_lines(text, end - 1);
3785 // current line percent
3786 // ------------- ~~ ----------
3787 // total lines 100
3788 if (tot > 0) {
3789 percent = (100 * cur) / tot;
3790 } else {
3791 cur = tot = 0;
3792 percent = 100;
3793 }
3794 psb("\"%s\""
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003795#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen3f980402001-04-04 17:31:15 +00003796 "%s"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003797#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen1c0d3112001-04-16 15:46:44 +00003798 "%s line %d of %d --%d%%--",
Eric Andersen3f980402001-04-04 17:31:15 +00003799 (cfn != 0 ? (char *) cfn : "No file"),
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003800#ifdef CONFIG_FEATURE_VI_READONLY
Matt Kraai1f0c4362001-12-20 23:13:26 +00003801 ((vi_readonly || readonly) ? " [Read only]" : ""),
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003802#endif /* CONFIG_FEATURE_VI_READONLY */
Matt Kraai1f0c4362001-12-20 23:13:26 +00003803 (file_modified ? " [modified]" : ""),
Eric Andersen1c0d3112001-04-16 15:46:44 +00003804 cur, tot, percent);
Eric Andersen3f980402001-04-04 17:31:15 +00003805}
3806
3807//----- Force refresh of all Lines -----------------------------
3808static void redraw(int full_screen)
3809{
Eric Andersen822c3832001-05-07 17:37:43 +00003810 place_cursor(0, 0, FALSE); // put cursor in correct place
Eric Andersen3f980402001-04-04 17:31:15 +00003811 clear_to_eos(); // tel terminal to erase display
3812 screen_erase(); // erase the internal screen buffer
3813 refresh(full_screen); // this will redraw the entire display
3814}
3815
Eric Andersen822c3832001-05-07 17:37:43 +00003816//----- Format a text[] line into a buffer ---------------------
3817static void format_line(Byte *dest, Byte *src, int li)
3818{
3819 int co;
3820 Byte c;
3821
3822 for (co= 0; co < MAX_SCR_COLS; co++) {
3823 c= ' '; // assume blank
3824 if (li > 0 && co == 0) {
3825 c = '~'; // not first line, assume Tilde
3826 }
3827 // are there chars in text[] and have we gone past the end
3828 if (text < end && src < end) {
3829 c = *src++;
3830 }
3831 if (c == '\n')
3832 break;
3833 if (c < ' ' || c > '~') {
3834 if (c == '\t') {
3835 c = ' ';
3836 // co % 8 != 7
3837 for (; (co % tabstop) != (tabstop - 1); co++) {
3838 dest[co] = c;
3839 }
3840 } else {
3841 dest[co++] = '^';
3842 c |= '@'; // make it visible
3843 c &= 0x7f; // get rid of hi bit
3844 }
3845 }
3846 // the co++ is done here so that the column will
3847 // not be overwritten when we blank-out the rest of line
3848 dest[co] = c;
3849 if (src >= end)
3850 break;
3851 }
3852}
3853
Eric Andersen3f980402001-04-04 17:31:15 +00003854//----- Refresh the changed screen lines -----------------------
3855// Copy the source line from text[] into the buffer and note
3856// if the current screenline is different from the new buffer.
3857// If they differ then that line needs redrawing on the terminal.
3858//
3859static void refresh(int full_screen)
3860{
3861 static int old_offset;
Eric Andersen822c3832001-05-07 17:37:43 +00003862 int li, changed;
3863 Byte buf[MAX_SCR_COLS];
Eric Andersen3f980402001-04-04 17:31:15 +00003864 Byte *tp, *sp; // pointer into text[] and screen[]
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003865#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
Eric Andersen822c3832001-05-07 17:37:43 +00003866 int last_li= -2; // last line that changed- for optimizing cursor movement
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003867#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
Eric Andersen3f980402001-04-04 17:31:15 +00003868
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003869#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
Eric Andersen3f980402001-04-04 17:31:15 +00003870 window_size_get(0);
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003871#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
Eric Andersen822c3832001-05-07 17:37:43 +00003872 sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
Eric Andersen3f980402001-04-04 17:31:15 +00003873 tp = screenbegin; // index into text[] of top line
Eric Andersen822c3832001-05-07 17:37:43 +00003874
Eric Andersen3f980402001-04-04 17:31:15 +00003875 // compare text[] to screen[] and mark screen[] lines that need updating
3876 for (li = 0; li < rows - 1; li++) {
Eric Andersen822c3832001-05-07 17:37:43 +00003877 int cs, ce; // column start & end
3878 memset(buf, ' ', MAX_SCR_COLS); // blank-out the buffer
3879 buf[MAX_SCR_COLS-1] = 0; // NULL terminate the buffer
3880 // format current text line into buf
3881 format_line(buf, tp, li);
Eric Andersen3f980402001-04-04 17:31:15 +00003882
Eric Andersen822c3832001-05-07 17:37:43 +00003883 // skip to the end of the current text[] line
3884 while (tp < end && *tp++ != '\n') /*no-op*/ ;
3885
3886 // see if there are any changes between vitual screen and buf
Eric Andersen3f980402001-04-04 17:31:15 +00003887 changed = FALSE; // assume no change
Eric Andersen822c3832001-05-07 17:37:43 +00003888 cs= 0;
3889 ce= columns-1;
Eric Andersen3f980402001-04-04 17:31:15 +00003890 sp = &screen[li * columns]; // start of screen line
Matt Kraai1f0c4362001-12-20 23:13:26 +00003891 if (full_screen) {
Eric Andersen822c3832001-05-07 17:37:43 +00003892 // force re-draw of every single column from 0 - columns-1
3893 goto re0;
3894 }
3895 // compare newly formatted buffer with virtual screen
3896 // look forward for first difference between buf and screen
3897 for ( ; cs <= ce; cs++) {
3898 if (buf[cs + offset] != sp[cs]) {
Eric Andersen3f980402001-04-04 17:31:15 +00003899 changed = TRUE; // mark for redraw
Eric Andersen822c3832001-05-07 17:37:43 +00003900 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003901 }
3902 }
Eric Andersen3f980402001-04-04 17:31:15 +00003903
Eric Andersen822c3832001-05-07 17:37:43 +00003904 // look backward for last difference between buf and screen
3905 for ( ; ce >= cs; ce--) {
3906 if (buf[ce + offset] != sp[ce]) {
3907 changed = TRUE; // mark for redraw
3908 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003909 }
Eric Andersen822c3832001-05-07 17:37:43 +00003910 }
3911 // now, cs is index of first diff, and ce is index of last diff
3912
3913 // if horz offset has changed, force a redraw
3914 if (offset != old_offset) {
3915 re0:
3916 changed = TRUE;
3917 }
3918
3919 // make a sanity check of columns indexes
3920 if (cs < 0) cs= 0;
3921 if (ce > columns-1) ce= columns-1;
3922 if (cs > ce) { cs= 0; ce= columns-1; }
3923 // is there a change between vitual screen and buf
Matt Kraai1f0c4362001-12-20 23:13:26 +00003924 if (changed) {
Eric Andersen822c3832001-05-07 17:37:43 +00003925 // copy changed part of buffer to virtual screen
3926 memmove(sp+cs, buf+(cs+offset), ce-cs+1);
3927
3928 // move cursor to column of first change
3929 if (offset != old_offset) {
3930 // opti_cur_move is still too stupid
3931 // to handle offsets correctly
3932 place_cursor(li, cs, FALSE);
3933 } else {
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003934#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
Eric Andersen822c3832001-05-07 17:37:43 +00003935 // if this just the next line
3936 // try to optimize cursor movement
3937 // otherwise, use standard ESC sequence
3938 place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE);
3939 last_li= li;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003940#else /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
Eric Andersen822c3832001-05-07 17:37:43 +00003941 place_cursor(li, cs, FALSE); // use standard ESC sequence
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003942#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
Eric Andersen822c3832001-05-07 17:37:43 +00003943 }
3944
Eric Andersen3f980402001-04-04 17:31:15 +00003945 // write line out to terminal
Eric Andersen822c3832001-05-07 17:37:43 +00003946 write(1, sp+cs, ce-cs+1);
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003947#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
Eric Andersen822c3832001-05-07 17:37:43 +00003948 last_row = li;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003949#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
Eric Andersen3f980402001-04-04 17:31:15 +00003950 }
3951 }
3952
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003953#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
Eric Andersenc33ebc92001-05-07 22:57:47 +00003954 place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE);
Eric Andersen822c3832001-05-07 17:37:43 +00003955 last_row = crow;
Eric Andersenc33ebc92001-05-07 22:57:47 +00003956#else
3957 place_cursor(crow, ccol, FALSE);
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003958#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
Eric Andersen822c3832001-05-07 17:37:43 +00003959
Eric Andersen3f980402001-04-04 17:31:15 +00003960 if (offset != old_offset)
3961 old_offset = offset;
3962}