blob: 76e4d66848ac492f20752482cad2047b5db3e24c [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[] =
Eric Andersena9eb33d2004-08-19 19:15:06 +000022 "$Id: vi.c,v 1.38 2004/08/19 19:15:06 andersen 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
Eric Andersenaff114c2004-04-14 17:51:38 +000048//---- Feature -------------- Bytes to implement
Eric Andersen3f980402001-04-04 17:31:15 +000049#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
Glenn L McGrath09adaca2002-12-02 21:18:10 +000090#ifdef CONFIG_LOCALE_SUPPORT
91#define Isprint(c) isprint((c))
92#else
93#define Isprint(c) ( (c) >= ' ' && (c) != 127 && (c) != ((unsigned char)'\233') )
94#endif
95
Eric Andersen3f980402001-04-04 17:31:15 +000096#ifndef TRUE
97#define TRUE ((int)1)
98#define FALSE ((int)0)
99#endif /* TRUE */
Eric Andersen1c0d3112001-04-16 15:46:44 +0000100#define MAX_SCR_COLS BUFSIZ
Eric Andersen3f980402001-04-04 17:31:15 +0000101
102// Misc. non-Ascii keys that report an escape sequence
103#define VI_K_UP 128 // cursor key Up
104#define VI_K_DOWN 129 // cursor key Down
105#define VI_K_RIGHT 130 // Cursor Key Right
106#define VI_K_LEFT 131 // cursor key Left
107#define VI_K_HOME 132 // Cursor Key Home
108#define VI_K_END 133 // Cursor Key End
109#define VI_K_INSERT 134 // Cursor Key Insert
110#define VI_K_PAGEUP 135 // Cursor Key Page Up
111#define VI_K_PAGEDOWN 136 // Cursor Key Page Down
112#define VI_K_FUN1 137 // Function Key F1
113#define VI_K_FUN2 138 // Function Key F2
114#define VI_K_FUN3 139 // Function Key F3
115#define VI_K_FUN4 140 // Function Key F4
116#define VI_K_FUN5 141 // Function Key F5
117#define VI_K_FUN6 142 // Function Key F6
118#define VI_K_FUN7 143 // Function Key F7
119#define VI_K_FUN8 144 // Function Key F8
120#define VI_K_FUN9 145 // Function Key F9
121#define VI_K_FUN10 146 // Function Key F10
122#define VI_K_FUN11 147 // Function Key F11
123#define VI_K_FUN12 148 // Function Key F12
124
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000125/* vt102 typical ESC sequence */
126/* terminal standout start/normal ESC sequence */
127static const char SOs[] = "\033[7m";
128static const char SOn[] = "\033[0m";
129/* terminal bell sequence */
130static const char bell[] = "\007";
131/* Clear-end-of-line and Clear-end-of-screen ESC sequence */
132static const char Ceol[] = "\033[0K";
133static const char Ceos [] = "\033[0J";
134/* Cursor motion arbitrary destination ESC sequence */
135static const char CMrc[] = "\033[%d;%dH";
136/* Cursor motion up and down ESC sequence */
137static const char CMup[] = "\033[A";
138static const char CMdown[] = "\n";
139
140
Eric Andersen3f980402001-04-04 17:31:15 +0000141static const int YANKONLY = FALSE;
142static const int YANKDEL = TRUE;
143static const int FORWARD = 1; // code depends on "1" for array index
144static const int BACK = -1; // code depends on "-1" for array index
145static const int LIMITED = 0; // how much of text[] in char_search
146static const int FULL = 1; // how much of text[] in char_search
147
148static const int S_BEFORE_WS = 1; // used in skip_thing() for moving "dot"
149static const int S_TO_WS = 2; // used in skip_thing() for moving "dot"
150static const int S_OVER_WS = 3; // used in skip_thing() for moving "dot"
151static const int S_END_PUNCT = 4; // used in skip_thing() for moving "dot"
152static const int S_END_ALNUM = 5; // used in skip_thing() for moving "dot"
153
154typedef unsigned char Byte;
155
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000156static int vi_setops;
157#define VI_AUTOINDENT 1
158#define VI_SHOWMATCH 2
159#define VI_IGNORECASE 4
160#define VI_ERR_METHOD 8
161#define autoindent (vi_setops & VI_AUTOINDENT)
162#define showmatch (vi_setops & VI_SHOWMATCH )
163#define ignorecase (vi_setops & VI_IGNORECASE)
164/* indicate error with beep or flash */
165#define err_method (vi_setops & VI_ERR_METHOD)
166
Eric Andersen3f980402001-04-04 17:31:15 +0000167
168static int editing; // >0 while we are editing a file
Paul Fox8552aec2005-09-16 12:20:05 +0000169static int cmd_mode; // 0=command 1=insert 2=replace
Eric Andersen3f980402001-04-04 17:31:15 +0000170static int file_modified; // buffer contents changed
Paul Fox8552aec2005-09-16 12:20:05 +0000171static int last_file_modified = -1;
Eric Andersen3f980402001-04-04 17:31:15 +0000172static int fn_start; // index of first cmd line file name
173static int save_argc; // how many file names on cmd line
174static int cmdcnt; // repetition count
175static fd_set rfds; // use select() for small sleeps
176static struct timeval tv; // use select() for small sleeps
Eric Andersen3f980402001-04-04 17:31:15 +0000177static int rows, columns; // the terminal screen is this size
178static int crow, ccol, offset; // cursor is on Crow x Ccol with Horz Ofset
Eric Andersen3f980402001-04-04 17:31:15 +0000179static Byte *status_buffer; // mesages to the user
Paul Fox8552aec2005-09-16 12:20:05 +0000180#define STATUS_BUFFER_LEN 200
181static int have_status_msg; // is default edit status needed?
182static int last_status_cksum; // hash of current status line
Eric Andersen3f980402001-04-04 17:31:15 +0000183static Byte *cfn; // previous, current, and next file name
184static Byte *text, *end, *textend; // pointers to the user data in memory
185static Byte *screen; // pointer to the virtual screen buffer
186static int screensize; // and its size
187static Byte *screenbegin; // index into text[], of top line on the screen
188static Byte *dot; // where all the action takes place
189static int tabstop;
190static struct termios term_orig, term_vi; // remember what the cooked mode was
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000191static Byte erase_char; // the users erase character
192static Byte last_input_char; // last char read from user
193static Byte last_forward_char; // last char searched for with 'f'
Eric Andersen3f980402001-04-04 17:31:15 +0000194
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000195#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
Eric Andersen822c3832001-05-07 17:37:43 +0000196static int last_row; // where the cursor was last moved to
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000197#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
198#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000199static jmp_buf restart; // catch_sig()
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000200#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000201#if defined(CONFIG_FEATURE_VI_USE_SIGNALS) || defined(CONFIG_FEATURE_VI_CRASHME)
202static int my_pid;
203#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000204#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000205static int adding2q; // are we currently adding user input to q
206static Byte *last_modifying_cmd; // last modifying cmd for "."
207static Byte *ioq, *ioq_start; // pointer to string for get_one_char to "read"
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000208#endif /* CONFIG_FEATURE_VI_DOT_CMD */
209#if defined(CONFIG_FEATURE_VI_DOT_CMD) || defined(CONFIG_FEATURE_VI_YANKMARK)
Eric Andersen3f980402001-04-04 17:31:15 +0000210static Byte *modifying_cmds; // cmds that modify text[]
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000211#endif /* CONFIG_FEATURE_VI_DOT_CMD || CONFIG_FEATURE_VI_YANKMARK */
212#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen822c3832001-05-07 17:37:43 +0000213static int vi_readonly, readonly;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000214#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000215#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000216static Byte *reg[28]; // named register a-z, "D", and "U" 0-25,26,27
217static int YDreg, Ureg; // default delete register and orig line for "U"
218static Byte *mark[28]; // user marks points somewhere in text[]- a-z and previous context ''
219static Byte *context_start, *context_end;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000220#endif /* CONFIG_FEATURE_VI_YANKMARK */
221#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +0000222static Byte *last_search_pattern; // last pattern from a '/' or '?' search
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000223#endif /* CONFIG_FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +0000224
225
226static void edit_file(Byte *); // edit one file
227static void do_cmd(Byte); // execute a command
228static void sync_cursor(Byte *, int *, int *); // synchronize the screen cursor to dot
229static Byte *begin_line(Byte *); // return pointer to cur line B-o-l
230static Byte *end_line(Byte *); // return pointer to cur line E-o-l
Eric Andersen3f980402001-04-04 17:31:15 +0000231static Byte *prev_line(Byte *); // return pointer to prev line B-o-l
232static Byte *next_line(Byte *); // return pointer to next line B-o-l
233static Byte *end_screen(void); // get pointer to last char on screen
234static int count_lines(Byte *, Byte *); // count line from start to stop
235static Byte *find_line(int); // find begining of line #li
236static Byte *move_to_col(Byte *, int); // move "p" to column l
237static int isblnk(Byte); // is the char a blank or tab
238static void dot_left(void); // move dot left- dont leave line
239static void dot_right(void); // move dot right- dont leave line
240static void dot_begin(void); // move dot to B-o-l
241static void dot_end(void); // move dot to E-o-l
242static void dot_next(void); // move dot to next line B-o-l
243static void dot_prev(void); // move dot to prev line B-o-l
244static void dot_scroll(int, int); // move the screen up or down
245static void dot_skip_over_ws(void); // move dot pat WS
246static void dot_delete(void); // delete the char at 'dot'
247static Byte *bound_dot(Byte *); // make sure text[0] <= P < "end"
248static Byte *new_screen(int, int); // malloc virtual screen memory
249static Byte *new_text(int); // malloc memory for text[] buffer
250static Byte *char_insert(Byte *, Byte); // insert the char c at 'p'
251static Byte *stupid_insert(Byte *, Byte); // stupidly insert the char c at 'p'
252static Byte find_range(Byte **, Byte **, Byte); // return pointers for an object
253static int st_test(Byte *, int, int, Byte *); // helper for skip_thing()
254static Byte *skip_thing(Byte *, int, int, int); // skip some object
255static Byte *find_pair(Byte *, Byte); // find matching pair () [] {}
256static Byte *text_hole_delete(Byte *, Byte *); // at "p", delete a 'size' byte hole
257static Byte *text_hole_make(Byte *, int); // at "p", make a 'size' byte hole
258static Byte *yank_delete(Byte *, Byte *, int, int); // yank text[] into register then delete
259static void show_help(void); // display some help info
Eric Andersen3f980402001-04-04 17:31:15 +0000260static void rawmode(void); // set "raw" mode on tty
261static void cookmode(void); // return to "cooked" mode on tty
262static int mysleep(int); // sleep for 'h' 1/100 seconds
263static Byte readit(void); // read (maybe cursor) key from stdin
264static Byte get_one_char(void); // read 1 char from stdin
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000265static int file_size(const Byte *); // what is the byte size of "fn"
Eric Andersen3f980402001-04-04 17:31:15 +0000266static int file_insert(Byte *, Byte *, int);
267static int file_write(Byte *, Byte *, Byte *);
Eric Andersen822c3832001-05-07 17:37:43 +0000268static void place_cursor(int, int, int);
Eric Andersenbff7a602001-11-17 07:15:43 +0000269static void screen_erase(void);
Eric Andersen3f980402001-04-04 17:31:15 +0000270static void clear_to_eol(void);
271static void clear_to_eos(void);
272static void standout_start(void); // send "start reverse video" sequence
273static void standout_end(void); // send "end reverse video" sequence
274static void flash(int); // flash the terminal screen
Eric Andersen3f980402001-04-04 17:31:15 +0000275static void show_status_line(void); // put a message on the bottom line
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000276static void psb(const char *, ...); // Print Status Buf
277static void psbs(const char *, ...); // Print Status Buf in standout mode
Eric Andersen3f980402001-04-04 17:31:15 +0000278static void ni(Byte *); // display messages
Paul Fox8552aec2005-09-16 12:20:05 +0000279static int format_edit_status(void); // format file status on status line
Eric Andersen3f980402001-04-04 17:31:15 +0000280static void redraw(int); // force a full screen refresh
Eric Andersen822c3832001-05-07 17:37:43 +0000281static void format_line(Byte*, Byte*, int);
Eric Andersen3f980402001-04-04 17:31:15 +0000282static void refresh(int); // update the terminal from screen[]
283
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000284static void Indicate_Error(void); // use flash or beep to indicate error
285#define indicate_error(c) Indicate_Error()
286
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000287#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +0000288static Byte *char_search(Byte *, Byte *, int, int); // search for pattern starting at p
289static int mycmp(Byte *, Byte *, int); // string cmp based in "ignorecase"
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000290#endif /* CONFIG_FEATURE_VI_SEARCH */
291#ifdef CONFIG_FEATURE_VI_COLON
Eric Andersen3f980402001-04-04 17:31:15 +0000292static void Hit_Return(void);
Eric Andersen1c0d3112001-04-16 15:46:44 +0000293static Byte *get_one_address(Byte *, int *); // get colon addr, if present
294static Byte *get_address(Byte *, int *, int *); // get two colon addrs, if present
Eric Andersen3f980402001-04-04 17:31:15 +0000295static void colon(Byte *); // execute the "colon" mode cmds
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000296#endif /* CONFIG_FEATURE_VI_COLON */
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000297#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000298static void winch_sig(int); // catch window size changes
299static void suspend_sig(int); // catch ctrl-Z
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000300static void catch_sig(int); // catch ctrl-C and alarm time-outs
Eric Andersen3f980402001-04-04 17:31:15 +0000301static void core_sig(int); // catch a core dump signal
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000302#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
303#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000304static void start_new_cmd_q(Byte); // new queue for command
Eric Andersenbff7a602001-11-17 07:15:43 +0000305static void end_cmd_q(void); // stop saving input chars
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000306#else /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +0000307#define end_cmd_q()
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000308#endif /* CONFIG_FEATURE_VI_DOT_CMD */
309#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
Eric Andersen3f980402001-04-04 17:31:15 +0000310static void window_size_get(int); // find out what size the window is
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000311#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
312#ifdef CONFIG_FEATURE_VI_SETOPTS
Eric Andersen3f980402001-04-04 17:31:15 +0000313static void showmatching(Byte *); // show the matching pair () [] {}
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000314#endif /* CONFIG_FEATURE_VI_SETOPTS */
315#if defined(CONFIG_FEATURE_VI_YANKMARK) || defined(CONFIG_FEATURE_VI_COLON) || defined(CONFIG_FEATURE_VI_CRASHME)
Eric Andersen3f980402001-04-04 17:31:15 +0000316static Byte *string_insert(Byte *, Byte *); // insert the string at 'p'
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000317#endif /* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_COLON || CONFIG_FEATURE_VI_CRASHME */
318#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000319static Byte *text_yank(Byte *, Byte *, int); // save copy of "p" into a register
320static Byte what_reg(void); // what is letter of current YDreg
321static void check_context(Byte); // remember context for '' command
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000322#endif /* CONFIG_FEATURE_VI_YANKMARK */
323#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000324static void crash_dummy();
325static void crash_test();
326static int crashme = 0;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000327#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen3f980402001-04-04 17:31:15 +0000328
329
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000330static void write1(const char *out)
331{
332 fputs(out, stdout);
333}
334
Eric Andersen3f980402001-04-04 17:31:15 +0000335extern int vi_main(int argc, char **argv)
Eric Andersen3f980402001-04-04 17:31:15 +0000336{
Eric Andersend402edf2001-04-04 19:29:48 +0000337 int c;
Paul Fox8552aec2005-09-16 12:20:05 +0000338 RESERVE_CONFIG_BUFFER(STATUS_BUFFER, STATUS_BUFFER_LEN);
Eric Andersen3f980402001-04-04 17:31:15 +0000339
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000340#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000341 int i;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000342#endif /* CONFIG_FEATURE_VI_YANKMARK */
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000343#if defined(CONFIG_FEATURE_VI_USE_SIGNALS) || defined(CONFIG_FEATURE_VI_CRASHME)
344 my_pid = getpid();
345#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000346#ifdef CONFIG_FEATURE_VI_CRASHME
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000347 (void) srand((long) my_pid);
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000348#endif /* CONFIG_FEATURE_VI_CRASHME */
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000349
350 status_buffer = STATUS_BUFFER;
Paul Fox8552aec2005-09-16 12:20:05 +0000351 last_status_cksum = 0;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000352
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000353#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen822c3832001-05-07 17:37:43 +0000354 vi_readonly = readonly = FALSE;
Eric Andersen3f980402001-04-04 17:31:15 +0000355 if (strncmp(argv[0], "view", 4) == 0) {
356 readonly = TRUE;
Eric Andersen822c3832001-05-07 17:37:43 +0000357 vi_readonly = TRUE;
Eric Andersen3f980402001-04-04 17:31:15 +0000358 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000359#endif /* CONFIG_FEATURE_VI_READONLY */
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000360 vi_setops = VI_AUTOINDENT | VI_SHOWMATCH | VI_IGNORECASE | VI_ERR_METHOD;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000361#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000362 for (i = 0; i < 28; i++) {
363 reg[i] = 0;
364 } // init the yank regs
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000365#endif /* CONFIG_FEATURE_VI_YANKMARK */
Tim Riker86c76a92002-04-26 07:41:22 +0000366#if defined(CONFIG_FEATURE_VI_DOT_CMD) || defined(CONFIG_FEATURE_VI_YANKMARK)
Eric Andersen3f980402001-04-04 17:31:15 +0000367 modifying_cmds = (Byte *) "aAcCdDiIJoOpPrRsxX<>~"; // cmds modifying text[]
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000368#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +0000369
370 // 1- process $HOME/.exrc file
371 // 2- process EXINIT variable from environment
372 // 3- process command line args
373 while ((c = getopt(argc, argv, "hCR")) != -1) {
374 switch (c) {
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000375#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000376 case 'C':
377 crashme = 1;
378 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000379#endif /* CONFIG_FEATURE_VI_CRASHME */
380#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen3f980402001-04-04 17:31:15 +0000381 case 'R': // Read-only flag
382 readonly = TRUE;
Eric Andersen90fb65f2004-03-31 11:12:51 +0000383 vi_readonly = TRUE;
Eric Andersen3f980402001-04-04 17:31:15 +0000384 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000385#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen822c3832001-05-07 17:37:43 +0000386 //case 'r': // recover flag- ignore- we don't use tmp file
387 //case 'x': // encryption flag- ignore
388 //case 'c': // execute command first
389 //case 'h': // help -- just use default
Eric Andersen3f980402001-04-04 17:31:15 +0000390 default:
391 show_help();
Eric Andersendd8500b2001-07-02 18:06:14 +0000392 return 1;
Eric Andersen3f980402001-04-04 17:31:15 +0000393 }
394 }
395
396 // The argv array can be used by the ":next" and ":rewind" commands
397 // save optind.
398 fn_start = optind; // remember first file name for :next and :rew
399 save_argc = argc;
400
401 //----- This is the main file handling loop --------------
402 if (optind >= argc) {
403 editing = 1; // 0= exit, 1= one file, 2= multiple files
404 edit_file(0);
405 } else {
406 for (; optind < argc; optind++) {
407 editing = 1; // 0=exit, 1=one file, 2+ =many files
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000408 free(cfn);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000409 cfn = (Byte *) bb_xstrdup(argv[optind]);
Eric Andersen3f980402001-04-04 17:31:15 +0000410 edit_file(cfn);
411 }
412 }
413 //-----------------------------------------------------------
414
415 return (0);
416}
417
Eric Andersen8efe9672003-09-15 08:33:45 +0000418#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
419//----- See what the window size currently is --------------------
420static inline void window_size_get(int fd)
421{
422 get_terminal_width_height(fd, &columns, &rows);
423}
424#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
425
Eric Andersen3f980402001-04-04 17:31:15 +0000426static void edit_file(Byte * fn)
427{
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000428 Byte c;
Eric Andersen1c0d3112001-04-16 15:46:44 +0000429 int cnt, size, ch;
Eric Andersen3f980402001-04-04 17:31:15 +0000430
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000431#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000432 int sig;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000433#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
434#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000435 static Byte *cur_line;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000436#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +0000437
438 rawmode();
439 rows = 24;
440 columns = 80;
Eric Andersen822c3832001-05-07 17:37:43 +0000441 ch= -1;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000442#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
Eric Andersen3f980402001-04-04 17:31:15 +0000443 window_size_get(0);
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000444#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
Eric Andersen3f980402001-04-04 17:31:15 +0000445 new_screen(rows, columns); // get memory for virtual screen
446
447 cnt = file_size(fn); // file size
448 size = 2 * cnt; // 200% of file size
449 new_text(size); // get a text[] buffer
450 screenbegin = dot = end = text;
451 if (fn != 0) {
Eric Andersen1c0d3112001-04-16 15:46:44 +0000452 ch= file_insert(fn, text, cnt);
453 }
454 if (ch < 1) {
Eric Andersen3f980402001-04-04 17:31:15 +0000455 (void) char_insert(text, '\n'); // start empty buf with dummy line
456 }
Paul Fox8552aec2005-09-16 12:20:05 +0000457 file_modified = 0;
458 last_file_modified = -1;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000459#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000460 YDreg = 26; // default Yank/Delete reg
461 Ureg = 27; // hold orig line for "U" cmd
462 for (cnt = 0; cnt < 28; cnt++) {
463 mark[cnt] = 0;
464 } // init the marks
465 mark[26] = mark[27] = text; // init "previous context"
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000466#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +0000467
Eric Andersen3f980402001-04-04 17:31:15 +0000468 last_forward_char = last_input_char = '\0';
469 crow = 0;
470 ccol = 0;
Eric Andersen3f980402001-04-04 17:31:15 +0000471
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000472#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000473 catch_sig(0);
474 core_sig(0);
Eric Andersen3f980402001-04-04 17:31:15 +0000475 signal(SIGWINCH, winch_sig);
476 signal(SIGTSTP, suspend_sig);
477 sig = setjmp(restart);
478 if (sig != 0) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000479 const char *msg = "";
480
Eric Andersen3f980402001-04-04 17:31:15 +0000481 if (sig == SIGWINCH)
482 msg = "(window resize)";
483 if (sig == SIGHUP)
484 msg = "(hangup)";
485 if (sig == SIGINT)
486 msg = "(interrupt)";
487 if (sig == SIGTERM)
488 msg = "(terminate)";
489 if (sig == SIGBUS)
490 msg = "(bus error)";
491 if (sig == SIGSEGV)
492 msg = "(I tried to touch invalid memory)";
493 if (sig == SIGALRM)
494 msg = "(alarm)";
495
496 psbs("-- caught signal %d %s--", sig, msg);
Eric Andersen1c0d3112001-04-16 15:46:44 +0000497 screenbegin = dot = text;
Eric Andersen3f980402001-04-04 17:31:15 +0000498 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000499#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Eric Andersen3f980402001-04-04 17:31:15 +0000500
501 editing = 1;
502 cmd_mode = 0; // 0=command 1=insert 2='R'eplace
503 cmdcnt = 0;
504 tabstop = 8;
505 offset = 0; // no horizontal offset
506 c = '\0';
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000507#ifdef CONFIG_FEATURE_VI_DOT_CMD
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000508 free(last_modifying_cmd);
509 free(ioq_start);
Eric Andersen3f980402001-04-04 17:31:15 +0000510 ioq = ioq_start = last_modifying_cmd = 0;
511 adding2q = 0;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000512#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen822c3832001-05-07 17:37:43 +0000513 redraw(FALSE); // dont force every col re-draw
Eric Andersen3f980402001-04-04 17:31:15 +0000514 show_status_line();
515
516 //------This is the main Vi cmd handling loop -----------------------
517 while (editing > 0) {
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000518#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000519 if (crashme > 0) {
520 if ((end - text) > 1) {
521 crash_dummy(); // generate a random command
522 } else {
523 crashme = 0;
524 dot =
525 string_insert(text, (Byte *) "\n\n##### Ran out of text to work on. #####\n\n"); // insert the string
526 refresh(FALSE);
527 }
528 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000529#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen3f980402001-04-04 17:31:15 +0000530 last_input_char = c = get_one_char(); // get a cmd from user
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000531#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000532 // save a copy of the current line- for the 'U" command
533 if (begin_line(dot) != cur_line) {
534 cur_line = begin_line(dot);
535 text_yank(begin_line(dot), end_line(dot), Ureg);
536 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000537#endif /* CONFIG_FEATURE_VI_YANKMARK */
538#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000539 // These are commands that change text[].
540 // Remember the input for the "." command
541 if (!adding2q && ioq_start == 0
542 && strchr((char *) modifying_cmds, c) != NULL) {
543 start_new_cmd_q(c);
544 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000545#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +0000546 do_cmd(c); // execute the user command
547 //
548 // poll to see if there is input already waiting. if we are
549 // not able to display output fast enough to keep up, skip
550 // the display update until we catch up with input.
551 if (mysleep(0) == 0) {
552 // no input pending- so update output
553 refresh(FALSE);
554 show_status_line();
555 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000556#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000557 if (crashme > 0)
558 crash_test(); // test editor variables
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000559#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen3f980402001-04-04 17:31:15 +0000560 }
561 //-------------------------------------------------------------------
562
Eric Andersen822c3832001-05-07 17:37:43 +0000563 place_cursor(rows, 0, FALSE); // go to bottom of screen
Eric Andersen3f980402001-04-04 17:31:15 +0000564 clear_to_eol(); // Erase to end of line
565 cookmode();
566}
567
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000568//----- The Colon commands -------------------------------------
569#ifdef CONFIG_FEATURE_VI_COLON
570static Byte *get_one_address(Byte * p, int *addr) // get colon addr, if present
571{
572 int st;
573 Byte *q;
574
575#ifdef CONFIG_FEATURE_VI_YANKMARK
576 Byte c;
577#endif /* CONFIG_FEATURE_VI_YANKMARK */
578#ifdef CONFIG_FEATURE_VI_SEARCH
579 Byte *pat, buf[BUFSIZ];
580#endif /* CONFIG_FEATURE_VI_SEARCH */
581
582 *addr = -1; // assume no addr
583 if (*p == '.') { // the current line
584 p++;
585 q = begin_line(dot);
586 *addr = count_lines(text, q);
587#ifdef CONFIG_FEATURE_VI_YANKMARK
588 } else if (*p == '\'') { // is this a mark addr
589 p++;
590 c = tolower(*p);
591 p++;
592 if (c >= 'a' && c <= 'z') {
593 // we have a mark
594 c = c - 'a';
595 q = mark[(int) c];
596 if (q != NULL) { // is mark valid
597 *addr = count_lines(text, q); // count lines
598 }
599 }
600#endif /* CONFIG_FEATURE_VI_YANKMARK */
601#ifdef CONFIG_FEATURE_VI_SEARCH
602 } else if (*p == '/') { // a search pattern
603 q = buf;
604 for (p++; *p; p++) {
605 if (*p == '/')
606 break;
607 *q++ = *p;
608 *q = '\0';
609 }
Manuel Novoa III cad53642003-03-19 09:13:01 +0000610 pat = (Byte *) bb_xstrdup((char *) buf); // save copy of pattern
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000611 if (*p == '/')
612 p++;
613 q = char_search(dot, pat, FORWARD, FULL);
614 if (q != NULL) {
615 *addr = count_lines(text, q);
616 }
617 free(pat);
618#endif /* CONFIG_FEATURE_VI_SEARCH */
619 } else if (*p == '$') { // the last line in file
620 p++;
621 q = begin_line(end - 1);
622 *addr = count_lines(text, q);
623 } else if (isdigit(*p)) { // specific line number
624 sscanf((char *) p, "%d%n", addr, &st);
625 p += st;
626 } else { // I don't reconise this
627 // unrecognised address- assume -1
628 *addr = -1;
629 }
630 return (p);
631}
632
633static Byte *get_address(Byte *p, int *b, int *e) // get two colon addrs, if present
634{
635 //----- get the address' i.e., 1,3 'a,'b -----
636 // get FIRST addr, if present
637 while (isblnk(*p))
638 p++; // skip over leading spaces
639 if (*p == '%') { // alias for 1,$
640 p++;
641 *b = 1;
642 *e = count_lines(text, end-1);
643 goto ga0;
644 }
645 p = get_one_address(p, b);
646 while (isblnk(*p))
647 p++;
Eric Andersenaff114c2004-04-14 17:51:38 +0000648 if (*p == ',') { // is there a address separator
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000649 p++;
650 while (isblnk(*p))
651 p++;
652 // get SECOND addr, if present
653 p = get_one_address(p, e);
654 }
655ga0:
656 while (isblnk(*p))
657 p++; // skip over trailing spaces
658 return (p);
659}
660
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000661#ifdef CONFIG_FEATURE_VI_SETOPTS
662static void setops(const Byte *args, const char *opname, int flg_no,
663 const char *short_opname, int opt)
664{
665 const char *a = (char *) args + flg_no;
666 int l = strlen(opname) - 1; /* opname have + ' ' */
667
668 if (strncasecmp(a, opname, l) == 0 ||
669 strncasecmp(a, short_opname, 2) == 0) {
670 if(flg_no)
671 vi_setops &= ~opt;
672 else
673 vi_setops |= opt;
674 }
675}
676#endif
677
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000678static void colon(Byte * buf)
679{
680 Byte c, *orig_buf, *buf1, *q, *r;
681 Byte *fn, cmd[BUFSIZ], args[BUFSIZ];
682 int i, l, li, ch, st, b, e;
Eric Andersena9eb33d2004-08-19 19:15:06 +0000683 int useforce = FALSE, forced = FALSE;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000684 struct stat st_buf;
685
686 // :3154 // if (-e line 3154) goto it else stay put
687 // :4,33w! foo // write a portion of buffer to file "foo"
688 // :w // write all of buffer to current file
689 // :q // quit
690 // :q! // quit- dont care about modified file
691 // :'a,'z!sort -u // filter block through sort
692 // :'f // goto mark "f"
693 // :'fl // list literal the mark "f" line
694 // :.r bar // read file "bar" into buffer before dot
695 // :/123/,/abc/d // delete lines from "123" line to "abc" line
696 // :/xyz/ // goto the "xyz" line
697 // :s/find/replace/ // substitute pattern "find" with "replace"
698 // :!<cmd> // run <cmd> then return
699 //
Eric Andersen165e8cb2004-07-20 06:44:46 +0000700
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000701 if (strlen((char *) buf) <= 0)
702 goto vc1;
703 if (*buf == ':')
704 buf++; // move past the ':'
705
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000706 li = st = ch = i = 0;
707 b = e = -1;
708 q = text; // assume 1,$ for the range
709 r = end - 1;
710 li = count_lines(text, end - 1);
711 fn = cfn; // default to current file
712 memset(cmd, '\0', BUFSIZ); // clear cmd[]
713 memset(args, '\0', BUFSIZ); // clear args[]
714
715 // look for optional address(es) :. :1 :1,9 :'q,'a :%
716 buf = get_address(buf, &b, &e);
717
718 // remember orig command line
719 orig_buf = buf;
720
721 // get the COMMAND into cmd[]
722 buf1 = cmd;
723 while (*buf != '\0') {
724 if (isspace(*buf))
725 break;
726 *buf1++ = *buf++;
727 }
728 // get any ARGuments
729 while (isblnk(*buf))
730 buf++;
731 strcpy((char *) args, (char *) buf);
732 buf1 = last_char_is((char *)cmd, '!');
733 if (buf1) {
734 useforce = TRUE;
735 *buf1 = '\0'; // get rid of !
736 }
737 if (b >= 0) {
738 // if there is only one addr, then the addr
739 // is the line number of the single line the
740 // user wants. So, reset the end
741 // pointer to point at end of the "b" line
742 q = find_line(b); // what line is #b
743 r = end_line(q);
744 li = 1;
745 }
746 if (e >= 0) {
747 // we were given two addrs. change the
748 // end pointer to the addr given by user.
749 r = find_line(e); // what line is #e
750 r = end_line(r);
751 li = e - b + 1;
752 }
753 // ------------ now look for the command ------------
754 i = strlen((char *) cmd);
755 if (i == 0) { // :123CR goto line #123
756 if (b >= 0) {
757 dot = find_line(b); // what line is #b
758 dot_skip_over_ws();
759 }
760 } else if (strncmp((char *) cmd, "!", 1) == 0) { // run a cmd
761 // :!ls run the <cmd>
762 (void) alarm(0); // wait for input- no alarms
763 place_cursor(rows - 1, 0, FALSE); // go to Status line
764 clear_to_eol(); // clear the line
765 cookmode();
766 system(orig_buf+1); // run the cmd
767 rawmode();
768 Hit_Return(); // let user see results
769 (void) alarm(3); // done waiting for input
770 } else if (strncmp((char *) cmd, "=", i) == 0) { // where is the address
771 if (b < 0) { // no addr given- use defaults
772 b = e = count_lines(text, dot);
773 }
774 psb("%d", b);
775 } else if (strncasecmp((char *) cmd, "delete", i) == 0) { // delete lines
776 if (b < 0) { // no addr given- use defaults
777 q = begin_line(dot); // assume .,. for the range
778 r = end_line(dot);
779 }
780 dot = yank_delete(q, r, 1, YANKDEL); // save, then delete lines
781 dot_skip_over_ws();
782 } else if (strncasecmp((char *) cmd, "edit", i) == 0) { // Edit a file
783 int sr;
784 sr= 0;
785 // don't edit, if the current file has been modified
786 if (file_modified && ! useforce) {
787 psbs("No write since last change (:edit! overrides)");
788 goto vc1;
789 }
790 if (strlen(args) > 0) {
791 // the user supplied a file name
792 fn= args;
793 } else if (cfn != 0 && strlen(cfn) > 0) {
794 // no user supplied name- use the current filename
795 fn= cfn;
796 goto vc5;
797 } else {
798 // no user file name, no current name- punt
799 psbs("No current filename");
800 goto vc1;
801 }
802
803 // see if file exists- if not, its just a new file request
804 if ((sr=stat((char*)fn, &st_buf)) < 0) {
805 // This is just a request for a new file creation.
806 // The file_insert below will fail but we get
807 // an empty buffer with a file name. Then the "write"
808 // command can do the create.
809 } else {
810 if ((st_buf.st_mode & (S_IFREG)) == 0) {
811 // This is not a regular file
812 psbs("\"%s\" is not a regular file", fn);
813 goto vc1;
814 }
815 if ((st_buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
816 // dont have any read permissions
817 psbs("\"%s\" is not readable", fn);
818 goto vc1;
819 }
820 }
821
822 // There is a read-able regular file
823 // make this the current file
Manuel Novoa III cad53642003-03-19 09:13:01 +0000824 q = (Byte *) bb_xstrdup((char *) fn); // save the cfn
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000825 free(cfn); // free the old name
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000826 cfn = q; // remember new cfn
827
828 vc5:
829 // delete all the contents of text[]
830 new_text(2 * file_size(fn));
831 screenbegin = dot = end = text;
832
833 // insert new file
834 ch = file_insert(fn, text, file_size(fn));
835
836 if (ch < 1) {
837 // start empty buf with dummy line
838 (void) char_insert(text, '\n');
839 ch= 1;
840 }
Paul Fox8552aec2005-09-16 12:20:05 +0000841 file_modified = 0;
842 last_file_modified = -1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000843#ifdef CONFIG_FEATURE_VI_YANKMARK
844 if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
845 free(reg[Ureg]); // free orig line reg- for 'U'
846 reg[Ureg]= 0;
847 }
848 if (YDreg >= 0 && YDreg < 28 && reg[YDreg] != 0) {
849 free(reg[YDreg]); // free default yank/delete register
850 reg[YDreg]= 0;
851 }
852 for (li = 0; li < 28; li++) {
853 mark[li] = 0;
854 } // init the marks
855#endif /* CONFIG_FEATURE_VI_YANKMARK */
856 // how many lines in text[]?
857 li = count_lines(text, end - 1);
858 psb("\"%s\"%s"
859#ifdef CONFIG_FEATURE_VI_READONLY
860 "%s"
861#endif /* CONFIG_FEATURE_VI_READONLY */
862 " %dL, %dC", cfn,
863 (sr < 0 ? " [New file]" : ""),
864#ifdef CONFIG_FEATURE_VI_READONLY
865 ((vi_readonly || readonly) ? " [Read only]" : ""),
866#endif /* CONFIG_FEATURE_VI_READONLY */
867 li, ch);
868 } else if (strncasecmp((char *) cmd, "file", i) == 0) { // what File is this
869 if (b != -1 || e != -1) {
870 ni((Byte *) "No address allowed on this command");
871 goto vc1;
872 }
873 if (strlen((char *) args) > 0) {
874 // user wants a new filename
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000875 free(cfn);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000876 cfn = (Byte *) bb_xstrdup((char *) args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000877 } else {
878 // user wants file status info
Paul Fox8552aec2005-09-16 12:20:05 +0000879 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000880 }
881 } else if (strncasecmp((char *) cmd, "features", i) == 0) { // what features are available
882 // print out values of all features
883 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
884 clear_to_eol(); // clear the line
885 cookmode();
886 show_help();
887 rawmode();
888 Hit_Return();
889 } else if (strncasecmp((char *) cmd, "list", i) == 0) { // literal print line
890 if (b < 0) { // no addr given- use defaults
891 q = begin_line(dot); // assume .,. for the range
892 r = end_line(dot);
893 }
894 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
895 clear_to_eol(); // clear the line
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000896 puts("\r");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000897 for (; q <= r; q++) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000898 int c_is_no_print;
899
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000900 c = *q;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000901 c_is_no_print = c > 127 && !Isprint(c);
902 if (c_is_no_print) {
903 c = '.';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000904 standout_start();
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000905 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000906 if (c == '\n') {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000907 write1("$\r");
908 } else if (c < ' ' || c == 127) {
909 putchar('^');
910 if(c == 127)
911 c = '?';
912 else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000913 c += '@';
914 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000915 putchar(c);
916 if (c_is_no_print)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000917 standout_end();
918 }
919#ifdef CONFIG_FEATURE_VI_SET
920 vc2:
921#endif /* CONFIG_FEATURE_VI_SET */
922 Hit_Return();
923 } else if ((strncasecmp((char *) cmd, "quit", i) == 0) || // Quit
924 (strncasecmp((char *) cmd, "next", i) == 0)) { // edit next file
925 if (useforce) {
926 // force end of argv list
927 if (*cmd == 'q') {
928 optind = save_argc;
929 }
930 editing = 0;
931 goto vc1;
932 }
933 // don't exit if the file been modified
934 if (file_modified) {
935 psbs("No write since last change (:%s! overrides)",
936 (*cmd == 'q' ? "quit" : "next"));
937 goto vc1;
938 }
939 // are there other file to edit
940 if (*cmd == 'q' && optind < save_argc - 1) {
941 psbs("%d more file to edit", (save_argc - optind - 1));
942 goto vc1;
943 }
944 if (*cmd == 'n' && optind >= save_argc - 1) {
945 psbs("No more files to edit");
946 goto vc1;
947 }
948 editing = 0;
949 } else if (strncasecmp((char *) cmd, "read", i) == 0) { // read file into text[]
950 fn = args;
951 if (strlen((char *) fn) <= 0) {
952 psbs("No filename given");
953 goto vc1;
954 }
955 if (b < 0) { // no addr given- use defaults
956 q = begin_line(dot); // assume "dot"
957 }
958 // read after current line- unless user said ":0r foo"
959 if (b != 0)
960 q = next_line(q);
961#ifdef CONFIG_FEATURE_VI_READONLY
962 l= readonly; // remember current files' status
963#endif
964 ch = file_insert(fn, q, file_size(fn));
965#ifdef CONFIG_FEATURE_VI_READONLY
966 readonly= l;
967#endif
968 if (ch < 0)
969 goto vc1; // nothing was inserted
970 // how many lines in text[]?
971 li = count_lines(q, q + ch - 1);
972 psb("\"%s\""
973#ifdef CONFIG_FEATURE_VI_READONLY
974 "%s"
975#endif /* CONFIG_FEATURE_VI_READONLY */
976 " %dL, %dC", fn,
977#ifdef CONFIG_FEATURE_VI_READONLY
978 ((vi_readonly || readonly) ? " [Read only]" : ""),
979#endif /* CONFIG_FEATURE_VI_READONLY */
980 li, ch);
981 if (ch > 0) {
982 // if the insert is before "dot" then we need to update
983 if (q <= dot)
984 dot += ch;
Paul Fox8552aec2005-09-16 12:20:05 +0000985 file_modified++;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000986 }
987 } else if (strncasecmp((char *) cmd, "rewind", i) == 0) { // rewind cmd line args
988 if (file_modified && ! useforce) {
989 psbs("No write since last change (:rewind! overrides)");
990 } else {
991 // reset the filenames to edit
992 optind = fn_start - 1;
993 editing = 0;
994 }
995#ifdef CONFIG_FEATURE_VI_SET
996 } else if (strncasecmp((char *) cmd, "set", i) == 0) { // set or clear features
997 i = 0; // offset into args
998 if (strlen((char *) args) == 0) {
999 // print out values of all options
1000 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
1001 clear_to_eol(); // clear the line
1002 printf("----------------------------------------\r\n");
1003#ifdef CONFIG_FEATURE_VI_SETOPTS
1004 if (!autoindent)
1005 printf("no");
1006 printf("autoindent ");
1007 if (!err_method)
1008 printf("no");
1009 printf("flash ");
1010 if (!ignorecase)
1011 printf("no");
1012 printf("ignorecase ");
1013 if (!showmatch)
1014 printf("no");
1015 printf("showmatch ");
1016 printf("tabstop=%d ", tabstop);
1017#endif /* CONFIG_FEATURE_VI_SETOPTS */
1018 printf("\r\n");
1019 goto vc2;
1020 }
1021 if (strncasecmp((char *) args, "no", 2) == 0)
1022 i = 2; // ":set noautoindent"
1023#ifdef CONFIG_FEATURE_VI_SETOPTS
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001024 setops(args, "autoindent ", i, "ai", VI_AUTOINDENT);
1025 setops(args, "flash ", i, "fl", VI_ERR_METHOD);
1026 setops(args, "ignorecase ", i, "ic", VI_IGNORECASE);
1027 setops(args, "showmatch ", i, "ic", VI_SHOWMATCH);
1028 if (strncasecmp((char *) args + i, "tabstop=%d ", 7) == 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001029 sscanf(strchr((char *) args + i, '='), "=%d", &ch);
1030 if (ch > 0 && ch < columns - 1)
1031 tabstop = ch;
1032 }
1033#endif /* CONFIG_FEATURE_VI_SETOPTS */
1034#endif /* CONFIG_FEATURE_VI_SET */
1035#ifdef CONFIG_FEATURE_VI_SEARCH
1036 } else if (strncasecmp((char *) cmd, "s", 1) == 0) { // substitute a pattern with a replacement pattern
1037 Byte *ls, *F, *R;
1038 int gflag;
1039
1040 // F points to the "find" pattern
1041 // R points to the "replace" pattern
1042 // replace the cmd line delimiters "/" with NULLs
1043 gflag = 0; // global replace flag
1044 c = orig_buf[1]; // what is the delimiter
1045 F = orig_buf + 2; // start of "find"
1046 R = (Byte *) strchr((char *) F, c); // middle delimiter
1047 if (!R) goto colon_s_fail;
1048 *R++ = '\0'; // terminate "find"
1049 buf1 = (Byte *) strchr((char *) R, c);
1050 if (!buf1) goto colon_s_fail;
1051 *buf1++ = '\0'; // terminate "replace"
1052 if (*buf1 == 'g') { // :s/foo/bar/g
1053 buf1++;
1054 gflag++; // turn on gflag
1055 }
1056 q = begin_line(q);
1057 if (b < 0) { // maybe :s/foo/bar/
1058 q = begin_line(dot); // start with cur line
1059 b = count_lines(text, q); // cur line number
1060 }
1061 if (e < 0)
1062 e = b; // maybe :.s/foo/bar/
1063 for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0
1064 ls = q; // orig line start
1065 vc4:
1066 buf1 = char_search(q, F, FORWARD, LIMITED); // search cur line only for "find"
1067 if (buf1 != NULL) {
1068 // we found the "find" pattern- delete it
1069 (void) text_hole_delete(buf1, buf1 + strlen((char *) F) - 1);
1070 // inset the "replace" patern
1071 (void) string_insert(buf1, R); // insert the string
1072 // check for "global" :s/foo/bar/g
1073 if (gflag == 1) {
1074 if ((buf1 + strlen((char *) R)) < end_line(ls)) {
1075 q = buf1 + strlen((char *) R);
1076 goto vc4; // don't let q move past cur line
1077 }
1078 }
1079 }
1080 q = next_line(ls);
1081 }
1082#endif /* CONFIG_FEATURE_VI_SEARCH */
1083 } else if (strncasecmp((char *) cmd, "version", i) == 0) { // show software version
1084 psb("%s", vi_Version);
1085 } else if ((strncasecmp((char *) cmd, "write", i) == 0) || // write text to file
1086 (strncasecmp((char *) cmd, "wq", i) == 0) ||
1087 (strncasecmp((char *) cmd, "x", i) == 0)) {
1088 // is there a file name to write to?
1089 if (strlen((char *) args) > 0) {
1090 fn = args;
1091 }
1092#ifdef CONFIG_FEATURE_VI_READONLY
1093 if ((vi_readonly || readonly) && ! useforce) {
1094 psbs("\"%s\" File is read only", fn);
1095 goto vc3;
1096 }
1097#endif /* CONFIG_FEATURE_VI_READONLY */
1098 // how many lines in text[]?
1099 li = count_lines(q, r);
1100 ch = r - q + 1;
1101 // see if file exists- if not, its just a new file request
1102 if (useforce) {
1103 // if "fn" is not write-able, chmod u+w
1104 // sprintf(syscmd, "chmod u+w %s", fn);
1105 // system(syscmd);
1106 forced = TRUE;
1107 }
1108 l = file_write(fn, q, r);
1109 if (useforce && forced) {
1110 // chmod u-w
1111 // sprintf(syscmd, "chmod u-w %s", fn);
1112 // system(syscmd);
1113 forced = FALSE;
1114 }
1115 psb("\"%s\" %dL, %dC", fn, li, l);
Paul Fox8552aec2005-09-16 12:20:05 +00001116 if (q == text && r == end - 1 && l == ch) {
1117 file_modified = 0;
1118 last_file_modified = -1;
1119 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001120 if ((cmd[0] == 'x' || cmd[1] == 'q') && l == ch) {
1121 editing = 0;
1122 }
1123#ifdef CONFIG_FEATURE_VI_READONLY
1124 vc3:;
1125#endif /* CONFIG_FEATURE_VI_READONLY */
1126#ifdef CONFIG_FEATURE_VI_YANKMARK
1127 } else if (strncasecmp((char *) cmd, "yank", i) == 0) { // yank lines
1128 if (b < 0) { // no addr given- use defaults
1129 q = begin_line(dot); // assume .,. for the range
1130 r = end_line(dot);
1131 }
1132 text_yank(q, r, YDreg);
1133 li = count_lines(q, r);
1134 psb("Yank %d lines (%d chars) into [%c]",
1135 li, strlen((char *) reg[YDreg]), what_reg());
1136#endif /* CONFIG_FEATURE_VI_YANKMARK */
1137 } else {
1138 // cmd unknown
1139 ni((Byte *) cmd);
1140 }
1141 vc1:
1142 dot = bound_dot(dot); // make sure "dot" is valid
1143 return;
1144#ifdef CONFIG_FEATURE_VI_SEARCH
1145colon_s_fail:
1146 psb(":s expression missing delimiters");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001147#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001148}
1149
1150static void Hit_Return(void)
1151{
1152 char c;
1153
1154 standout_start(); // start reverse video
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001155 write1("[Hit return to continue]");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001156 standout_end(); // end reverse video
1157 while ((c = get_one_char()) != '\n' && c != '\r') /*do nothing */
1158 ;
1159 redraw(TRUE); // force redraw all
1160}
1161#endif /* CONFIG_FEATURE_VI_COLON */
1162
1163//----- Synchronize the cursor to Dot --------------------------
1164static void sync_cursor(Byte * d, int *row, int *col)
1165{
1166 Byte *beg_cur, *end_cur; // begin and end of "d" line
1167 Byte *beg_scr, *end_scr; // begin and end of screen
1168 Byte *tp;
1169 int cnt, ro, co;
1170
1171 beg_cur = begin_line(d); // first char of cur line
1172 end_cur = end_line(d); // last char of cur line
1173
1174 beg_scr = end_scr = screenbegin; // first char of screen
1175 end_scr = end_screen(); // last char of screen
1176
1177 if (beg_cur < screenbegin) {
1178 // "d" is before top line on screen
1179 // how many lines do we have to move
1180 cnt = count_lines(beg_cur, screenbegin);
1181 sc1:
1182 screenbegin = beg_cur;
1183 if (cnt > (rows - 1) / 2) {
1184 // we moved too many lines. put "dot" in middle of screen
1185 for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
1186 screenbegin = prev_line(screenbegin);
1187 }
1188 }
1189 } else if (beg_cur > end_scr) {
1190 // "d" is after bottom line on screen
1191 // how many lines do we have to move
1192 cnt = count_lines(end_scr, beg_cur);
1193 if (cnt > (rows - 1) / 2)
1194 goto sc1; // too many lines
1195 for (ro = 0; ro < cnt - 1; ro++) {
1196 // move screen begin the same amount
1197 screenbegin = next_line(screenbegin);
1198 // now, move the end of screen
1199 end_scr = next_line(end_scr);
1200 end_scr = end_line(end_scr);
1201 }
1202 }
1203 // "d" is on screen- find out which row
1204 tp = screenbegin;
1205 for (ro = 0; ro < rows - 1; ro++) { // drive "ro" to correct row
1206 if (tp == beg_cur)
1207 break;
1208 tp = next_line(tp);
1209 }
1210
1211 // find out what col "d" is on
1212 co = 0;
1213 do { // drive "co" to correct column
1214 if (*tp == '\n' || *tp == '\0')
1215 break;
1216 if (*tp == '\t') {
1217 // 7 - (co % 8 )
1218 co += ((tabstop - 1) - (co % tabstop));
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001219 } else if (*tp < ' ' || *tp == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001220 co++; // display as ^X, use 2 columns
1221 }
1222 } while (tp++ < d && ++co);
1223
1224 // "co" is the column where "dot" is.
1225 // The screen has "columns" columns.
1226 // The currently displayed columns are 0+offset -- columns+ofset
1227 // |-------------------------------------------------------------|
1228 // ^ ^ ^
1229 // offset | |------- columns ----------------|
1230 //
1231 // If "co" is already in this range then we do not have to adjust offset
1232 // but, we do have to subtract the "offset" bias from "co".
1233 // If "co" is outside this range then we have to change "offset".
1234 // If the first char of a line is a tab the cursor will try to stay
1235 // in column 7, but we have to set offset to 0.
1236
1237 if (co < 0 + offset) {
1238 offset = co;
1239 }
1240 if (co >= columns + offset) {
1241 offset = co - columns + 1;
1242 }
1243 // if the first char of the line is a tab, and "dot" is sitting on it
1244 // force offset to 0.
1245 if (d == beg_cur && *d == '\t') {
1246 offset = 0;
1247 }
1248 co -= offset;
1249
1250 *row = ro;
1251 *col = co;
1252}
1253
1254//----- Text Movement Routines ---------------------------------
1255static Byte *begin_line(Byte * p) // return pointer to first char cur line
1256{
1257 while (p > text && p[-1] != '\n')
1258 p--; // go to cur line B-o-l
1259 return (p);
1260}
1261
1262static Byte *end_line(Byte * p) // return pointer to NL of cur line line
1263{
1264 while (p < end - 1 && *p != '\n')
1265 p++; // go to cur line E-o-l
1266 return (p);
1267}
1268
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001269static inline Byte *dollar_line(Byte * p) // return pointer to just before NL line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001270{
1271 while (p < end - 1 && *p != '\n')
1272 p++; // go to cur line E-o-l
1273 // Try to stay off of the Newline
1274 if (*p == '\n' && (p - begin_line(p)) > 0)
1275 p--;
1276 return (p);
1277}
1278
1279static Byte *prev_line(Byte * p) // return pointer first char prev line
1280{
1281 p = begin_line(p); // goto begining of cur line
1282 if (p[-1] == '\n' && p > text)
1283 p--; // step to prev line
1284 p = begin_line(p); // goto begining of prev line
1285 return (p);
1286}
1287
1288static Byte *next_line(Byte * p) // return pointer first char next line
1289{
1290 p = end_line(p);
1291 if (*p == '\n' && p < end - 1)
1292 p++; // step to next line
1293 return (p);
1294}
1295
1296//----- Text Information Routines ------------------------------
1297static Byte *end_screen(void)
1298{
1299 Byte *q;
1300 int cnt;
1301
1302 // find new bottom line
1303 q = screenbegin;
1304 for (cnt = 0; cnt < rows - 2; cnt++)
1305 q = next_line(q);
1306 q = end_line(q);
1307 return (q);
1308}
1309
1310static int count_lines(Byte * start, Byte * stop) // count line from start to stop
1311{
1312 Byte *q;
1313 int cnt;
1314
1315 if (stop < start) { // start and stop are backwards- reverse them
1316 q = start;
1317 start = stop;
1318 stop = q;
1319 }
1320 cnt = 0;
1321 stop = end_line(stop); // get to end of this line
1322 for (q = start; q <= stop && q <= end - 1; q++) {
1323 if (*q == '\n')
1324 cnt++;
1325 }
1326 return (cnt);
1327}
1328
1329static Byte *find_line(int li) // find begining of line #li
1330{
1331 Byte *q;
1332
1333 for (q = text; li > 1; li--) {
1334 q = next_line(q);
1335 }
1336 return (q);
1337}
1338
1339//----- Dot Movement Routines ----------------------------------
1340static void dot_left(void)
1341{
1342 if (dot > text && dot[-1] != '\n')
1343 dot--;
1344}
1345
1346static void dot_right(void)
1347{
1348 if (dot < end - 1 && *dot != '\n')
1349 dot++;
1350}
1351
1352static void dot_begin(void)
1353{
1354 dot = begin_line(dot); // return pointer to first char cur line
1355}
1356
1357static void dot_end(void)
1358{
1359 dot = end_line(dot); // return pointer to last char cur line
1360}
1361
1362static Byte *move_to_col(Byte * p, int l)
1363{
1364 int co;
1365
1366 p = begin_line(p);
1367 co = 0;
1368 do {
1369 if (*p == '\n' || *p == '\0')
1370 break;
1371 if (*p == '\t') {
1372 // 7 - (co % 8 )
1373 co += ((tabstop - 1) - (co % tabstop));
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001374 } else if (*p < ' ' || *p == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001375 co++; // display as ^X, use 2 columns
1376 }
1377 } while (++co <= l && p++ < end);
1378 return (p);
1379}
1380
1381static void dot_next(void)
1382{
1383 dot = next_line(dot);
1384}
1385
1386static void dot_prev(void)
1387{
1388 dot = prev_line(dot);
1389}
1390
1391static void dot_scroll(int cnt, int dir)
1392{
1393 Byte *q;
1394
1395 for (; cnt > 0; cnt--) {
1396 if (dir < 0) {
1397 // scroll Backwards
1398 // ctrl-Y scroll up one line
1399 screenbegin = prev_line(screenbegin);
1400 } else {
1401 // scroll Forwards
1402 // ctrl-E scroll down one line
1403 screenbegin = next_line(screenbegin);
1404 }
1405 }
1406 // make sure "dot" stays on the screen so we dont scroll off
1407 if (dot < screenbegin)
1408 dot = screenbegin;
1409 q = end_screen(); // find new bottom line
1410 if (dot > q)
1411 dot = begin_line(q); // is dot is below bottom line?
1412 dot_skip_over_ws();
1413}
1414
1415static void dot_skip_over_ws(void)
1416{
1417 // skip WS
1418 while (isspace(*dot) && *dot != '\n' && dot < end - 1)
1419 dot++;
1420}
1421
1422static void dot_delete(void) // delete the char at 'dot'
1423{
1424 (void) text_hole_delete(dot, dot);
1425}
1426
1427static Byte *bound_dot(Byte * p) // make sure text[0] <= P < "end"
1428{
1429 if (p >= end && end > text) {
1430 p = end - 1;
1431 indicate_error('1');
1432 }
1433 if (p < text) {
1434 p = text;
1435 indicate_error('2');
1436 }
1437 return (p);
1438}
1439
1440//----- Helper Utility Routines --------------------------------
1441
1442//----------------------------------------------------------------
1443//----- Char Routines --------------------------------------------
1444/* Chars that are part of a word-
1445 * 0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
1446 * Chars that are Not part of a word (stoppers)
1447 * !"#$%&'()*+,-./:;<=>?@[\]^`{|}~
1448 * Chars that are WhiteSpace
1449 * TAB NEWLINE VT FF RETURN SPACE
1450 * DO NOT COUNT NEWLINE AS WHITESPACE
1451 */
1452
1453static Byte *new_screen(int ro, int co)
1454{
1455 int li;
1456
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001457 free(screen);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001458 screensize = ro * co + 8;
1459 screen = (Byte *) xmalloc(screensize);
1460 // initialize the new screen. assume this will be a empty file.
1461 screen_erase();
Eric Andersenaff114c2004-04-14 17:51:38 +00001462 // non-existent text[] lines start with a tilde (~).
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001463 for (li = 1; li < ro - 1; li++) {
1464 screen[(li * co) + 0] = '~';
1465 }
1466 return (screen);
1467}
1468
1469static Byte *new_text(int size)
1470{
1471 if (size < 10240)
1472 size = 10240; // have a minimum size for new files
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001473 free(text);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001474 text = (Byte *) xmalloc(size + 8);
1475 memset(text, '\0', size); // clear new text[]
1476 //text += 4; // leave some room for "oops"
1477 textend = text + size - 1;
1478 //textend -= 4; // leave some root for "oops"
1479 return (text);
1480}
1481
1482#ifdef CONFIG_FEATURE_VI_SEARCH
1483static int mycmp(Byte * s1, Byte * s2, int len)
1484{
1485 int i;
1486
1487 i = strncmp((char *) s1, (char *) s2, len);
1488#ifdef CONFIG_FEATURE_VI_SETOPTS
1489 if (ignorecase) {
1490 i = strncasecmp((char *) s1, (char *) s2, len);
1491 }
1492#endif /* CONFIG_FEATURE_VI_SETOPTS */
1493 return (i);
1494}
1495
1496static Byte *char_search(Byte * p, Byte * pat, int dir, int range) // search for pattern starting at p
1497{
1498#ifndef REGEX_SEARCH
1499 Byte *start, *stop;
1500 int len;
1501
1502 len = strlen((char *) pat);
1503 if (dir == FORWARD) {
1504 stop = end - 1; // assume range is p - end-1
1505 if (range == LIMITED)
1506 stop = next_line(p); // range is to next line
1507 for (start = p; start < stop; start++) {
1508 if (mycmp(start, pat, len) == 0) {
1509 return (start);
1510 }
1511 }
1512 } else if (dir == BACK) {
1513 stop = text; // assume range is text - p
1514 if (range == LIMITED)
1515 stop = prev_line(p); // range is to prev line
1516 for (start = p - len; start >= stop; start--) {
1517 if (mycmp(start, pat, len) == 0) {
1518 return (start);
1519 }
1520 }
1521 }
1522 // pattern not found
1523 return (NULL);
1524#else /*REGEX_SEARCH */
1525 char *q;
1526 struct re_pattern_buffer preg;
1527 int i;
1528 int size, range;
1529
1530 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
1531 preg.translate = 0;
1532 preg.fastmap = 0;
1533 preg.buffer = 0;
1534 preg.allocated = 0;
1535
1536 // assume a LIMITED forward search
1537 q = next_line(p);
1538 q = end_line(q);
1539 q = end - 1;
1540 if (dir == BACK) {
1541 q = prev_line(p);
1542 q = text;
1543 }
1544 // count the number of chars to search over, forward or backward
1545 size = q - p;
1546 if (size < 0)
1547 size = p - q;
1548 // RANGE could be negative if we are searching backwards
1549 range = q - p;
1550
1551 q = (char *) re_compile_pattern(pat, strlen((char *) pat), &preg);
1552 if (q != 0) {
1553 // The pattern was not compiled
1554 psbs("bad search pattern: \"%s\": %s", pat, q);
1555 i = 0; // return p if pattern not compiled
1556 goto cs1;
1557 }
1558
1559 q = p;
1560 if (range < 0) {
1561 q = p - size;
1562 if (q < text)
1563 q = text;
1564 }
1565 // search for the compiled pattern, preg, in p[]
1566 // range < 0- search backward
1567 // range > 0- search forward
1568 // 0 < start < size
1569 // re_search() < 0 not found or error
1570 // re_search() > 0 index of found pattern
1571 // struct pattern char int int int struct reg
1572 // re_search (*pattern_buffer, *string, size, start, range, *regs)
1573 i = re_search(&preg, q, size, 0, range, 0);
1574 if (i == -1) {
1575 p = 0;
1576 i = 0; // return NULL if pattern not found
1577 }
1578 cs1:
1579 if (dir == FORWARD) {
1580 p = p + i;
1581 } else {
1582 p = p - i;
1583 }
1584 return (p);
1585#endif /*REGEX_SEARCH */
1586}
1587#endif /* CONFIG_FEATURE_VI_SEARCH */
1588
1589static Byte *char_insert(Byte * p, Byte c) // insert the char c at 'p'
1590{
1591 if (c == 22) { // Is this an ctrl-V?
1592 p = stupid_insert(p, '^'); // use ^ to indicate literal next
1593 p--; // backup onto ^
1594 refresh(FALSE); // show the ^
1595 c = get_one_char();
1596 *p = c;
1597 p++;
Paul Fox8552aec2005-09-16 12:20:05 +00001598 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001599 } else if (c == 27) { // Is this an ESC?
1600 cmd_mode = 0;
1601 cmdcnt = 0;
1602 end_cmd_q(); // stop adding to q
Paul Fox8552aec2005-09-16 12:20:05 +00001603 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001604 if ((p[-1] != '\n') && (dot>text)) {
1605 p--;
1606 }
Paul Foxd13b90b2005-07-18 22:17:25 +00001607 } else if (c == erase_char || c == 8 || c == 127) { // Is this a BS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001608 // 123456789
1609 if ((p[-1] != '\n') && (dot>text)) {
1610 p--;
1611 p = text_hole_delete(p, p); // shrink buffer 1 char
1612#ifdef CONFIG_FEATURE_VI_DOT_CMD
1613 // also rmove char from last_modifying_cmd
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001614 if (last_modifying_cmd != 0 && strlen((char *) last_modifying_cmd) > 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001615 Byte *q;
1616
1617 q = last_modifying_cmd;
1618 q[strlen((char *) q) - 1] = '\0'; // erase BS
1619 q[strlen((char *) q) - 1] = '\0'; // erase prev char
1620 }
1621#endif /* CONFIG_FEATURE_VI_DOT_CMD */
1622 }
1623 } else {
1624 // insert a char into text[]
1625 Byte *sp; // "save p"
1626
1627 if (c == 13)
1628 c = '\n'; // translate \r to \n
1629 sp = p; // remember addr of insert
1630 p = stupid_insert(p, c); // insert the char
1631#ifdef CONFIG_FEATURE_VI_SETOPTS
1632 if (showmatch && strchr(")]}", *sp) != NULL) {
1633 showmatching(sp);
1634 }
1635 if (autoindent && c == '\n') { // auto indent the new line
1636 Byte *q;
1637
1638 q = prev_line(p); // use prev line as templet
1639 for (; isblnk(*q); q++) {
1640 p = stupid_insert(p, *q); // insert the char
1641 }
1642 }
1643#endif /* CONFIG_FEATURE_VI_SETOPTS */
1644 }
1645 return (p);
1646}
1647
1648static Byte *stupid_insert(Byte * p, Byte c) // stupidly insert the char c at 'p'
1649{
1650 p = text_hole_make(p, 1);
1651 if (p != 0) {
1652 *p = c;
Paul Fox8552aec2005-09-16 12:20:05 +00001653 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001654 p++;
1655 }
1656 return (p);
1657}
1658
1659static Byte find_range(Byte ** start, Byte ** stop, Byte c)
1660{
1661 Byte *save_dot, *p, *q;
1662 int cnt;
1663
1664 save_dot = dot;
1665 p = q = dot;
1666
1667 if (strchr("cdy><", c)) {
1668 // these cmds operate on whole lines
1669 p = q = begin_line(p);
1670 for (cnt = 1; cnt < cmdcnt; cnt++) {
1671 q = next_line(q);
1672 }
1673 q = end_line(q);
1674 } else if (strchr("^%$0bBeEft", c)) {
1675 // These cmds operate on char positions
1676 do_cmd(c); // execute movement cmd
1677 q = dot;
1678 } else if (strchr("wW", c)) {
1679 do_cmd(c); // execute movement cmd
Eric Andersenaeea32c2004-02-04 11:19:44 +00001680 // if we are at the next word's first char
1681 // step back one char
1682 // but check the possibilities when it is true
Eric Andersen5cc90ea2004-02-06 10:36:08 +00001683 if (dot > text && ((isspace(dot[-1]) && !isspace(dot[0]))
Eric Andersenaeea32c2004-02-04 11:19:44 +00001684 || (ispunct(dot[-1]) && !ispunct(dot[0]))
1685 || (isalnum(dot[-1]) && !isalnum(dot[0]))))
1686 dot--; // move back off of next word
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001687 if (dot > text && *dot == '\n')
1688 dot--; // stay off NL
1689 q = dot;
1690 } else if (strchr("H-k{", c)) {
1691 // these operate on multi-lines backwards
1692 q = end_line(dot); // find NL
1693 do_cmd(c); // execute movement cmd
1694 dot_begin();
1695 p = dot;
1696 } else if (strchr("L+j}\r\n", c)) {
1697 // these operate on multi-lines forwards
1698 p = begin_line(dot);
1699 do_cmd(c); // execute movement cmd
1700 dot_end(); // find NL
1701 q = dot;
1702 } else {
1703 c = 27; // error- return an ESC char
1704 //break;
1705 }
1706 *start = p;
1707 *stop = q;
1708 if (q < p) {
1709 *start = q;
1710 *stop = p;
1711 }
1712 dot = save_dot;
1713 return (c);
1714}
1715
1716static int st_test(Byte * p, int type, int dir, Byte * tested)
1717{
1718 Byte c, c0, ci;
1719 int test, inc;
1720
1721 inc = dir;
1722 c = c0 = p[0];
1723 ci = p[inc];
1724 test = 0;
1725
1726 if (type == S_BEFORE_WS) {
1727 c = ci;
1728 test = ((!isspace(c)) || c == '\n');
1729 }
1730 if (type == S_TO_WS) {
1731 c = c0;
1732 test = ((!isspace(c)) || c == '\n');
1733 }
1734 if (type == S_OVER_WS) {
1735 c = c0;
1736 test = ((isspace(c)));
1737 }
1738 if (type == S_END_PUNCT) {
1739 c = ci;
1740 test = ((ispunct(c)));
1741 }
1742 if (type == S_END_ALNUM) {
1743 c = ci;
1744 test = ((isalnum(c)) || c == '_');
1745 }
1746 *tested = c;
1747 return (test);
1748}
1749
1750static Byte *skip_thing(Byte * p, int linecnt, int dir, int type)
1751{
1752 Byte c;
1753
1754 while (st_test(p, type, dir, &c)) {
1755 // make sure we limit search to correct number of lines
1756 if (c == '\n' && --linecnt < 1)
1757 break;
1758 if (dir >= 0 && p >= end - 1)
1759 break;
1760 if (dir < 0 && p <= text)
1761 break;
1762 p += dir; // move to next char
1763 }
1764 return (p);
1765}
1766
1767// find matching char of pair () [] {}
1768static Byte *find_pair(Byte * p, Byte c)
1769{
1770 Byte match, *q;
1771 int dir, level;
1772
1773 match = ')';
1774 level = 1;
1775 dir = 1; // assume forward
1776 switch (c) {
1777 case '(':
1778 match = ')';
1779 break;
1780 case '[':
1781 match = ']';
1782 break;
1783 case '{':
1784 match = '}';
1785 break;
1786 case ')':
1787 match = '(';
1788 dir = -1;
1789 break;
1790 case ']':
1791 match = '[';
1792 dir = -1;
1793 break;
1794 case '}':
1795 match = '{';
1796 dir = -1;
1797 break;
1798 }
1799 for (q = p + dir; text <= q && q < end; q += dir) {
1800 // look for match, count levels of pairs (( ))
1801 if (*q == c)
1802 level++; // increase pair levels
1803 if (*q == match)
1804 level--; // reduce pair level
1805 if (level == 0)
1806 break; // found matching pair
1807 }
1808 if (level != 0)
1809 q = NULL; // indicate no match
1810 return (q);
1811}
1812
1813#ifdef CONFIG_FEATURE_VI_SETOPTS
1814// show the matching char of a pair, () [] {}
1815static void showmatching(Byte * p)
1816{
1817 Byte *q, *save_dot;
1818
1819 // we found half of a pair
1820 q = find_pair(p, *p); // get loc of matching char
1821 if (q == NULL) {
1822 indicate_error('3'); // no matching char
1823 } else {
1824 // "q" now points to matching pair
1825 save_dot = dot; // remember where we are
1826 dot = q; // go to new loc
1827 refresh(FALSE); // let the user see it
1828 (void) mysleep(40); // give user some time
1829 dot = save_dot; // go back to old loc
1830 refresh(FALSE);
1831 }
1832}
1833#endif /* CONFIG_FEATURE_VI_SETOPTS */
1834
1835// open a hole in text[]
1836static Byte *text_hole_make(Byte * p, int size) // at "p", make a 'size' byte hole
1837{
1838 Byte *src, *dest;
1839 int cnt;
1840
1841 if (size <= 0)
1842 goto thm0;
1843 src = p;
1844 dest = p + size;
1845 cnt = end - src; // the rest of buffer
1846 if (memmove(dest, src, cnt) != dest) {
1847 psbs("can't create room for new characters");
1848 }
1849 memset(p, ' ', size); // clear new hole
1850 end = end + size; // adjust the new END
Paul Fox8552aec2005-09-16 12:20:05 +00001851 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001852 thm0:
1853 return (p);
1854}
1855
1856// close a hole in text[]
1857static Byte *text_hole_delete(Byte * p, Byte * q) // delete "p" thru "q", inclusive
1858{
1859 Byte *src, *dest;
1860 int cnt, hole_size;
1861
1862 // move forwards, from beginning
1863 // assume p <= q
1864 src = q + 1;
1865 dest = p;
1866 if (q < p) { // they are backward- swap them
1867 src = p + 1;
1868 dest = q;
1869 }
1870 hole_size = q - p + 1;
1871 cnt = end - src;
1872 if (src < text || src > end)
1873 goto thd0;
1874 if (dest < text || dest >= end)
1875 goto thd0;
1876 if (src >= end)
1877 goto thd_atend; // just delete the end of the buffer
1878 if (memmove(dest, src, cnt) != dest) {
1879 psbs("can't delete the character");
1880 }
1881 thd_atend:
1882 end = end - hole_size; // adjust the new END
1883 if (dest >= end)
1884 dest = end - 1; // make sure dest in below end-1
1885 if (end <= text)
1886 dest = end = text; // keep pointers valid
Paul Fox8552aec2005-09-16 12:20:05 +00001887 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001888 thd0:
1889 return (dest);
1890}
1891
1892// copy text into register, then delete text.
1893// if dist <= 0, do not include, or go past, a NewLine
1894//
1895static Byte *yank_delete(Byte * start, Byte * stop, int dist, int yf)
1896{
1897 Byte *p;
1898
1899 // make sure start <= stop
1900 if (start > stop) {
1901 // they are backwards, reverse them
1902 p = start;
1903 start = stop;
1904 stop = p;
1905 }
1906 if (dist <= 0) {
1907 // we can not cross NL boundaries
1908 p = start;
1909 if (*p == '\n')
1910 return (p);
1911 // dont go past a NewLine
1912 for (; p + 1 <= stop; p++) {
1913 if (p[1] == '\n') {
1914 stop = p; // "stop" just before NewLine
1915 break;
1916 }
1917 }
1918 }
1919 p = start;
1920#ifdef CONFIG_FEATURE_VI_YANKMARK
1921 text_yank(start, stop, YDreg);
1922#endif /* CONFIG_FEATURE_VI_YANKMARK */
1923 if (yf == YANKDEL) {
1924 p = text_hole_delete(start, stop);
1925 } // delete lines
1926 return (p);
1927}
1928
1929static void show_help(void)
1930{
1931 puts("These features are available:"
1932#ifdef CONFIG_FEATURE_VI_SEARCH
1933 "\n\tPattern searches with / and ?"
1934#endif /* CONFIG_FEATURE_VI_SEARCH */
1935#ifdef CONFIG_FEATURE_VI_DOT_CMD
1936 "\n\tLast command repeat with \'.\'"
1937#endif /* CONFIG_FEATURE_VI_DOT_CMD */
1938#ifdef CONFIG_FEATURE_VI_YANKMARK
1939 "\n\tLine marking with 'x"
1940 "\n\tNamed buffers with \"x"
1941#endif /* CONFIG_FEATURE_VI_YANKMARK */
1942#ifdef CONFIG_FEATURE_VI_READONLY
1943 "\n\tReadonly if vi is called as \"view\""
1944 "\n\tReadonly with -R command line arg"
1945#endif /* CONFIG_FEATURE_VI_READONLY */
1946#ifdef CONFIG_FEATURE_VI_SET
1947 "\n\tSome colon mode commands with \':\'"
1948#endif /* CONFIG_FEATURE_VI_SET */
1949#ifdef CONFIG_FEATURE_VI_SETOPTS
1950 "\n\tSettable options with \":set\""
1951#endif /* CONFIG_FEATURE_VI_SETOPTS */
1952#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
1953 "\n\tSignal catching- ^C"
1954 "\n\tJob suspend and resume with ^Z"
1955#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
1956#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
1957 "\n\tAdapt to window re-sizes"
1958#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
1959 );
1960}
1961
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001962static inline void print_literal(Byte * buf, Byte * s) // copy s to buf, convert unprintable
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001963{
1964 Byte c, b[2];
1965
1966 b[1] = '\0';
1967 strcpy((char *) buf, ""); // init buf
1968 if (strlen((char *) s) <= 0)
1969 s = (Byte *) "(NULL)";
1970 for (; *s > '\0'; s++) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001971 int c_is_no_print;
1972
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001973 c = *s;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001974 c_is_no_print = c > 127 && !Isprint(c);
1975 if (c_is_no_print) {
1976 strcat((char *) buf, SOn);
1977 c = '.';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001978 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001979 if (c < ' ' || c == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001980 strcat((char *) buf, "^");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001981 if(c == 127)
1982 c = '?';
1983 else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001984 c += '@';
1985 }
1986 b[0] = c;
1987 strcat((char *) buf, (char *) b);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001988 if (c_is_no_print)
1989 strcat((char *) buf, SOs);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001990 if (*s == '\n') {
1991 strcat((char *) buf, "$");
1992 }
1993 }
1994}
1995
1996#ifdef CONFIG_FEATURE_VI_DOT_CMD
1997static void start_new_cmd_q(Byte c)
1998{
1999 // release old cmd
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002000 free(last_modifying_cmd);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002001 // get buffer for new cmd
2002 last_modifying_cmd = (Byte *) xmalloc(BUFSIZ);
2003 memset(last_modifying_cmd, '\0', BUFSIZ); // clear new cmd queue
2004 // if there is a current cmd count put it in the buffer first
2005 if (cmdcnt > 0)
2006 sprintf((char *) last_modifying_cmd, "%d", cmdcnt);
2007 // save char c onto queue
2008 last_modifying_cmd[strlen((char *) last_modifying_cmd)] = c;
2009 adding2q = 1;
2010 return;
2011}
2012
2013static void end_cmd_q(void)
2014{
2015#ifdef CONFIG_FEATURE_VI_YANKMARK
2016 YDreg = 26; // go back to default Yank/Delete reg
2017#endif /* CONFIG_FEATURE_VI_YANKMARK */
2018 adding2q = 0;
2019 return;
2020}
2021#endif /* CONFIG_FEATURE_VI_DOT_CMD */
2022
2023#if defined(CONFIG_FEATURE_VI_YANKMARK) || defined(CONFIG_FEATURE_VI_COLON) || defined(CONFIG_FEATURE_VI_CRASHME)
2024static Byte *string_insert(Byte * p, Byte * s) // insert the string at 'p'
2025{
2026 int cnt, i;
2027
2028 i = strlen((char *) s);
2029 p = text_hole_make(p, i);
2030 strncpy((char *) p, (char *) s, i);
2031 for (cnt = 0; *s != '\0'; s++) {
2032 if (*s == '\n')
2033 cnt++;
2034 }
2035#ifdef CONFIG_FEATURE_VI_YANKMARK
2036 psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
2037#endif /* CONFIG_FEATURE_VI_YANKMARK */
2038 return (p);
2039}
2040#endif /* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_COLON || CONFIG_FEATURE_VI_CRASHME */
2041
2042#ifdef CONFIG_FEATURE_VI_YANKMARK
2043static Byte *text_yank(Byte * p, Byte * q, int dest) // copy text into a register
2044{
2045 Byte *t;
2046 int cnt;
2047
2048 if (q < p) { // they are backwards- reverse them
2049 t = q;
2050 q = p;
2051 p = t;
2052 }
2053 cnt = q - p + 1;
2054 t = reg[dest];
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002055 free(t); // if already a yank register, free it
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002056 t = (Byte *) xmalloc(cnt + 1); // get a new register
2057 memset(t, '\0', cnt + 1); // clear new text[]
2058 strncpy((char *) t, (char *) p, cnt); // copy text[] into bufer
2059 reg[dest] = t;
2060 return (p);
2061}
2062
2063static Byte what_reg(void)
2064{
2065 Byte c;
2066 int i;
2067
2068 i = 0;
2069 c = 'D'; // default to D-reg
2070 if (0 <= YDreg && YDreg <= 25)
2071 c = 'a' + (Byte) YDreg;
2072 if (YDreg == 26)
2073 c = 'D';
2074 if (YDreg == 27)
2075 c = 'U';
2076 return (c);
2077}
2078
2079static void check_context(Byte cmd)
2080{
2081 // A context is defined to be "modifying text"
2082 // Any modifying command establishes a new context.
2083
2084 if (dot < context_start || dot > context_end) {
2085 if (strchr((char *) modifying_cmds, cmd) != NULL) {
2086 // we are trying to modify text[]- make this the current context
2087 mark[27] = mark[26]; // move cur to prev
2088 mark[26] = dot; // move local to cur
2089 context_start = prev_line(prev_line(dot));
2090 context_end = next_line(next_line(dot));
2091 //loiter= start_loiter= now;
2092 }
2093 }
2094 return;
2095}
2096
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002097static inline Byte *swap_context(Byte * p) // goto new context for '' command make this the current context
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002098{
2099 Byte *tmp;
2100
2101 // the current context is in mark[26]
2102 // the previous context is in mark[27]
2103 // only swap context if other context is valid
2104 if (text <= mark[27] && mark[27] <= end - 1) {
2105 tmp = mark[27];
2106 mark[27] = mark[26];
2107 mark[26] = tmp;
2108 p = mark[26]; // where we are going- previous context
2109 context_start = prev_line(prev_line(prev_line(p)));
2110 context_end = next_line(next_line(next_line(p)));
2111 }
2112 return (p);
2113}
2114#endif /* CONFIG_FEATURE_VI_YANKMARK */
2115
2116static int isblnk(Byte c) // is the char a blank or tab
2117{
2118 return (c == ' ' || c == '\t');
2119}
2120
2121//----- Set terminal attributes --------------------------------
2122static void rawmode(void)
2123{
2124 tcgetattr(0, &term_orig);
2125 term_vi = term_orig;
2126 term_vi.c_lflag &= (~ICANON & ~ECHO); // leave ISIG ON- allow intr's
2127 term_vi.c_iflag &= (~IXON & ~ICRNL);
2128 term_vi.c_oflag &= (~ONLCR);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002129 term_vi.c_cc[VMIN] = 1;
2130 term_vi.c_cc[VTIME] = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002131 erase_char = term_vi.c_cc[VERASE];
2132 tcsetattr(0, TCSANOW, &term_vi);
2133}
2134
2135static void cookmode(void)
2136{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002137 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002138 tcsetattr(0, TCSANOW, &term_orig);
2139}
2140
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002141//----- Come here when we get a window resize signal ---------
2142#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
2143static void winch_sig(int sig)
2144{
2145 signal(SIGWINCH, winch_sig);
2146#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
2147 window_size_get(0);
2148#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
2149 new_screen(rows, columns); // get memory for virtual screen
2150 redraw(TRUE); // re-draw the screen
2151}
2152
2153//----- Come here when we get a continue signal -------------------
2154static void cont_sig(int sig)
2155{
2156 rawmode(); // terminal to "raw"
Paul Fox8552aec2005-09-16 12:20:05 +00002157 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002158 redraw(TRUE); // re-draw the screen
2159
2160 signal(SIGTSTP, suspend_sig);
2161 signal(SIGCONT, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002162 kill(my_pid, SIGCONT);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002163}
2164
2165//----- Come here when we get a Suspend signal -------------------
2166static void suspend_sig(int sig)
2167{
2168 place_cursor(rows - 1, 0, FALSE); // go to bottom of screen
2169 clear_to_eol(); // Erase to end of line
2170 cookmode(); // terminal to "cooked"
2171
2172 signal(SIGCONT, cont_sig);
2173 signal(SIGTSTP, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002174 kill(my_pid, SIGTSTP);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002175}
2176
2177//----- Come here when we get a signal ---------------------------
2178static void catch_sig(int sig)
2179{
2180 signal(SIGHUP, catch_sig);
2181 signal(SIGINT, catch_sig);
2182 signal(SIGTERM, catch_sig);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002183 signal(SIGALRM, catch_sig);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002184 if(sig)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002185 longjmp(restart, sig);
2186}
2187
2188//----- Come here when we get a core dump signal -----------------
2189static void core_sig(int sig)
2190{
2191 signal(SIGQUIT, core_sig);
2192 signal(SIGILL, core_sig);
2193 signal(SIGTRAP, core_sig);
2194 signal(SIGIOT, core_sig);
2195 signal(SIGABRT, core_sig);
2196 signal(SIGFPE, core_sig);
2197 signal(SIGBUS, core_sig);
2198 signal(SIGSEGV, core_sig);
2199#ifdef SIGSYS
2200 signal(SIGSYS, core_sig);
2201#endif
2202
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002203 if(sig) { // signaled
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002204 dot = bound_dot(dot); // make sure "dot" is valid
2205
2206 longjmp(restart, sig);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002207 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002208}
2209#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
2210
2211static int mysleep(int hund) // sleep for 'h' 1/100 seconds
2212{
2213 // Don't hang- Wait 5/100 seconds- 1 Sec= 1000000
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002214 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002215 FD_ZERO(&rfds);
2216 FD_SET(0, &rfds);
2217 tv.tv_sec = 0;
2218 tv.tv_usec = hund * 10000;
2219 select(1, &rfds, NULL, NULL, &tv);
2220 return (FD_ISSET(0, &rfds));
2221}
2222
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002223static Byte readbuffer[BUFSIZ];
2224static int readed_for_parse;
2225
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002226//----- IO Routines --------------------------------------------
2227static Byte readit(void) // read (maybe cursor) key from stdin
2228{
2229 Byte c;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002230 int n;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002231 struct esc_cmds {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002232 const char *seq;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002233 Byte val;
2234 };
2235
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002236 static const struct esc_cmds esccmds[] = {
2237 {"OA", (Byte) VI_K_UP}, // cursor key Up
2238 {"OB", (Byte) VI_K_DOWN}, // cursor key Down
2239 {"OC", (Byte) VI_K_RIGHT}, // Cursor Key Right
2240 {"OD", (Byte) VI_K_LEFT}, // cursor key Left
2241 {"OH", (Byte) VI_K_HOME}, // Cursor Key Home
2242 {"OF", (Byte) VI_K_END}, // Cursor Key End
2243 {"[A", (Byte) VI_K_UP}, // cursor key Up
2244 {"[B", (Byte) VI_K_DOWN}, // cursor key Down
2245 {"[C", (Byte) VI_K_RIGHT}, // Cursor Key Right
2246 {"[D", (Byte) VI_K_LEFT}, // cursor key Left
2247 {"[H", (Byte) VI_K_HOME}, // Cursor Key Home
2248 {"[F", (Byte) VI_K_END}, // Cursor Key End
2249 {"[1~", (Byte) VI_K_HOME}, // Cursor Key Home
2250 {"[2~", (Byte) VI_K_INSERT}, // Cursor Key Insert
2251 {"[4~", (Byte) VI_K_END}, // Cursor Key End
2252 {"[5~", (Byte) VI_K_PAGEUP}, // Cursor Key Page Up
2253 {"[6~", (Byte) VI_K_PAGEDOWN}, // Cursor Key Page Down
2254 {"OP", (Byte) VI_K_FUN1}, // Function Key F1
2255 {"OQ", (Byte) VI_K_FUN2}, // Function Key F2
2256 {"OR", (Byte) VI_K_FUN3}, // Function Key F3
2257 {"OS", (Byte) VI_K_FUN4}, // Function Key F4
2258 {"[15~", (Byte) VI_K_FUN5}, // Function Key F5
2259 {"[17~", (Byte) VI_K_FUN6}, // Function Key F6
2260 {"[18~", (Byte) VI_K_FUN7}, // Function Key F7
2261 {"[19~", (Byte) VI_K_FUN8}, // Function Key F8
2262 {"[20~", (Byte) VI_K_FUN9}, // Function Key F9
2263 {"[21~", (Byte) VI_K_FUN10}, // Function Key F10
2264 {"[23~", (Byte) VI_K_FUN11}, // Function Key F11
2265 {"[24~", (Byte) VI_K_FUN12}, // Function Key F12
2266 {"[11~", (Byte) VI_K_FUN1}, // Function Key F1
2267 {"[12~", (Byte) VI_K_FUN2}, // Function Key F2
2268 {"[13~", (Byte) VI_K_FUN3}, // Function Key F3
2269 {"[14~", (Byte) VI_K_FUN4}, // Function Key F4
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002270 };
2271
2272#define ESCCMDS_COUNT (sizeof(esccmds)/sizeof(struct esc_cmds))
2273
2274 (void) alarm(0); // turn alarm OFF while we wait for input
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002275 fflush(stdout);
2276 n = readed_for_parse;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002277 // get input from User- are there already input chars in Q?
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002278 if (n <= 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002279 ri0:
2280 // the Q is empty, wait for a typed char
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002281 n = read(0, readbuffer, BUFSIZ - 1);
2282 if (n < 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002283 if (errno == EINTR)
2284 goto ri0; // interrupted sys call
2285 if (errno == EBADF)
2286 editing = 0;
2287 if (errno == EFAULT)
2288 editing = 0;
2289 if (errno == EINVAL)
2290 editing = 0;
2291 if (errno == EIO)
2292 editing = 0;
2293 errno = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002294 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002295 if(n <= 0)
2296 return 0; // error
2297 if (readbuffer[0] == 27) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002298 // This is an ESC char. Is this Esc sequence?
2299 // Could be bare Esc key. See if there are any
2300 // more chars to read after the ESC. This would
2301 // be a Function or Cursor Key sequence.
2302 FD_ZERO(&rfds);
2303 FD_SET(0, &rfds);
2304 tv.tv_sec = 0;
2305 tv.tv_usec = 50000; // Wait 5/100 seconds- 1 Sec=1000000
2306
2307 // keep reading while there are input chars and room in buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002308 while (select(1, &rfds, NULL, NULL, &tv) > 0 && n <= (BUFSIZ - 5)) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002309 // read the rest of the ESC string
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002310 int r = read(0, (void *) (readbuffer + n), BUFSIZ - n);
2311 if (r > 0) {
2312 n += r;
2313 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002314 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002315 }
2316 readed_for_parse = n;
2317 }
2318 c = readbuffer[0];
2319 if(c == 27 && n > 1) {
2320 // Maybe cursor or function key?
2321 const struct esc_cmds *eindex;
2322
2323 for (eindex = esccmds; eindex < &esccmds[ESCCMDS_COUNT]; eindex++) {
2324 int cnt = strlen(eindex->seq);
2325
2326 if(n <= cnt)
2327 continue;
2328 if(strncmp(eindex->seq, (char *) readbuffer + 1, cnt))
2329 continue;
2330 // is a Cursor key- put derived value back into Q
2331 c = eindex->val;
2332 // for squeeze out the ESC sequence
2333 n = cnt + 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002334 break;
2335 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002336 if(eindex == &esccmds[ESCCMDS_COUNT]) {
2337 /* defined ESC sequence not found, set only one ESC */
2338 n = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002339 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002340 } else {
2341 n = 1;
2342 }
2343 // remove key sequence from Q
2344 readed_for_parse -= n;
2345 memmove(readbuffer, readbuffer + n, BUFSIZ - n);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002346 (void) alarm(3); // we are done waiting for input, turn alarm ON
2347 return (c);
2348}
2349
2350//----- IO Routines --------------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002351static Byte get_one_char(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002352{
2353 static Byte c;
2354
2355#ifdef CONFIG_FEATURE_VI_DOT_CMD
2356 // ! adding2q && ioq == 0 read()
2357 // ! adding2q && ioq != 0 *ioq
2358 // adding2q *last_modifying_cmd= read()
2359 if (!adding2q) {
2360 // we are not adding to the q.
2361 // but, we may be reading from a q
2362 if (ioq == 0) {
2363 // there is no current q, read from STDIN
2364 c = readit(); // get the users input
2365 } else {
2366 // there is a queue to get chars from first
2367 c = *ioq++;
2368 if (c == '\0') {
2369 // the end of the q, read from STDIN
2370 free(ioq_start);
2371 ioq_start = ioq = 0;
2372 c = readit(); // get the users input
2373 }
2374 }
2375 } else {
2376 // adding STDIN chars to q
2377 c = readit(); // get the users input
2378 if (last_modifying_cmd != 0) {
Eric Andersenfda2b7f2002-10-26 10:19:19 +00002379 int len = strlen((char *) last_modifying_cmd);
2380 if (len + 1 >= BUFSIZ) {
2381 psbs("last_modifying_cmd overrun");
2382 } else {
2383 // add new char to q
2384 last_modifying_cmd[len] = c;
2385 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002386 }
2387 }
2388#else /* CONFIG_FEATURE_VI_DOT_CMD */
2389 c = readit(); // get the users input
2390#endif /* CONFIG_FEATURE_VI_DOT_CMD */
2391 return (c); // return the char, where ever it came from
2392}
2393
2394static Byte *get_input_line(Byte * prompt) // get input line- use "status line"
2395{
2396 Byte buf[BUFSIZ];
2397 Byte c;
2398 int i;
2399 static Byte *obufp = NULL;
2400
2401 strcpy((char *) buf, (char *) prompt);
Paul Fox8552aec2005-09-16 12:20:05 +00002402 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002403 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
2404 clear_to_eol(); // clear the line
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002405 write1(prompt); // write out the :, /, or ? prompt
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002406
2407 for (i = strlen((char *) buf); i < BUFSIZ;) {
2408 c = get_one_char(); // read user input
2409 if (c == '\n' || c == '\r' || c == 27)
2410 break; // is this end of input
Paul Foxf2de0b72005-09-13 22:20:37 +00002411 if (c == erase_char || c == 8 || c == 127) {
2412 // user wants to erase prev char
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002413 i--; // backup to prev char
2414 buf[i] = '\0'; // erase the char
2415 buf[i + 1] = '\0'; // null terminate buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002416 write1("\b \b"); // erase char on screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002417 if (i <= 0) { // user backs up before b-o-l, exit
2418 break;
2419 }
2420 } else {
2421 buf[i] = c; // save char in buffer
2422 buf[i + 1] = '\0'; // make sure buffer is null terminated
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002423 putchar(c); // echo the char back to user
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002424 i++;
2425 }
2426 }
2427 refresh(FALSE);
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002428 free(obufp);
Manuel Novoa III cad53642003-03-19 09:13:01 +00002429 obufp = (Byte *) bb_xstrdup((char *) buf);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002430 return (obufp);
2431}
2432
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002433static int file_size(const Byte * fn) // what is the byte size of "fn"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002434{
2435 struct stat st_buf;
2436 int cnt, sr;
2437
2438 if (fn == 0 || strlen(fn) <= 0)
2439 return (-1);
2440 cnt = -1;
2441 sr = stat((char *) fn, &st_buf); // see if file exists
2442 if (sr >= 0) {
2443 cnt = (int) st_buf.st_size;
2444 }
2445 return (cnt);
2446}
2447
2448static int file_insert(Byte * fn, Byte * p, int size)
2449{
2450 int fd, cnt;
2451
2452 cnt = -1;
2453#ifdef CONFIG_FEATURE_VI_READONLY
2454 readonly = FALSE;
2455#endif /* CONFIG_FEATURE_VI_READONLY */
2456 if (fn == 0 || strlen((char*) fn) <= 0) {
2457 psbs("No filename given");
2458 goto fi0;
2459 }
2460 if (size == 0) {
2461 // OK- this is just a no-op
2462 cnt = 0;
2463 goto fi0;
2464 }
2465 if (size < 0) {
2466 psbs("Trying to insert a negative number (%d) of characters", size);
2467 goto fi0;
2468 }
2469 if (p < text || p > end) {
2470 psbs("Trying to insert file outside of memory");
2471 goto fi0;
2472 }
2473
2474 // see if we can open the file
2475#ifdef CONFIG_FEATURE_VI_READONLY
2476 if (vi_readonly) goto fi1; // do not try write-mode
2477#endif
2478 fd = open((char *) fn, O_RDWR); // assume read & write
2479 if (fd < 0) {
2480 // could not open for writing- maybe file is read only
2481#ifdef CONFIG_FEATURE_VI_READONLY
2482 fi1:
2483#endif
2484 fd = open((char *) fn, O_RDONLY); // try read-only
2485 if (fd < 0) {
2486 psbs("\"%s\" %s", fn, "could not open file");
2487 goto fi0;
2488 }
2489#ifdef CONFIG_FEATURE_VI_READONLY
2490 // got the file- read-only
2491 readonly = TRUE;
2492#endif /* CONFIG_FEATURE_VI_READONLY */
2493 }
2494 p = text_hole_make(p, size);
2495 cnt = read(fd, p, size);
2496 close(fd);
2497 if (cnt < 0) {
2498 cnt = -1;
2499 p = text_hole_delete(p, p + size - 1); // un-do buffer insert
2500 psbs("could not read file \"%s\"", fn);
2501 } else if (cnt < size) {
2502 // There was a partial read, shrink unused space text[]
2503 p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert
2504 psbs("could not read all of file \"%s\"", fn);
2505 }
2506 if (cnt >= size)
Paul Fox8552aec2005-09-16 12:20:05 +00002507 file_modified++;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002508 fi0:
2509 return (cnt);
2510}
2511
2512static int file_write(Byte * fn, Byte * first, Byte * last)
2513{
2514 int fd, cnt, charcnt;
2515
2516 if (fn == 0) {
2517 psbs("No current filename");
2518 return (-1);
2519 }
2520 charcnt = 0;
2521 // FIXIT- use the correct umask()
2522 fd = open((char *) fn, (O_WRONLY | O_CREAT | O_TRUNC), 0664);
2523 if (fd < 0)
2524 return (-1);
2525 cnt = last - first + 1;
2526 charcnt = write(fd, first, cnt);
2527 if (charcnt == cnt) {
2528 // good write
2529 //file_modified= FALSE; // the file has not been modified
2530 } else {
2531 charcnt = 0;
2532 }
2533 close(fd);
2534 return (charcnt);
2535}
2536
2537//----- Terminal Drawing ---------------------------------------
2538// The terminal is made up of 'rows' line of 'columns' columns.
Eric Andersenaff114c2004-04-14 17:51:38 +00002539// classically this would be 24 x 80.
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002540// screen coordinates
2541// 0,0 ... 0,79
2542// 1,0 ... 1,79
2543// . ... .
2544// . ... .
2545// 22,0 ... 22,79
2546// 23,0 ... 23,79 status line
2547//
2548
2549//----- Move the cursor to row x col (count from 0, not 1) -------
2550static void place_cursor(int row, int col, int opti)
2551{
2552 char cm1[BUFSIZ];
2553 char *cm;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002554#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2555 char cm2[BUFSIZ];
2556 Byte *screenp;
2557 // char cm3[BUFSIZ];
2558 int Rrow= last_row;
2559#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002560
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002561 memset(cm1, '\0', BUFSIZ - 1); // clear the buffer
2562
2563 if (row < 0) row = 0;
2564 if (row >= rows) row = rows - 1;
2565 if (col < 0) col = 0;
2566 if (col >= columns) col = columns - 1;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002567
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002568 //----- 1. Try the standard terminal ESC sequence
2569 sprintf((char *) cm1, CMrc, row + 1, col + 1);
2570 cm= cm1;
2571 if (! opti) goto pc0;
2572
2573#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2574 //----- find the minimum # of chars to move cursor -------------
2575 //----- 2. Try moving with discreet chars (Newline, [back]space, ...)
2576 memset(cm2, '\0', BUFSIZ - 1); // clear the buffer
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002577
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002578 // move to the correct row
2579 while (row < Rrow) {
2580 // the cursor has to move up
2581 strcat(cm2, CMup);
2582 Rrow--;
2583 }
2584 while (row > Rrow) {
2585 // the cursor has to move down
2586 strcat(cm2, CMdown);
2587 Rrow++;
2588 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002589
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002590 // now move to the correct column
2591 strcat(cm2, "\r"); // start at col 0
2592 // just send out orignal source char to get to correct place
2593 screenp = &screen[row * columns]; // start of screen line
2594 strncat(cm2, screenp, col);
2595
2596 //----- 3. Try some other way of moving cursor
2597 //---------------------------------------------
2598
2599 // pick the shortest cursor motion to send out
2600 cm= cm1;
2601 if (strlen(cm2) < strlen(cm)) {
2602 cm= cm2;
2603 } /* else if (strlen(cm3) < strlen(cm)) {
2604 cm= cm3;
2605 } */
2606#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2607 pc0:
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002608 write1(cm); // move the cursor
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002609}
2610
2611//----- Erase from cursor to end of line -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002612static void clear_to_eol(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002613{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002614 write1(Ceol); // Erase from cursor to end of line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002615}
2616
2617//----- Erase from cursor to end of screen -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002618static void clear_to_eos(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002619{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002620 write1(Ceos); // Erase from cursor to end of screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002621}
2622
2623//----- Start standout mode ------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002624static void standout_start(void) // send "start reverse video" sequence
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002625{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002626 write1(SOs); // Start reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002627}
2628
2629//----- End standout mode --------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002630static void standout_end(void) // send "end reverse video" sequence
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002631{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002632 write1(SOn); // End reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002633}
2634
2635//----- Flash the screen --------------------------------------
2636static void flash(int h)
2637{
2638 standout_start(); // send "start reverse video" sequence
2639 redraw(TRUE);
2640 (void) mysleep(h);
2641 standout_end(); // send "end reverse video" sequence
2642 redraw(TRUE);
2643}
2644
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002645static void Indicate_Error(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002646{
2647#ifdef CONFIG_FEATURE_VI_CRASHME
2648 if (crashme > 0)
2649 return; // generate a random command
2650#endif /* CONFIG_FEATURE_VI_CRASHME */
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002651 if (!err_method) {
2652 write1(bell); // send out a bell character
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002653 } else {
2654 flash(10);
2655 }
2656}
2657
2658//----- Screen[] Routines --------------------------------------
2659//----- Erase the Screen[] memory ------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002660static void screen_erase(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002661{
2662 memset(screen, ' ', screensize); // clear new screen
2663}
2664
Paul Fox8552aec2005-09-16 12:20:05 +00002665static int bufsum(char *buf, int count)
2666{
2667 int sum = 0;
2668 char *e = buf + count;
2669 while (buf < e)
2670 sum += *buf++;
2671 return sum;
2672}
2673
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002674//----- Draw the status line at bottom of the screen -------------
2675static void show_status_line(void)
2676{
Paul Foxc3504852005-09-16 12:48:18 +00002677 int cnt = 0, cksum = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002678
Paul Fox8552aec2005-09-16 12:20:05 +00002679 // either we already have an error or status message, or we
2680 // create one.
2681 if (!have_status_msg) {
2682 cnt = format_edit_status();
2683 cksum = bufsum(status_buffer, cnt);
2684 }
2685 if (have_status_msg || ((cnt > 0 && last_status_cksum != cksum))) {
2686 last_status_cksum= cksum; // remember if we have seen this line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002687 place_cursor(rows - 1, 0, FALSE); // put cursor on status line
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002688 write1(status_buffer);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002689 clear_to_eol();
Paul Fox8552aec2005-09-16 12:20:05 +00002690 if (have_status_msg) {
2691 if ((strlen(status_buffer) - (have_status_msg - 1)) >
2692 (columns - 1) ) {
2693 have_status_msg = 0;
2694 Hit_Return();
2695 }
2696 have_status_msg = 0;
2697 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002698 place_cursor(crow, ccol, FALSE); // put cursor back in correct place
2699 }
Eric Andersena9eb33d2004-08-19 19:15:06 +00002700 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002701}
2702
2703//----- format the status buffer, the bottom line of screen ------
Paul Fox8552aec2005-09-16 12:20:05 +00002704// format status buffer, with STANDOUT mode
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002705static void psbs(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002706{
2707 va_list args;
2708
2709 va_start(args, format);
2710 strcpy((char *) status_buffer, SOs); // Terminal standout mode on
Eric Andersen0ef24c62005-07-18 10:32:59 +00002711 vsprintf((char *) status_buffer + strlen((char *) status_buffer), format, args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002712 strcat((char *) status_buffer, SOn); // Terminal standout mode off
2713 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00002714
2715 have_status_msg = 1 + sizeof(SOs) + sizeof(SOn) - 2;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002716
2717 return;
2718}
2719
Paul Fox8552aec2005-09-16 12:20:05 +00002720// format status buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002721static void psb(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002722{
2723 va_list args;
2724
2725 va_start(args, format);
2726 vsprintf((char *) status_buffer, format, args);
2727 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00002728
2729 have_status_msg = 1;
2730
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002731 return;
2732}
2733
2734static void ni(Byte * s) // display messages
2735{
2736 Byte buf[BUFSIZ];
2737
2738 print_literal(buf, s);
2739 psbs("\'%s\' is not implemented", buf);
2740}
2741
Paul Fox8552aec2005-09-16 12:20:05 +00002742static int format_edit_status(void) // show file status on status line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002743{
Paul Fox8552aec2005-09-16 12:20:05 +00002744 int cur, percent, ret, trunc_at;
2745 static int tot;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002746
Paul Fox8552aec2005-09-16 12:20:05 +00002747 // file_modified is now a counter rather than a flag. this
2748 // helps reduce the amount of line counting we need to do.
2749 // (this will cause a mis-reporting of modified status
2750 // once every MAXINT editing operations.)
2751
2752 // it would be nice to do a similar optimization here -- if
2753 // we haven't done a motion that could have changed which line
2754 // we're on, then we shouldn't have to do this count_lines()
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002755 cur = count_lines(text, dot);
Paul Fox8552aec2005-09-16 12:20:05 +00002756
2757 // reduce counting -- the total lines can't have
2758 // changed if we haven't done any edits.
2759 if (file_modified != last_file_modified) {
2760 tot = cur + count_lines(dot, end - 1) - 1;
2761 last_file_modified = file_modified;
2762 }
2763
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002764 // current line percent
2765 // ------------- ~~ ----------
2766 // total lines 100
2767 if (tot > 0) {
2768 percent = (100 * cur) / tot;
2769 } else {
2770 cur = tot = 0;
2771 percent = 100;
2772 }
Eric Andersen0ef24c62005-07-18 10:32:59 +00002773
Paul Fox8552aec2005-09-16 12:20:05 +00002774 trunc_at = columns < STATUS_BUFFER_LEN-1 ?
2775 columns : STATUS_BUFFER_LEN-1;
2776
2777 ret = snprintf((char *) status_buffer, trunc_at+1,
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002778#ifdef CONFIG_FEATURE_VI_READONLY
Paul Fox8552aec2005-09-16 12:20:05 +00002779 "%c %s%s%s %d/%d %d%%",
2780#else
2781 "%c %s%s %d/%d %d%%",
2782#endif
2783 (cmd_mode ? (cmd_mode == 2 ? 'R':'I'):'-'),
2784 (cfn != 0 ? (char *) cfn : "No file"),
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002785#ifdef CONFIG_FEATURE_VI_READONLY
Paul Fox8552aec2005-09-16 12:20:05 +00002786 ((vi_readonly || readonly) ? " [Read-only]" : ""),
2787#endif
2788 (file_modified ? " [modified]" : ""),
2789 cur, tot, percent);
2790
2791 if (ret >= 0 && ret < trunc_at)
2792 return ret; /* it all fit */
2793
2794 return trunc_at; /* had to truncate */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002795}
2796
2797//----- Force refresh of all Lines -----------------------------
2798static void redraw(int full_screen)
2799{
2800 place_cursor(0, 0, FALSE); // put cursor in correct place
2801 clear_to_eos(); // tel terminal to erase display
2802 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00002803 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002804 refresh(full_screen); // this will redraw the entire display
Paul Fox8552aec2005-09-16 12:20:05 +00002805 show_status_line();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002806}
2807
2808//----- Format a text[] line into a buffer ---------------------
2809static void format_line(Byte *dest, Byte *src, int li)
2810{
2811 int co;
2812 Byte c;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002813
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002814 for (co= 0; co < MAX_SCR_COLS; co++) {
2815 c= ' '; // assume blank
2816 if (li > 0 && co == 0) {
2817 c = '~'; // not first line, assume Tilde
2818 }
2819 // are there chars in text[] and have we gone past the end
2820 if (text < end && src < end) {
2821 c = *src++;
2822 }
2823 if (c == '\n')
2824 break;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002825 if (c > 127 && !Isprint(c)) {
2826 c = '.';
2827 }
2828 if (c < ' ' || c == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002829 if (c == '\t') {
2830 c = ' ';
2831 // co % 8 != 7
2832 for (; (co % tabstop) != (tabstop - 1); co++) {
2833 dest[co] = c;
2834 }
2835 } else {
2836 dest[co++] = '^';
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002837 if(c == 127)
2838 c = '?';
2839 else
2840 c += '@'; // make it visible
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002841 }
2842 }
2843 // the co++ is done here so that the column will
2844 // not be overwritten when we blank-out the rest of line
2845 dest[co] = c;
2846 if (src >= end)
2847 break;
2848 }
2849}
2850
2851//----- Refresh the changed screen lines -----------------------
2852// Copy the source line from text[] into the buffer and note
2853// if the current screenline is different from the new buffer.
2854// If they differ then that line needs redrawing on the terminal.
2855//
2856static void refresh(int full_screen)
2857{
2858 static int old_offset;
2859 int li, changed;
2860 Byte buf[MAX_SCR_COLS];
2861 Byte *tp, *sp; // pointer into text[] and screen[]
2862#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2863 int last_li= -2; // last line that changed- for optimizing cursor movement
2864#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2865
2866#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
2867 window_size_get(0);
2868#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
2869 sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
2870 tp = screenbegin; // index into text[] of top line
2871
2872 // compare text[] to screen[] and mark screen[] lines that need updating
2873 for (li = 0; li < rows - 1; li++) {
2874 int cs, ce; // column start & end
2875 memset(buf, ' ', MAX_SCR_COLS); // blank-out the buffer
2876 buf[MAX_SCR_COLS-1] = 0; // NULL terminate the buffer
2877 // format current text line into buf
2878 format_line(buf, tp, li);
2879
2880 // skip to the end of the current text[] line
2881 while (tp < end && *tp++ != '\n') /*no-op*/ ;
2882
2883 // see if there are any changes between vitual screen and buf
2884 changed = FALSE; // assume no change
2885 cs= 0;
2886 ce= columns-1;
2887 sp = &screen[li * columns]; // start of screen line
2888 if (full_screen) {
2889 // force re-draw of every single column from 0 - columns-1
2890 goto re0;
2891 }
2892 // compare newly formatted buffer with virtual screen
2893 // look forward for first difference between buf and screen
2894 for ( ; cs <= ce; cs++) {
2895 if (buf[cs + offset] != sp[cs]) {
2896 changed = TRUE; // mark for redraw
2897 break;
2898 }
2899 }
2900
2901 // look backward for last difference between buf and screen
2902 for ( ; ce >= cs; ce--) {
2903 if (buf[ce + offset] != sp[ce]) {
2904 changed = TRUE; // mark for redraw
2905 break;
2906 }
2907 }
2908 // now, cs is index of first diff, and ce is index of last diff
2909
2910 // if horz offset has changed, force a redraw
2911 if (offset != old_offset) {
2912 re0:
2913 changed = TRUE;
2914 }
2915
2916 // make a sanity check of columns indexes
2917 if (cs < 0) cs= 0;
2918 if (ce > columns-1) ce= columns-1;
2919 if (cs > ce) { cs= 0; ce= columns-1; }
2920 // is there a change between vitual screen and buf
2921 if (changed) {
2922 // copy changed part of buffer to virtual screen
2923 memmove(sp+cs, buf+(cs+offset), ce-cs+1);
2924
2925 // move cursor to column of first change
2926 if (offset != old_offset) {
2927 // opti_cur_move is still too stupid
2928 // to handle offsets correctly
2929 place_cursor(li, cs, FALSE);
2930 } else {
2931#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2932 // if this just the next line
2933 // try to optimize cursor movement
2934 // otherwise, use standard ESC sequence
2935 place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE);
2936 last_li= li;
2937#else /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2938 place_cursor(li, cs, FALSE); // use standard ESC sequence
2939#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2940 }
2941
2942 // write line out to terminal
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002943 {
2944 int nic = ce-cs+1;
2945 char *out = sp+cs;
2946
2947 while(nic-- > 0) {
2948 putchar(*out);
2949 out++;
2950 }
2951 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002952#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2953 last_row = li;
2954#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2955 }
2956 }
2957
2958#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2959 place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE);
2960 last_row = crow;
2961#else
2962 place_cursor(crow, ccol, FALSE);
2963#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002964
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002965 if (offset != old_offset)
2966 old_offset = offset;
2967}
2968
Eric Andersen3f980402001-04-04 17:31:15 +00002969//---------------------------------------------------------------------
2970//----- the Ascii Chart -----------------------------------------------
2971//
2972// 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel
2973// 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si
2974// 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb
2975// 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us
2976// 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 '
2977// 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f /
2978// 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7
2979// 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ?
2980// 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G
2981// 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O
2982// 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W
2983// 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _
2984// 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g
2985// 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o
2986// 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w
2987// 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del
2988//---------------------------------------------------------------------
2989
2990//----- Execute a Vi Command -----------------------------------
2991static void do_cmd(Byte c)
2992{
2993 Byte c1, *p, *q, *msg, buf[9], *save_dot;
2994 int cnt, i, j, dir, yf;
2995
2996 c1 = c; // quiet the compiler
2997 cnt = yf = dir = 0; // quiet the compiler
2998 p = q = save_dot = msg = buf; // quiet the compiler
2999 memset(buf, '\0', 9); // clear buf
Eric Andersenbff7a602001-11-17 07:15:43 +00003000
Paul Fox8552aec2005-09-16 12:20:05 +00003001 show_status_line();
3002
Eric Andersenbff7a602001-11-17 07:15:43 +00003003 /* if this is a cursor key, skip these checks */
3004 switch (c) {
3005 case VI_K_UP:
3006 case VI_K_DOWN:
3007 case VI_K_LEFT:
3008 case VI_K_RIGHT:
3009 case VI_K_HOME:
3010 case VI_K_END:
3011 case VI_K_PAGEUP:
3012 case VI_K_PAGEDOWN:
3013 goto key_cmd_mode;
3014 }
3015
Eric Andersen3f980402001-04-04 17:31:15 +00003016 if (cmd_mode == 2) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003017 // flip-flop Insert/Replace mode
3018 if (c == VI_K_INSERT) goto dc_i;
Eric Andersen3f980402001-04-04 17:31:15 +00003019 // we are 'R'eplacing the current *dot with new char
3020 if (*dot == '\n') {
3021 // don't Replace past E-o-l
3022 cmd_mode = 1; // convert to insert
3023 } else {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003024 if (1 <= c || Isprint(c)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003025 if (c != 27)
3026 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3027 dot = char_insert(dot, c); // insert new char
3028 }
3029 goto dc1;
3030 }
3031 }
3032 if (cmd_mode == 1) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00003033 // hitting "Insert" twice means "R" replace mode
3034 if (c == VI_K_INSERT) goto dc5;
Eric Andersen3f980402001-04-04 17:31:15 +00003035 // insert the char c at "dot"
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003036 if (1 <= c || Isprint(c)) {
3037 dot = char_insert(dot, c);
Eric Andersen3f980402001-04-04 17:31:15 +00003038 }
3039 goto dc1;
3040 }
3041
Eric Andersenbff7a602001-11-17 07:15:43 +00003042key_cmd_mode:
Eric Andersen3f980402001-04-04 17:31:15 +00003043 switch (c) {
Eric Andersen822c3832001-05-07 17:37:43 +00003044 //case 0x01: // soh
3045 //case 0x09: // ht
3046 //case 0x0b: // vt
3047 //case 0x0e: // so
3048 //case 0x0f: // si
3049 //case 0x10: // dle
3050 //case 0x11: // dc1
3051 //case 0x13: // dc3
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003052#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen1c0d3112001-04-16 15:46:44 +00003053 case 0x14: // dc4 ctrl-T
Eric Andersen3f980402001-04-04 17:31:15 +00003054 crashme = (crashme == 0) ? 1 : 0;
Eric Andersen3f980402001-04-04 17:31:15 +00003055 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003056#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen822c3832001-05-07 17:37:43 +00003057 //case 0x16: // syn
3058 //case 0x17: // etb
3059 //case 0x18: // can
3060 //case 0x1c: // fs
3061 //case 0x1d: // gs
3062 //case 0x1e: // rs
3063 //case 0x1f: // us
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003064 //case '!': // !-
3065 //case '#': // #-
3066 //case '&': // &-
3067 //case '(': // (-
3068 //case ')': // )-
3069 //case '*': // *-
3070 //case ',': // ,-
3071 //case '=': // =-
3072 //case '@': // @-
3073 //case 'F': // F-
3074 //case 'K': // K-
3075 //case 'Q': // Q-
3076 //case 'S': // S-
3077 //case 'T': // T-
3078 //case 'V': // V-
3079 //case '[': // [-
3080 //case '\\': // \-
3081 //case ']': // ]-
3082 //case '_': // _-
3083 //case '`': // `-
3084 //case 'g': // g-
Eric Andersen1c0d3112001-04-16 15:46:44 +00003085 //case 'u': // u- FIXME- there is no undo
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003086 //case 'v': // v-
Eric Andersen3f980402001-04-04 17:31:15 +00003087 default: // unrecognised command
3088 buf[0] = c;
3089 buf[1] = '\0';
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003090 if (c < ' ') {
Eric Andersen3f980402001-04-04 17:31:15 +00003091 buf[0] = '^';
3092 buf[1] = c + '@';
3093 buf[2] = '\0';
3094 }
3095 ni((Byte *) buf);
3096 end_cmd_q(); // stop adding to q
3097 case 0x00: // nul- ignore
3098 break;
3099 case 2: // ctrl-B scroll up full screen
3100 case VI_K_PAGEUP: // Cursor Key Page Up
3101 dot_scroll(rows - 2, -1);
3102 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003103#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +00003104 case 0x03: // ctrl-C interrupt
3105 longjmp(restart, 1);
3106 break;
3107 case 26: // ctrl-Z suspend
3108 suspend_sig(SIGTSTP);
3109 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003110#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Eric Andersen3f980402001-04-04 17:31:15 +00003111 case 4: // ctrl-D scroll down half screen
3112 dot_scroll((rows - 2) / 2, 1);
3113 break;
3114 case 5: // ctrl-E scroll down one line
3115 dot_scroll(1, 1);
3116 break;
3117 case 6: // ctrl-F scroll down full screen
3118 case VI_K_PAGEDOWN: // Cursor Key Page Down
3119 dot_scroll(rows - 2, 1);
3120 break;
3121 case 7: // ctrl-G show current status
Paul Fox8552aec2005-09-16 12:20:05 +00003122 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003123 break;
3124 case 'h': // h- move left
3125 case VI_K_LEFT: // cursor key Left
Paul Foxd13b90b2005-07-18 22:17:25 +00003126 case 8: // ctrl-H- move left (This may be ERASE char)
3127 case 127: // DEL- move left (This may be ERASE char)
Eric Andersen3f980402001-04-04 17:31:15 +00003128 if (cmdcnt-- > 1) {
3129 do_cmd(c);
3130 } // repeat cnt
3131 dot_left();
3132 break;
3133 case 10: // Newline ^J
3134 case 'j': // j- goto next line, same col
3135 case VI_K_DOWN: // cursor key Down
3136 if (cmdcnt-- > 1) {
3137 do_cmd(c);
3138 } // repeat cnt
3139 dot_next(); // go to next B-o-l
3140 dot = move_to_col(dot, ccol + offset); // try stay in same col
3141 break;
3142 case 12: // ctrl-L force redraw whole screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003143 case 18: // ctrl-R force redraw
Eric Andersen822c3832001-05-07 17:37:43 +00003144 place_cursor(0, 0, FALSE); // put cursor in correct place
Eric Andersen3f980402001-04-04 17:31:15 +00003145 clear_to_eos(); // tel terminal to erase display
3146 (void) mysleep(10);
3147 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00003148 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003149 refresh(TRUE); // this will redraw the entire display
3150 break;
3151 case 13: // Carriage Return ^M
3152 case '+': // +- goto next line
3153 if (cmdcnt-- > 1) {
3154 do_cmd(c);
3155 } // repeat cnt
3156 dot_next();
3157 dot_skip_over_ws();
3158 break;
3159 case 21: // ctrl-U scroll up half screen
3160 dot_scroll((rows - 2) / 2, -1);
3161 break;
3162 case 25: // ctrl-Y scroll up one line
3163 dot_scroll(1, -1);
3164 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003165 case 27: // esc
Eric Andersen3f980402001-04-04 17:31:15 +00003166 if (cmd_mode == 0)
3167 indicate_error(c);
3168 cmd_mode = 0; // stop insrting
3169 end_cmd_q();
Paul Fox8552aec2005-09-16 12:20:05 +00003170 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003171 break;
3172 case ' ': // move right
3173 case 'l': // move right
3174 case VI_K_RIGHT: // Cursor Key Right
3175 if (cmdcnt-- > 1) {
3176 do_cmd(c);
3177 } // repeat cnt
3178 dot_right();
3179 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003180#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003181 case '"': // "- name a register to use for Delete/Yank
3182 c1 = get_one_char();
3183 c1 = tolower(c1);
3184 if (islower(c1)) {
3185 YDreg = c1 - 'a';
3186 } else {
3187 indicate_error(c);
3188 }
3189 break;
3190 case '\'': // '- goto a specific mark
3191 c1 = get_one_char();
3192 c1 = tolower(c1);
3193 if (islower(c1)) {
3194 c1 = c1 - 'a';
3195 // get the b-o-l
3196 q = mark[(int) c1];
3197 if (text <= q && q < end) {
3198 dot = q;
3199 dot_begin(); // go to B-o-l
3200 dot_skip_over_ws();
3201 }
3202 } else if (c1 == '\'') { // goto previous context
3203 dot = swap_context(dot); // swap current and previous context
3204 dot_begin(); // go to B-o-l
3205 dot_skip_over_ws();
3206 } else {
3207 indicate_error(c);
3208 }
3209 break;
3210 case 'm': // m- Mark a line
3211 // this is really stupid. If there are any inserts or deletes
3212 // between text[0] and dot then this mark will not point to the
3213 // correct location! It could be off by many lines!
3214 // Well..., at least its quick and dirty.
3215 c1 = get_one_char();
3216 c1 = tolower(c1);
3217 if (islower(c1)) {
3218 c1 = c1 - 'a';
3219 // remember the line
3220 mark[(int) c1] = dot;
3221 } else {
3222 indicate_error(c);
3223 }
3224 break;
3225 case 'P': // P- Put register before
3226 case 'p': // p- put register after
3227 p = reg[YDreg];
3228 if (p == 0) {
3229 psbs("Nothing in register %c", what_reg());
3230 break;
3231 }
3232 // are we putting whole lines or strings
3233 if (strchr((char *) p, '\n') != NULL) {
3234 if (c == 'P') {
3235 dot_begin(); // putting lines- Put above
3236 }
3237 if (c == 'p') {
3238 // are we putting after very last line?
3239 if (end_line(dot) == (end - 1)) {
3240 dot = end; // force dot to end of text[]
3241 } else {
3242 dot_next(); // next line, then put before
3243 }
3244 }
3245 } else {
3246 if (c == 'p')
3247 dot_right(); // move to right, can move to NL
3248 }
3249 dot = string_insert(dot, p); // insert the string
3250 end_cmd_q(); // stop adding to q
3251 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003252 case 'U': // U- Undo; replace current line with original version
3253 if (reg[Ureg] != 0) {
3254 p = begin_line(dot);
3255 q = end_line(dot);
3256 p = text_hole_delete(p, q); // delete cur line
3257 p = string_insert(p, reg[Ureg]); // insert orig line
3258 dot = p;
3259 dot_skip_over_ws();
3260 }
3261 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003262#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003263 case '$': // $- goto end of line
3264 case VI_K_END: // Cursor Key End
3265 if (cmdcnt-- > 1) {
3266 do_cmd(c);
3267 } // repeat cnt
Glenn L McGrathee829062004-01-21 10:59:45 +00003268 dot = end_line(dot);
Eric Andersen3f980402001-04-04 17:31:15 +00003269 break;
3270 case '%': // %- find matching char of pair () [] {}
3271 for (q = dot; q < end && *q != '\n'; q++) {
3272 if (strchr("()[]{}", *q) != NULL) {
3273 // we found half of a pair
3274 p = find_pair(q, *q);
3275 if (p == NULL) {
3276 indicate_error(c);
3277 } else {
3278 dot = p;
3279 }
3280 break;
3281 }
3282 }
3283 if (*q == '\n')
3284 indicate_error(c);
3285 break;
3286 case 'f': // f- forward to a user specified char
3287 last_forward_char = get_one_char(); // get the search char
3288 //
Eric Andersenaff114c2004-04-14 17:51:38 +00003289 // dont separate these two commands. 'f' depends on ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003290 //
Paul Foxd13b90b2005-07-18 22:17:25 +00003291 //**** fall thru to ... ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003292 case ';': // ;- look at rest of line for last forward char
3293 if (cmdcnt-- > 1) {
Eric Andersen822c3832001-05-07 17:37:43 +00003294 do_cmd(';');
Eric Andersen3f980402001-04-04 17:31:15 +00003295 } // repeat cnt
Eric Andersen822c3832001-05-07 17:37:43 +00003296 if (last_forward_char == 0) break;
Eric Andersen3f980402001-04-04 17:31:15 +00003297 q = dot + 1;
3298 while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
3299 q++;
3300 }
3301 if (*q == last_forward_char)
3302 dot = q;
3303 break;
3304 case '-': // -- goto prev line
3305 if (cmdcnt-- > 1) {
3306 do_cmd(c);
3307 } // repeat cnt
3308 dot_prev();
3309 dot_skip_over_ws();
3310 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003311#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003312 case '.': // .- repeat the last modifying command
3313 // Stuff the last_modifying_cmd back into stdin
3314 // and let it be re-executed.
3315 if (last_modifying_cmd != 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +00003316 ioq = ioq_start = (Byte *) bb_xstrdup((char *) last_modifying_cmd);
Eric Andersen3f980402001-04-04 17:31:15 +00003317 }
3318 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003319#endif /* CONFIG_FEATURE_VI_DOT_CMD */
3320#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +00003321 case '?': // /- search for a pattern
3322 case '/': // /- search for a pattern
3323 buf[0] = c;
3324 buf[1] = '\0';
3325 q = get_input_line(buf); // get input line- use "status line"
3326 if (strlen((char *) q) == 1)
3327 goto dc3; // if no pat re-use old pat
3328 if (strlen((char *) q) > 1) { // new pat- save it and find
3329 // there is a new pat
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00003330 free(last_search_pattern);
Manuel Novoa III cad53642003-03-19 09:13:01 +00003331 last_search_pattern = (Byte *) bb_xstrdup((char *) q);
Eric Andersen3f980402001-04-04 17:31:15 +00003332 goto dc3; // now find the pattern
3333 }
3334 // user changed mind and erased the "/"- do nothing
3335 break;
3336 case 'N': // N- backward search for last pattern
3337 if (cmdcnt-- > 1) {
3338 do_cmd(c);
3339 } // repeat cnt
3340 dir = BACK; // assume BACKWARD search
3341 p = dot - 1;
3342 if (last_search_pattern[0] == '?') {
3343 dir = FORWARD;
3344 p = dot + 1;
3345 }
3346 goto dc4; // now search for pattern
3347 break;
3348 case 'n': // n- repeat search for last pattern
3349 // search rest of text[] starting at next char
3350 // if search fails return orignal "p" not the "p+1" address
3351 if (cmdcnt-- > 1) {
3352 do_cmd(c);
3353 } // repeat cnt
3354 dc3:
3355 if (last_search_pattern == 0) {
3356 msg = (Byte *) "No previous regular expression";
3357 goto dc2;
3358 }
3359 if (last_search_pattern[0] == '/') {
3360 dir = FORWARD; // assume FORWARD search
3361 p = dot + 1;
3362 }
3363 if (last_search_pattern[0] == '?') {
3364 dir = BACK;
3365 p = dot - 1;
3366 }
3367 dc4:
3368 q = char_search(p, last_search_pattern + 1, dir, FULL);
3369 if (q != NULL) {
3370 dot = q; // good search, update "dot"
3371 msg = (Byte *) "";
3372 goto dc2;
3373 }
3374 // no pattern found between "dot" and "end"- continue at top
3375 p = text;
3376 if (dir == BACK) {
3377 p = end - 1;
3378 }
3379 q = char_search(p, last_search_pattern + 1, dir, FULL);
3380 if (q != NULL) { // found something
3381 dot = q; // found new pattern- goto it
3382 msg = (Byte *) "search hit BOTTOM, continuing at TOP";
3383 if (dir == BACK) {
3384 msg = (Byte *) "search hit TOP, continuing at BOTTOM";
3385 }
3386 } else {
3387 msg = (Byte *) "Pattern not found";
3388 }
3389 dc2:
Paul Fox8552aec2005-09-16 12:20:05 +00003390 if (*msg) psbs("%s", msg);
Eric Andersen3f980402001-04-04 17:31:15 +00003391 break;
3392 case '{': // {- move backward paragraph
3393 q = char_search(dot, (Byte *) "\n\n", BACK, FULL);
3394 if (q != NULL) { // found blank line
3395 dot = next_line(q); // move to next blank line
3396 }
3397 break;
3398 case '}': // }- move forward paragraph
3399 q = char_search(dot, (Byte *) "\n\n", FORWARD, FULL);
3400 if (q != NULL) { // found blank line
3401 dot = next_line(q); // move to next blank line
3402 }
3403 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003404#endif /* CONFIG_FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +00003405 case '0': // 0- goto begining of line
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003406 case '1': // 1-
3407 case '2': // 2-
3408 case '3': // 3-
3409 case '4': // 4-
3410 case '5': // 5-
3411 case '6': // 6-
3412 case '7': // 7-
3413 case '8': // 8-
3414 case '9': // 9-
Eric Andersen3f980402001-04-04 17:31:15 +00003415 if (c == '0' && cmdcnt < 1) {
3416 dot_begin(); // this was a standalone zero
3417 } else {
3418 cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number
3419 }
3420 break;
3421 case ':': // :- the colon mode commands
Eric Andersen3f980402001-04-04 17:31:15 +00003422 p = get_input_line((Byte *) ":"); // get input line- use "status line"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003423#ifdef CONFIG_FEATURE_VI_COLON
Eric Andersen3f980402001-04-04 17:31:15 +00003424 colon(p); // execute the command
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003425#else /* CONFIG_FEATURE_VI_COLON */
Eric Andersen822c3832001-05-07 17:37:43 +00003426 if (*p == ':')
3427 p++; // move past the ':'
3428 cnt = strlen((char *) p);
3429 if (cnt <= 0)
3430 break;
3431 if (strncasecmp((char *) p, "quit", cnt) == 0 ||
3432 strncasecmp((char *) p, "q!", cnt) == 0) { // delete lines
Matt Kraai1f0c4362001-12-20 23:13:26 +00003433 if (file_modified && p[1] != '!') {
Eric Andersen3f980402001-04-04 17:31:15 +00003434 psbs("No write since last change (:quit! overrides)");
3435 } else {
3436 editing = 0;
3437 }
Eric Andersen822c3832001-05-07 17:37:43 +00003438 } else if (strncasecmp((char *) p, "write", cnt) == 0 ||
Robert Griebla71389b2002-07-31 21:22:21 +00003439 strncasecmp((char *) p, "wq", cnt) == 0 ||
3440 strncasecmp((char *) p, "x", cnt) == 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003441 cnt = file_write(cfn, text, end - 1);
Paul Fox8552aec2005-09-16 12:20:05 +00003442 file_modified = 0;
3443 last_file_modified = -1;
Eric Andersen3f980402001-04-04 17:31:15 +00003444 psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt);
Robert Griebla71389b2002-07-31 21:22:21 +00003445 if (p[0] == 'x' || p[1] == 'q') {
Eric Andersen3f980402001-04-04 17:31:15 +00003446 editing = 0;
3447 }
Eric Andersen822c3832001-05-07 17:37:43 +00003448 } else if (strncasecmp((char *) p, "file", cnt) == 0 ) {
Paul Fox8552aec2005-09-16 12:20:05 +00003449 last_status_cksum = 0; // force status update
Eric Andersen822c3832001-05-07 17:37:43 +00003450 } else if (sscanf((char *) p, "%d", &j) > 0) {
3451 dot = find_line(j); // go to line # j
3452 dot_skip_over_ws();
Eric Andersen3f980402001-04-04 17:31:15 +00003453 } else { // unrecognised cmd
Eric Andersen822c3832001-05-07 17:37:43 +00003454 ni((Byte *) p);
Eric Andersen3f980402001-04-04 17:31:15 +00003455 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003456#endif /* CONFIG_FEATURE_VI_COLON */
Eric Andersen3f980402001-04-04 17:31:15 +00003457 break;
3458 case '<': // <- Left shift something
3459 case '>': // >- Right shift something
3460 cnt = count_lines(text, dot); // remember what line we are on
3461 c1 = get_one_char(); // get the type of thing to delete
3462 find_range(&p, &q, c1);
3463 (void) yank_delete(p, q, 1, YANKONLY); // save copy before change
3464 p = begin_line(p);
3465 q = end_line(q);
3466 i = count_lines(p, q); // # of lines we are shifting
3467 for ( ; i > 0; i--, p = next_line(p)) {
3468 if (c == '<') {
3469 // shift left- remove tab or 8 spaces
3470 if (*p == '\t') {
3471 // shrink buffer 1 char
3472 (void) text_hole_delete(p, p);
3473 } else if (*p == ' ') {
3474 // we should be calculating columns, not just SPACE
3475 for (j = 0; *p == ' ' && j < tabstop; j++) {
3476 (void) text_hole_delete(p, p);
3477 }
3478 }
3479 } else if (c == '>') {
3480 // shift right -- add tab or 8 spaces
3481 (void) char_insert(p, '\t');
3482 }
3483 }
3484 dot = find_line(cnt); // what line were we on
3485 dot_skip_over_ws();
3486 end_cmd_q(); // stop adding to q
3487 break;
3488 case 'A': // A- append at e-o-l
3489 dot_end(); // go to e-o-l
3490 //**** fall thru to ... 'a'
3491 case 'a': // a- append after current char
3492 if (*dot != '\n')
3493 dot++;
3494 goto dc_i;
3495 break;
3496 case 'B': // B- back a blank-delimited Word
3497 case 'E': // E- end of a blank-delimited word
3498 case 'W': // W- forward a blank-delimited word
3499 if (cmdcnt-- > 1) {
3500 do_cmd(c);
3501 } // repeat cnt
3502 dir = FORWARD;
3503 if (c == 'B')
3504 dir = BACK;
3505 if (c == 'W' || isspace(dot[dir])) {
3506 dot = skip_thing(dot, 1, dir, S_TO_WS);
3507 dot = skip_thing(dot, 2, dir, S_OVER_WS);
3508 }
3509 if (c != 'W')
3510 dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
3511 break;
3512 case 'C': // C- Change to e-o-l
3513 case 'D': // D- delete to e-o-l
3514 save_dot = dot;
3515 dot = dollar_line(dot); // move to before NL
3516 // copy text into a register and delete
3517 dot = yank_delete(save_dot, dot, 0, YANKDEL); // delete to e-o-l
3518 if (c == 'C')
3519 goto dc_i; // start inserting
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003520#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003521 if (c == 'D')
3522 end_cmd_q(); // stop adding to q
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003523#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +00003524 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003525 case 'G': // G- goto to a line number (default= E-O-F)
3526 dot = end - 1; // assume E-O-F
Eric Andersen1c0d3112001-04-16 15:46:44 +00003527 if (cmdcnt > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00003528 dot = find_line(cmdcnt); // what line is #cmdcnt
Eric Andersen1c0d3112001-04-16 15:46:44 +00003529 }
3530 dot_skip_over_ws();
3531 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003532 case 'H': // H- goto top line on screen
3533 dot = screenbegin;
3534 if (cmdcnt > (rows - 1)) {
3535 cmdcnt = (rows - 1);
3536 }
3537 if (cmdcnt-- > 1) {
3538 do_cmd('+');
3539 } // repeat cnt
3540 dot_skip_over_ws();
3541 break;
3542 case 'I': // I- insert before first non-blank
3543 dot_begin(); // 0
3544 dot_skip_over_ws();
3545 //**** fall thru to ... 'i'
3546 case 'i': // i- insert before current char
3547 case VI_K_INSERT: // Cursor Key Insert
3548 dc_i:
3549 cmd_mode = 1; // start insrting
Eric Andersen3f980402001-04-04 17:31:15 +00003550 break;
3551 case 'J': // J- join current and next lines together
3552 if (cmdcnt-- > 2) {
3553 do_cmd(c);
3554 } // repeat cnt
3555 dot_end(); // move to NL
3556 if (dot < end - 1) { // make sure not last char in text[]
3557 *dot++ = ' '; // replace NL with space
Paul Fox8552aec2005-09-16 12:20:05 +00003558 file_modified++;
Eric Andersen3f980402001-04-04 17:31:15 +00003559 while (isblnk(*dot)) { // delete leading WS
3560 dot_delete();
3561 }
3562 }
3563 end_cmd_q(); // stop adding to q
3564 break;
3565 case 'L': // L- goto bottom line on screen
3566 dot = end_screen();
3567 if (cmdcnt > (rows - 1)) {
3568 cmdcnt = (rows - 1);
3569 }
3570 if (cmdcnt-- > 1) {
3571 do_cmd('-');
3572 } // repeat cnt
3573 dot_begin();
3574 dot_skip_over_ws();
3575 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003576 case 'M': // M- goto middle line on screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003577 dot = screenbegin;
3578 for (cnt = 0; cnt < (rows-1) / 2; cnt++)
3579 dot = next_line(dot);
3580 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003581 case 'O': // O- open a empty line above
Eric Andersen822c3832001-05-07 17:37:43 +00003582 // 0i\n ESC -i
Eric Andersen3f980402001-04-04 17:31:15 +00003583 p = begin_line(dot);
3584 if (p[-1] == '\n') {
3585 dot_prev();
3586 case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
3587 dot_end();
3588 dot = char_insert(dot, '\n');
3589 } else {
3590 dot_begin(); // 0
Eric Andersen822c3832001-05-07 17:37:43 +00003591 dot = char_insert(dot, '\n'); // i\n ESC
Eric Andersen3f980402001-04-04 17:31:15 +00003592 dot_prev(); // -
3593 }
3594 goto dc_i;
3595 break;
3596 case 'R': // R- continuous Replace char
Eric Andersen1c0d3112001-04-16 15:46:44 +00003597 dc5:
Eric Andersen3f980402001-04-04 17:31:15 +00003598 cmd_mode = 2;
Eric Andersen3f980402001-04-04 17:31:15 +00003599 break;
3600 case 'X': // X- delete char before dot
3601 case 'x': // x- delete the current char
3602 case 's': // s- substitute the current char
3603 if (cmdcnt-- > 1) {
3604 do_cmd(c);
3605 } // repeat cnt
3606 dir = 0;
3607 if (c == 'X')
3608 dir = -1;
3609 if (dot[dir] != '\n') {
3610 if (c == 'X')
3611 dot--; // delete prev char
3612 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3613 }
3614 if (c == 's')
3615 goto dc_i; // start insrting
3616 end_cmd_q(); // stop adding to q
3617 break;
3618 case 'Z': // Z- if modified, {write}; exit
3619 // ZZ means to save file (if necessary), then exit
3620 c1 = get_one_char();
3621 if (c1 != 'Z') {
3622 indicate_error(c);
3623 break;
3624 }
Matt Kraai1f0c4362001-12-20 23:13:26 +00003625 if (file_modified
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003626#ifdef CONFIG_FEATURE_VI_READONLY
Matt Kraai1f0c4362001-12-20 23:13:26 +00003627 && ! vi_readonly
3628 && ! readonly
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003629#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen3f980402001-04-04 17:31:15 +00003630 ) {
3631 cnt = file_write(cfn, text, end - 1);
3632 if (cnt == (end - 1 - text + 1)) {
3633 editing = 0;
3634 }
3635 } else {
3636 editing = 0;
3637 }
3638 break;
3639 case '^': // ^- move to first non-blank on line
3640 dot_begin();
3641 dot_skip_over_ws();
3642 break;
3643 case 'b': // b- back a word
3644 case 'e': // e- end of word
3645 if (cmdcnt-- > 1) {
3646 do_cmd(c);
3647 } // repeat cnt
3648 dir = FORWARD;
3649 if (c == 'b')
3650 dir = BACK;
3651 if ((dot + dir) < text || (dot + dir) > end - 1)
3652 break;
3653 dot += dir;
3654 if (isspace(*dot)) {
3655 dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
3656 }
3657 if (isalnum(*dot) || *dot == '_') {
3658 dot = skip_thing(dot, 1, dir, S_END_ALNUM);
3659 } else if (ispunct(*dot)) {
3660 dot = skip_thing(dot, 1, dir, S_END_PUNCT);
3661 }
3662 break;
3663 case 'c': // c- change something
3664 case 'd': // d- delete something
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003665#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003666 case 'y': // y- yank something
3667 case 'Y': // Y- Yank a line
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003668#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003669 yf = YANKDEL; // assume either "c" or "d"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003670#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003671 if (c == 'y' || c == 'Y')
3672 yf = YANKONLY;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003673#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003674 c1 = 'y';
3675 if (c != 'Y')
3676 c1 = get_one_char(); // get the type of thing to delete
3677 find_range(&p, &q, c1);
3678 if (c1 == 27) { // ESC- user changed mind and wants out
3679 c = c1 = 27; // Escape- do nothing
3680 } else if (strchr("wW", c1)) {
3681 if (c == 'c') {
3682 // don't include trailing WS as part of word
3683 while (isblnk(*q)) {
3684 if (q <= text || q[-1] == '\n')
3685 break;
3686 q--;
3687 }
3688 }
3689 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen822c3832001-05-07 17:37:43 +00003690 } else if (strchr("^0bBeEft$", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003691 // single line copy text into a register and delete
3692 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen1c0d3112001-04-16 15:46:44 +00003693 } else if (strchr("cdykjHL%+-{}\r\n", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003694 // multiple line copy text into a register and delete
3695 dot = yank_delete(p, q, 1, yf); // delete lines
Eric Andersen1c0d3112001-04-16 15:46:44 +00003696 if (c == 'c') {
3697 dot = char_insert(dot, '\n');
3698 // on the last line of file don't move to prev line
3699 if (dot != (end-1)) {
3700 dot_prev();
3701 }
3702 } else if (c == 'd') {
Eric Andersen3f980402001-04-04 17:31:15 +00003703 dot_begin();
3704 dot_skip_over_ws();
3705 }
3706 } else {
3707 // could not recognize object
3708 c = c1 = 27; // error-
3709 indicate_error(c);
3710 }
3711 if (c1 != 27) {
3712 // if CHANGING, not deleting, start inserting after the delete
3713 if (c == 'c') {
3714 strcpy((char *) buf, "Change");
3715 goto dc_i; // start inserting
3716 }
3717 if (c == 'd') {
3718 strcpy((char *) buf, "Delete");
3719 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003720#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003721 if (c == 'y' || c == 'Y') {
3722 strcpy((char *) buf, "Yank");
3723 }
3724 p = reg[YDreg];
3725 q = p + strlen((char *) p);
3726 for (cnt = 0; p <= q; p++) {
3727 if (*p == '\n')
3728 cnt++;
3729 }
3730 psb("%s %d lines (%d chars) using [%c]",
3731 buf, cnt, strlen((char *) reg[YDreg]), what_reg());
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003732#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003733 end_cmd_q(); // stop adding to q
3734 }
3735 break;
3736 case 'k': // k- goto prev line, same col
3737 case VI_K_UP: // cursor key Up
3738 if (cmdcnt-- > 1) {
3739 do_cmd(c);
3740 } // repeat cnt
3741 dot_prev();
3742 dot = move_to_col(dot, ccol + offset); // try stay in same col
3743 break;
3744 case 'r': // r- replace the current char with user input
3745 c1 = get_one_char(); // get the replacement char
3746 if (*dot != '\n') {
3747 *dot = c1;
Paul Fox8552aec2005-09-16 12:20:05 +00003748 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003749 }
3750 end_cmd_q(); // stop adding to q
3751 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003752 case 't': // t- move to char prior to next x
3753 last_forward_char = get_one_char();
3754 do_cmd(';');
3755 if (*dot == last_forward_char)
3756 dot_left();
3757 last_forward_char= 0;
3758 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003759 case 'w': // w- forward a word
3760 if (cmdcnt-- > 1) {
3761 do_cmd(c);
3762 } // repeat cnt
3763 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
3764 dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
3765 } else if (ispunct(*dot)) { // we are on PUNCT
3766 dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
3767 }
3768 if (dot < end - 1)
3769 dot++; // move over word
3770 if (isspace(*dot)) {
3771 dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
3772 }
3773 break;
3774 case 'z': // z-
3775 c1 = get_one_char(); // get the replacement char
3776 cnt = 0;
3777 if (c1 == '.')
3778 cnt = (rows - 2) / 2; // put dot at center
3779 if (c1 == '-')
3780 cnt = rows - 2; // put dot at bottom
3781 screenbegin = begin_line(dot); // start dot at top
3782 dot_scroll(cnt, -1);
3783 break;
3784 case '|': // |- move to column "cmdcnt"
3785 dot = move_to_col(dot, cmdcnt - 1); // try to move to column
3786 break;
3787 case '~': // ~- flip the case of letters a-z -> A-Z
3788 if (cmdcnt-- > 1) {
3789 do_cmd(c);
3790 } // repeat cnt
3791 if (islower(*dot)) {
3792 *dot = toupper(*dot);
Paul Fox8552aec2005-09-16 12:20:05 +00003793 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003794 } else if (isupper(*dot)) {
3795 *dot = tolower(*dot);
Paul Fox8552aec2005-09-16 12:20:05 +00003796 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003797 }
3798 dot_right();
3799 end_cmd_q(); // stop adding to q
3800 break;
3801 //----- The Cursor and Function Keys -----------------------------
3802 case VI_K_HOME: // Cursor Key Home
3803 dot_begin();
3804 break;
3805 // The Fn keys could point to do_macro which could translate them
3806 case VI_K_FUN1: // Function Key F1
3807 case VI_K_FUN2: // Function Key F2
3808 case VI_K_FUN3: // Function Key F3
3809 case VI_K_FUN4: // Function Key F4
3810 case VI_K_FUN5: // Function Key F5
3811 case VI_K_FUN6: // Function Key F6
3812 case VI_K_FUN7: // Function Key F7
3813 case VI_K_FUN8: // Function Key F8
3814 case VI_K_FUN9: // Function Key F9
3815 case VI_K_FUN10: // Function Key F10
3816 case VI_K_FUN11: // Function Key F11
3817 case VI_K_FUN12: // Function Key F12
3818 break;
3819 }
3820
3821 dc1:
3822 // if text[] just became empty, add back an empty line
3823 if (end == text) {
3824 (void) char_insert(text, '\n'); // start empty buf with dummy line
3825 dot = text;
3826 }
3827 // it is OK for dot to exactly equal to end, otherwise check dot validity
3828 if (dot != end) {
3829 dot = bound_dot(dot); // make sure "dot" is valid
3830 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003831#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003832 check_context(c); // update the current context
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003833#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003834
3835 if (!isdigit(c))
3836 cmdcnt = 0; // cmd was not a number, reset cmdcnt
3837 cnt = dot - begin_line(dot);
3838 // Try to stay off of the Newline
3839 if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
3840 dot--;
3841}
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003842
3843#ifdef CONFIG_FEATURE_VI_CRASHME
3844static int totalcmds = 0;
3845static int Mp = 85; // Movement command Probability
3846static int Np = 90; // Non-movement command Probability
3847static int Dp = 96; // Delete command Probability
3848static int Ip = 97; // Insert command Probability
3849static int Yp = 98; // Yank command Probability
3850static int Pp = 99; // Put command Probability
3851static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
3852char chars[20] = "\t012345 abcdABCD-=.$";
3853char *words[20] = { "this", "is", "a", "test",
3854 "broadcast", "the", "emergency", "of",
3855 "system", "quick", "brown", "fox",
3856 "jumped", "over", "lazy", "dogs",
3857 "back", "January", "Febuary", "March"
3858};
3859char *lines[20] = {
3860 "You should have received a copy of the GNU General Public License\n",
3861 "char c, cm, *cmd, *cmd1;\n",
3862 "generate a command by percentages\n",
3863 "Numbers may be typed as a prefix to some commands.\n",
3864 "Quit, discarding changes!\n",
3865 "Forced write, if permission originally not valid.\n",
3866 "In general, any ex or ed command (such as substitute or delete).\n",
3867 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3868 "Please get w/ me and I will go over it with you.\n",
3869 "The following is a list of scheduled, committed changes.\n",
3870 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3871 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3872 "Any question about transactions please contact Sterling Huxley.\n",
3873 "I will try to get back to you by Friday, December 31.\n",
3874 "This Change will be implemented on Friday.\n",
3875 "Let me know if you have problems accessing this;\n",
3876 "Sterling Huxley recently added you to the access list.\n",
3877 "Would you like to go to lunch?\n",
3878 "The last command will be automatically run.\n",
3879 "This is too much english for a computer geek.\n",
3880};
3881char *multilines[20] = {
3882 "You should have received a copy of the GNU General Public License\n",
3883 "char c, cm, *cmd, *cmd1;\n",
3884 "generate a command by percentages\n",
3885 "Numbers may be typed as a prefix to some commands.\n",
3886 "Quit, discarding changes!\n",
3887 "Forced write, if permission originally not valid.\n",
3888 "In general, any ex or ed command (such as substitute or delete).\n",
3889 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3890 "Please get w/ me and I will go over it with you.\n",
3891 "The following is a list of scheduled, committed changes.\n",
3892 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3893 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3894 "Any question about transactions please contact Sterling Huxley.\n",
3895 "I will try to get back to you by Friday, December 31.\n",
3896 "This Change will be implemented on Friday.\n",
3897 "Let me know if you have problems accessing this;\n",
3898 "Sterling Huxley recently added you to the access list.\n",
3899 "Would you like to go to lunch?\n",
3900 "The last command will be automatically run.\n",
3901 "This is too much english for a computer geek.\n",
3902};
3903
3904// create a random command to execute
3905static void crash_dummy()
3906{
3907 static int sleeptime; // how long to pause between commands
3908 char c, cm, *cmd, *cmd1;
3909 int i, cnt, thing, rbi, startrbi, percent;
3910
3911 // "dot" movement commands
3912 cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
3913
3914 // is there already a command running?
3915 if (readed_for_parse > 0)
3916 goto cd1;
3917 cd0:
3918 startrbi = rbi = 0;
3919 sleeptime = 0; // how long to pause between commands
3920 memset(readbuffer, '\0', BUFSIZ); // clear the read buffer
3921 // generate a command by percentages
3922 percent = (int) lrand48() % 100; // get a number from 0-99
3923 if (percent < Mp) { // Movement commands
3924 // available commands
3925 cmd = cmd1;
3926 M++;
3927 } else if (percent < Np) { // non-movement commands
3928 cmd = "mz<>\'\""; // available commands
3929 N++;
3930 } else if (percent < Dp) { // Delete commands
3931 cmd = "dx"; // available commands
3932 D++;
3933 } else if (percent < Ip) { // Inset commands
3934 cmd = "iIaAsrJ"; // available commands
3935 I++;
3936 } else if (percent < Yp) { // Yank commands
3937 cmd = "yY"; // available commands
3938 Y++;
3939 } else if (percent < Pp) { // Put commands
3940 cmd = "pP"; // available commands
3941 P++;
3942 } else {
3943 // We do not know how to handle this command, try again
3944 U++;
3945 goto cd0;
3946 }
3947 // randomly pick one of the available cmds from "cmd[]"
3948 i = (int) lrand48() % strlen(cmd);
3949 cm = cmd[i];
3950 if (strchr(":\024", cm))
3951 goto cd0; // dont allow colon or ctrl-T commands
3952 readbuffer[rbi++] = cm; // put cmd into input buffer
3953
3954 // now we have the command-
3955 // there are 1, 2, and multi char commands
3956 // find out which and generate the rest of command as necessary
3957 if (strchr("dmryz<>\'\"", cm)) { // 2-char commands
3958 cmd1 = " \n\r0$^-+wWeEbBhjklHL";
3959 if (cm == 'm' || cm == '\'' || cm == '\"') { // pick a reg[]
3960 cmd1 = "abcdefghijklmnopqrstuvwxyz";
3961 }
3962 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3963 c = cmd1[thing];
3964 readbuffer[rbi++] = c; // add movement to input buffer
3965 }
3966 if (strchr("iIaAsc", cm)) { // multi-char commands
3967 if (cm == 'c') {
3968 // change some thing
3969 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3970 c = cmd1[thing];
3971 readbuffer[rbi++] = c; // add movement to input buffer
3972 }
3973 thing = (int) lrand48() % 4; // what thing to insert
3974 cnt = (int) lrand48() % 10; // how many to insert
3975 for (i = 0; i < cnt; i++) {
3976 if (thing == 0) { // insert chars
3977 readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
3978 } else if (thing == 1) { // insert words
3979 strcat((char *) readbuffer, words[(int) lrand48() % 20]);
3980 strcat((char *) readbuffer, " ");
3981 sleeptime = 0; // how fast to type
3982 } else if (thing == 2) { // insert lines
3983 strcat((char *) readbuffer, lines[(int) lrand48() % 20]);
3984 sleeptime = 0; // how fast to type
3985 } else { // insert multi-lines
3986 strcat((char *) readbuffer, multilines[(int) lrand48() % 20]);
3987 sleeptime = 0; // how fast to type
3988 }
3989 }
3990 strcat((char *) readbuffer, "\033");
3991 }
3992 readed_for_parse = strlen(readbuffer);
3993 cd1:
3994 totalcmds++;
3995 if (sleeptime > 0)
3996 (void) mysleep(sleeptime); // sleep 1/100 sec
3997}
3998
3999// test to see if there are any errors
4000static void crash_test()
4001{
4002 static time_t oldtim;
4003 time_t tim;
Glenn L McGrath7127b582002-12-03 21:48:15 +00004004 char d[2], msg[BUFSIZ];
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004005
4006 msg[0] = '\0';
4007 if (end < text) {
4008 strcat((char *) msg, "end<text ");
4009 }
4010 if (end > textend) {
4011 strcat((char *) msg, "end>textend ");
4012 }
4013 if (dot < text) {
4014 strcat((char *) msg, "dot<text ");
4015 }
4016 if (dot > end) {
4017 strcat((char *) msg, "dot>end ");
4018 }
4019 if (screenbegin < text) {
4020 strcat((char *) msg, "screenbegin<text ");
4021 }
4022 if (screenbegin > end - 1) {
4023 strcat((char *) msg, "screenbegin>end-1 ");
4024 }
4025
4026 if (strlen(msg) > 0) {
4027 alarm(0);
Glenn L McGrath7127b582002-12-03 21:48:15 +00004028 printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004029 totalcmds, last_input_char, msg, SOs, SOn);
4030 fflush(stdout);
4031 while (read(0, d, 1) > 0) {
4032 if (d[0] == '\n' || d[0] == '\r')
4033 break;
4034 }
4035 alarm(3);
4036 }
4037 tim = (time_t) time((time_t *) 0);
4038 if (tim >= (oldtim + 3)) {
4039 sprintf((char *) status_buffer,
4040 "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
4041 totalcmds, M, N, I, D, Y, P, U, end - text + 1);
4042 oldtim = tim;
4043 }
4044 return;
4045}
4046#endif /* CONFIG_FEATURE_VI_CRASHME */