blob: 1cc1270366f13f6be63ac2b5d261829dc4b70e21 [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()
Paul Fox90372ed2005-10-09 14:26:26 +0000286static void Hit_Return(void);
287
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000288#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +0000289static Byte *char_search(Byte *, Byte *, int, int); // search for pattern starting at p
290static int mycmp(Byte *, Byte *, int); // string cmp based in "ignorecase"
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000291#endif /* CONFIG_FEATURE_VI_SEARCH */
292#ifdef CONFIG_FEATURE_VI_COLON
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 }
Paul Fox61e45db2005-10-09 14:43:22 +00001115 if (l < 0) {
1116 if (l == -1)
1117 psbs("Write error: %s", strerror(errno));
1118 } else {
1119 psb("\"%s\" %dL, %dC", fn, li, l);
1120 if (q == text && r == end - 1 && l == ch) {
1121 file_modified = 0;
1122 last_file_modified = -1;
1123 }
1124 if ((cmd[0] == 'x' || cmd[1] == 'q') && l == ch) {
1125 editing = 0;
1126 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001127 }
1128#ifdef CONFIG_FEATURE_VI_READONLY
1129 vc3:;
1130#endif /* CONFIG_FEATURE_VI_READONLY */
1131#ifdef CONFIG_FEATURE_VI_YANKMARK
1132 } else if (strncasecmp((char *) cmd, "yank", i) == 0) { // yank lines
1133 if (b < 0) { // no addr given- use defaults
1134 q = begin_line(dot); // assume .,. for the range
1135 r = end_line(dot);
1136 }
1137 text_yank(q, r, YDreg);
1138 li = count_lines(q, r);
1139 psb("Yank %d lines (%d chars) into [%c]",
1140 li, strlen((char *) reg[YDreg]), what_reg());
1141#endif /* CONFIG_FEATURE_VI_YANKMARK */
1142 } else {
1143 // cmd unknown
1144 ni((Byte *) cmd);
1145 }
1146 vc1:
1147 dot = bound_dot(dot); // make sure "dot" is valid
1148 return;
1149#ifdef CONFIG_FEATURE_VI_SEARCH
1150colon_s_fail:
1151 psb(":s expression missing delimiters");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001152#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001153}
Paul Fox61e45db2005-10-09 14:43:22 +00001154
Paul Fox90372ed2005-10-09 14:26:26 +00001155#endif /* CONFIG_FEATURE_VI_COLON */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001156
1157static void Hit_Return(void)
1158{
1159 char c;
1160
1161 standout_start(); // start reverse video
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001162 write1("[Hit return to continue]");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001163 standout_end(); // end reverse video
1164 while ((c = get_one_char()) != '\n' && c != '\r') /*do nothing */
1165 ;
1166 redraw(TRUE); // force redraw all
1167}
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001168
1169//----- Synchronize the cursor to Dot --------------------------
1170static void sync_cursor(Byte * d, int *row, int *col)
1171{
1172 Byte *beg_cur, *end_cur; // begin and end of "d" line
1173 Byte *beg_scr, *end_scr; // begin and end of screen
1174 Byte *tp;
1175 int cnt, ro, co;
1176
1177 beg_cur = begin_line(d); // first char of cur line
1178 end_cur = end_line(d); // last char of cur line
1179
1180 beg_scr = end_scr = screenbegin; // first char of screen
1181 end_scr = end_screen(); // last char of screen
1182
1183 if (beg_cur < screenbegin) {
1184 // "d" is before top line on screen
1185 // how many lines do we have to move
1186 cnt = count_lines(beg_cur, screenbegin);
1187 sc1:
1188 screenbegin = beg_cur;
1189 if (cnt > (rows - 1) / 2) {
1190 // we moved too many lines. put "dot" in middle of screen
1191 for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
1192 screenbegin = prev_line(screenbegin);
1193 }
1194 }
1195 } else if (beg_cur > end_scr) {
1196 // "d" is after bottom line on screen
1197 // how many lines do we have to move
1198 cnt = count_lines(end_scr, beg_cur);
1199 if (cnt > (rows - 1) / 2)
1200 goto sc1; // too many lines
1201 for (ro = 0; ro < cnt - 1; ro++) {
1202 // move screen begin the same amount
1203 screenbegin = next_line(screenbegin);
1204 // now, move the end of screen
1205 end_scr = next_line(end_scr);
1206 end_scr = end_line(end_scr);
1207 }
1208 }
1209 // "d" is on screen- find out which row
1210 tp = screenbegin;
1211 for (ro = 0; ro < rows - 1; ro++) { // drive "ro" to correct row
1212 if (tp == beg_cur)
1213 break;
1214 tp = next_line(tp);
1215 }
1216
1217 // find out what col "d" is on
1218 co = 0;
1219 do { // drive "co" to correct column
1220 if (*tp == '\n' || *tp == '\0')
1221 break;
1222 if (*tp == '\t') {
1223 // 7 - (co % 8 )
1224 co += ((tabstop - 1) - (co % tabstop));
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001225 } else if (*tp < ' ' || *tp == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001226 co++; // display as ^X, use 2 columns
1227 }
1228 } while (tp++ < d && ++co);
1229
1230 // "co" is the column where "dot" is.
1231 // The screen has "columns" columns.
1232 // The currently displayed columns are 0+offset -- columns+ofset
1233 // |-------------------------------------------------------------|
1234 // ^ ^ ^
1235 // offset | |------- columns ----------------|
1236 //
1237 // If "co" is already in this range then we do not have to adjust offset
1238 // but, we do have to subtract the "offset" bias from "co".
1239 // If "co" is outside this range then we have to change "offset".
1240 // If the first char of a line is a tab the cursor will try to stay
1241 // in column 7, but we have to set offset to 0.
1242
1243 if (co < 0 + offset) {
1244 offset = co;
1245 }
1246 if (co >= columns + offset) {
1247 offset = co - columns + 1;
1248 }
1249 // if the first char of the line is a tab, and "dot" is sitting on it
1250 // force offset to 0.
1251 if (d == beg_cur && *d == '\t') {
1252 offset = 0;
1253 }
1254 co -= offset;
1255
1256 *row = ro;
1257 *col = co;
1258}
1259
1260//----- Text Movement Routines ---------------------------------
1261static Byte *begin_line(Byte * p) // return pointer to first char cur line
1262{
1263 while (p > text && p[-1] != '\n')
1264 p--; // go to cur line B-o-l
1265 return (p);
1266}
1267
1268static Byte *end_line(Byte * p) // return pointer to NL of cur line line
1269{
1270 while (p < end - 1 && *p != '\n')
1271 p++; // go to cur line E-o-l
1272 return (p);
1273}
1274
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001275static inline Byte *dollar_line(Byte * p) // return pointer to just before NL line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001276{
1277 while (p < end - 1 && *p != '\n')
1278 p++; // go to cur line E-o-l
1279 // Try to stay off of the Newline
1280 if (*p == '\n' && (p - begin_line(p)) > 0)
1281 p--;
1282 return (p);
1283}
1284
1285static Byte *prev_line(Byte * p) // return pointer first char prev line
1286{
1287 p = begin_line(p); // goto begining of cur line
1288 if (p[-1] == '\n' && p > text)
1289 p--; // step to prev line
1290 p = begin_line(p); // goto begining of prev line
1291 return (p);
1292}
1293
1294static Byte *next_line(Byte * p) // return pointer first char next line
1295{
1296 p = end_line(p);
1297 if (*p == '\n' && p < end - 1)
1298 p++; // step to next line
1299 return (p);
1300}
1301
1302//----- Text Information Routines ------------------------------
1303static Byte *end_screen(void)
1304{
1305 Byte *q;
1306 int cnt;
1307
1308 // find new bottom line
1309 q = screenbegin;
1310 for (cnt = 0; cnt < rows - 2; cnt++)
1311 q = next_line(q);
1312 q = end_line(q);
1313 return (q);
1314}
1315
1316static int count_lines(Byte * start, Byte * stop) // count line from start to stop
1317{
1318 Byte *q;
1319 int cnt;
1320
1321 if (stop < start) { // start and stop are backwards- reverse them
1322 q = start;
1323 start = stop;
1324 stop = q;
1325 }
1326 cnt = 0;
1327 stop = end_line(stop); // get to end of this line
1328 for (q = start; q <= stop && q <= end - 1; q++) {
1329 if (*q == '\n')
1330 cnt++;
1331 }
1332 return (cnt);
1333}
1334
1335static Byte *find_line(int li) // find begining of line #li
1336{
1337 Byte *q;
1338
1339 for (q = text; li > 1; li--) {
1340 q = next_line(q);
1341 }
1342 return (q);
1343}
1344
1345//----- Dot Movement Routines ----------------------------------
1346static void dot_left(void)
1347{
1348 if (dot > text && dot[-1] != '\n')
1349 dot--;
1350}
1351
1352static void dot_right(void)
1353{
1354 if (dot < end - 1 && *dot != '\n')
1355 dot++;
1356}
1357
1358static void dot_begin(void)
1359{
1360 dot = begin_line(dot); // return pointer to first char cur line
1361}
1362
1363static void dot_end(void)
1364{
1365 dot = end_line(dot); // return pointer to last char cur line
1366}
1367
1368static Byte *move_to_col(Byte * p, int l)
1369{
1370 int co;
1371
1372 p = begin_line(p);
1373 co = 0;
1374 do {
1375 if (*p == '\n' || *p == '\0')
1376 break;
1377 if (*p == '\t') {
1378 // 7 - (co % 8 )
1379 co += ((tabstop - 1) - (co % tabstop));
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001380 } else if (*p < ' ' || *p == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001381 co++; // display as ^X, use 2 columns
1382 }
1383 } while (++co <= l && p++ < end);
1384 return (p);
1385}
1386
1387static void dot_next(void)
1388{
1389 dot = next_line(dot);
1390}
1391
1392static void dot_prev(void)
1393{
1394 dot = prev_line(dot);
1395}
1396
1397static void dot_scroll(int cnt, int dir)
1398{
1399 Byte *q;
1400
1401 for (; cnt > 0; cnt--) {
1402 if (dir < 0) {
1403 // scroll Backwards
1404 // ctrl-Y scroll up one line
1405 screenbegin = prev_line(screenbegin);
1406 } else {
1407 // scroll Forwards
1408 // ctrl-E scroll down one line
1409 screenbegin = next_line(screenbegin);
1410 }
1411 }
1412 // make sure "dot" stays on the screen so we dont scroll off
1413 if (dot < screenbegin)
1414 dot = screenbegin;
1415 q = end_screen(); // find new bottom line
1416 if (dot > q)
1417 dot = begin_line(q); // is dot is below bottom line?
1418 dot_skip_over_ws();
1419}
1420
1421static void dot_skip_over_ws(void)
1422{
1423 // skip WS
1424 while (isspace(*dot) && *dot != '\n' && dot < end - 1)
1425 dot++;
1426}
1427
1428static void dot_delete(void) // delete the char at 'dot'
1429{
1430 (void) text_hole_delete(dot, dot);
1431}
1432
1433static Byte *bound_dot(Byte * p) // make sure text[0] <= P < "end"
1434{
1435 if (p >= end && end > text) {
1436 p = end - 1;
1437 indicate_error('1');
1438 }
1439 if (p < text) {
1440 p = text;
1441 indicate_error('2');
1442 }
1443 return (p);
1444}
1445
1446//----- Helper Utility Routines --------------------------------
1447
1448//----------------------------------------------------------------
1449//----- Char Routines --------------------------------------------
1450/* Chars that are part of a word-
1451 * 0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
1452 * Chars that are Not part of a word (stoppers)
1453 * !"#$%&'()*+,-./:;<=>?@[\]^`{|}~
1454 * Chars that are WhiteSpace
1455 * TAB NEWLINE VT FF RETURN SPACE
1456 * DO NOT COUNT NEWLINE AS WHITESPACE
1457 */
1458
1459static Byte *new_screen(int ro, int co)
1460{
1461 int li;
1462
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001463 free(screen);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001464 screensize = ro * co + 8;
1465 screen = (Byte *) xmalloc(screensize);
1466 // initialize the new screen. assume this will be a empty file.
1467 screen_erase();
Eric Andersenaff114c2004-04-14 17:51:38 +00001468 // non-existent text[] lines start with a tilde (~).
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001469 for (li = 1; li < ro - 1; li++) {
1470 screen[(li * co) + 0] = '~';
1471 }
1472 return (screen);
1473}
1474
1475static Byte *new_text(int size)
1476{
1477 if (size < 10240)
1478 size = 10240; // have a minimum size for new files
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001479 free(text);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001480 text = (Byte *) xmalloc(size + 8);
1481 memset(text, '\0', size); // clear new text[]
1482 //text += 4; // leave some room for "oops"
1483 textend = text + size - 1;
1484 //textend -= 4; // leave some root for "oops"
1485 return (text);
1486}
1487
1488#ifdef CONFIG_FEATURE_VI_SEARCH
1489static int mycmp(Byte * s1, Byte * s2, int len)
1490{
1491 int i;
1492
1493 i = strncmp((char *) s1, (char *) s2, len);
1494#ifdef CONFIG_FEATURE_VI_SETOPTS
1495 if (ignorecase) {
1496 i = strncasecmp((char *) s1, (char *) s2, len);
1497 }
1498#endif /* CONFIG_FEATURE_VI_SETOPTS */
1499 return (i);
1500}
1501
1502static Byte *char_search(Byte * p, Byte * pat, int dir, int range) // search for pattern starting at p
1503{
1504#ifndef REGEX_SEARCH
1505 Byte *start, *stop;
1506 int len;
1507
1508 len = strlen((char *) pat);
1509 if (dir == FORWARD) {
1510 stop = end - 1; // assume range is p - end-1
1511 if (range == LIMITED)
1512 stop = next_line(p); // range is to next line
1513 for (start = p; start < stop; start++) {
1514 if (mycmp(start, pat, len) == 0) {
1515 return (start);
1516 }
1517 }
1518 } else if (dir == BACK) {
1519 stop = text; // assume range is text - p
1520 if (range == LIMITED)
1521 stop = prev_line(p); // range is to prev line
1522 for (start = p - len; start >= stop; start--) {
1523 if (mycmp(start, pat, len) == 0) {
1524 return (start);
1525 }
1526 }
1527 }
1528 // pattern not found
1529 return (NULL);
1530#else /*REGEX_SEARCH */
1531 char *q;
1532 struct re_pattern_buffer preg;
1533 int i;
1534 int size, range;
1535
1536 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
1537 preg.translate = 0;
1538 preg.fastmap = 0;
1539 preg.buffer = 0;
1540 preg.allocated = 0;
1541
1542 // assume a LIMITED forward search
1543 q = next_line(p);
1544 q = end_line(q);
1545 q = end - 1;
1546 if (dir == BACK) {
1547 q = prev_line(p);
1548 q = text;
1549 }
1550 // count the number of chars to search over, forward or backward
1551 size = q - p;
1552 if (size < 0)
1553 size = p - q;
1554 // RANGE could be negative if we are searching backwards
1555 range = q - p;
1556
1557 q = (char *) re_compile_pattern(pat, strlen((char *) pat), &preg);
1558 if (q != 0) {
1559 // The pattern was not compiled
1560 psbs("bad search pattern: \"%s\": %s", pat, q);
1561 i = 0; // return p if pattern not compiled
1562 goto cs1;
1563 }
1564
1565 q = p;
1566 if (range < 0) {
1567 q = p - size;
1568 if (q < text)
1569 q = text;
1570 }
1571 // search for the compiled pattern, preg, in p[]
1572 // range < 0- search backward
1573 // range > 0- search forward
1574 // 0 < start < size
1575 // re_search() < 0 not found or error
1576 // re_search() > 0 index of found pattern
1577 // struct pattern char int int int struct reg
1578 // re_search (*pattern_buffer, *string, size, start, range, *regs)
1579 i = re_search(&preg, q, size, 0, range, 0);
1580 if (i == -1) {
1581 p = 0;
1582 i = 0; // return NULL if pattern not found
1583 }
1584 cs1:
1585 if (dir == FORWARD) {
1586 p = p + i;
1587 } else {
1588 p = p - i;
1589 }
1590 return (p);
1591#endif /*REGEX_SEARCH */
1592}
1593#endif /* CONFIG_FEATURE_VI_SEARCH */
1594
1595static Byte *char_insert(Byte * p, Byte c) // insert the char c at 'p'
1596{
1597 if (c == 22) { // Is this an ctrl-V?
1598 p = stupid_insert(p, '^'); // use ^ to indicate literal next
1599 p--; // backup onto ^
1600 refresh(FALSE); // show the ^
1601 c = get_one_char();
1602 *p = c;
1603 p++;
Paul Fox8552aec2005-09-16 12:20:05 +00001604 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001605 } else if (c == 27) { // Is this an ESC?
1606 cmd_mode = 0;
1607 cmdcnt = 0;
1608 end_cmd_q(); // stop adding to q
Paul Fox8552aec2005-09-16 12:20:05 +00001609 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001610 if ((p[-1] != '\n') && (dot>text)) {
1611 p--;
1612 }
Paul Foxd13b90b2005-07-18 22:17:25 +00001613 } else if (c == erase_char || c == 8 || c == 127) { // Is this a BS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001614 // 123456789
1615 if ((p[-1] != '\n') && (dot>text)) {
1616 p--;
1617 p = text_hole_delete(p, p); // shrink buffer 1 char
1618#ifdef CONFIG_FEATURE_VI_DOT_CMD
1619 // also rmove char from last_modifying_cmd
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001620 if (last_modifying_cmd != 0 && strlen((char *) last_modifying_cmd) > 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001621 Byte *q;
1622
1623 q = last_modifying_cmd;
1624 q[strlen((char *) q) - 1] = '\0'; // erase BS
1625 q[strlen((char *) q) - 1] = '\0'; // erase prev char
1626 }
1627#endif /* CONFIG_FEATURE_VI_DOT_CMD */
1628 }
1629 } else {
1630 // insert a char into text[]
1631 Byte *sp; // "save p"
1632
1633 if (c == 13)
1634 c = '\n'; // translate \r to \n
1635 sp = p; // remember addr of insert
1636 p = stupid_insert(p, c); // insert the char
1637#ifdef CONFIG_FEATURE_VI_SETOPTS
1638 if (showmatch && strchr(")]}", *sp) != NULL) {
1639 showmatching(sp);
1640 }
1641 if (autoindent && c == '\n') { // auto indent the new line
1642 Byte *q;
1643
1644 q = prev_line(p); // use prev line as templet
1645 for (; isblnk(*q); q++) {
1646 p = stupid_insert(p, *q); // insert the char
1647 }
1648 }
1649#endif /* CONFIG_FEATURE_VI_SETOPTS */
1650 }
1651 return (p);
1652}
1653
1654static Byte *stupid_insert(Byte * p, Byte c) // stupidly insert the char c at 'p'
1655{
1656 p = text_hole_make(p, 1);
1657 if (p != 0) {
1658 *p = c;
Paul Fox8552aec2005-09-16 12:20:05 +00001659 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001660 p++;
1661 }
1662 return (p);
1663}
1664
1665static Byte find_range(Byte ** start, Byte ** stop, Byte c)
1666{
1667 Byte *save_dot, *p, *q;
1668 int cnt;
1669
1670 save_dot = dot;
1671 p = q = dot;
1672
1673 if (strchr("cdy><", c)) {
1674 // these cmds operate on whole lines
1675 p = q = begin_line(p);
1676 for (cnt = 1; cnt < cmdcnt; cnt++) {
1677 q = next_line(q);
1678 }
1679 q = end_line(q);
1680 } else if (strchr("^%$0bBeEft", c)) {
1681 // These cmds operate on char positions
1682 do_cmd(c); // execute movement cmd
1683 q = dot;
1684 } else if (strchr("wW", c)) {
1685 do_cmd(c); // execute movement cmd
Eric Andersenaeea32c2004-02-04 11:19:44 +00001686 // if we are at the next word's first char
1687 // step back one char
1688 // but check the possibilities when it is true
Eric Andersen5cc90ea2004-02-06 10:36:08 +00001689 if (dot > text && ((isspace(dot[-1]) && !isspace(dot[0]))
Eric Andersenaeea32c2004-02-04 11:19:44 +00001690 || (ispunct(dot[-1]) && !ispunct(dot[0]))
1691 || (isalnum(dot[-1]) && !isalnum(dot[0]))))
1692 dot--; // move back off of next word
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001693 if (dot > text && *dot == '\n')
1694 dot--; // stay off NL
1695 q = dot;
1696 } else if (strchr("H-k{", c)) {
1697 // these operate on multi-lines backwards
1698 q = end_line(dot); // find NL
1699 do_cmd(c); // execute movement cmd
1700 dot_begin();
1701 p = dot;
1702 } else if (strchr("L+j}\r\n", c)) {
1703 // these operate on multi-lines forwards
1704 p = begin_line(dot);
1705 do_cmd(c); // execute movement cmd
1706 dot_end(); // find NL
1707 q = dot;
1708 } else {
1709 c = 27; // error- return an ESC char
1710 //break;
1711 }
1712 *start = p;
1713 *stop = q;
1714 if (q < p) {
1715 *start = q;
1716 *stop = p;
1717 }
1718 dot = save_dot;
1719 return (c);
1720}
1721
1722static int st_test(Byte * p, int type, int dir, Byte * tested)
1723{
1724 Byte c, c0, ci;
1725 int test, inc;
1726
1727 inc = dir;
1728 c = c0 = p[0];
1729 ci = p[inc];
1730 test = 0;
1731
1732 if (type == S_BEFORE_WS) {
1733 c = ci;
1734 test = ((!isspace(c)) || c == '\n');
1735 }
1736 if (type == S_TO_WS) {
1737 c = c0;
1738 test = ((!isspace(c)) || c == '\n');
1739 }
1740 if (type == S_OVER_WS) {
1741 c = c0;
1742 test = ((isspace(c)));
1743 }
1744 if (type == S_END_PUNCT) {
1745 c = ci;
1746 test = ((ispunct(c)));
1747 }
1748 if (type == S_END_ALNUM) {
1749 c = ci;
1750 test = ((isalnum(c)) || c == '_');
1751 }
1752 *tested = c;
1753 return (test);
1754}
1755
1756static Byte *skip_thing(Byte * p, int linecnt, int dir, int type)
1757{
1758 Byte c;
1759
1760 while (st_test(p, type, dir, &c)) {
1761 // make sure we limit search to correct number of lines
1762 if (c == '\n' && --linecnt < 1)
1763 break;
1764 if (dir >= 0 && p >= end - 1)
1765 break;
1766 if (dir < 0 && p <= text)
1767 break;
1768 p += dir; // move to next char
1769 }
1770 return (p);
1771}
1772
1773// find matching char of pair () [] {}
1774static Byte *find_pair(Byte * p, Byte c)
1775{
1776 Byte match, *q;
1777 int dir, level;
1778
1779 match = ')';
1780 level = 1;
1781 dir = 1; // assume forward
1782 switch (c) {
1783 case '(':
1784 match = ')';
1785 break;
1786 case '[':
1787 match = ']';
1788 break;
1789 case '{':
1790 match = '}';
1791 break;
1792 case ')':
1793 match = '(';
1794 dir = -1;
1795 break;
1796 case ']':
1797 match = '[';
1798 dir = -1;
1799 break;
1800 case '}':
1801 match = '{';
1802 dir = -1;
1803 break;
1804 }
1805 for (q = p + dir; text <= q && q < end; q += dir) {
1806 // look for match, count levels of pairs (( ))
1807 if (*q == c)
1808 level++; // increase pair levels
1809 if (*q == match)
1810 level--; // reduce pair level
1811 if (level == 0)
1812 break; // found matching pair
1813 }
1814 if (level != 0)
1815 q = NULL; // indicate no match
1816 return (q);
1817}
1818
1819#ifdef CONFIG_FEATURE_VI_SETOPTS
1820// show the matching char of a pair, () [] {}
1821static void showmatching(Byte * p)
1822{
1823 Byte *q, *save_dot;
1824
1825 // we found half of a pair
1826 q = find_pair(p, *p); // get loc of matching char
1827 if (q == NULL) {
1828 indicate_error('3'); // no matching char
1829 } else {
1830 // "q" now points to matching pair
1831 save_dot = dot; // remember where we are
1832 dot = q; // go to new loc
1833 refresh(FALSE); // let the user see it
1834 (void) mysleep(40); // give user some time
1835 dot = save_dot; // go back to old loc
1836 refresh(FALSE);
1837 }
1838}
1839#endif /* CONFIG_FEATURE_VI_SETOPTS */
1840
1841// open a hole in text[]
1842static Byte *text_hole_make(Byte * p, int size) // at "p", make a 'size' byte hole
1843{
1844 Byte *src, *dest;
1845 int cnt;
1846
1847 if (size <= 0)
1848 goto thm0;
1849 src = p;
1850 dest = p + size;
1851 cnt = end - src; // the rest of buffer
1852 if (memmove(dest, src, cnt) != dest) {
1853 psbs("can't create room for new characters");
1854 }
1855 memset(p, ' ', size); // clear new hole
1856 end = end + size; // adjust the new END
Paul Fox8552aec2005-09-16 12:20:05 +00001857 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001858 thm0:
1859 return (p);
1860}
1861
1862// close a hole in text[]
1863static Byte *text_hole_delete(Byte * p, Byte * q) // delete "p" thru "q", inclusive
1864{
1865 Byte *src, *dest;
1866 int cnt, hole_size;
1867
1868 // move forwards, from beginning
1869 // assume p <= q
1870 src = q + 1;
1871 dest = p;
1872 if (q < p) { // they are backward- swap them
1873 src = p + 1;
1874 dest = q;
1875 }
1876 hole_size = q - p + 1;
1877 cnt = end - src;
1878 if (src < text || src > end)
1879 goto thd0;
1880 if (dest < text || dest >= end)
1881 goto thd0;
1882 if (src >= end)
1883 goto thd_atend; // just delete the end of the buffer
1884 if (memmove(dest, src, cnt) != dest) {
1885 psbs("can't delete the character");
1886 }
1887 thd_atend:
1888 end = end - hole_size; // adjust the new END
1889 if (dest >= end)
1890 dest = end - 1; // make sure dest in below end-1
1891 if (end <= text)
1892 dest = end = text; // keep pointers valid
Paul Fox8552aec2005-09-16 12:20:05 +00001893 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001894 thd0:
1895 return (dest);
1896}
1897
1898// copy text into register, then delete text.
1899// if dist <= 0, do not include, or go past, a NewLine
1900//
1901static Byte *yank_delete(Byte * start, Byte * stop, int dist, int yf)
1902{
1903 Byte *p;
1904
1905 // make sure start <= stop
1906 if (start > stop) {
1907 // they are backwards, reverse them
1908 p = start;
1909 start = stop;
1910 stop = p;
1911 }
1912 if (dist <= 0) {
1913 // we can not cross NL boundaries
1914 p = start;
1915 if (*p == '\n')
1916 return (p);
1917 // dont go past a NewLine
1918 for (; p + 1 <= stop; p++) {
1919 if (p[1] == '\n') {
1920 stop = p; // "stop" just before NewLine
1921 break;
1922 }
1923 }
1924 }
1925 p = start;
1926#ifdef CONFIG_FEATURE_VI_YANKMARK
1927 text_yank(start, stop, YDreg);
1928#endif /* CONFIG_FEATURE_VI_YANKMARK */
1929 if (yf == YANKDEL) {
1930 p = text_hole_delete(start, stop);
1931 } // delete lines
1932 return (p);
1933}
1934
1935static void show_help(void)
1936{
1937 puts("These features are available:"
1938#ifdef CONFIG_FEATURE_VI_SEARCH
1939 "\n\tPattern searches with / and ?"
1940#endif /* CONFIG_FEATURE_VI_SEARCH */
1941#ifdef CONFIG_FEATURE_VI_DOT_CMD
1942 "\n\tLast command repeat with \'.\'"
1943#endif /* CONFIG_FEATURE_VI_DOT_CMD */
1944#ifdef CONFIG_FEATURE_VI_YANKMARK
1945 "\n\tLine marking with 'x"
1946 "\n\tNamed buffers with \"x"
1947#endif /* CONFIG_FEATURE_VI_YANKMARK */
1948#ifdef CONFIG_FEATURE_VI_READONLY
1949 "\n\tReadonly if vi is called as \"view\""
1950 "\n\tReadonly with -R command line arg"
1951#endif /* CONFIG_FEATURE_VI_READONLY */
1952#ifdef CONFIG_FEATURE_VI_SET
1953 "\n\tSome colon mode commands with \':\'"
1954#endif /* CONFIG_FEATURE_VI_SET */
1955#ifdef CONFIG_FEATURE_VI_SETOPTS
1956 "\n\tSettable options with \":set\""
1957#endif /* CONFIG_FEATURE_VI_SETOPTS */
1958#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
1959 "\n\tSignal catching- ^C"
1960 "\n\tJob suspend and resume with ^Z"
1961#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
1962#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
1963 "\n\tAdapt to window re-sizes"
1964#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
1965 );
1966}
1967
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001968static inline void print_literal(Byte * buf, Byte * s) // copy s to buf, convert unprintable
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001969{
1970 Byte c, b[2];
1971
1972 b[1] = '\0';
1973 strcpy((char *) buf, ""); // init buf
1974 if (strlen((char *) s) <= 0)
1975 s = (Byte *) "(NULL)";
1976 for (; *s > '\0'; s++) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001977 int c_is_no_print;
1978
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001979 c = *s;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001980 c_is_no_print = c > 127 && !Isprint(c);
1981 if (c_is_no_print) {
1982 strcat((char *) buf, SOn);
1983 c = '.';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001984 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001985 if (c < ' ' || c == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001986 strcat((char *) buf, "^");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001987 if(c == 127)
1988 c = '?';
1989 else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001990 c += '@';
1991 }
1992 b[0] = c;
1993 strcat((char *) buf, (char *) b);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001994 if (c_is_no_print)
1995 strcat((char *) buf, SOs);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001996 if (*s == '\n') {
1997 strcat((char *) buf, "$");
1998 }
1999 }
2000}
2001
2002#ifdef CONFIG_FEATURE_VI_DOT_CMD
2003static void start_new_cmd_q(Byte c)
2004{
2005 // release old cmd
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002006 free(last_modifying_cmd);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002007 // get buffer for new cmd
2008 last_modifying_cmd = (Byte *) xmalloc(BUFSIZ);
2009 memset(last_modifying_cmd, '\0', BUFSIZ); // clear new cmd queue
2010 // if there is a current cmd count put it in the buffer first
2011 if (cmdcnt > 0)
2012 sprintf((char *) last_modifying_cmd, "%d", cmdcnt);
2013 // save char c onto queue
2014 last_modifying_cmd[strlen((char *) last_modifying_cmd)] = c;
2015 adding2q = 1;
2016 return;
2017}
2018
2019static void end_cmd_q(void)
2020{
2021#ifdef CONFIG_FEATURE_VI_YANKMARK
2022 YDreg = 26; // go back to default Yank/Delete reg
2023#endif /* CONFIG_FEATURE_VI_YANKMARK */
2024 adding2q = 0;
2025 return;
2026}
2027#endif /* CONFIG_FEATURE_VI_DOT_CMD */
2028
2029#if defined(CONFIG_FEATURE_VI_YANKMARK) || defined(CONFIG_FEATURE_VI_COLON) || defined(CONFIG_FEATURE_VI_CRASHME)
2030static Byte *string_insert(Byte * p, Byte * s) // insert the string at 'p'
2031{
2032 int cnt, i;
2033
2034 i = strlen((char *) s);
2035 p = text_hole_make(p, i);
2036 strncpy((char *) p, (char *) s, i);
2037 for (cnt = 0; *s != '\0'; s++) {
2038 if (*s == '\n')
2039 cnt++;
2040 }
2041#ifdef CONFIG_FEATURE_VI_YANKMARK
2042 psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
2043#endif /* CONFIG_FEATURE_VI_YANKMARK */
2044 return (p);
2045}
2046#endif /* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_COLON || CONFIG_FEATURE_VI_CRASHME */
2047
2048#ifdef CONFIG_FEATURE_VI_YANKMARK
2049static Byte *text_yank(Byte * p, Byte * q, int dest) // copy text into a register
2050{
2051 Byte *t;
2052 int cnt;
2053
2054 if (q < p) { // they are backwards- reverse them
2055 t = q;
2056 q = p;
2057 p = t;
2058 }
2059 cnt = q - p + 1;
2060 t = reg[dest];
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002061 free(t); // if already a yank register, free it
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002062 t = (Byte *) xmalloc(cnt + 1); // get a new register
2063 memset(t, '\0', cnt + 1); // clear new text[]
2064 strncpy((char *) t, (char *) p, cnt); // copy text[] into bufer
2065 reg[dest] = t;
2066 return (p);
2067}
2068
2069static Byte what_reg(void)
2070{
2071 Byte c;
2072 int i;
2073
2074 i = 0;
2075 c = 'D'; // default to D-reg
2076 if (0 <= YDreg && YDreg <= 25)
2077 c = 'a' + (Byte) YDreg;
2078 if (YDreg == 26)
2079 c = 'D';
2080 if (YDreg == 27)
2081 c = 'U';
2082 return (c);
2083}
2084
2085static void check_context(Byte cmd)
2086{
2087 // A context is defined to be "modifying text"
2088 // Any modifying command establishes a new context.
2089
2090 if (dot < context_start || dot > context_end) {
2091 if (strchr((char *) modifying_cmds, cmd) != NULL) {
2092 // we are trying to modify text[]- make this the current context
2093 mark[27] = mark[26]; // move cur to prev
2094 mark[26] = dot; // move local to cur
2095 context_start = prev_line(prev_line(dot));
2096 context_end = next_line(next_line(dot));
2097 //loiter= start_loiter= now;
2098 }
2099 }
2100 return;
2101}
2102
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002103static inline Byte *swap_context(Byte * p) // goto new context for '' command make this the current context
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002104{
2105 Byte *tmp;
2106
2107 // the current context is in mark[26]
2108 // the previous context is in mark[27]
2109 // only swap context if other context is valid
2110 if (text <= mark[27] && mark[27] <= end - 1) {
2111 tmp = mark[27];
2112 mark[27] = mark[26];
2113 mark[26] = tmp;
2114 p = mark[26]; // where we are going- previous context
2115 context_start = prev_line(prev_line(prev_line(p)));
2116 context_end = next_line(next_line(next_line(p)));
2117 }
2118 return (p);
2119}
2120#endif /* CONFIG_FEATURE_VI_YANKMARK */
2121
2122static int isblnk(Byte c) // is the char a blank or tab
2123{
2124 return (c == ' ' || c == '\t');
2125}
2126
2127//----- Set terminal attributes --------------------------------
2128static void rawmode(void)
2129{
2130 tcgetattr(0, &term_orig);
2131 term_vi = term_orig;
2132 term_vi.c_lflag &= (~ICANON & ~ECHO); // leave ISIG ON- allow intr's
2133 term_vi.c_iflag &= (~IXON & ~ICRNL);
2134 term_vi.c_oflag &= (~ONLCR);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002135 term_vi.c_cc[VMIN] = 1;
2136 term_vi.c_cc[VTIME] = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002137 erase_char = term_vi.c_cc[VERASE];
2138 tcsetattr(0, TCSANOW, &term_vi);
2139}
2140
2141static void cookmode(void)
2142{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002143 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002144 tcsetattr(0, TCSANOW, &term_orig);
2145}
2146
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002147//----- Come here when we get a window resize signal ---------
2148#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
2149static void winch_sig(int sig)
2150{
2151 signal(SIGWINCH, winch_sig);
2152#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
2153 window_size_get(0);
2154#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
2155 new_screen(rows, columns); // get memory for virtual screen
2156 redraw(TRUE); // re-draw the screen
2157}
2158
2159//----- Come here when we get a continue signal -------------------
2160static void cont_sig(int sig)
2161{
2162 rawmode(); // terminal to "raw"
Paul Fox8552aec2005-09-16 12:20:05 +00002163 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002164 redraw(TRUE); // re-draw the screen
2165
2166 signal(SIGTSTP, suspend_sig);
2167 signal(SIGCONT, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002168 kill(my_pid, SIGCONT);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002169}
2170
2171//----- Come here when we get a Suspend signal -------------------
2172static void suspend_sig(int sig)
2173{
2174 place_cursor(rows - 1, 0, FALSE); // go to bottom of screen
2175 clear_to_eol(); // Erase to end of line
2176 cookmode(); // terminal to "cooked"
2177
2178 signal(SIGCONT, cont_sig);
2179 signal(SIGTSTP, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002180 kill(my_pid, SIGTSTP);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002181}
2182
2183//----- Come here when we get a signal ---------------------------
2184static void catch_sig(int sig)
2185{
2186 signal(SIGHUP, catch_sig);
2187 signal(SIGINT, catch_sig);
2188 signal(SIGTERM, catch_sig);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002189 signal(SIGALRM, catch_sig);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002190 if(sig)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002191 longjmp(restart, sig);
2192}
2193
2194//----- Come here when we get a core dump signal -----------------
2195static void core_sig(int sig)
2196{
2197 signal(SIGQUIT, core_sig);
2198 signal(SIGILL, core_sig);
2199 signal(SIGTRAP, core_sig);
2200 signal(SIGIOT, core_sig);
2201 signal(SIGABRT, core_sig);
2202 signal(SIGFPE, core_sig);
2203 signal(SIGBUS, core_sig);
2204 signal(SIGSEGV, core_sig);
2205#ifdef SIGSYS
2206 signal(SIGSYS, core_sig);
2207#endif
2208
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002209 if(sig) { // signaled
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002210 dot = bound_dot(dot); // make sure "dot" is valid
2211
2212 longjmp(restart, sig);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002213 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002214}
2215#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
2216
2217static int mysleep(int hund) // sleep for 'h' 1/100 seconds
2218{
2219 // Don't hang- Wait 5/100 seconds- 1 Sec= 1000000
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002220 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002221 FD_ZERO(&rfds);
2222 FD_SET(0, &rfds);
2223 tv.tv_sec = 0;
2224 tv.tv_usec = hund * 10000;
2225 select(1, &rfds, NULL, NULL, &tv);
2226 return (FD_ISSET(0, &rfds));
2227}
2228
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002229static Byte readbuffer[BUFSIZ];
2230static int readed_for_parse;
2231
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002232//----- IO Routines --------------------------------------------
2233static Byte readit(void) // read (maybe cursor) key from stdin
2234{
2235 Byte c;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002236 int n;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002237 struct esc_cmds {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002238 const char *seq;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002239 Byte val;
2240 };
2241
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002242 static const struct esc_cmds esccmds[] = {
2243 {"OA", (Byte) VI_K_UP}, // cursor key Up
2244 {"OB", (Byte) VI_K_DOWN}, // cursor key Down
2245 {"OC", (Byte) VI_K_RIGHT}, // Cursor Key Right
2246 {"OD", (Byte) VI_K_LEFT}, // cursor key Left
2247 {"OH", (Byte) VI_K_HOME}, // Cursor Key Home
2248 {"OF", (Byte) VI_K_END}, // Cursor Key End
2249 {"[A", (Byte) VI_K_UP}, // cursor key Up
2250 {"[B", (Byte) VI_K_DOWN}, // cursor key Down
2251 {"[C", (Byte) VI_K_RIGHT}, // Cursor Key Right
2252 {"[D", (Byte) VI_K_LEFT}, // cursor key Left
2253 {"[H", (Byte) VI_K_HOME}, // Cursor Key Home
2254 {"[F", (Byte) VI_K_END}, // Cursor Key End
2255 {"[1~", (Byte) VI_K_HOME}, // Cursor Key Home
2256 {"[2~", (Byte) VI_K_INSERT}, // Cursor Key Insert
2257 {"[4~", (Byte) VI_K_END}, // Cursor Key End
2258 {"[5~", (Byte) VI_K_PAGEUP}, // Cursor Key Page Up
2259 {"[6~", (Byte) VI_K_PAGEDOWN}, // Cursor Key Page Down
2260 {"OP", (Byte) VI_K_FUN1}, // Function Key F1
2261 {"OQ", (Byte) VI_K_FUN2}, // Function Key F2
2262 {"OR", (Byte) VI_K_FUN3}, // Function Key F3
2263 {"OS", (Byte) VI_K_FUN4}, // Function Key F4
2264 {"[15~", (Byte) VI_K_FUN5}, // Function Key F5
2265 {"[17~", (Byte) VI_K_FUN6}, // Function Key F6
2266 {"[18~", (Byte) VI_K_FUN7}, // Function Key F7
2267 {"[19~", (Byte) VI_K_FUN8}, // Function Key F8
2268 {"[20~", (Byte) VI_K_FUN9}, // Function Key F9
2269 {"[21~", (Byte) VI_K_FUN10}, // Function Key F10
2270 {"[23~", (Byte) VI_K_FUN11}, // Function Key F11
2271 {"[24~", (Byte) VI_K_FUN12}, // Function Key F12
2272 {"[11~", (Byte) VI_K_FUN1}, // Function Key F1
2273 {"[12~", (Byte) VI_K_FUN2}, // Function Key F2
2274 {"[13~", (Byte) VI_K_FUN3}, // Function Key F3
2275 {"[14~", (Byte) VI_K_FUN4}, // Function Key F4
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002276 };
2277
2278#define ESCCMDS_COUNT (sizeof(esccmds)/sizeof(struct esc_cmds))
2279
2280 (void) alarm(0); // turn alarm OFF while we wait for input
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002281 fflush(stdout);
2282 n = readed_for_parse;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002283 // get input from User- are there already input chars in Q?
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002284 if (n <= 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002285 ri0:
2286 // the Q is empty, wait for a typed char
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002287 n = read(0, readbuffer, BUFSIZ - 1);
2288 if (n < 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002289 if (errno == EINTR)
2290 goto ri0; // interrupted sys call
2291 if (errno == EBADF)
2292 editing = 0;
2293 if (errno == EFAULT)
2294 editing = 0;
2295 if (errno == EINVAL)
2296 editing = 0;
2297 if (errno == EIO)
2298 editing = 0;
2299 errno = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002300 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002301 if(n <= 0)
2302 return 0; // error
2303 if (readbuffer[0] == 27) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002304 // This is an ESC char. Is this Esc sequence?
2305 // Could be bare Esc key. See if there are any
2306 // more chars to read after the ESC. This would
2307 // be a Function or Cursor Key sequence.
2308 FD_ZERO(&rfds);
2309 FD_SET(0, &rfds);
2310 tv.tv_sec = 0;
2311 tv.tv_usec = 50000; // Wait 5/100 seconds- 1 Sec=1000000
2312
2313 // keep reading while there are input chars and room in buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002314 while (select(1, &rfds, NULL, NULL, &tv) > 0 && n <= (BUFSIZ - 5)) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002315 // read the rest of the ESC string
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002316 int r = read(0, (void *) (readbuffer + n), BUFSIZ - n);
2317 if (r > 0) {
2318 n += r;
2319 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002320 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002321 }
2322 readed_for_parse = n;
2323 }
2324 c = readbuffer[0];
2325 if(c == 27 && n > 1) {
2326 // Maybe cursor or function key?
2327 const struct esc_cmds *eindex;
2328
2329 for (eindex = esccmds; eindex < &esccmds[ESCCMDS_COUNT]; eindex++) {
2330 int cnt = strlen(eindex->seq);
2331
2332 if(n <= cnt)
2333 continue;
2334 if(strncmp(eindex->seq, (char *) readbuffer + 1, cnt))
2335 continue;
2336 // is a Cursor key- put derived value back into Q
2337 c = eindex->val;
2338 // for squeeze out the ESC sequence
2339 n = cnt + 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002340 break;
2341 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002342 if(eindex == &esccmds[ESCCMDS_COUNT]) {
2343 /* defined ESC sequence not found, set only one ESC */
2344 n = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002345 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002346 } else {
2347 n = 1;
2348 }
2349 // remove key sequence from Q
2350 readed_for_parse -= n;
2351 memmove(readbuffer, readbuffer + n, BUFSIZ - n);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002352 (void) alarm(3); // we are done waiting for input, turn alarm ON
2353 return (c);
2354}
2355
2356//----- IO Routines --------------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002357static Byte get_one_char(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002358{
2359 static Byte c;
2360
2361#ifdef CONFIG_FEATURE_VI_DOT_CMD
2362 // ! adding2q && ioq == 0 read()
2363 // ! adding2q && ioq != 0 *ioq
2364 // adding2q *last_modifying_cmd= read()
2365 if (!adding2q) {
2366 // we are not adding to the q.
2367 // but, we may be reading from a q
2368 if (ioq == 0) {
2369 // there is no current q, read from STDIN
2370 c = readit(); // get the users input
2371 } else {
2372 // there is a queue to get chars from first
2373 c = *ioq++;
2374 if (c == '\0') {
2375 // the end of the q, read from STDIN
2376 free(ioq_start);
2377 ioq_start = ioq = 0;
2378 c = readit(); // get the users input
2379 }
2380 }
2381 } else {
2382 // adding STDIN chars to q
2383 c = readit(); // get the users input
2384 if (last_modifying_cmd != 0) {
Eric Andersenfda2b7f2002-10-26 10:19:19 +00002385 int len = strlen((char *) last_modifying_cmd);
2386 if (len + 1 >= BUFSIZ) {
2387 psbs("last_modifying_cmd overrun");
2388 } else {
2389 // add new char to q
2390 last_modifying_cmd[len] = c;
2391 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002392 }
2393 }
2394#else /* CONFIG_FEATURE_VI_DOT_CMD */
2395 c = readit(); // get the users input
2396#endif /* CONFIG_FEATURE_VI_DOT_CMD */
2397 return (c); // return the char, where ever it came from
2398}
2399
2400static Byte *get_input_line(Byte * prompt) // get input line- use "status line"
2401{
2402 Byte buf[BUFSIZ];
2403 Byte c;
2404 int i;
2405 static Byte *obufp = NULL;
2406
2407 strcpy((char *) buf, (char *) prompt);
Paul Fox8552aec2005-09-16 12:20:05 +00002408 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002409 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
2410 clear_to_eol(); // clear the line
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002411 write1(prompt); // write out the :, /, or ? prompt
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002412
2413 for (i = strlen((char *) buf); i < BUFSIZ;) {
2414 c = get_one_char(); // read user input
2415 if (c == '\n' || c == '\r' || c == 27)
2416 break; // is this end of input
Paul Foxf2de0b72005-09-13 22:20:37 +00002417 if (c == erase_char || c == 8 || c == 127) {
2418 // user wants to erase prev char
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002419 i--; // backup to prev char
2420 buf[i] = '\0'; // erase the char
2421 buf[i + 1] = '\0'; // null terminate buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002422 write1("\b \b"); // erase char on screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002423 if (i <= 0) { // user backs up before b-o-l, exit
2424 break;
2425 }
2426 } else {
2427 buf[i] = c; // save char in buffer
2428 buf[i + 1] = '\0'; // make sure buffer is null terminated
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002429 putchar(c); // echo the char back to user
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002430 i++;
2431 }
2432 }
2433 refresh(FALSE);
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002434 free(obufp);
Manuel Novoa III cad53642003-03-19 09:13:01 +00002435 obufp = (Byte *) bb_xstrdup((char *) buf);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002436 return (obufp);
2437}
2438
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002439static int file_size(const Byte * fn) // what is the byte size of "fn"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002440{
2441 struct stat st_buf;
2442 int cnt, sr;
2443
2444 if (fn == 0 || strlen(fn) <= 0)
2445 return (-1);
2446 cnt = -1;
2447 sr = stat((char *) fn, &st_buf); // see if file exists
2448 if (sr >= 0) {
2449 cnt = (int) st_buf.st_size;
2450 }
2451 return (cnt);
2452}
2453
2454static int file_insert(Byte * fn, Byte * p, int size)
2455{
2456 int fd, cnt;
2457
2458 cnt = -1;
2459#ifdef CONFIG_FEATURE_VI_READONLY
2460 readonly = FALSE;
2461#endif /* CONFIG_FEATURE_VI_READONLY */
2462 if (fn == 0 || strlen((char*) fn) <= 0) {
2463 psbs("No filename given");
2464 goto fi0;
2465 }
2466 if (size == 0) {
2467 // OK- this is just a no-op
2468 cnt = 0;
2469 goto fi0;
2470 }
2471 if (size < 0) {
2472 psbs("Trying to insert a negative number (%d) of characters", size);
2473 goto fi0;
2474 }
2475 if (p < text || p > end) {
2476 psbs("Trying to insert file outside of memory");
2477 goto fi0;
2478 }
2479
2480 // see if we can open the file
2481#ifdef CONFIG_FEATURE_VI_READONLY
2482 if (vi_readonly) goto fi1; // do not try write-mode
2483#endif
2484 fd = open((char *) fn, O_RDWR); // assume read & write
2485 if (fd < 0) {
2486 // could not open for writing- maybe file is read only
2487#ifdef CONFIG_FEATURE_VI_READONLY
2488 fi1:
2489#endif
2490 fd = open((char *) fn, O_RDONLY); // try read-only
2491 if (fd < 0) {
2492 psbs("\"%s\" %s", fn, "could not open file");
2493 goto fi0;
2494 }
2495#ifdef CONFIG_FEATURE_VI_READONLY
2496 // got the file- read-only
2497 readonly = TRUE;
2498#endif /* CONFIG_FEATURE_VI_READONLY */
2499 }
2500 p = text_hole_make(p, size);
2501 cnt = read(fd, p, size);
2502 close(fd);
2503 if (cnt < 0) {
2504 cnt = -1;
2505 p = text_hole_delete(p, p + size - 1); // un-do buffer insert
2506 psbs("could not read file \"%s\"", fn);
2507 } else if (cnt < size) {
2508 // There was a partial read, shrink unused space text[]
2509 p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert
2510 psbs("could not read all of file \"%s\"", fn);
2511 }
2512 if (cnt >= size)
Paul Fox8552aec2005-09-16 12:20:05 +00002513 file_modified++;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002514 fi0:
2515 return (cnt);
2516}
2517
2518static int file_write(Byte * fn, Byte * first, Byte * last)
2519{
2520 int fd, cnt, charcnt;
2521
2522 if (fn == 0) {
2523 psbs("No current filename");
Paul Fox61e45db2005-10-09 14:43:22 +00002524 return (-2);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002525 }
2526 charcnt = 0;
2527 // FIXIT- use the correct umask()
2528 fd = open((char *) fn, (O_WRONLY | O_CREAT | O_TRUNC), 0664);
2529 if (fd < 0)
2530 return (-1);
2531 cnt = last - first + 1;
2532 charcnt = write(fd, first, cnt);
2533 if (charcnt == cnt) {
2534 // good write
2535 //file_modified= FALSE; // the file has not been modified
2536 } else {
2537 charcnt = 0;
2538 }
2539 close(fd);
2540 return (charcnt);
2541}
2542
2543//----- Terminal Drawing ---------------------------------------
2544// The terminal is made up of 'rows' line of 'columns' columns.
Eric Andersenaff114c2004-04-14 17:51:38 +00002545// classically this would be 24 x 80.
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002546// screen coordinates
2547// 0,0 ... 0,79
2548// 1,0 ... 1,79
2549// . ... .
2550// . ... .
2551// 22,0 ... 22,79
2552// 23,0 ... 23,79 status line
2553//
2554
2555//----- Move the cursor to row x col (count from 0, not 1) -------
2556static void place_cursor(int row, int col, int opti)
2557{
2558 char cm1[BUFSIZ];
2559 char *cm;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002560#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2561 char cm2[BUFSIZ];
2562 Byte *screenp;
2563 // char cm3[BUFSIZ];
2564 int Rrow= last_row;
2565#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002566
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002567 memset(cm1, '\0', BUFSIZ - 1); // clear the buffer
2568
2569 if (row < 0) row = 0;
2570 if (row >= rows) row = rows - 1;
2571 if (col < 0) col = 0;
2572 if (col >= columns) col = columns - 1;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002573
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002574 //----- 1. Try the standard terminal ESC sequence
2575 sprintf((char *) cm1, CMrc, row + 1, col + 1);
2576 cm= cm1;
2577 if (! opti) goto pc0;
2578
2579#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2580 //----- find the minimum # of chars to move cursor -------------
2581 //----- 2. Try moving with discreet chars (Newline, [back]space, ...)
2582 memset(cm2, '\0', BUFSIZ - 1); // clear the buffer
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002583
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002584 // move to the correct row
2585 while (row < Rrow) {
2586 // the cursor has to move up
2587 strcat(cm2, CMup);
2588 Rrow--;
2589 }
2590 while (row > Rrow) {
2591 // the cursor has to move down
2592 strcat(cm2, CMdown);
2593 Rrow++;
2594 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002595
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002596 // now move to the correct column
2597 strcat(cm2, "\r"); // start at col 0
2598 // just send out orignal source char to get to correct place
2599 screenp = &screen[row * columns]; // start of screen line
2600 strncat(cm2, screenp, col);
2601
2602 //----- 3. Try some other way of moving cursor
2603 //---------------------------------------------
2604
2605 // pick the shortest cursor motion to send out
2606 cm= cm1;
2607 if (strlen(cm2) < strlen(cm)) {
2608 cm= cm2;
2609 } /* else if (strlen(cm3) < strlen(cm)) {
2610 cm= cm3;
2611 } */
2612#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2613 pc0:
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002614 write1(cm); // move the cursor
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002615}
2616
2617//----- Erase from cursor to end of line -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002618static void clear_to_eol(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002619{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002620 write1(Ceol); // Erase from cursor to end of line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002621}
2622
2623//----- Erase from cursor to end of screen -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002624static void clear_to_eos(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002625{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002626 write1(Ceos); // Erase from cursor to end of screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002627}
2628
2629//----- Start standout mode ------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002630static void standout_start(void) // send "start reverse video" sequence
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002631{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002632 write1(SOs); // Start reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002633}
2634
2635//----- End standout mode --------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002636static void standout_end(void) // send "end reverse video" sequence
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002637{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002638 write1(SOn); // End reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002639}
2640
2641//----- Flash the screen --------------------------------------
2642static void flash(int h)
2643{
2644 standout_start(); // send "start reverse video" sequence
2645 redraw(TRUE);
2646 (void) mysleep(h);
2647 standout_end(); // send "end reverse video" sequence
2648 redraw(TRUE);
2649}
2650
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002651static void Indicate_Error(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002652{
2653#ifdef CONFIG_FEATURE_VI_CRASHME
2654 if (crashme > 0)
2655 return; // generate a random command
2656#endif /* CONFIG_FEATURE_VI_CRASHME */
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002657 if (!err_method) {
2658 write1(bell); // send out a bell character
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002659 } else {
2660 flash(10);
2661 }
2662}
2663
2664//----- Screen[] Routines --------------------------------------
2665//----- Erase the Screen[] memory ------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002666static void screen_erase(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002667{
2668 memset(screen, ' ', screensize); // clear new screen
2669}
2670
Paul Fox8552aec2005-09-16 12:20:05 +00002671static int bufsum(char *buf, int count)
2672{
2673 int sum = 0;
2674 char *e = buf + count;
2675 while (buf < e)
2676 sum += *buf++;
2677 return sum;
2678}
2679
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002680//----- Draw the status line at bottom of the screen -------------
2681static void show_status_line(void)
2682{
Paul Foxc3504852005-09-16 12:48:18 +00002683 int cnt = 0, cksum = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002684
Paul Fox8552aec2005-09-16 12:20:05 +00002685 // either we already have an error or status message, or we
2686 // create one.
2687 if (!have_status_msg) {
2688 cnt = format_edit_status();
2689 cksum = bufsum(status_buffer, cnt);
2690 }
2691 if (have_status_msg || ((cnt > 0 && last_status_cksum != cksum))) {
2692 last_status_cksum= cksum; // remember if we have seen this line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002693 place_cursor(rows - 1, 0, FALSE); // put cursor on status line
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002694 write1(status_buffer);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002695 clear_to_eol();
Paul Fox8552aec2005-09-16 12:20:05 +00002696 if (have_status_msg) {
2697 if ((strlen(status_buffer) - (have_status_msg - 1)) >
2698 (columns - 1) ) {
2699 have_status_msg = 0;
2700 Hit_Return();
2701 }
2702 have_status_msg = 0;
2703 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002704 place_cursor(crow, ccol, FALSE); // put cursor back in correct place
2705 }
Eric Andersena9eb33d2004-08-19 19:15:06 +00002706 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002707}
2708
2709//----- format the status buffer, the bottom line of screen ------
Paul Fox8552aec2005-09-16 12:20:05 +00002710// format status buffer, with STANDOUT mode
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002711static void psbs(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002712{
2713 va_list args;
2714
2715 va_start(args, format);
2716 strcpy((char *) status_buffer, SOs); // Terminal standout mode on
Eric Andersen0ef24c62005-07-18 10:32:59 +00002717 vsprintf((char *) status_buffer + strlen((char *) status_buffer), format, args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002718 strcat((char *) status_buffer, SOn); // Terminal standout mode off
2719 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00002720
2721 have_status_msg = 1 + sizeof(SOs) + sizeof(SOn) - 2;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002722
2723 return;
2724}
2725
Paul Fox8552aec2005-09-16 12:20:05 +00002726// format status buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002727static void psb(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002728{
2729 va_list args;
2730
2731 va_start(args, format);
2732 vsprintf((char *) status_buffer, format, args);
2733 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00002734
2735 have_status_msg = 1;
2736
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002737 return;
2738}
2739
2740static void ni(Byte * s) // display messages
2741{
2742 Byte buf[BUFSIZ];
2743
2744 print_literal(buf, s);
2745 psbs("\'%s\' is not implemented", buf);
2746}
2747
Paul Fox8552aec2005-09-16 12:20:05 +00002748static int format_edit_status(void) // show file status on status line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002749{
Paul Fox8552aec2005-09-16 12:20:05 +00002750 int cur, percent, ret, trunc_at;
2751 static int tot;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002752
Paul Fox8552aec2005-09-16 12:20:05 +00002753 // file_modified is now a counter rather than a flag. this
2754 // helps reduce the amount of line counting we need to do.
2755 // (this will cause a mis-reporting of modified status
2756 // once every MAXINT editing operations.)
2757
2758 // it would be nice to do a similar optimization here -- if
2759 // we haven't done a motion that could have changed which line
2760 // we're on, then we shouldn't have to do this count_lines()
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002761 cur = count_lines(text, dot);
Paul Fox8552aec2005-09-16 12:20:05 +00002762
2763 // reduce counting -- the total lines can't have
2764 // changed if we haven't done any edits.
2765 if (file_modified != last_file_modified) {
2766 tot = cur + count_lines(dot, end - 1) - 1;
2767 last_file_modified = file_modified;
2768 }
2769
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002770 // current line percent
2771 // ------------- ~~ ----------
2772 // total lines 100
2773 if (tot > 0) {
2774 percent = (100 * cur) / tot;
2775 } else {
2776 cur = tot = 0;
2777 percent = 100;
2778 }
Eric Andersen0ef24c62005-07-18 10:32:59 +00002779
Paul Fox8552aec2005-09-16 12:20:05 +00002780 trunc_at = columns < STATUS_BUFFER_LEN-1 ?
2781 columns : STATUS_BUFFER_LEN-1;
2782
2783 ret = snprintf((char *) status_buffer, trunc_at+1,
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002784#ifdef CONFIG_FEATURE_VI_READONLY
Paul Fox8552aec2005-09-16 12:20:05 +00002785 "%c %s%s%s %d/%d %d%%",
2786#else
2787 "%c %s%s %d/%d %d%%",
2788#endif
2789 (cmd_mode ? (cmd_mode == 2 ? 'R':'I'):'-'),
2790 (cfn != 0 ? (char *) cfn : "No file"),
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002791#ifdef CONFIG_FEATURE_VI_READONLY
Paul Fox8552aec2005-09-16 12:20:05 +00002792 ((vi_readonly || readonly) ? " [Read-only]" : ""),
2793#endif
2794 (file_modified ? " [modified]" : ""),
2795 cur, tot, percent);
2796
2797 if (ret >= 0 && ret < trunc_at)
2798 return ret; /* it all fit */
2799
2800 return trunc_at; /* had to truncate */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002801}
2802
2803//----- Force refresh of all Lines -----------------------------
2804static void redraw(int full_screen)
2805{
2806 place_cursor(0, 0, FALSE); // put cursor in correct place
2807 clear_to_eos(); // tel terminal to erase display
2808 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00002809 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002810 refresh(full_screen); // this will redraw the entire display
Paul Fox8552aec2005-09-16 12:20:05 +00002811 show_status_line();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002812}
2813
2814//----- Format a text[] line into a buffer ---------------------
2815static void format_line(Byte *dest, Byte *src, int li)
2816{
2817 int co;
2818 Byte c;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002819
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002820 for (co= 0; co < MAX_SCR_COLS; co++) {
2821 c= ' '; // assume blank
2822 if (li > 0 && co == 0) {
2823 c = '~'; // not first line, assume Tilde
2824 }
2825 // are there chars in text[] and have we gone past the end
2826 if (text < end && src < end) {
2827 c = *src++;
2828 }
2829 if (c == '\n')
2830 break;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002831 if (c > 127 && !Isprint(c)) {
2832 c = '.';
2833 }
2834 if (c < ' ' || c == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002835 if (c == '\t') {
2836 c = ' ';
2837 // co % 8 != 7
2838 for (; (co % tabstop) != (tabstop - 1); co++) {
2839 dest[co] = c;
2840 }
2841 } else {
2842 dest[co++] = '^';
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002843 if(c == 127)
2844 c = '?';
2845 else
2846 c += '@'; // make it visible
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002847 }
2848 }
2849 // the co++ is done here so that the column will
2850 // not be overwritten when we blank-out the rest of line
2851 dest[co] = c;
2852 if (src >= end)
2853 break;
2854 }
2855}
2856
2857//----- Refresh the changed screen lines -----------------------
2858// Copy the source line from text[] into the buffer and note
2859// if the current screenline is different from the new buffer.
2860// If they differ then that line needs redrawing on the terminal.
2861//
2862static void refresh(int full_screen)
2863{
2864 static int old_offset;
2865 int li, changed;
2866 Byte buf[MAX_SCR_COLS];
2867 Byte *tp, *sp; // pointer into text[] and screen[]
2868#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2869 int last_li= -2; // last line that changed- for optimizing cursor movement
2870#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2871
2872#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
2873 window_size_get(0);
2874#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
2875 sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
2876 tp = screenbegin; // index into text[] of top line
2877
2878 // compare text[] to screen[] and mark screen[] lines that need updating
2879 for (li = 0; li < rows - 1; li++) {
2880 int cs, ce; // column start & end
2881 memset(buf, ' ', MAX_SCR_COLS); // blank-out the buffer
2882 buf[MAX_SCR_COLS-1] = 0; // NULL terminate the buffer
2883 // format current text line into buf
2884 format_line(buf, tp, li);
2885
2886 // skip to the end of the current text[] line
2887 while (tp < end && *tp++ != '\n') /*no-op*/ ;
2888
2889 // see if there are any changes between vitual screen and buf
2890 changed = FALSE; // assume no change
2891 cs= 0;
2892 ce= columns-1;
2893 sp = &screen[li * columns]; // start of screen line
2894 if (full_screen) {
2895 // force re-draw of every single column from 0 - columns-1
2896 goto re0;
2897 }
2898 // compare newly formatted buffer with virtual screen
2899 // look forward for first difference between buf and screen
2900 for ( ; cs <= ce; cs++) {
2901 if (buf[cs + offset] != sp[cs]) {
2902 changed = TRUE; // mark for redraw
2903 break;
2904 }
2905 }
2906
2907 // look backward for last difference between buf and screen
2908 for ( ; ce >= cs; ce--) {
2909 if (buf[ce + offset] != sp[ce]) {
2910 changed = TRUE; // mark for redraw
2911 break;
2912 }
2913 }
2914 // now, cs is index of first diff, and ce is index of last diff
2915
2916 // if horz offset has changed, force a redraw
2917 if (offset != old_offset) {
2918 re0:
2919 changed = TRUE;
2920 }
2921
2922 // make a sanity check of columns indexes
2923 if (cs < 0) cs= 0;
2924 if (ce > columns-1) ce= columns-1;
2925 if (cs > ce) { cs= 0; ce= columns-1; }
2926 // is there a change between vitual screen and buf
2927 if (changed) {
2928 // copy changed part of buffer to virtual screen
2929 memmove(sp+cs, buf+(cs+offset), ce-cs+1);
2930
2931 // move cursor to column of first change
2932 if (offset != old_offset) {
2933 // opti_cur_move is still too stupid
2934 // to handle offsets correctly
2935 place_cursor(li, cs, FALSE);
2936 } else {
2937#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2938 // if this just the next line
2939 // try to optimize cursor movement
2940 // otherwise, use standard ESC sequence
2941 place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE);
2942 last_li= li;
2943#else /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2944 place_cursor(li, cs, FALSE); // use standard ESC sequence
2945#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2946 }
2947
2948 // write line out to terminal
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002949 {
2950 int nic = ce-cs+1;
2951 char *out = sp+cs;
2952
2953 while(nic-- > 0) {
2954 putchar(*out);
2955 out++;
2956 }
2957 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002958#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2959 last_row = li;
2960#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2961 }
2962 }
2963
2964#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2965 place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE);
2966 last_row = crow;
2967#else
2968 place_cursor(crow, ccol, FALSE);
2969#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002970
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002971 if (offset != old_offset)
2972 old_offset = offset;
2973}
2974
Eric Andersen3f980402001-04-04 17:31:15 +00002975//---------------------------------------------------------------------
2976//----- the Ascii Chart -----------------------------------------------
2977//
2978// 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel
2979// 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si
2980// 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb
2981// 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us
2982// 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 '
2983// 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f /
2984// 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7
2985// 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ?
2986// 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G
2987// 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O
2988// 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W
2989// 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _
2990// 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g
2991// 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o
2992// 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w
2993// 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del
2994//---------------------------------------------------------------------
2995
2996//----- Execute a Vi Command -----------------------------------
2997static void do_cmd(Byte c)
2998{
2999 Byte c1, *p, *q, *msg, buf[9], *save_dot;
3000 int cnt, i, j, dir, yf;
3001
3002 c1 = c; // quiet the compiler
3003 cnt = yf = dir = 0; // quiet the compiler
3004 p = q = save_dot = msg = buf; // quiet the compiler
3005 memset(buf, '\0', 9); // clear buf
Eric Andersenbff7a602001-11-17 07:15:43 +00003006
Paul Fox8552aec2005-09-16 12:20:05 +00003007 show_status_line();
3008
Eric Andersenbff7a602001-11-17 07:15:43 +00003009 /* if this is a cursor key, skip these checks */
3010 switch (c) {
3011 case VI_K_UP:
3012 case VI_K_DOWN:
3013 case VI_K_LEFT:
3014 case VI_K_RIGHT:
3015 case VI_K_HOME:
3016 case VI_K_END:
3017 case VI_K_PAGEUP:
3018 case VI_K_PAGEDOWN:
3019 goto key_cmd_mode;
3020 }
3021
Eric Andersen3f980402001-04-04 17:31:15 +00003022 if (cmd_mode == 2) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003023 // flip-flop Insert/Replace mode
3024 if (c == VI_K_INSERT) goto dc_i;
Eric Andersen3f980402001-04-04 17:31:15 +00003025 // we are 'R'eplacing the current *dot with new char
3026 if (*dot == '\n') {
3027 // don't Replace past E-o-l
3028 cmd_mode = 1; // convert to insert
3029 } else {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003030 if (1 <= c || Isprint(c)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003031 if (c != 27)
3032 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3033 dot = char_insert(dot, c); // insert new char
3034 }
3035 goto dc1;
3036 }
3037 }
3038 if (cmd_mode == 1) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00003039 // hitting "Insert" twice means "R" replace mode
3040 if (c == VI_K_INSERT) goto dc5;
Eric Andersen3f980402001-04-04 17:31:15 +00003041 // insert the char c at "dot"
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003042 if (1 <= c || Isprint(c)) {
3043 dot = char_insert(dot, c);
Eric Andersen3f980402001-04-04 17:31:15 +00003044 }
3045 goto dc1;
3046 }
3047
Eric Andersenbff7a602001-11-17 07:15:43 +00003048key_cmd_mode:
Eric Andersen3f980402001-04-04 17:31:15 +00003049 switch (c) {
Eric Andersen822c3832001-05-07 17:37:43 +00003050 //case 0x01: // soh
3051 //case 0x09: // ht
3052 //case 0x0b: // vt
3053 //case 0x0e: // so
3054 //case 0x0f: // si
3055 //case 0x10: // dle
3056 //case 0x11: // dc1
3057 //case 0x13: // dc3
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003058#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen1c0d3112001-04-16 15:46:44 +00003059 case 0x14: // dc4 ctrl-T
Eric Andersen3f980402001-04-04 17:31:15 +00003060 crashme = (crashme == 0) ? 1 : 0;
Eric Andersen3f980402001-04-04 17:31:15 +00003061 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003062#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen822c3832001-05-07 17:37:43 +00003063 //case 0x16: // syn
3064 //case 0x17: // etb
3065 //case 0x18: // can
3066 //case 0x1c: // fs
3067 //case 0x1d: // gs
3068 //case 0x1e: // rs
3069 //case 0x1f: // us
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003070 //case '!': // !-
3071 //case '#': // #-
3072 //case '&': // &-
3073 //case '(': // (-
3074 //case ')': // )-
3075 //case '*': // *-
3076 //case ',': // ,-
3077 //case '=': // =-
3078 //case '@': // @-
3079 //case 'F': // F-
3080 //case 'K': // K-
3081 //case 'Q': // Q-
3082 //case 'S': // S-
3083 //case 'T': // T-
3084 //case 'V': // V-
3085 //case '[': // [-
3086 //case '\\': // \-
3087 //case ']': // ]-
3088 //case '_': // _-
3089 //case '`': // `-
3090 //case 'g': // g-
Eric Andersen1c0d3112001-04-16 15:46:44 +00003091 //case 'u': // u- FIXME- there is no undo
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003092 //case 'v': // v-
Eric Andersen3f980402001-04-04 17:31:15 +00003093 default: // unrecognised command
3094 buf[0] = c;
3095 buf[1] = '\0';
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003096 if (c < ' ') {
Eric Andersen3f980402001-04-04 17:31:15 +00003097 buf[0] = '^';
3098 buf[1] = c + '@';
3099 buf[2] = '\0';
3100 }
3101 ni((Byte *) buf);
3102 end_cmd_q(); // stop adding to q
3103 case 0x00: // nul- ignore
3104 break;
3105 case 2: // ctrl-B scroll up full screen
3106 case VI_K_PAGEUP: // Cursor Key Page Up
3107 dot_scroll(rows - 2, -1);
3108 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003109#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +00003110 case 0x03: // ctrl-C interrupt
3111 longjmp(restart, 1);
3112 break;
3113 case 26: // ctrl-Z suspend
3114 suspend_sig(SIGTSTP);
3115 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003116#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Eric Andersen3f980402001-04-04 17:31:15 +00003117 case 4: // ctrl-D scroll down half screen
3118 dot_scroll((rows - 2) / 2, 1);
3119 break;
3120 case 5: // ctrl-E scroll down one line
3121 dot_scroll(1, 1);
3122 break;
3123 case 6: // ctrl-F scroll down full screen
3124 case VI_K_PAGEDOWN: // Cursor Key Page Down
3125 dot_scroll(rows - 2, 1);
3126 break;
3127 case 7: // ctrl-G show current status
Paul Fox8552aec2005-09-16 12:20:05 +00003128 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003129 break;
3130 case 'h': // h- move left
3131 case VI_K_LEFT: // cursor key Left
Paul Foxd13b90b2005-07-18 22:17:25 +00003132 case 8: // ctrl-H- move left (This may be ERASE char)
3133 case 127: // DEL- move left (This may be ERASE char)
Eric Andersen3f980402001-04-04 17:31:15 +00003134 if (cmdcnt-- > 1) {
3135 do_cmd(c);
3136 } // repeat cnt
3137 dot_left();
3138 break;
3139 case 10: // Newline ^J
3140 case 'j': // j- goto next line, same col
3141 case VI_K_DOWN: // cursor key Down
3142 if (cmdcnt-- > 1) {
3143 do_cmd(c);
3144 } // repeat cnt
3145 dot_next(); // go to next B-o-l
3146 dot = move_to_col(dot, ccol + offset); // try stay in same col
3147 break;
3148 case 12: // ctrl-L force redraw whole screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003149 case 18: // ctrl-R force redraw
Eric Andersen822c3832001-05-07 17:37:43 +00003150 place_cursor(0, 0, FALSE); // put cursor in correct place
Eric Andersen3f980402001-04-04 17:31:15 +00003151 clear_to_eos(); // tel terminal to erase display
3152 (void) mysleep(10);
3153 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00003154 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003155 refresh(TRUE); // this will redraw the entire display
3156 break;
3157 case 13: // Carriage Return ^M
3158 case '+': // +- goto next line
3159 if (cmdcnt-- > 1) {
3160 do_cmd(c);
3161 } // repeat cnt
3162 dot_next();
3163 dot_skip_over_ws();
3164 break;
3165 case 21: // ctrl-U scroll up half screen
3166 dot_scroll((rows - 2) / 2, -1);
3167 break;
3168 case 25: // ctrl-Y scroll up one line
3169 dot_scroll(1, -1);
3170 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003171 case 27: // esc
Eric Andersen3f980402001-04-04 17:31:15 +00003172 if (cmd_mode == 0)
3173 indicate_error(c);
3174 cmd_mode = 0; // stop insrting
3175 end_cmd_q();
Paul Fox8552aec2005-09-16 12:20:05 +00003176 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003177 break;
3178 case ' ': // move right
3179 case 'l': // move right
3180 case VI_K_RIGHT: // Cursor Key Right
3181 if (cmdcnt-- > 1) {
3182 do_cmd(c);
3183 } // repeat cnt
3184 dot_right();
3185 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003186#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003187 case '"': // "- name a register to use for Delete/Yank
3188 c1 = get_one_char();
3189 c1 = tolower(c1);
3190 if (islower(c1)) {
3191 YDreg = c1 - 'a';
3192 } else {
3193 indicate_error(c);
3194 }
3195 break;
3196 case '\'': // '- goto a specific mark
3197 c1 = get_one_char();
3198 c1 = tolower(c1);
3199 if (islower(c1)) {
3200 c1 = c1 - 'a';
3201 // get the b-o-l
3202 q = mark[(int) c1];
3203 if (text <= q && q < end) {
3204 dot = q;
3205 dot_begin(); // go to B-o-l
3206 dot_skip_over_ws();
3207 }
3208 } else if (c1 == '\'') { // goto previous context
3209 dot = swap_context(dot); // swap current and previous context
3210 dot_begin(); // go to B-o-l
3211 dot_skip_over_ws();
3212 } else {
3213 indicate_error(c);
3214 }
3215 break;
3216 case 'm': // m- Mark a line
3217 // this is really stupid. If there are any inserts or deletes
3218 // between text[0] and dot then this mark will not point to the
3219 // correct location! It could be off by many lines!
3220 // Well..., at least its quick and dirty.
3221 c1 = get_one_char();
3222 c1 = tolower(c1);
3223 if (islower(c1)) {
3224 c1 = c1 - 'a';
3225 // remember the line
3226 mark[(int) c1] = dot;
3227 } else {
3228 indicate_error(c);
3229 }
3230 break;
3231 case 'P': // P- Put register before
3232 case 'p': // p- put register after
3233 p = reg[YDreg];
3234 if (p == 0) {
3235 psbs("Nothing in register %c", what_reg());
3236 break;
3237 }
3238 // are we putting whole lines or strings
3239 if (strchr((char *) p, '\n') != NULL) {
3240 if (c == 'P') {
3241 dot_begin(); // putting lines- Put above
3242 }
3243 if (c == 'p') {
3244 // are we putting after very last line?
3245 if (end_line(dot) == (end - 1)) {
3246 dot = end; // force dot to end of text[]
3247 } else {
3248 dot_next(); // next line, then put before
3249 }
3250 }
3251 } else {
3252 if (c == 'p')
3253 dot_right(); // move to right, can move to NL
3254 }
3255 dot = string_insert(dot, p); // insert the string
3256 end_cmd_q(); // stop adding to q
3257 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003258 case 'U': // U- Undo; replace current line with original version
3259 if (reg[Ureg] != 0) {
3260 p = begin_line(dot);
3261 q = end_line(dot);
3262 p = text_hole_delete(p, q); // delete cur line
3263 p = string_insert(p, reg[Ureg]); // insert orig line
3264 dot = p;
3265 dot_skip_over_ws();
3266 }
3267 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003268#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003269 case '$': // $- goto end of line
3270 case VI_K_END: // Cursor Key End
3271 if (cmdcnt-- > 1) {
3272 do_cmd(c);
3273 } // repeat cnt
Glenn L McGrathee829062004-01-21 10:59:45 +00003274 dot = end_line(dot);
Eric Andersen3f980402001-04-04 17:31:15 +00003275 break;
3276 case '%': // %- find matching char of pair () [] {}
3277 for (q = dot; q < end && *q != '\n'; q++) {
3278 if (strchr("()[]{}", *q) != NULL) {
3279 // we found half of a pair
3280 p = find_pair(q, *q);
3281 if (p == NULL) {
3282 indicate_error(c);
3283 } else {
3284 dot = p;
3285 }
3286 break;
3287 }
3288 }
3289 if (*q == '\n')
3290 indicate_error(c);
3291 break;
3292 case 'f': // f- forward to a user specified char
3293 last_forward_char = get_one_char(); // get the search char
3294 //
Eric Andersenaff114c2004-04-14 17:51:38 +00003295 // dont separate these two commands. 'f' depends on ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003296 //
Paul Foxd13b90b2005-07-18 22:17:25 +00003297 //**** fall thru to ... ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003298 case ';': // ;- look at rest of line for last forward char
3299 if (cmdcnt-- > 1) {
Eric Andersen822c3832001-05-07 17:37:43 +00003300 do_cmd(';');
Eric Andersen3f980402001-04-04 17:31:15 +00003301 } // repeat cnt
Eric Andersen822c3832001-05-07 17:37:43 +00003302 if (last_forward_char == 0) break;
Eric Andersen3f980402001-04-04 17:31:15 +00003303 q = dot + 1;
3304 while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
3305 q++;
3306 }
3307 if (*q == last_forward_char)
3308 dot = q;
3309 break;
3310 case '-': // -- goto prev line
3311 if (cmdcnt-- > 1) {
3312 do_cmd(c);
3313 } // repeat cnt
3314 dot_prev();
3315 dot_skip_over_ws();
3316 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003317#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003318 case '.': // .- repeat the last modifying command
3319 // Stuff the last_modifying_cmd back into stdin
3320 // and let it be re-executed.
3321 if (last_modifying_cmd != 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +00003322 ioq = ioq_start = (Byte *) bb_xstrdup((char *) last_modifying_cmd);
Eric Andersen3f980402001-04-04 17:31:15 +00003323 }
3324 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003325#endif /* CONFIG_FEATURE_VI_DOT_CMD */
3326#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +00003327 case '?': // /- search for a pattern
3328 case '/': // /- search for a pattern
3329 buf[0] = c;
3330 buf[1] = '\0';
3331 q = get_input_line(buf); // get input line- use "status line"
3332 if (strlen((char *) q) == 1)
3333 goto dc3; // if no pat re-use old pat
3334 if (strlen((char *) q) > 1) { // new pat- save it and find
3335 // there is a new pat
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00003336 free(last_search_pattern);
Manuel Novoa III cad53642003-03-19 09:13:01 +00003337 last_search_pattern = (Byte *) bb_xstrdup((char *) q);
Eric Andersen3f980402001-04-04 17:31:15 +00003338 goto dc3; // now find the pattern
3339 }
3340 // user changed mind and erased the "/"- do nothing
3341 break;
3342 case 'N': // N- backward search for last pattern
3343 if (cmdcnt-- > 1) {
3344 do_cmd(c);
3345 } // repeat cnt
3346 dir = BACK; // assume BACKWARD search
3347 p = dot - 1;
3348 if (last_search_pattern[0] == '?') {
3349 dir = FORWARD;
3350 p = dot + 1;
3351 }
3352 goto dc4; // now search for pattern
3353 break;
3354 case 'n': // n- repeat search for last pattern
3355 // search rest of text[] starting at next char
3356 // if search fails return orignal "p" not the "p+1" address
3357 if (cmdcnt-- > 1) {
3358 do_cmd(c);
3359 } // repeat cnt
3360 dc3:
3361 if (last_search_pattern == 0) {
3362 msg = (Byte *) "No previous regular expression";
3363 goto dc2;
3364 }
3365 if (last_search_pattern[0] == '/') {
3366 dir = FORWARD; // assume FORWARD search
3367 p = dot + 1;
3368 }
3369 if (last_search_pattern[0] == '?') {
3370 dir = BACK;
3371 p = dot - 1;
3372 }
3373 dc4:
3374 q = char_search(p, last_search_pattern + 1, dir, FULL);
3375 if (q != NULL) {
3376 dot = q; // good search, update "dot"
3377 msg = (Byte *) "";
3378 goto dc2;
3379 }
3380 // no pattern found between "dot" and "end"- continue at top
3381 p = text;
3382 if (dir == BACK) {
3383 p = end - 1;
3384 }
3385 q = char_search(p, last_search_pattern + 1, dir, FULL);
3386 if (q != NULL) { // found something
3387 dot = q; // found new pattern- goto it
3388 msg = (Byte *) "search hit BOTTOM, continuing at TOP";
3389 if (dir == BACK) {
3390 msg = (Byte *) "search hit TOP, continuing at BOTTOM";
3391 }
3392 } else {
3393 msg = (Byte *) "Pattern not found";
3394 }
3395 dc2:
Paul Fox8552aec2005-09-16 12:20:05 +00003396 if (*msg) psbs("%s", msg);
Eric Andersen3f980402001-04-04 17:31:15 +00003397 break;
3398 case '{': // {- move backward paragraph
3399 q = char_search(dot, (Byte *) "\n\n", BACK, FULL);
3400 if (q != NULL) { // found blank line
3401 dot = next_line(q); // move to next blank line
3402 }
3403 break;
3404 case '}': // }- move forward paragraph
3405 q = char_search(dot, (Byte *) "\n\n", FORWARD, FULL);
3406 if (q != NULL) { // found blank line
3407 dot = next_line(q); // move to next blank line
3408 }
3409 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003410#endif /* CONFIG_FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +00003411 case '0': // 0- goto begining of line
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003412 case '1': // 1-
3413 case '2': // 2-
3414 case '3': // 3-
3415 case '4': // 4-
3416 case '5': // 5-
3417 case '6': // 6-
3418 case '7': // 7-
3419 case '8': // 8-
3420 case '9': // 9-
Eric Andersen3f980402001-04-04 17:31:15 +00003421 if (c == '0' && cmdcnt < 1) {
3422 dot_begin(); // this was a standalone zero
3423 } else {
3424 cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number
3425 }
3426 break;
3427 case ':': // :- the colon mode commands
Eric Andersen3f980402001-04-04 17:31:15 +00003428 p = get_input_line((Byte *) ":"); // get input line- use "status line"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003429#ifdef CONFIG_FEATURE_VI_COLON
Eric Andersen3f980402001-04-04 17:31:15 +00003430 colon(p); // execute the command
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003431#else /* CONFIG_FEATURE_VI_COLON */
Eric Andersen822c3832001-05-07 17:37:43 +00003432 if (*p == ':')
3433 p++; // move past the ':'
3434 cnt = strlen((char *) p);
3435 if (cnt <= 0)
3436 break;
3437 if (strncasecmp((char *) p, "quit", cnt) == 0 ||
3438 strncasecmp((char *) p, "q!", cnt) == 0) { // delete lines
Matt Kraai1f0c4362001-12-20 23:13:26 +00003439 if (file_modified && p[1] != '!') {
Eric Andersen3f980402001-04-04 17:31:15 +00003440 psbs("No write since last change (:quit! overrides)");
3441 } else {
3442 editing = 0;
3443 }
Eric Andersen822c3832001-05-07 17:37:43 +00003444 } else if (strncasecmp((char *) p, "write", cnt) == 0 ||
Robert Griebla71389b2002-07-31 21:22:21 +00003445 strncasecmp((char *) p, "wq", cnt) == 0 ||
3446 strncasecmp((char *) p, "x", cnt) == 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003447 cnt = file_write(cfn, text, end - 1);
Paul Fox61e45db2005-10-09 14:43:22 +00003448 if (cnt < 0) {
3449 if (cnt == -1)
3450 psbs("Write error: %s", strerror(errno));
3451 } else {
3452 file_modified = 0;
3453 last_file_modified = -1;
3454 psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt);
3455 if (p[0] == 'x' || p[1] == 'q') {
3456 editing = 0;
3457 }
Eric Andersen3f980402001-04-04 17:31:15 +00003458 }
Eric Andersen822c3832001-05-07 17:37:43 +00003459 } else if (strncasecmp((char *) p, "file", cnt) == 0 ) {
Paul Fox8552aec2005-09-16 12:20:05 +00003460 last_status_cksum = 0; // force status update
Eric Andersen822c3832001-05-07 17:37:43 +00003461 } else if (sscanf((char *) p, "%d", &j) > 0) {
3462 dot = find_line(j); // go to line # j
3463 dot_skip_over_ws();
Eric Andersen3f980402001-04-04 17:31:15 +00003464 } else { // unrecognised cmd
Eric Andersen822c3832001-05-07 17:37:43 +00003465 ni((Byte *) p);
Eric Andersen3f980402001-04-04 17:31:15 +00003466 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003467#endif /* CONFIG_FEATURE_VI_COLON */
Eric Andersen3f980402001-04-04 17:31:15 +00003468 break;
3469 case '<': // <- Left shift something
3470 case '>': // >- Right shift something
3471 cnt = count_lines(text, dot); // remember what line we are on
3472 c1 = get_one_char(); // get the type of thing to delete
3473 find_range(&p, &q, c1);
3474 (void) yank_delete(p, q, 1, YANKONLY); // save copy before change
3475 p = begin_line(p);
3476 q = end_line(q);
3477 i = count_lines(p, q); // # of lines we are shifting
3478 for ( ; i > 0; i--, p = next_line(p)) {
3479 if (c == '<') {
3480 // shift left- remove tab or 8 spaces
3481 if (*p == '\t') {
3482 // shrink buffer 1 char
3483 (void) text_hole_delete(p, p);
3484 } else if (*p == ' ') {
3485 // we should be calculating columns, not just SPACE
3486 for (j = 0; *p == ' ' && j < tabstop; j++) {
3487 (void) text_hole_delete(p, p);
3488 }
3489 }
3490 } else if (c == '>') {
3491 // shift right -- add tab or 8 spaces
3492 (void) char_insert(p, '\t');
3493 }
3494 }
3495 dot = find_line(cnt); // what line were we on
3496 dot_skip_over_ws();
3497 end_cmd_q(); // stop adding to q
3498 break;
3499 case 'A': // A- append at e-o-l
3500 dot_end(); // go to e-o-l
3501 //**** fall thru to ... 'a'
3502 case 'a': // a- append after current char
3503 if (*dot != '\n')
3504 dot++;
3505 goto dc_i;
3506 break;
3507 case 'B': // B- back a blank-delimited Word
3508 case 'E': // E- end of a blank-delimited word
3509 case 'W': // W- forward a blank-delimited word
3510 if (cmdcnt-- > 1) {
3511 do_cmd(c);
3512 } // repeat cnt
3513 dir = FORWARD;
3514 if (c == 'B')
3515 dir = BACK;
3516 if (c == 'W' || isspace(dot[dir])) {
3517 dot = skip_thing(dot, 1, dir, S_TO_WS);
3518 dot = skip_thing(dot, 2, dir, S_OVER_WS);
3519 }
3520 if (c != 'W')
3521 dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
3522 break;
3523 case 'C': // C- Change to e-o-l
3524 case 'D': // D- delete to e-o-l
3525 save_dot = dot;
3526 dot = dollar_line(dot); // move to before NL
3527 // copy text into a register and delete
3528 dot = yank_delete(save_dot, dot, 0, YANKDEL); // delete to e-o-l
3529 if (c == 'C')
3530 goto dc_i; // start inserting
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003531#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003532 if (c == 'D')
3533 end_cmd_q(); // stop adding to q
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003534#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +00003535 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003536 case 'G': // G- goto to a line number (default= E-O-F)
3537 dot = end - 1; // assume E-O-F
Eric Andersen1c0d3112001-04-16 15:46:44 +00003538 if (cmdcnt > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00003539 dot = find_line(cmdcnt); // what line is #cmdcnt
Eric Andersen1c0d3112001-04-16 15:46:44 +00003540 }
3541 dot_skip_over_ws();
3542 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003543 case 'H': // H- goto top line on screen
3544 dot = screenbegin;
3545 if (cmdcnt > (rows - 1)) {
3546 cmdcnt = (rows - 1);
3547 }
3548 if (cmdcnt-- > 1) {
3549 do_cmd('+');
3550 } // repeat cnt
3551 dot_skip_over_ws();
3552 break;
3553 case 'I': // I- insert before first non-blank
3554 dot_begin(); // 0
3555 dot_skip_over_ws();
3556 //**** fall thru to ... 'i'
3557 case 'i': // i- insert before current char
3558 case VI_K_INSERT: // Cursor Key Insert
3559 dc_i:
3560 cmd_mode = 1; // start insrting
Eric Andersen3f980402001-04-04 17:31:15 +00003561 break;
3562 case 'J': // J- join current and next lines together
3563 if (cmdcnt-- > 2) {
3564 do_cmd(c);
3565 } // repeat cnt
3566 dot_end(); // move to NL
3567 if (dot < end - 1) { // make sure not last char in text[]
3568 *dot++ = ' '; // replace NL with space
Paul Fox8552aec2005-09-16 12:20:05 +00003569 file_modified++;
Eric Andersen3f980402001-04-04 17:31:15 +00003570 while (isblnk(*dot)) { // delete leading WS
3571 dot_delete();
3572 }
3573 }
3574 end_cmd_q(); // stop adding to q
3575 break;
3576 case 'L': // L- goto bottom line on screen
3577 dot = end_screen();
3578 if (cmdcnt > (rows - 1)) {
3579 cmdcnt = (rows - 1);
3580 }
3581 if (cmdcnt-- > 1) {
3582 do_cmd('-');
3583 } // repeat cnt
3584 dot_begin();
3585 dot_skip_over_ws();
3586 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003587 case 'M': // M- goto middle line on screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003588 dot = screenbegin;
3589 for (cnt = 0; cnt < (rows-1) / 2; cnt++)
3590 dot = next_line(dot);
3591 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003592 case 'O': // O- open a empty line above
Eric Andersen822c3832001-05-07 17:37:43 +00003593 // 0i\n ESC -i
Eric Andersen3f980402001-04-04 17:31:15 +00003594 p = begin_line(dot);
3595 if (p[-1] == '\n') {
3596 dot_prev();
3597 case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
3598 dot_end();
3599 dot = char_insert(dot, '\n');
3600 } else {
3601 dot_begin(); // 0
Eric Andersen822c3832001-05-07 17:37:43 +00003602 dot = char_insert(dot, '\n'); // i\n ESC
Eric Andersen3f980402001-04-04 17:31:15 +00003603 dot_prev(); // -
3604 }
3605 goto dc_i;
3606 break;
3607 case 'R': // R- continuous Replace char
Eric Andersen1c0d3112001-04-16 15:46:44 +00003608 dc5:
Eric Andersen3f980402001-04-04 17:31:15 +00003609 cmd_mode = 2;
Eric Andersen3f980402001-04-04 17:31:15 +00003610 break;
3611 case 'X': // X- delete char before dot
3612 case 'x': // x- delete the current char
3613 case 's': // s- substitute the current char
3614 if (cmdcnt-- > 1) {
3615 do_cmd(c);
3616 } // repeat cnt
3617 dir = 0;
3618 if (c == 'X')
3619 dir = -1;
3620 if (dot[dir] != '\n') {
3621 if (c == 'X')
3622 dot--; // delete prev char
3623 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3624 }
3625 if (c == 's')
3626 goto dc_i; // start insrting
3627 end_cmd_q(); // stop adding to q
3628 break;
3629 case 'Z': // Z- if modified, {write}; exit
3630 // ZZ means to save file (if necessary), then exit
3631 c1 = get_one_char();
3632 if (c1 != 'Z') {
3633 indicate_error(c);
3634 break;
3635 }
Matt Kraai1f0c4362001-12-20 23:13:26 +00003636 if (file_modified
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003637#ifdef CONFIG_FEATURE_VI_READONLY
Matt Kraai1f0c4362001-12-20 23:13:26 +00003638 && ! vi_readonly
3639 && ! readonly
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003640#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen3f980402001-04-04 17:31:15 +00003641 ) {
3642 cnt = file_write(cfn, text, end - 1);
Paul Fox61e45db2005-10-09 14:43:22 +00003643 if (cnt < 0) {
3644 if (cnt == -1)
3645 psbs("Write error: %s", strerror(errno));
3646 } else if (cnt == (end - 1 - text + 1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003647 editing = 0;
3648 }
3649 } else {
3650 editing = 0;
3651 }
3652 break;
3653 case '^': // ^- move to first non-blank on line
3654 dot_begin();
3655 dot_skip_over_ws();
3656 break;
3657 case 'b': // b- back a word
3658 case 'e': // e- end of word
3659 if (cmdcnt-- > 1) {
3660 do_cmd(c);
3661 } // repeat cnt
3662 dir = FORWARD;
3663 if (c == 'b')
3664 dir = BACK;
3665 if ((dot + dir) < text || (dot + dir) > end - 1)
3666 break;
3667 dot += dir;
3668 if (isspace(*dot)) {
3669 dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
3670 }
3671 if (isalnum(*dot) || *dot == '_') {
3672 dot = skip_thing(dot, 1, dir, S_END_ALNUM);
3673 } else if (ispunct(*dot)) {
3674 dot = skip_thing(dot, 1, dir, S_END_PUNCT);
3675 }
3676 break;
3677 case 'c': // c- change something
3678 case 'd': // d- delete something
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003679#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003680 case 'y': // y- yank something
3681 case 'Y': // Y- Yank a line
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003682#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003683 yf = YANKDEL; // assume either "c" or "d"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003684#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003685 if (c == 'y' || c == 'Y')
3686 yf = YANKONLY;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003687#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003688 c1 = 'y';
3689 if (c != 'Y')
3690 c1 = get_one_char(); // get the type of thing to delete
3691 find_range(&p, &q, c1);
3692 if (c1 == 27) { // ESC- user changed mind and wants out
3693 c = c1 = 27; // Escape- do nothing
3694 } else if (strchr("wW", c1)) {
3695 if (c == 'c') {
3696 // don't include trailing WS as part of word
3697 while (isblnk(*q)) {
3698 if (q <= text || q[-1] == '\n')
3699 break;
3700 q--;
3701 }
3702 }
3703 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen822c3832001-05-07 17:37:43 +00003704 } else if (strchr("^0bBeEft$", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003705 // single line copy text into a register and delete
3706 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen1c0d3112001-04-16 15:46:44 +00003707 } else if (strchr("cdykjHL%+-{}\r\n", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003708 // multiple line copy text into a register and delete
3709 dot = yank_delete(p, q, 1, yf); // delete lines
Eric Andersen1c0d3112001-04-16 15:46:44 +00003710 if (c == 'c') {
3711 dot = char_insert(dot, '\n');
3712 // on the last line of file don't move to prev line
3713 if (dot != (end-1)) {
3714 dot_prev();
3715 }
3716 } else if (c == 'd') {
Eric Andersen3f980402001-04-04 17:31:15 +00003717 dot_begin();
3718 dot_skip_over_ws();
3719 }
3720 } else {
3721 // could not recognize object
3722 c = c1 = 27; // error-
3723 indicate_error(c);
3724 }
3725 if (c1 != 27) {
3726 // if CHANGING, not deleting, start inserting after the delete
3727 if (c == 'c') {
3728 strcpy((char *) buf, "Change");
3729 goto dc_i; // start inserting
3730 }
3731 if (c == 'd') {
3732 strcpy((char *) buf, "Delete");
3733 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003734#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003735 if (c == 'y' || c == 'Y') {
3736 strcpy((char *) buf, "Yank");
3737 }
3738 p = reg[YDreg];
3739 q = p + strlen((char *) p);
3740 for (cnt = 0; p <= q; p++) {
3741 if (*p == '\n')
3742 cnt++;
3743 }
3744 psb("%s %d lines (%d chars) using [%c]",
3745 buf, cnt, strlen((char *) reg[YDreg]), what_reg());
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003746#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003747 end_cmd_q(); // stop adding to q
3748 }
3749 break;
3750 case 'k': // k- goto prev line, same col
3751 case VI_K_UP: // cursor key Up
3752 if (cmdcnt-- > 1) {
3753 do_cmd(c);
3754 } // repeat cnt
3755 dot_prev();
3756 dot = move_to_col(dot, ccol + offset); // try stay in same col
3757 break;
3758 case 'r': // r- replace the current char with user input
3759 c1 = get_one_char(); // get the replacement char
3760 if (*dot != '\n') {
3761 *dot = c1;
Paul Fox8552aec2005-09-16 12:20:05 +00003762 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003763 }
3764 end_cmd_q(); // stop adding to q
3765 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003766 case 't': // t- move to char prior to next x
3767 last_forward_char = get_one_char();
3768 do_cmd(';');
3769 if (*dot == last_forward_char)
3770 dot_left();
3771 last_forward_char= 0;
3772 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003773 case 'w': // w- forward a word
3774 if (cmdcnt-- > 1) {
3775 do_cmd(c);
3776 } // repeat cnt
3777 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
3778 dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
3779 } else if (ispunct(*dot)) { // we are on PUNCT
3780 dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
3781 }
3782 if (dot < end - 1)
3783 dot++; // move over word
3784 if (isspace(*dot)) {
3785 dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
3786 }
3787 break;
3788 case 'z': // z-
3789 c1 = get_one_char(); // get the replacement char
3790 cnt = 0;
3791 if (c1 == '.')
3792 cnt = (rows - 2) / 2; // put dot at center
3793 if (c1 == '-')
3794 cnt = rows - 2; // put dot at bottom
3795 screenbegin = begin_line(dot); // start dot at top
3796 dot_scroll(cnt, -1);
3797 break;
3798 case '|': // |- move to column "cmdcnt"
3799 dot = move_to_col(dot, cmdcnt - 1); // try to move to column
3800 break;
3801 case '~': // ~- flip the case of letters a-z -> A-Z
3802 if (cmdcnt-- > 1) {
3803 do_cmd(c);
3804 } // repeat cnt
3805 if (islower(*dot)) {
3806 *dot = toupper(*dot);
Paul Fox8552aec2005-09-16 12:20:05 +00003807 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003808 } else if (isupper(*dot)) {
3809 *dot = tolower(*dot);
Paul Fox8552aec2005-09-16 12:20:05 +00003810 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003811 }
3812 dot_right();
3813 end_cmd_q(); // stop adding to q
3814 break;
3815 //----- The Cursor and Function Keys -----------------------------
3816 case VI_K_HOME: // Cursor Key Home
3817 dot_begin();
3818 break;
3819 // The Fn keys could point to do_macro which could translate them
3820 case VI_K_FUN1: // Function Key F1
3821 case VI_K_FUN2: // Function Key F2
3822 case VI_K_FUN3: // Function Key F3
3823 case VI_K_FUN4: // Function Key F4
3824 case VI_K_FUN5: // Function Key F5
3825 case VI_K_FUN6: // Function Key F6
3826 case VI_K_FUN7: // Function Key F7
3827 case VI_K_FUN8: // Function Key F8
3828 case VI_K_FUN9: // Function Key F9
3829 case VI_K_FUN10: // Function Key F10
3830 case VI_K_FUN11: // Function Key F11
3831 case VI_K_FUN12: // Function Key F12
3832 break;
3833 }
3834
3835 dc1:
3836 // if text[] just became empty, add back an empty line
3837 if (end == text) {
3838 (void) char_insert(text, '\n'); // start empty buf with dummy line
3839 dot = text;
3840 }
3841 // it is OK for dot to exactly equal to end, otherwise check dot validity
3842 if (dot != end) {
3843 dot = bound_dot(dot); // make sure "dot" is valid
3844 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003845#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003846 check_context(c); // update the current context
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003847#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003848
3849 if (!isdigit(c))
3850 cmdcnt = 0; // cmd was not a number, reset cmdcnt
3851 cnt = dot - begin_line(dot);
3852 // Try to stay off of the Newline
3853 if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
3854 dot--;
3855}
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003856
3857#ifdef CONFIG_FEATURE_VI_CRASHME
3858static int totalcmds = 0;
3859static int Mp = 85; // Movement command Probability
3860static int Np = 90; // Non-movement command Probability
3861static int Dp = 96; // Delete command Probability
3862static int Ip = 97; // Insert command Probability
3863static int Yp = 98; // Yank command Probability
3864static int Pp = 99; // Put command Probability
3865static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
3866char chars[20] = "\t012345 abcdABCD-=.$";
3867char *words[20] = { "this", "is", "a", "test",
3868 "broadcast", "the", "emergency", "of",
3869 "system", "quick", "brown", "fox",
3870 "jumped", "over", "lazy", "dogs",
3871 "back", "January", "Febuary", "March"
3872};
3873char *lines[20] = {
3874 "You should have received a copy of the GNU General Public License\n",
3875 "char c, cm, *cmd, *cmd1;\n",
3876 "generate a command by percentages\n",
3877 "Numbers may be typed as a prefix to some commands.\n",
3878 "Quit, discarding changes!\n",
3879 "Forced write, if permission originally not valid.\n",
3880 "In general, any ex or ed command (such as substitute or delete).\n",
3881 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3882 "Please get w/ me and I will go over it with you.\n",
3883 "The following is a list of scheduled, committed changes.\n",
3884 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3885 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3886 "Any question about transactions please contact Sterling Huxley.\n",
3887 "I will try to get back to you by Friday, December 31.\n",
3888 "This Change will be implemented on Friday.\n",
3889 "Let me know if you have problems accessing this;\n",
3890 "Sterling Huxley recently added you to the access list.\n",
3891 "Would you like to go to lunch?\n",
3892 "The last command will be automatically run.\n",
3893 "This is too much english for a computer geek.\n",
3894};
3895char *multilines[20] = {
3896 "You should have received a copy of the GNU General Public License\n",
3897 "char c, cm, *cmd, *cmd1;\n",
3898 "generate a command by percentages\n",
3899 "Numbers may be typed as a prefix to some commands.\n",
3900 "Quit, discarding changes!\n",
3901 "Forced write, if permission originally not valid.\n",
3902 "In general, any ex or ed command (such as substitute or delete).\n",
3903 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3904 "Please get w/ me and I will go over it with you.\n",
3905 "The following is a list of scheduled, committed changes.\n",
3906 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3907 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3908 "Any question about transactions please contact Sterling Huxley.\n",
3909 "I will try to get back to you by Friday, December 31.\n",
3910 "This Change will be implemented on Friday.\n",
3911 "Let me know if you have problems accessing this;\n",
3912 "Sterling Huxley recently added you to the access list.\n",
3913 "Would you like to go to lunch?\n",
3914 "The last command will be automatically run.\n",
3915 "This is too much english for a computer geek.\n",
3916};
3917
3918// create a random command to execute
3919static void crash_dummy()
3920{
3921 static int sleeptime; // how long to pause between commands
3922 char c, cm, *cmd, *cmd1;
3923 int i, cnt, thing, rbi, startrbi, percent;
3924
3925 // "dot" movement commands
3926 cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
3927
3928 // is there already a command running?
3929 if (readed_for_parse > 0)
3930 goto cd1;
3931 cd0:
3932 startrbi = rbi = 0;
3933 sleeptime = 0; // how long to pause between commands
3934 memset(readbuffer, '\0', BUFSIZ); // clear the read buffer
3935 // generate a command by percentages
3936 percent = (int) lrand48() % 100; // get a number from 0-99
3937 if (percent < Mp) { // Movement commands
3938 // available commands
3939 cmd = cmd1;
3940 M++;
3941 } else if (percent < Np) { // non-movement commands
3942 cmd = "mz<>\'\""; // available commands
3943 N++;
3944 } else if (percent < Dp) { // Delete commands
3945 cmd = "dx"; // available commands
3946 D++;
3947 } else if (percent < Ip) { // Inset commands
3948 cmd = "iIaAsrJ"; // available commands
3949 I++;
3950 } else if (percent < Yp) { // Yank commands
3951 cmd = "yY"; // available commands
3952 Y++;
3953 } else if (percent < Pp) { // Put commands
3954 cmd = "pP"; // available commands
3955 P++;
3956 } else {
3957 // We do not know how to handle this command, try again
3958 U++;
3959 goto cd0;
3960 }
3961 // randomly pick one of the available cmds from "cmd[]"
3962 i = (int) lrand48() % strlen(cmd);
3963 cm = cmd[i];
3964 if (strchr(":\024", cm))
3965 goto cd0; // dont allow colon or ctrl-T commands
3966 readbuffer[rbi++] = cm; // put cmd into input buffer
3967
3968 // now we have the command-
3969 // there are 1, 2, and multi char commands
3970 // find out which and generate the rest of command as necessary
3971 if (strchr("dmryz<>\'\"", cm)) { // 2-char commands
3972 cmd1 = " \n\r0$^-+wWeEbBhjklHL";
3973 if (cm == 'm' || cm == '\'' || cm == '\"') { // pick a reg[]
3974 cmd1 = "abcdefghijklmnopqrstuvwxyz";
3975 }
3976 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3977 c = cmd1[thing];
3978 readbuffer[rbi++] = c; // add movement to input buffer
3979 }
3980 if (strchr("iIaAsc", cm)) { // multi-char commands
3981 if (cm == 'c') {
3982 // change some thing
3983 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3984 c = cmd1[thing];
3985 readbuffer[rbi++] = c; // add movement to input buffer
3986 }
3987 thing = (int) lrand48() % 4; // what thing to insert
3988 cnt = (int) lrand48() % 10; // how many to insert
3989 for (i = 0; i < cnt; i++) {
3990 if (thing == 0) { // insert chars
3991 readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
3992 } else if (thing == 1) { // insert words
3993 strcat((char *) readbuffer, words[(int) lrand48() % 20]);
3994 strcat((char *) readbuffer, " ");
3995 sleeptime = 0; // how fast to type
3996 } else if (thing == 2) { // insert lines
3997 strcat((char *) readbuffer, lines[(int) lrand48() % 20]);
3998 sleeptime = 0; // how fast to type
3999 } else { // insert multi-lines
4000 strcat((char *) readbuffer, multilines[(int) lrand48() % 20]);
4001 sleeptime = 0; // how fast to type
4002 }
4003 }
4004 strcat((char *) readbuffer, "\033");
4005 }
4006 readed_for_parse = strlen(readbuffer);
4007 cd1:
4008 totalcmds++;
4009 if (sleeptime > 0)
4010 (void) mysleep(sleeptime); // sleep 1/100 sec
4011}
4012
4013// test to see if there are any errors
4014static void crash_test()
4015{
4016 static time_t oldtim;
4017 time_t tim;
Glenn L McGrath7127b582002-12-03 21:48:15 +00004018 char d[2], msg[BUFSIZ];
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004019
4020 msg[0] = '\0';
4021 if (end < text) {
4022 strcat((char *) msg, "end<text ");
4023 }
4024 if (end > textend) {
4025 strcat((char *) msg, "end>textend ");
4026 }
4027 if (dot < text) {
4028 strcat((char *) msg, "dot<text ");
4029 }
4030 if (dot > end) {
4031 strcat((char *) msg, "dot>end ");
4032 }
4033 if (screenbegin < text) {
4034 strcat((char *) msg, "screenbegin<text ");
4035 }
4036 if (screenbegin > end - 1) {
4037 strcat((char *) msg, "screenbegin>end-1 ");
4038 }
4039
4040 if (strlen(msg) > 0) {
4041 alarm(0);
Glenn L McGrath7127b582002-12-03 21:48:15 +00004042 printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004043 totalcmds, last_input_char, msg, SOs, SOn);
4044 fflush(stdout);
4045 while (read(0, d, 1) > 0) {
4046 if (d[0] == '\n' || d[0] == '\r')
4047 break;
4048 }
4049 alarm(3);
4050 }
4051 tim = (time_t) time((time_t *) 0);
4052 if (tim >= (oldtim + 3)) {
4053 sprintf((char *) status_buffer,
4054 "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
4055 totalcmds, M, N, I, D, Y, P, U, end - text + 1);
4056 oldtim = tim;
4057 }
4058 return;
4059}
4060#endif /* CONFIG_FEATURE_VI_CRASHME */