blob: 6689e290bd3be4fff8361273ec9531825c502348 [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
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001618 }
1619 } else {
1620 // insert a char into text[]
1621 Byte *sp; // "save p"
1622
1623 if (c == 13)
1624 c = '\n'; // translate \r to \n
1625 sp = p; // remember addr of insert
1626 p = stupid_insert(p, c); // insert the char
1627#ifdef CONFIG_FEATURE_VI_SETOPTS
1628 if (showmatch && strchr(")]}", *sp) != NULL) {
1629 showmatching(sp);
1630 }
1631 if (autoindent && c == '\n') { // auto indent the new line
1632 Byte *q;
1633
1634 q = prev_line(p); // use prev line as templet
1635 for (; isblnk(*q); q++) {
1636 p = stupid_insert(p, *q); // insert the char
1637 }
1638 }
1639#endif /* CONFIG_FEATURE_VI_SETOPTS */
1640 }
1641 return (p);
1642}
1643
1644static Byte *stupid_insert(Byte * p, Byte c) // stupidly insert the char c at 'p'
1645{
1646 p = text_hole_make(p, 1);
1647 if (p != 0) {
1648 *p = c;
Paul Fox8552aec2005-09-16 12:20:05 +00001649 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001650 p++;
1651 }
1652 return (p);
1653}
1654
1655static Byte find_range(Byte ** start, Byte ** stop, Byte c)
1656{
1657 Byte *save_dot, *p, *q;
1658 int cnt;
1659
1660 save_dot = dot;
1661 p = q = dot;
1662
1663 if (strchr("cdy><", c)) {
1664 // these cmds operate on whole lines
1665 p = q = begin_line(p);
1666 for (cnt = 1; cnt < cmdcnt; cnt++) {
1667 q = next_line(q);
1668 }
1669 q = end_line(q);
1670 } else if (strchr("^%$0bBeEft", c)) {
1671 // These cmds operate on char positions
1672 do_cmd(c); // execute movement cmd
1673 q = dot;
1674 } else if (strchr("wW", c)) {
1675 do_cmd(c); // execute movement cmd
Eric Andersenaeea32c2004-02-04 11:19:44 +00001676 // if we are at the next word's first char
1677 // step back one char
1678 // but check the possibilities when it is true
Eric Andersen5cc90ea2004-02-06 10:36:08 +00001679 if (dot > text && ((isspace(dot[-1]) && !isspace(dot[0]))
Eric Andersenaeea32c2004-02-04 11:19:44 +00001680 || (ispunct(dot[-1]) && !ispunct(dot[0]))
1681 || (isalnum(dot[-1]) && !isalnum(dot[0]))))
1682 dot--; // move back off of next word
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001683 if (dot > text && *dot == '\n')
1684 dot--; // stay off NL
1685 q = dot;
1686 } else if (strchr("H-k{", c)) {
1687 // these operate on multi-lines backwards
1688 q = end_line(dot); // find NL
1689 do_cmd(c); // execute movement cmd
1690 dot_begin();
1691 p = dot;
1692 } else if (strchr("L+j}\r\n", c)) {
1693 // these operate on multi-lines forwards
1694 p = begin_line(dot);
1695 do_cmd(c); // execute movement cmd
1696 dot_end(); // find NL
1697 q = dot;
1698 } else {
1699 c = 27; // error- return an ESC char
1700 //break;
1701 }
1702 *start = p;
1703 *stop = q;
1704 if (q < p) {
1705 *start = q;
1706 *stop = p;
1707 }
1708 dot = save_dot;
1709 return (c);
1710}
1711
1712static int st_test(Byte * p, int type, int dir, Byte * tested)
1713{
1714 Byte c, c0, ci;
1715 int test, inc;
1716
1717 inc = dir;
1718 c = c0 = p[0];
1719 ci = p[inc];
1720 test = 0;
1721
1722 if (type == S_BEFORE_WS) {
1723 c = ci;
1724 test = ((!isspace(c)) || c == '\n');
1725 }
1726 if (type == S_TO_WS) {
1727 c = c0;
1728 test = ((!isspace(c)) || c == '\n');
1729 }
1730 if (type == S_OVER_WS) {
1731 c = c0;
1732 test = ((isspace(c)));
1733 }
1734 if (type == S_END_PUNCT) {
1735 c = ci;
1736 test = ((ispunct(c)));
1737 }
1738 if (type == S_END_ALNUM) {
1739 c = ci;
1740 test = ((isalnum(c)) || c == '_');
1741 }
1742 *tested = c;
1743 return (test);
1744}
1745
1746static Byte *skip_thing(Byte * p, int linecnt, int dir, int type)
1747{
1748 Byte c;
1749
1750 while (st_test(p, type, dir, &c)) {
1751 // make sure we limit search to correct number of lines
1752 if (c == '\n' && --linecnt < 1)
1753 break;
1754 if (dir >= 0 && p >= end - 1)
1755 break;
1756 if (dir < 0 && p <= text)
1757 break;
1758 p += dir; // move to next char
1759 }
1760 return (p);
1761}
1762
1763// find matching char of pair () [] {}
1764static Byte *find_pair(Byte * p, Byte c)
1765{
1766 Byte match, *q;
1767 int dir, level;
1768
1769 match = ')';
1770 level = 1;
1771 dir = 1; // assume forward
1772 switch (c) {
1773 case '(':
1774 match = ')';
1775 break;
1776 case '[':
1777 match = ']';
1778 break;
1779 case '{':
1780 match = '}';
1781 break;
1782 case ')':
1783 match = '(';
1784 dir = -1;
1785 break;
1786 case ']':
1787 match = '[';
1788 dir = -1;
1789 break;
1790 case '}':
1791 match = '{';
1792 dir = -1;
1793 break;
1794 }
1795 for (q = p + dir; text <= q && q < end; q += dir) {
1796 // look for match, count levels of pairs (( ))
1797 if (*q == c)
1798 level++; // increase pair levels
1799 if (*q == match)
1800 level--; // reduce pair level
1801 if (level == 0)
1802 break; // found matching pair
1803 }
1804 if (level != 0)
1805 q = NULL; // indicate no match
1806 return (q);
1807}
1808
1809#ifdef CONFIG_FEATURE_VI_SETOPTS
1810// show the matching char of a pair, () [] {}
1811static void showmatching(Byte * p)
1812{
1813 Byte *q, *save_dot;
1814
1815 // we found half of a pair
1816 q = find_pair(p, *p); // get loc of matching char
1817 if (q == NULL) {
1818 indicate_error('3'); // no matching char
1819 } else {
1820 // "q" now points to matching pair
1821 save_dot = dot; // remember where we are
1822 dot = q; // go to new loc
1823 refresh(FALSE); // let the user see it
1824 (void) mysleep(40); // give user some time
1825 dot = save_dot; // go back to old loc
1826 refresh(FALSE);
1827 }
1828}
1829#endif /* CONFIG_FEATURE_VI_SETOPTS */
1830
1831// open a hole in text[]
1832static Byte *text_hole_make(Byte * p, int size) // at "p", make a 'size' byte hole
1833{
1834 Byte *src, *dest;
1835 int cnt;
1836
1837 if (size <= 0)
1838 goto thm0;
1839 src = p;
1840 dest = p + size;
1841 cnt = end - src; // the rest of buffer
1842 if (memmove(dest, src, cnt) != dest) {
1843 psbs("can't create room for new characters");
1844 }
1845 memset(p, ' ', size); // clear new hole
1846 end = end + size; // adjust the new END
Paul Fox8552aec2005-09-16 12:20:05 +00001847 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001848 thm0:
1849 return (p);
1850}
1851
1852// close a hole in text[]
1853static Byte *text_hole_delete(Byte * p, Byte * q) // delete "p" thru "q", inclusive
1854{
1855 Byte *src, *dest;
1856 int cnt, hole_size;
1857
1858 // move forwards, from beginning
1859 // assume p <= q
1860 src = q + 1;
1861 dest = p;
1862 if (q < p) { // they are backward- swap them
1863 src = p + 1;
1864 dest = q;
1865 }
1866 hole_size = q - p + 1;
1867 cnt = end - src;
1868 if (src < text || src > end)
1869 goto thd0;
1870 if (dest < text || dest >= end)
1871 goto thd0;
1872 if (src >= end)
1873 goto thd_atend; // just delete the end of the buffer
1874 if (memmove(dest, src, cnt) != dest) {
1875 psbs("can't delete the character");
1876 }
1877 thd_atend:
1878 end = end - hole_size; // adjust the new END
1879 if (dest >= end)
1880 dest = end - 1; // make sure dest in below end-1
1881 if (end <= text)
1882 dest = end = text; // keep pointers valid
Paul Fox8552aec2005-09-16 12:20:05 +00001883 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001884 thd0:
1885 return (dest);
1886}
1887
1888// copy text into register, then delete text.
1889// if dist <= 0, do not include, or go past, a NewLine
1890//
1891static Byte *yank_delete(Byte * start, Byte * stop, int dist, int yf)
1892{
1893 Byte *p;
1894
1895 // make sure start <= stop
1896 if (start > stop) {
1897 // they are backwards, reverse them
1898 p = start;
1899 start = stop;
1900 stop = p;
1901 }
1902 if (dist <= 0) {
1903 // we can not cross NL boundaries
1904 p = start;
1905 if (*p == '\n')
1906 return (p);
1907 // dont go past a NewLine
1908 for (; p + 1 <= stop; p++) {
1909 if (p[1] == '\n') {
1910 stop = p; // "stop" just before NewLine
1911 break;
1912 }
1913 }
1914 }
1915 p = start;
1916#ifdef CONFIG_FEATURE_VI_YANKMARK
1917 text_yank(start, stop, YDreg);
1918#endif /* CONFIG_FEATURE_VI_YANKMARK */
1919 if (yf == YANKDEL) {
1920 p = text_hole_delete(start, stop);
1921 } // delete lines
1922 return (p);
1923}
1924
1925static void show_help(void)
1926{
1927 puts("These features are available:"
1928#ifdef CONFIG_FEATURE_VI_SEARCH
1929 "\n\tPattern searches with / and ?"
1930#endif /* CONFIG_FEATURE_VI_SEARCH */
1931#ifdef CONFIG_FEATURE_VI_DOT_CMD
1932 "\n\tLast command repeat with \'.\'"
1933#endif /* CONFIG_FEATURE_VI_DOT_CMD */
1934#ifdef CONFIG_FEATURE_VI_YANKMARK
1935 "\n\tLine marking with 'x"
1936 "\n\tNamed buffers with \"x"
1937#endif /* CONFIG_FEATURE_VI_YANKMARK */
1938#ifdef CONFIG_FEATURE_VI_READONLY
1939 "\n\tReadonly if vi is called as \"view\""
1940 "\n\tReadonly with -R command line arg"
1941#endif /* CONFIG_FEATURE_VI_READONLY */
1942#ifdef CONFIG_FEATURE_VI_SET
1943 "\n\tSome colon mode commands with \':\'"
1944#endif /* CONFIG_FEATURE_VI_SET */
1945#ifdef CONFIG_FEATURE_VI_SETOPTS
1946 "\n\tSettable options with \":set\""
1947#endif /* CONFIG_FEATURE_VI_SETOPTS */
1948#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
1949 "\n\tSignal catching- ^C"
1950 "\n\tJob suspend and resume with ^Z"
1951#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
1952#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
1953 "\n\tAdapt to window re-sizes"
1954#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
1955 );
1956}
1957
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001958static inline void print_literal(Byte * buf, Byte * s) // copy s to buf, convert unprintable
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001959{
1960 Byte c, b[2];
1961
1962 b[1] = '\0';
1963 strcpy((char *) buf, ""); // init buf
1964 if (strlen((char *) s) <= 0)
1965 s = (Byte *) "(NULL)";
1966 for (; *s > '\0'; s++) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001967 int c_is_no_print;
1968
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001969 c = *s;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001970 c_is_no_print = c > 127 && !Isprint(c);
1971 if (c_is_no_print) {
1972 strcat((char *) buf, SOn);
1973 c = '.';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001974 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001975 if (c < ' ' || c == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001976 strcat((char *) buf, "^");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001977 if(c == 127)
1978 c = '?';
1979 else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001980 c += '@';
1981 }
1982 b[0] = c;
1983 strcat((char *) buf, (char *) b);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001984 if (c_is_no_print)
1985 strcat((char *) buf, SOs);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001986 if (*s == '\n') {
1987 strcat((char *) buf, "$");
1988 }
1989 }
1990}
1991
1992#ifdef CONFIG_FEATURE_VI_DOT_CMD
1993static void start_new_cmd_q(Byte c)
1994{
1995 // release old cmd
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001996 free(last_modifying_cmd);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001997 // get buffer for new cmd
1998 last_modifying_cmd = (Byte *) xmalloc(BUFSIZ);
1999 memset(last_modifying_cmd, '\0', BUFSIZ); // clear new cmd queue
2000 // if there is a current cmd count put it in the buffer first
2001 if (cmdcnt > 0)
Paul Foxd957b952005-11-28 18:07:53 +00002002 sprintf((char *) last_modifying_cmd, "%d%c", cmdcnt, c);
2003 else // just save char c onto queue
2004 last_modifying_cmd[0] = c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002005 adding2q = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002006}
2007
2008static void end_cmd_q(void)
2009{
2010#ifdef CONFIG_FEATURE_VI_YANKMARK
2011 YDreg = 26; // go back to default Yank/Delete reg
2012#endif /* CONFIG_FEATURE_VI_YANKMARK */
2013 adding2q = 0;
2014 return;
2015}
2016#endif /* CONFIG_FEATURE_VI_DOT_CMD */
2017
2018#if defined(CONFIG_FEATURE_VI_YANKMARK) || defined(CONFIG_FEATURE_VI_COLON) || defined(CONFIG_FEATURE_VI_CRASHME)
2019static Byte *string_insert(Byte * p, Byte * s) // insert the string at 'p'
2020{
2021 int cnt, i;
2022
2023 i = strlen((char *) s);
2024 p = text_hole_make(p, i);
2025 strncpy((char *) p, (char *) s, i);
2026 for (cnt = 0; *s != '\0'; s++) {
2027 if (*s == '\n')
2028 cnt++;
2029 }
2030#ifdef CONFIG_FEATURE_VI_YANKMARK
2031 psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
2032#endif /* CONFIG_FEATURE_VI_YANKMARK */
2033 return (p);
2034}
2035#endif /* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_COLON || CONFIG_FEATURE_VI_CRASHME */
2036
2037#ifdef CONFIG_FEATURE_VI_YANKMARK
2038static Byte *text_yank(Byte * p, Byte * q, int dest) // copy text into a register
2039{
2040 Byte *t;
2041 int cnt;
2042
2043 if (q < p) { // they are backwards- reverse them
2044 t = q;
2045 q = p;
2046 p = t;
2047 }
2048 cnt = q - p + 1;
2049 t = reg[dest];
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002050 free(t); // if already a yank register, free it
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002051 t = (Byte *) xmalloc(cnt + 1); // get a new register
2052 memset(t, '\0', cnt + 1); // clear new text[]
2053 strncpy((char *) t, (char *) p, cnt); // copy text[] into bufer
2054 reg[dest] = t;
2055 return (p);
2056}
2057
2058static Byte what_reg(void)
2059{
2060 Byte c;
2061 int i;
2062
2063 i = 0;
2064 c = 'D'; // default to D-reg
2065 if (0 <= YDreg && YDreg <= 25)
2066 c = 'a' + (Byte) YDreg;
2067 if (YDreg == 26)
2068 c = 'D';
2069 if (YDreg == 27)
2070 c = 'U';
2071 return (c);
2072}
2073
2074static void check_context(Byte cmd)
2075{
2076 // A context is defined to be "modifying text"
2077 // Any modifying command establishes a new context.
2078
2079 if (dot < context_start || dot > context_end) {
2080 if (strchr((char *) modifying_cmds, cmd) != NULL) {
2081 // we are trying to modify text[]- make this the current context
2082 mark[27] = mark[26]; // move cur to prev
2083 mark[26] = dot; // move local to cur
2084 context_start = prev_line(prev_line(dot));
2085 context_end = next_line(next_line(dot));
2086 //loiter= start_loiter= now;
2087 }
2088 }
2089 return;
2090}
2091
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002092static inline Byte *swap_context(Byte * p) // goto new context for '' command make this the current context
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002093{
2094 Byte *tmp;
2095
2096 // the current context is in mark[26]
2097 // the previous context is in mark[27]
2098 // only swap context if other context is valid
2099 if (text <= mark[27] && mark[27] <= end - 1) {
2100 tmp = mark[27];
2101 mark[27] = mark[26];
2102 mark[26] = tmp;
2103 p = mark[26]; // where we are going- previous context
2104 context_start = prev_line(prev_line(prev_line(p)));
2105 context_end = next_line(next_line(next_line(p)));
2106 }
2107 return (p);
2108}
2109#endif /* CONFIG_FEATURE_VI_YANKMARK */
2110
2111static int isblnk(Byte c) // is the char a blank or tab
2112{
2113 return (c == ' ' || c == '\t');
2114}
2115
2116//----- Set terminal attributes --------------------------------
2117static void rawmode(void)
2118{
2119 tcgetattr(0, &term_orig);
2120 term_vi = term_orig;
2121 term_vi.c_lflag &= (~ICANON & ~ECHO); // leave ISIG ON- allow intr's
2122 term_vi.c_iflag &= (~IXON & ~ICRNL);
2123 term_vi.c_oflag &= (~ONLCR);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002124 term_vi.c_cc[VMIN] = 1;
2125 term_vi.c_cc[VTIME] = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002126 erase_char = term_vi.c_cc[VERASE];
2127 tcsetattr(0, TCSANOW, &term_vi);
2128}
2129
2130static void cookmode(void)
2131{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002132 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002133 tcsetattr(0, TCSANOW, &term_orig);
2134}
2135
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002136//----- Come here when we get a window resize signal ---------
2137#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
2138static void winch_sig(int sig)
2139{
2140 signal(SIGWINCH, winch_sig);
2141#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
2142 window_size_get(0);
2143#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
2144 new_screen(rows, columns); // get memory for virtual screen
2145 redraw(TRUE); // re-draw the screen
2146}
2147
2148//----- Come here when we get a continue signal -------------------
2149static void cont_sig(int sig)
2150{
2151 rawmode(); // terminal to "raw"
Paul Fox8552aec2005-09-16 12:20:05 +00002152 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002153 redraw(TRUE); // re-draw the screen
2154
2155 signal(SIGTSTP, suspend_sig);
2156 signal(SIGCONT, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002157 kill(my_pid, SIGCONT);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002158}
2159
2160//----- Come here when we get a Suspend signal -------------------
2161static void suspend_sig(int sig)
2162{
2163 place_cursor(rows - 1, 0, FALSE); // go to bottom of screen
2164 clear_to_eol(); // Erase to end of line
2165 cookmode(); // terminal to "cooked"
2166
2167 signal(SIGCONT, cont_sig);
2168 signal(SIGTSTP, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002169 kill(my_pid, SIGTSTP);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002170}
2171
2172//----- Come here when we get a signal ---------------------------
2173static void catch_sig(int sig)
2174{
2175 signal(SIGHUP, catch_sig);
2176 signal(SIGINT, catch_sig);
2177 signal(SIGTERM, catch_sig);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002178 signal(SIGALRM, catch_sig);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002179 if(sig)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002180 longjmp(restart, sig);
2181}
2182
2183//----- Come here when we get a core dump signal -----------------
2184static void core_sig(int sig)
2185{
2186 signal(SIGQUIT, core_sig);
2187 signal(SIGILL, core_sig);
2188 signal(SIGTRAP, core_sig);
2189 signal(SIGIOT, core_sig);
2190 signal(SIGABRT, core_sig);
2191 signal(SIGFPE, core_sig);
2192 signal(SIGBUS, core_sig);
2193 signal(SIGSEGV, core_sig);
2194#ifdef SIGSYS
2195 signal(SIGSYS, core_sig);
2196#endif
2197
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002198 if(sig) { // signaled
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002199 dot = bound_dot(dot); // make sure "dot" is valid
2200
2201 longjmp(restart, sig);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002202 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002203}
2204#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
2205
2206static int mysleep(int hund) // sleep for 'h' 1/100 seconds
2207{
2208 // Don't hang- Wait 5/100 seconds- 1 Sec= 1000000
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002209 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002210 FD_ZERO(&rfds);
2211 FD_SET(0, &rfds);
2212 tv.tv_sec = 0;
2213 tv.tv_usec = hund * 10000;
2214 select(1, &rfds, NULL, NULL, &tv);
2215 return (FD_ISSET(0, &rfds));
2216}
2217
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002218#define readbuffer bb_common_bufsiz1
2219
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002220static int readed_for_parse;
2221
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002222//----- IO Routines --------------------------------------------
2223static Byte readit(void) // read (maybe cursor) key from stdin
2224{
2225 Byte c;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002226 int n;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002227 struct esc_cmds {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002228 const char *seq;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002229 Byte val;
2230 };
2231
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002232 static const struct esc_cmds esccmds[] = {
2233 {"OA", (Byte) VI_K_UP}, // cursor key Up
2234 {"OB", (Byte) VI_K_DOWN}, // cursor key Down
2235 {"OC", (Byte) VI_K_RIGHT}, // Cursor Key Right
2236 {"OD", (Byte) VI_K_LEFT}, // cursor key Left
2237 {"OH", (Byte) VI_K_HOME}, // Cursor Key Home
2238 {"OF", (Byte) VI_K_END}, // Cursor Key End
2239 {"[A", (Byte) VI_K_UP}, // cursor key Up
2240 {"[B", (Byte) VI_K_DOWN}, // cursor key Down
2241 {"[C", (Byte) VI_K_RIGHT}, // Cursor Key Right
2242 {"[D", (Byte) VI_K_LEFT}, // cursor key Left
2243 {"[H", (Byte) VI_K_HOME}, // Cursor Key Home
2244 {"[F", (Byte) VI_K_END}, // Cursor Key End
2245 {"[1~", (Byte) VI_K_HOME}, // Cursor Key Home
2246 {"[2~", (Byte) VI_K_INSERT}, // Cursor Key Insert
2247 {"[4~", (Byte) VI_K_END}, // Cursor Key End
2248 {"[5~", (Byte) VI_K_PAGEUP}, // Cursor Key Page Up
2249 {"[6~", (Byte) VI_K_PAGEDOWN}, // Cursor Key Page Down
2250 {"OP", (Byte) VI_K_FUN1}, // Function Key F1
2251 {"OQ", (Byte) VI_K_FUN2}, // Function Key F2
2252 {"OR", (Byte) VI_K_FUN3}, // Function Key F3
2253 {"OS", (Byte) VI_K_FUN4}, // Function Key F4
2254 {"[15~", (Byte) VI_K_FUN5}, // Function Key F5
2255 {"[17~", (Byte) VI_K_FUN6}, // Function Key F6
2256 {"[18~", (Byte) VI_K_FUN7}, // Function Key F7
2257 {"[19~", (Byte) VI_K_FUN8}, // Function Key F8
2258 {"[20~", (Byte) VI_K_FUN9}, // Function Key F9
2259 {"[21~", (Byte) VI_K_FUN10}, // Function Key F10
2260 {"[23~", (Byte) VI_K_FUN11}, // Function Key F11
2261 {"[24~", (Byte) VI_K_FUN12}, // Function Key F12
2262 {"[11~", (Byte) VI_K_FUN1}, // Function Key F1
2263 {"[12~", (Byte) VI_K_FUN2}, // Function Key F2
2264 {"[13~", (Byte) VI_K_FUN3}, // Function Key F3
2265 {"[14~", (Byte) VI_K_FUN4}, // Function Key F4
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002266 };
2267
2268#define ESCCMDS_COUNT (sizeof(esccmds)/sizeof(struct esc_cmds))
2269
2270 (void) alarm(0); // turn alarm OFF while we wait for input
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002271 fflush(stdout);
2272 n = readed_for_parse;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002273 // get input from User- are there already input chars in Q?
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002274 if (n <= 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002275 ri0:
2276 // the Q is empty, wait for a typed char
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002277 n = read(0, readbuffer, BUFSIZ - 1);
2278 if (n < 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002279 if (errno == EINTR)
2280 goto ri0; // interrupted sys call
2281 if (errno == EBADF)
2282 editing = 0;
2283 if (errno == EFAULT)
2284 editing = 0;
2285 if (errno == EINVAL)
2286 editing = 0;
2287 if (errno == EIO)
2288 editing = 0;
2289 errno = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002290 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002291 if(n <= 0)
2292 return 0; // error
2293 if (readbuffer[0] == 27) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002294 // This is an ESC char. Is this Esc sequence?
2295 // Could be bare Esc key. See if there are any
2296 // more chars to read after the ESC. This would
2297 // be a Function or Cursor Key sequence.
2298 FD_ZERO(&rfds);
2299 FD_SET(0, &rfds);
2300 tv.tv_sec = 0;
2301 tv.tv_usec = 50000; // Wait 5/100 seconds- 1 Sec=1000000
2302
2303 // keep reading while there are input chars and room in buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002304 while (select(1, &rfds, NULL, NULL, &tv) > 0 && n <= (BUFSIZ - 5)) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002305 // read the rest of the ESC string
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002306 int r = read(0, (void *) (readbuffer + n), BUFSIZ - n);
2307 if (r > 0) {
2308 n += r;
2309 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002310 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002311 }
2312 readed_for_parse = n;
2313 }
2314 c = readbuffer[0];
2315 if(c == 27 && n > 1) {
2316 // Maybe cursor or function key?
2317 const struct esc_cmds *eindex;
2318
2319 for (eindex = esccmds; eindex < &esccmds[ESCCMDS_COUNT]; eindex++) {
2320 int cnt = strlen(eindex->seq);
2321
2322 if(n <= cnt)
2323 continue;
2324 if(strncmp(eindex->seq, (char *) readbuffer + 1, cnt))
2325 continue;
2326 // is a Cursor key- put derived value back into Q
2327 c = eindex->val;
2328 // for squeeze out the ESC sequence
2329 n = cnt + 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002330 break;
2331 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002332 if(eindex == &esccmds[ESCCMDS_COUNT]) {
2333 /* defined ESC sequence not found, set only one ESC */
2334 n = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002335 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002336 } else {
2337 n = 1;
2338 }
2339 // remove key sequence from Q
2340 readed_for_parse -= n;
2341 memmove(readbuffer, readbuffer + n, BUFSIZ - n);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002342 (void) alarm(3); // we are done waiting for input, turn alarm ON
2343 return (c);
2344}
2345
2346//----- IO Routines --------------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002347static Byte get_one_char(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002348{
2349 static Byte c;
2350
2351#ifdef CONFIG_FEATURE_VI_DOT_CMD
2352 // ! adding2q && ioq == 0 read()
2353 // ! adding2q && ioq != 0 *ioq
2354 // adding2q *last_modifying_cmd= read()
2355 if (!adding2q) {
2356 // we are not adding to the q.
2357 // but, we may be reading from a q
2358 if (ioq == 0) {
2359 // there is no current q, read from STDIN
2360 c = readit(); // get the users input
2361 } else {
2362 // there is a queue to get chars from first
2363 c = *ioq++;
2364 if (c == '\0') {
2365 // the end of the q, read from STDIN
2366 free(ioq_start);
2367 ioq_start = ioq = 0;
2368 c = readit(); // get the users input
2369 }
2370 }
2371 } else {
2372 // adding STDIN chars to q
2373 c = readit(); // get the users input
2374 if (last_modifying_cmd != 0) {
Eric Andersenfda2b7f2002-10-26 10:19:19 +00002375 int len = strlen((char *) last_modifying_cmd);
2376 if (len + 1 >= BUFSIZ) {
2377 psbs("last_modifying_cmd overrun");
2378 } else {
2379 // add new char to q
2380 last_modifying_cmd[len] = c;
2381 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002382 }
2383 }
2384#else /* CONFIG_FEATURE_VI_DOT_CMD */
2385 c = readit(); // get the users input
2386#endif /* CONFIG_FEATURE_VI_DOT_CMD */
2387 return (c); // return the char, where ever it came from
2388}
2389
2390static Byte *get_input_line(Byte * prompt) // get input line- use "status line"
2391{
2392 Byte buf[BUFSIZ];
2393 Byte c;
2394 int i;
2395 static Byte *obufp = NULL;
2396
2397 strcpy((char *) buf, (char *) prompt);
Paul Fox8552aec2005-09-16 12:20:05 +00002398 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002399 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
2400 clear_to_eol(); // clear the line
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002401 write1(prompt); // write out the :, /, or ? prompt
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002402
2403 for (i = strlen((char *) buf); i < BUFSIZ;) {
2404 c = get_one_char(); // read user input
2405 if (c == '\n' || c == '\r' || c == 27)
2406 break; // is this end of input
Paul Foxf2de0b72005-09-13 22:20:37 +00002407 if (c == erase_char || c == 8 || c == 127) {
2408 // user wants to erase prev char
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002409 i--; // backup to prev char
2410 buf[i] = '\0'; // erase the char
2411 buf[i + 1] = '\0'; // null terminate buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002412 write1("\b \b"); // erase char on screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002413 if (i <= 0) { // user backs up before b-o-l, exit
2414 break;
2415 }
2416 } else {
2417 buf[i] = c; // save char in buffer
2418 buf[i + 1] = '\0'; // make sure buffer is null terminated
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002419 putchar(c); // echo the char back to user
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002420 i++;
2421 }
2422 }
2423 refresh(FALSE);
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002424 free(obufp);
Manuel Novoa III cad53642003-03-19 09:13:01 +00002425 obufp = (Byte *) bb_xstrdup((char *) buf);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002426 return (obufp);
2427}
2428
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002429static int file_size(const Byte * fn) // what is the byte size of "fn"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002430{
2431 struct stat st_buf;
2432 int cnt, sr;
2433
2434 if (fn == 0 || strlen(fn) <= 0)
2435 return (-1);
2436 cnt = -1;
2437 sr = stat((char *) fn, &st_buf); // see if file exists
2438 if (sr >= 0) {
2439 cnt = (int) st_buf.st_size;
2440 }
2441 return (cnt);
2442}
2443
2444static int file_insert(Byte * fn, Byte * p, int size)
2445{
2446 int fd, cnt;
2447
2448 cnt = -1;
2449#ifdef CONFIG_FEATURE_VI_READONLY
2450 readonly = FALSE;
2451#endif /* CONFIG_FEATURE_VI_READONLY */
2452 if (fn == 0 || strlen((char*) fn) <= 0) {
2453 psbs("No filename given");
2454 goto fi0;
2455 }
2456 if (size == 0) {
2457 // OK- this is just a no-op
2458 cnt = 0;
2459 goto fi0;
2460 }
2461 if (size < 0) {
2462 psbs("Trying to insert a negative number (%d) of characters", size);
2463 goto fi0;
2464 }
2465 if (p < text || p > end) {
2466 psbs("Trying to insert file outside of memory");
2467 goto fi0;
2468 }
2469
2470 // see if we can open the file
2471#ifdef CONFIG_FEATURE_VI_READONLY
2472 if (vi_readonly) goto fi1; // do not try write-mode
2473#endif
2474 fd = open((char *) fn, O_RDWR); // assume read & write
2475 if (fd < 0) {
2476 // could not open for writing- maybe file is read only
2477#ifdef CONFIG_FEATURE_VI_READONLY
2478 fi1:
2479#endif
2480 fd = open((char *) fn, O_RDONLY); // try read-only
2481 if (fd < 0) {
2482 psbs("\"%s\" %s", fn, "could not open file");
2483 goto fi0;
2484 }
2485#ifdef CONFIG_FEATURE_VI_READONLY
2486 // got the file- read-only
2487 readonly = TRUE;
2488#endif /* CONFIG_FEATURE_VI_READONLY */
2489 }
2490 p = text_hole_make(p, size);
2491 cnt = read(fd, p, size);
2492 close(fd);
2493 if (cnt < 0) {
2494 cnt = -1;
2495 p = text_hole_delete(p, p + size - 1); // un-do buffer insert
2496 psbs("could not read file \"%s\"", fn);
2497 } else if (cnt < size) {
2498 // There was a partial read, shrink unused space text[]
2499 p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert
2500 psbs("could not read all of file \"%s\"", fn);
2501 }
2502 if (cnt >= size)
Paul Fox8552aec2005-09-16 12:20:05 +00002503 file_modified++;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002504 fi0:
2505 return (cnt);
2506}
2507
2508static int file_write(Byte * fn, Byte * first, Byte * last)
2509{
2510 int fd, cnt, charcnt;
2511
2512 if (fn == 0) {
2513 psbs("No current filename");
Paul Fox61e45db2005-10-09 14:43:22 +00002514 return (-2);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002515 }
2516 charcnt = 0;
2517 // FIXIT- use the correct umask()
2518 fd = open((char *) fn, (O_WRONLY | O_CREAT | O_TRUNC), 0664);
2519 if (fd < 0)
2520 return (-1);
2521 cnt = last - first + 1;
2522 charcnt = write(fd, first, cnt);
2523 if (charcnt == cnt) {
2524 // good write
2525 //file_modified= FALSE; // the file has not been modified
2526 } else {
2527 charcnt = 0;
2528 }
2529 close(fd);
2530 return (charcnt);
2531}
2532
2533//----- Terminal Drawing ---------------------------------------
2534// The terminal is made up of 'rows' line of 'columns' columns.
Eric Andersenaff114c2004-04-14 17:51:38 +00002535// classically this would be 24 x 80.
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002536// screen coordinates
2537// 0,0 ... 0,79
2538// 1,0 ... 1,79
2539// . ... .
2540// . ... .
2541// 22,0 ... 22,79
2542// 23,0 ... 23,79 status line
2543//
2544
2545//----- Move the cursor to row x col (count from 0, not 1) -------
2546static void place_cursor(int row, int col, int opti)
2547{
2548 char cm1[BUFSIZ];
2549 char *cm;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002550#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2551 char cm2[BUFSIZ];
2552 Byte *screenp;
2553 // char cm3[BUFSIZ];
2554 int Rrow= last_row;
2555#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002556
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002557 memset(cm1, '\0', BUFSIZ - 1); // clear the buffer
2558
2559 if (row < 0) row = 0;
2560 if (row >= rows) row = rows - 1;
2561 if (col < 0) col = 0;
2562 if (col >= columns) col = columns - 1;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002563
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002564 //----- 1. Try the standard terminal ESC sequence
2565 sprintf((char *) cm1, CMrc, row + 1, col + 1);
2566 cm= cm1;
2567 if (! opti) goto pc0;
2568
2569#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2570 //----- find the minimum # of chars to move cursor -------------
2571 //----- 2. Try moving with discreet chars (Newline, [back]space, ...)
2572 memset(cm2, '\0', BUFSIZ - 1); // clear the buffer
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002573
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002574 // move to the correct row
2575 while (row < Rrow) {
2576 // the cursor has to move up
2577 strcat(cm2, CMup);
2578 Rrow--;
2579 }
2580 while (row > Rrow) {
2581 // the cursor has to move down
2582 strcat(cm2, CMdown);
2583 Rrow++;
2584 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002585
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002586 // now move to the correct column
2587 strcat(cm2, "\r"); // start at col 0
2588 // just send out orignal source char to get to correct place
2589 screenp = &screen[row * columns]; // start of screen line
2590 strncat(cm2, screenp, col);
2591
2592 //----- 3. Try some other way of moving cursor
2593 //---------------------------------------------
2594
2595 // pick the shortest cursor motion to send out
2596 cm= cm1;
2597 if (strlen(cm2) < strlen(cm)) {
2598 cm= cm2;
2599 } /* else if (strlen(cm3) < strlen(cm)) {
2600 cm= cm3;
2601 } */
2602#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2603 pc0:
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002604 write1(cm); // move the cursor
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002605}
2606
2607//----- Erase from cursor to end of line -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002608static void clear_to_eol(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002609{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002610 write1(Ceol); // Erase from cursor to end of line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002611}
2612
2613//----- Erase from cursor to end of screen -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002614static void clear_to_eos(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002615{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002616 write1(Ceos); // Erase from cursor to end of screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002617}
2618
2619//----- Start standout mode ------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002620static void standout_start(void) // send "start reverse video" sequence
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002621{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002622 write1(SOs); // Start reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002623}
2624
2625//----- End standout mode --------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002626static void standout_end(void) // send "end reverse video" sequence
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002627{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002628 write1(SOn); // End reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002629}
2630
2631//----- Flash the screen --------------------------------------
2632static void flash(int h)
2633{
2634 standout_start(); // send "start reverse video" sequence
2635 redraw(TRUE);
2636 (void) mysleep(h);
2637 standout_end(); // send "end reverse video" sequence
2638 redraw(TRUE);
2639}
2640
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002641static void Indicate_Error(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002642{
2643#ifdef CONFIG_FEATURE_VI_CRASHME
2644 if (crashme > 0)
2645 return; // generate a random command
2646#endif /* CONFIG_FEATURE_VI_CRASHME */
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002647 if (!err_method) {
2648 write1(bell); // send out a bell character
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002649 } else {
2650 flash(10);
2651 }
2652}
2653
2654//----- Screen[] Routines --------------------------------------
2655//----- Erase the Screen[] memory ------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002656static void screen_erase(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002657{
2658 memset(screen, ' ', screensize); // clear new screen
2659}
2660
Paul Fox8552aec2005-09-16 12:20:05 +00002661static int bufsum(char *buf, int count)
2662{
2663 int sum = 0;
2664 char *e = buf + count;
2665 while (buf < e)
2666 sum += *buf++;
2667 return sum;
2668}
2669
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002670//----- Draw the status line at bottom of the screen -------------
2671static void show_status_line(void)
2672{
Paul Foxc3504852005-09-16 12:48:18 +00002673 int cnt = 0, cksum = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002674
Paul Fox8552aec2005-09-16 12:20:05 +00002675 // either we already have an error or status message, or we
2676 // create one.
2677 if (!have_status_msg) {
2678 cnt = format_edit_status();
2679 cksum = bufsum(status_buffer, cnt);
2680 }
2681 if (have_status_msg || ((cnt > 0 && last_status_cksum != cksum))) {
2682 last_status_cksum= cksum; // remember if we have seen this line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002683 place_cursor(rows - 1, 0, FALSE); // put cursor on status line
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002684 write1(status_buffer);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002685 clear_to_eol();
Paul Fox8552aec2005-09-16 12:20:05 +00002686 if (have_status_msg) {
2687 if ((strlen(status_buffer) - (have_status_msg - 1)) >
2688 (columns - 1) ) {
2689 have_status_msg = 0;
2690 Hit_Return();
2691 }
2692 have_status_msg = 0;
2693 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002694 place_cursor(crow, ccol, FALSE); // put cursor back in correct place
2695 }
Eric Andersena9eb33d2004-08-19 19:15:06 +00002696 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002697}
2698
2699//----- format the status buffer, the bottom line of screen ------
Paul Fox8552aec2005-09-16 12:20:05 +00002700// format status buffer, with STANDOUT mode
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002701static void psbs(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002702{
2703 va_list args;
2704
2705 va_start(args, format);
2706 strcpy((char *) status_buffer, SOs); // Terminal standout mode on
Eric Andersen0ef24c62005-07-18 10:32:59 +00002707 vsprintf((char *) status_buffer + strlen((char *) status_buffer), format, args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002708 strcat((char *) status_buffer, SOn); // Terminal standout mode off
2709 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00002710
2711 have_status_msg = 1 + sizeof(SOs) + sizeof(SOn) - 2;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002712
2713 return;
2714}
2715
Paul Fox8552aec2005-09-16 12:20:05 +00002716// format status buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002717static void psb(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002718{
2719 va_list args;
2720
2721 va_start(args, format);
2722 vsprintf((char *) status_buffer, format, args);
2723 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00002724
2725 have_status_msg = 1;
2726
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002727 return;
2728}
2729
2730static void ni(Byte * s) // display messages
2731{
2732 Byte buf[BUFSIZ];
2733
2734 print_literal(buf, s);
2735 psbs("\'%s\' is not implemented", buf);
2736}
2737
Paul Fox8552aec2005-09-16 12:20:05 +00002738static int format_edit_status(void) // show file status on status line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002739{
Paul Fox8552aec2005-09-16 12:20:05 +00002740 int cur, percent, ret, trunc_at;
2741 static int tot;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002742
Paul Fox8552aec2005-09-16 12:20:05 +00002743 // file_modified is now a counter rather than a flag. this
2744 // helps reduce the amount of line counting we need to do.
2745 // (this will cause a mis-reporting of modified status
2746 // once every MAXINT editing operations.)
2747
2748 // it would be nice to do a similar optimization here -- if
2749 // we haven't done a motion that could have changed which line
2750 // we're on, then we shouldn't have to do this count_lines()
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002751 cur = count_lines(text, dot);
Paul Fox8552aec2005-09-16 12:20:05 +00002752
2753 // reduce counting -- the total lines can't have
2754 // changed if we haven't done any edits.
2755 if (file_modified != last_file_modified) {
2756 tot = cur + count_lines(dot, end - 1) - 1;
2757 last_file_modified = file_modified;
2758 }
2759
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002760 // current line percent
2761 // ------------- ~~ ----------
2762 // total lines 100
2763 if (tot > 0) {
2764 percent = (100 * cur) / tot;
2765 } else {
2766 cur = tot = 0;
2767 percent = 100;
2768 }
Eric Andersen0ef24c62005-07-18 10:32:59 +00002769
Paul Fox8552aec2005-09-16 12:20:05 +00002770 trunc_at = columns < STATUS_BUFFER_LEN-1 ?
2771 columns : STATUS_BUFFER_LEN-1;
2772
2773 ret = snprintf((char *) status_buffer, trunc_at+1,
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002774#ifdef CONFIG_FEATURE_VI_READONLY
Paul Fox8552aec2005-09-16 12:20:05 +00002775 "%c %s%s%s %d/%d %d%%",
2776#else
2777 "%c %s%s %d/%d %d%%",
2778#endif
2779 (cmd_mode ? (cmd_mode == 2 ? 'R':'I'):'-'),
2780 (cfn != 0 ? (char *) cfn : "No file"),
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002781#ifdef CONFIG_FEATURE_VI_READONLY
Paul Fox8552aec2005-09-16 12:20:05 +00002782 ((vi_readonly || readonly) ? " [Read-only]" : ""),
2783#endif
2784 (file_modified ? " [modified]" : ""),
2785 cur, tot, percent);
2786
2787 if (ret >= 0 && ret < trunc_at)
2788 return ret; /* it all fit */
2789
2790 return trunc_at; /* had to truncate */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002791}
2792
2793//----- Force refresh of all Lines -----------------------------
2794static void redraw(int full_screen)
2795{
2796 place_cursor(0, 0, FALSE); // put cursor in correct place
2797 clear_to_eos(); // tel terminal to erase display
2798 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00002799 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002800 refresh(full_screen); // this will redraw the entire display
Paul Fox8552aec2005-09-16 12:20:05 +00002801 show_status_line();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002802}
2803
2804//----- Format a text[] line into a buffer ---------------------
2805static void format_line(Byte *dest, Byte *src, int li)
2806{
2807 int co;
2808 Byte c;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002809
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002810 for (co= 0; co < MAX_SCR_COLS; co++) {
2811 c= ' '; // assume blank
2812 if (li > 0 && co == 0) {
2813 c = '~'; // not first line, assume Tilde
2814 }
2815 // are there chars in text[] and have we gone past the end
2816 if (text < end && src < end) {
2817 c = *src++;
2818 }
2819 if (c == '\n')
2820 break;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002821 if (c > 127 && !Isprint(c)) {
2822 c = '.';
2823 }
2824 if (c < ' ' || c == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002825 if (c == '\t') {
2826 c = ' ';
2827 // co % 8 != 7
2828 for (; (co % tabstop) != (tabstop - 1); co++) {
2829 dest[co] = c;
2830 }
2831 } else {
2832 dest[co++] = '^';
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002833 if(c == 127)
2834 c = '?';
2835 else
2836 c += '@'; // make it visible
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002837 }
2838 }
2839 // the co++ is done here so that the column will
2840 // not be overwritten when we blank-out the rest of line
2841 dest[co] = c;
2842 if (src >= end)
2843 break;
2844 }
2845}
2846
2847//----- Refresh the changed screen lines -----------------------
2848// Copy the source line from text[] into the buffer and note
2849// if the current screenline is different from the new buffer.
2850// If they differ then that line needs redrawing on the terminal.
2851//
2852static void refresh(int full_screen)
2853{
2854 static int old_offset;
2855 int li, changed;
2856 Byte buf[MAX_SCR_COLS];
2857 Byte *tp, *sp; // pointer into text[] and screen[]
2858#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2859 int last_li= -2; // last line that changed- for optimizing cursor movement
2860#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2861
2862#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
2863 window_size_get(0);
2864#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
2865 sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
2866 tp = screenbegin; // index into text[] of top line
2867
2868 // compare text[] to screen[] and mark screen[] lines that need updating
2869 for (li = 0; li < rows - 1; li++) {
2870 int cs, ce; // column start & end
2871 memset(buf, ' ', MAX_SCR_COLS); // blank-out the buffer
2872 buf[MAX_SCR_COLS-1] = 0; // NULL terminate the buffer
2873 // format current text line into buf
2874 format_line(buf, tp, li);
2875
2876 // skip to the end of the current text[] line
2877 while (tp < end && *tp++ != '\n') /*no-op*/ ;
2878
2879 // see if there are any changes between vitual screen and buf
2880 changed = FALSE; // assume no change
2881 cs= 0;
2882 ce= columns-1;
2883 sp = &screen[li * columns]; // start of screen line
2884 if (full_screen) {
2885 // force re-draw of every single column from 0 - columns-1
2886 goto re0;
2887 }
2888 // compare newly formatted buffer with virtual screen
2889 // look forward for first difference between buf and screen
2890 for ( ; cs <= ce; cs++) {
2891 if (buf[cs + offset] != sp[cs]) {
2892 changed = TRUE; // mark for redraw
2893 break;
2894 }
2895 }
2896
2897 // look backward for last difference between buf and screen
2898 for ( ; ce >= cs; ce--) {
2899 if (buf[ce + offset] != sp[ce]) {
2900 changed = TRUE; // mark for redraw
2901 break;
2902 }
2903 }
2904 // now, cs is index of first diff, and ce is index of last diff
2905
2906 // if horz offset has changed, force a redraw
2907 if (offset != old_offset) {
2908 re0:
2909 changed = TRUE;
2910 }
2911
2912 // make a sanity check of columns indexes
2913 if (cs < 0) cs= 0;
2914 if (ce > columns-1) ce= columns-1;
2915 if (cs > ce) { cs= 0; ce= columns-1; }
2916 // is there a change between vitual screen and buf
2917 if (changed) {
2918 // copy changed part of buffer to virtual screen
2919 memmove(sp+cs, buf+(cs+offset), ce-cs+1);
2920
2921 // move cursor to column of first change
2922 if (offset != old_offset) {
2923 // opti_cur_move is still too stupid
2924 // to handle offsets correctly
2925 place_cursor(li, cs, FALSE);
2926 } else {
2927#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2928 // if this just the next line
2929 // try to optimize cursor movement
2930 // otherwise, use standard ESC sequence
2931 place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE);
2932 last_li= li;
2933#else /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2934 place_cursor(li, cs, FALSE); // use standard ESC sequence
2935#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2936 }
2937
2938 // write line out to terminal
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002939 {
2940 int nic = ce-cs+1;
2941 char *out = sp+cs;
2942
2943 while(nic-- > 0) {
2944 putchar(*out);
2945 out++;
2946 }
2947 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002948#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2949 last_row = li;
2950#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2951 }
2952 }
2953
2954#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2955 place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE);
2956 last_row = crow;
2957#else
2958 place_cursor(crow, ccol, FALSE);
2959#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002960
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002961 if (offset != old_offset)
2962 old_offset = offset;
2963}
2964
Eric Andersen3f980402001-04-04 17:31:15 +00002965//---------------------------------------------------------------------
2966//----- the Ascii Chart -----------------------------------------------
2967//
2968// 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel
2969// 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si
2970// 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb
2971// 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us
2972// 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 '
2973// 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f /
2974// 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7
2975// 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ?
2976// 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G
2977// 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O
2978// 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W
2979// 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _
2980// 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g
2981// 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o
2982// 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w
2983// 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del
2984//---------------------------------------------------------------------
2985
2986//----- Execute a Vi Command -----------------------------------
2987static void do_cmd(Byte c)
2988{
2989 Byte c1, *p, *q, *msg, buf[9], *save_dot;
2990 int cnt, i, j, dir, yf;
2991
2992 c1 = c; // quiet the compiler
2993 cnt = yf = dir = 0; // quiet the compiler
2994 p = q = save_dot = msg = buf; // quiet the compiler
2995 memset(buf, '\0', 9); // clear buf
Eric Andersenbff7a602001-11-17 07:15:43 +00002996
Paul Fox8552aec2005-09-16 12:20:05 +00002997 show_status_line();
2998
Eric Andersenbff7a602001-11-17 07:15:43 +00002999 /* if this is a cursor key, skip these checks */
3000 switch (c) {
3001 case VI_K_UP:
3002 case VI_K_DOWN:
3003 case VI_K_LEFT:
3004 case VI_K_RIGHT:
3005 case VI_K_HOME:
3006 case VI_K_END:
3007 case VI_K_PAGEUP:
3008 case VI_K_PAGEDOWN:
3009 goto key_cmd_mode;
3010 }
3011
Eric Andersen3f980402001-04-04 17:31:15 +00003012 if (cmd_mode == 2) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003013 // flip-flop Insert/Replace mode
3014 if (c == VI_K_INSERT) goto dc_i;
Eric Andersen3f980402001-04-04 17:31:15 +00003015 // we are 'R'eplacing the current *dot with new char
3016 if (*dot == '\n') {
3017 // don't Replace past E-o-l
3018 cmd_mode = 1; // convert to insert
3019 } else {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003020 if (1 <= c || Isprint(c)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003021 if (c != 27)
3022 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3023 dot = char_insert(dot, c); // insert new char
3024 }
3025 goto dc1;
3026 }
3027 }
3028 if (cmd_mode == 1) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00003029 // hitting "Insert" twice means "R" replace mode
3030 if (c == VI_K_INSERT) goto dc5;
Eric Andersen3f980402001-04-04 17:31:15 +00003031 // insert the char c at "dot"
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003032 if (1 <= c || Isprint(c)) {
3033 dot = char_insert(dot, c);
Eric Andersen3f980402001-04-04 17:31:15 +00003034 }
3035 goto dc1;
3036 }
3037
Eric Andersenbff7a602001-11-17 07:15:43 +00003038key_cmd_mode:
Eric Andersen3f980402001-04-04 17:31:15 +00003039 switch (c) {
Eric Andersen822c3832001-05-07 17:37:43 +00003040 //case 0x01: // soh
3041 //case 0x09: // ht
3042 //case 0x0b: // vt
3043 //case 0x0e: // so
3044 //case 0x0f: // si
3045 //case 0x10: // dle
3046 //case 0x11: // dc1
3047 //case 0x13: // dc3
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003048#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen1c0d3112001-04-16 15:46:44 +00003049 case 0x14: // dc4 ctrl-T
Eric Andersen3f980402001-04-04 17:31:15 +00003050 crashme = (crashme == 0) ? 1 : 0;
Eric Andersen3f980402001-04-04 17:31:15 +00003051 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003052#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen822c3832001-05-07 17:37:43 +00003053 //case 0x16: // syn
3054 //case 0x17: // etb
3055 //case 0x18: // can
3056 //case 0x1c: // fs
3057 //case 0x1d: // gs
3058 //case 0x1e: // rs
3059 //case 0x1f: // us
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003060 //case '!': // !-
3061 //case '#': // #-
3062 //case '&': // &-
3063 //case '(': // (-
3064 //case ')': // )-
3065 //case '*': // *-
3066 //case ',': // ,-
3067 //case '=': // =-
3068 //case '@': // @-
3069 //case 'F': // F-
3070 //case 'K': // K-
3071 //case 'Q': // Q-
3072 //case 'S': // S-
3073 //case 'T': // T-
3074 //case 'V': // V-
3075 //case '[': // [-
3076 //case '\\': // \-
3077 //case ']': // ]-
3078 //case '_': // _-
3079 //case '`': // `-
3080 //case 'g': // g-
Eric Andersen1c0d3112001-04-16 15:46:44 +00003081 //case 'u': // u- FIXME- there is no undo
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003082 //case 'v': // v-
Eric Andersen3f980402001-04-04 17:31:15 +00003083 default: // unrecognised command
3084 buf[0] = c;
3085 buf[1] = '\0';
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003086 if (c < ' ') {
Eric Andersen3f980402001-04-04 17:31:15 +00003087 buf[0] = '^';
3088 buf[1] = c + '@';
3089 buf[2] = '\0';
3090 }
3091 ni((Byte *) buf);
3092 end_cmd_q(); // stop adding to q
3093 case 0x00: // nul- ignore
3094 break;
3095 case 2: // ctrl-B scroll up full screen
3096 case VI_K_PAGEUP: // Cursor Key Page Up
3097 dot_scroll(rows - 2, -1);
3098 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003099#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +00003100 case 0x03: // ctrl-C interrupt
3101 longjmp(restart, 1);
3102 break;
3103 case 26: // ctrl-Z suspend
3104 suspend_sig(SIGTSTP);
3105 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003106#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Eric Andersen3f980402001-04-04 17:31:15 +00003107 case 4: // ctrl-D scroll down half screen
3108 dot_scroll((rows - 2) / 2, 1);
3109 break;
3110 case 5: // ctrl-E scroll down one line
3111 dot_scroll(1, 1);
3112 break;
3113 case 6: // ctrl-F scroll down full screen
3114 case VI_K_PAGEDOWN: // Cursor Key Page Down
3115 dot_scroll(rows - 2, 1);
3116 break;
3117 case 7: // ctrl-G show current status
Paul Fox8552aec2005-09-16 12:20:05 +00003118 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003119 break;
3120 case 'h': // h- move left
3121 case VI_K_LEFT: // cursor key Left
Paul Foxd13b90b2005-07-18 22:17:25 +00003122 case 8: // ctrl-H- move left (This may be ERASE char)
3123 case 127: // DEL- move left (This may be ERASE char)
Eric Andersen3f980402001-04-04 17:31:15 +00003124 if (cmdcnt-- > 1) {
3125 do_cmd(c);
3126 } // repeat cnt
3127 dot_left();
3128 break;
3129 case 10: // Newline ^J
3130 case 'j': // j- goto next line, same col
3131 case VI_K_DOWN: // cursor key Down
3132 if (cmdcnt-- > 1) {
3133 do_cmd(c);
3134 } // repeat cnt
3135 dot_next(); // go to next B-o-l
3136 dot = move_to_col(dot, ccol + offset); // try stay in same col
3137 break;
3138 case 12: // ctrl-L force redraw whole screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003139 case 18: // ctrl-R force redraw
Eric Andersen822c3832001-05-07 17:37:43 +00003140 place_cursor(0, 0, FALSE); // put cursor in correct place
Eric Andersen3f980402001-04-04 17:31:15 +00003141 clear_to_eos(); // tel terminal to erase display
3142 (void) mysleep(10);
3143 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00003144 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003145 refresh(TRUE); // this will redraw the entire display
3146 break;
3147 case 13: // Carriage Return ^M
3148 case '+': // +- goto next line
3149 if (cmdcnt-- > 1) {
3150 do_cmd(c);
3151 } // repeat cnt
3152 dot_next();
3153 dot_skip_over_ws();
3154 break;
3155 case 21: // ctrl-U scroll up half screen
3156 dot_scroll((rows - 2) / 2, -1);
3157 break;
3158 case 25: // ctrl-Y scroll up one line
3159 dot_scroll(1, -1);
3160 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003161 case 27: // esc
Eric Andersen3f980402001-04-04 17:31:15 +00003162 if (cmd_mode == 0)
3163 indicate_error(c);
3164 cmd_mode = 0; // stop insrting
3165 end_cmd_q();
Paul Fox8552aec2005-09-16 12:20:05 +00003166 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003167 break;
3168 case ' ': // move right
3169 case 'l': // move right
3170 case VI_K_RIGHT: // Cursor Key Right
3171 if (cmdcnt-- > 1) {
3172 do_cmd(c);
3173 } // repeat cnt
3174 dot_right();
3175 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003176#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003177 case '"': // "- name a register to use for Delete/Yank
3178 c1 = get_one_char();
3179 c1 = tolower(c1);
3180 if (islower(c1)) {
3181 YDreg = c1 - 'a';
3182 } else {
3183 indicate_error(c);
3184 }
3185 break;
3186 case '\'': // '- goto a specific mark
3187 c1 = get_one_char();
3188 c1 = tolower(c1);
3189 if (islower(c1)) {
3190 c1 = c1 - 'a';
3191 // get the b-o-l
3192 q = mark[(int) c1];
3193 if (text <= q && q < end) {
3194 dot = q;
3195 dot_begin(); // go to B-o-l
3196 dot_skip_over_ws();
3197 }
3198 } else if (c1 == '\'') { // goto previous context
3199 dot = swap_context(dot); // swap current and previous context
3200 dot_begin(); // go to B-o-l
3201 dot_skip_over_ws();
3202 } else {
3203 indicate_error(c);
3204 }
3205 break;
3206 case 'm': // m- Mark a line
3207 // this is really stupid. If there are any inserts or deletes
3208 // between text[0] and dot then this mark will not point to the
3209 // correct location! It could be off by many lines!
3210 // Well..., at least its quick and dirty.
3211 c1 = get_one_char();
3212 c1 = tolower(c1);
3213 if (islower(c1)) {
3214 c1 = c1 - 'a';
3215 // remember the line
3216 mark[(int) c1] = dot;
3217 } else {
3218 indicate_error(c);
3219 }
3220 break;
3221 case 'P': // P- Put register before
3222 case 'p': // p- put register after
3223 p = reg[YDreg];
3224 if (p == 0) {
3225 psbs("Nothing in register %c", what_reg());
3226 break;
3227 }
3228 // are we putting whole lines or strings
3229 if (strchr((char *) p, '\n') != NULL) {
3230 if (c == 'P') {
3231 dot_begin(); // putting lines- Put above
3232 }
3233 if (c == 'p') {
3234 // are we putting after very last line?
3235 if (end_line(dot) == (end - 1)) {
3236 dot = end; // force dot to end of text[]
3237 } else {
3238 dot_next(); // next line, then put before
3239 }
3240 }
3241 } else {
3242 if (c == 'p')
3243 dot_right(); // move to right, can move to NL
3244 }
3245 dot = string_insert(dot, p); // insert the string
3246 end_cmd_q(); // stop adding to q
3247 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003248 case 'U': // U- Undo; replace current line with original version
3249 if (reg[Ureg] != 0) {
3250 p = begin_line(dot);
3251 q = end_line(dot);
3252 p = text_hole_delete(p, q); // delete cur line
3253 p = string_insert(p, reg[Ureg]); // insert orig line
3254 dot = p;
3255 dot_skip_over_ws();
3256 }
3257 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003258#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003259 case '$': // $- goto end of line
3260 case VI_K_END: // Cursor Key End
3261 if (cmdcnt-- > 1) {
3262 do_cmd(c);
3263 } // repeat cnt
Glenn L McGrathee829062004-01-21 10:59:45 +00003264 dot = end_line(dot);
Eric Andersen3f980402001-04-04 17:31:15 +00003265 break;
3266 case '%': // %- find matching char of pair () [] {}
3267 for (q = dot; q < end && *q != '\n'; q++) {
3268 if (strchr("()[]{}", *q) != NULL) {
3269 // we found half of a pair
3270 p = find_pair(q, *q);
3271 if (p == NULL) {
3272 indicate_error(c);
3273 } else {
3274 dot = p;
3275 }
3276 break;
3277 }
3278 }
3279 if (*q == '\n')
3280 indicate_error(c);
3281 break;
3282 case 'f': // f- forward to a user specified char
3283 last_forward_char = get_one_char(); // get the search char
3284 //
Eric Andersenaff114c2004-04-14 17:51:38 +00003285 // dont separate these two commands. 'f' depends on ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003286 //
Paul Foxd13b90b2005-07-18 22:17:25 +00003287 //**** fall thru to ... ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003288 case ';': // ;- look at rest of line for last forward char
3289 if (cmdcnt-- > 1) {
Eric Andersen822c3832001-05-07 17:37:43 +00003290 do_cmd(';');
Eric Andersen3f980402001-04-04 17:31:15 +00003291 } // repeat cnt
Eric Andersen822c3832001-05-07 17:37:43 +00003292 if (last_forward_char == 0) break;
Eric Andersen3f980402001-04-04 17:31:15 +00003293 q = dot + 1;
3294 while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
3295 q++;
3296 }
3297 if (*q == last_forward_char)
3298 dot = q;
3299 break;
3300 case '-': // -- goto prev line
3301 if (cmdcnt-- > 1) {
3302 do_cmd(c);
3303 } // repeat cnt
3304 dot_prev();
3305 dot_skip_over_ws();
3306 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003307#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003308 case '.': // .- repeat the last modifying command
3309 // Stuff the last_modifying_cmd back into stdin
3310 // and let it be re-executed.
3311 if (last_modifying_cmd != 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +00003312 ioq = ioq_start = (Byte *) bb_xstrdup((char *) last_modifying_cmd);
Eric Andersen3f980402001-04-04 17:31:15 +00003313 }
3314 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003315#endif /* CONFIG_FEATURE_VI_DOT_CMD */
3316#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +00003317 case '?': // /- search for a pattern
3318 case '/': // /- search for a pattern
3319 buf[0] = c;
3320 buf[1] = '\0';
3321 q = get_input_line(buf); // get input line- use "status line"
3322 if (strlen((char *) q) == 1)
3323 goto dc3; // if no pat re-use old pat
3324 if (strlen((char *) q) > 1) { // new pat- save it and find
3325 // there is a new pat
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00003326 free(last_search_pattern);
Manuel Novoa III cad53642003-03-19 09:13:01 +00003327 last_search_pattern = (Byte *) bb_xstrdup((char *) q);
Eric Andersen3f980402001-04-04 17:31:15 +00003328 goto dc3; // now find the pattern
3329 }
3330 // user changed mind and erased the "/"- do nothing
3331 break;
3332 case 'N': // N- backward search for last pattern
3333 if (cmdcnt-- > 1) {
3334 do_cmd(c);
3335 } // repeat cnt
3336 dir = BACK; // assume BACKWARD search
3337 p = dot - 1;
3338 if (last_search_pattern[0] == '?') {
3339 dir = FORWARD;
3340 p = dot + 1;
3341 }
3342 goto dc4; // now search for pattern
3343 break;
3344 case 'n': // n- repeat search for last pattern
3345 // search rest of text[] starting at next char
3346 // if search fails return orignal "p" not the "p+1" address
3347 if (cmdcnt-- > 1) {
3348 do_cmd(c);
3349 } // repeat cnt
3350 dc3:
3351 if (last_search_pattern == 0) {
3352 msg = (Byte *) "No previous regular expression";
3353 goto dc2;
3354 }
3355 if (last_search_pattern[0] == '/') {
3356 dir = FORWARD; // assume FORWARD search
3357 p = dot + 1;
3358 }
3359 if (last_search_pattern[0] == '?') {
3360 dir = BACK;
3361 p = dot - 1;
3362 }
3363 dc4:
3364 q = char_search(p, last_search_pattern + 1, dir, FULL);
3365 if (q != NULL) {
3366 dot = q; // good search, update "dot"
3367 msg = (Byte *) "";
3368 goto dc2;
3369 }
3370 // no pattern found between "dot" and "end"- continue at top
3371 p = text;
3372 if (dir == BACK) {
3373 p = end - 1;
3374 }
3375 q = char_search(p, last_search_pattern + 1, dir, FULL);
3376 if (q != NULL) { // found something
3377 dot = q; // found new pattern- goto it
3378 msg = (Byte *) "search hit BOTTOM, continuing at TOP";
3379 if (dir == BACK) {
3380 msg = (Byte *) "search hit TOP, continuing at BOTTOM";
3381 }
3382 } else {
3383 msg = (Byte *) "Pattern not found";
3384 }
3385 dc2:
Paul Fox8552aec2005-09-16 12:20:05 +00003386 if (*msg) psbs("%s", msg);
Eric Andersen3f980402001-04-04 17:31:15 +00003387 break;
3388 case '{': // {- move backward paragraph
3389 q = char_search(dot, (Byte *) "\n\n", BACK, FULL);
3390 if (q != NULL) { // found blank line
3391 dot = next_line(q); // move to next blank line
3392 }
3393 break;
3394 case '}': // }- move forward paragraph
3395 q = char_search(dot, (Byte *) "\n\n", FORWARD, FULL);
3396 if (q != NULL) { // found blank line
3397 dot = next_line(q); // move to next blank line
3398 }
3399 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003400#endif /* CONFIG_FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +00003401 case '0': // 0- goto begining of line
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003402 case '1': // 1-
3403 case '2': // 2-
3404 case '3': // 3-
3405 case '4': // 4-
3406 case '5': // 5-
3407 case '6': // 6-
3408 case '7': // 7-
3409 case '8': // 8-
3410 case '9': // 9-
Eric Andersen3f980402001-04-04 17:31:15 +00003411 if (c == '0' && cmdcnt < 1) {
3412 dot_begin(); // this was a standalone zero
3413 } else {
3414 cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number
3415 }
3416 break;
3417 case ':': // :- the colon mode commands
Eric Andersen3f980402001-04-04 17:31:15 +00003418 p = get_input_line((Byte *) ":"); // get input line- use "status line"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003419#ifdef CONFIG_FEATURE_VI_COLON
Eric Andersen3f980402001-04-04 17:31:15 +00003420 colon(p); // execute the command
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003421#else /* CONFIG_FEATURE_VI_COLON */
Eric Andersen822c3832001-05-07 17:37:43 +00003422 if (*p == ':')
3423 p++; // move past the ':'
3424 cnt = strlen((char *) p);
3425 if (cnt <= 0)
3426 break;
3427 if (strncasecmp((char *) p, "quit", cnt) == 0 ||
3428 strncasecmp((char *) p, "q!", cnt) == 0) { // delete lines
Matt Kraai1f0c4362001-12-20 23:13:26 +00003429 if (file_modified && p[1] != '!') {
Eric Andersen3f980402001-04-04 17:31:15 +00003430 psbs("No write since last change (:quit! overrides)");
3431 } else {
3432 editing = 0;
3433 }
Eric Andersen822c3832001-05-07 17:37:43 +00003434 } else if (strncasecmp((char *) p, "write", cnt) == 0 ||
Robert Griebla71389b2002-07-31 21:22:21 +00003435 strncasecmp((char *) p, "wq", cnt) == 0 ||
3436 strncasecmp((char *) p, "x", cnt) == 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003437 cnt = file_write(cfn, text, end - 1);
Paul Fox61e45db2005-10-09 14:43:22 +00003438 if (cnt < 0) {
3439 if (cnt == -1)
3440 psbs("Write error: %s", strerror(errno));
3441 } else {
3442 file_modified = 0;
3443 last_file_modified = -1;
3444 psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt);
3445 if (p[0] == 'x' || p[1] == 'q') {
3446 editing = 0;
3447 }
Eric Andersen3f980402001-04-04 17:31:15 +00003448 }
Eric Andersen822c3832001-05-07 17:37:43 +00003449 } else if (strncasecmp((char *) p, "file", cnt) == 0 ) {
Paul Fox8552aec2005-09-16 12:20:05 +00003450 last_status_cksum = 0; // force status update
Eric Andersen822c3832001-05-07 17:37:43 +00003451 } else if (sscanf((char *) p, "%d", &j) > 0) {
3452 dot = find_line(j); // go to line # j
3453 dot_skip_over_ws();
Eric Andersen3f980402001-04-04 17:31:15 +00003454 } else { // unrecognised cmd
Eric Andersen822c3832001-05-07 17:37:43 +00003455 ni((Byte *) p);
Eric Andersen3f980402001-04-04 17:31:15 +00003456 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003457#endif /* CONFIG_FEATURE_VI_COLON */
Eric Andersen3f980402001-04-04 17:31:15 +00003458 break;
3459 case '<': // <- Left shift something
3460 case '>': // >- Right shift something
3461 cnt = count_lines(text, dot); // remember what line we are on
3462 c1 = get_one_char(); // get the type of thing to delete
3463 find_range(&p, &q, c1);
3464 (void) yank_delete(p, q, 1, YANKONLY); // save copy before change
3465 p = begin_line(p);
3466 q = end_line(q);
3467 i = count_lines(p, q); // # of lines we are shifting
3468 for ( ; i > 0; i--, p = next_line(p)) {
3469 if (c == '<') {
3470 // shift left- remove tab or 8 spaces
3471 if (*p == '\t') {
3472 // shrink buffer 1 char
3473 (void) text_hole_delete(p, p);
3474 } else if (*p == ' ') {
3475 // we should be calculating columns, not just SPACE
3476 for (j = 0; *p == ' ' && j < tabstop; j++) {
3477 (void) text_hole_delete(p, p);
3478 }
3479 }
3480 } else if (c == '>') {
3481 // shift right -- add tab or 8 spaces
3482 (void) char_insert(p, '\t');
3483 }
3484 }
3485 dot = find_line(cnt); // what line were we on
3486 dot_skip_over_ws();
3487 end_cmd_q(); // stop adding to q
3488 break;
3489 case 'A': // A- append at e-o-l
3490 dot_end(); // go to e-o-l
3491 //**** fall thru to ... 'a'
3492 case 'a': // a- append after current char
3493 if (*dot != '\n')
3494 dot++;
3495 goto dc_i;
3496 break;
3497 case 'B': // B- back a blank-delimited Word
3498 case 'E': // E- end of a blank-delimited word
3499 case 'W': // W- forward a blank-delimited word
3500 if (cmdcnt-- > 1) {
3501 do_cmd(c);
3502 } // repeat cnt
3503 dir = FORWARD;
3504 if (c == 'B')
3505 dir = BACK;
3506 if (c == 'W' || isspace(dot[dir])) {
3507 dot = skip_thing(dot, 1, dir, S_TO_WS);
3508 dot = skip_thing(dot, 2, dir, S_OVER_WS);
3509 }
3510 if (c != 'W')
3511 dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
3512 break;
3513 case 'C': // C- Change to e-o-l
3514 case 'D': // D- delete to e-o-l
3515 save_dot = dot;
3516 dot = dollar_line(dot); // move to before NL
3517 // copy text into a register and delete
3518 dot = yank_delete(save_dot, dot, 0, YANKDEL); // delete to e-o-l
3519 if (c == 'C')
3520 goto dc_i; // start inserting
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003521#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003522 if (c == 'D')
3523 end_cmd_q(); // stop adding to q
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003524#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +00003525 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003526 case 'G': // G- goto to a line number (default= E-O-F)
3527 dot = end - 1; // assume E-O-F
Eric Andersen1c0d3112001-04-16 15:46:44 +00003528 if (cmdcnt > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00003529 dot = find_line(cmdcnt); // what line is #cmdcnt
Eric Andersen1c0d3112001-04-16 15:46:44 +00003530 }
3531 dot_skip_over_ws();
3532 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003533 case 'H': // H- goto top line on screen
3534 dot = screenbegin;
3535 if (cmdcnt > (rows - 1)) {
3536 cmdcnt = (rows - 1);
3537 }
3538 if (cmdcnt-- > 1) {
3539 do_cmd('+');
3540 } // repeat cnt
3541 dot_skip_over_ws();
3542 break;
3543 case 'I': // I- insert before first non-blank
3544 dot_begin(); // 0
3545 dot_skip_over_ws();
3546 //**** fall thru to ... 'i'
3547 case 'i': // i- insert before current char
3548 case VI_K_INSERT: // Cursor Key Insert
3549 dc_i:
3550 cmd_mode = 1; // start insrting
Eric Andersen3f980402001-04-04 17:31:15 +00003551 break;
3552 case 'J': // J- join current and next lines together
3553 if (cmdcnt-- > 2) {
3554 do_cmd(c);
3555 } // repeat cnt
3556 dot_end(); // move to NL
3557 if (dot < end - 1) { // make sure not last char in text[]
3558 *dot++ = ' '; // replace NL with space
Paul Fox8552aec2005-09-16 12:20:05 +00003559 file_modified++;
Eric Andersen3f980402001-04-04 17:31:15 +00003560 while (isblnk(*dot)) { // delete leading WS
3561 dot_delete();
3562 }
3563 }
3564 end_cmd_q(); // stop adding to q
3565 break;
3566 case 'L': // L- goto bottom line on screen
3567 dot = end_screen();
3568 if (cmdcnt > (rows - 1)) {
3569 cmdcnt = (rows - 1);
3570 }
3571 if (cmdcnt-- > 1) {
3572 do_cmd('-');
3573 } // repeat cnt
3574 dot_begin();
3575 dot_skip_over_ws();
3576 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003577 case 'M': // M- goto middle line on screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003578 dot = screenbegin;
3579 for (cnt = 0; cnt < (rows-1) / 2; cnt++)
3580 dot = next_line(dot);
3581 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003582 case 'O': // O- open a empty line above
Eric Andersen822c3832001-05-07 17:37:43 +00003583 // 0i\n ESC -i
Eric Andersen3f980402001-04-04 17:31:15 +00003584 p = begin_line(dot);
3585 if (p[-1] == '\n') {
3586 dot_prev();
3587 case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
3588 dot_end();
3589 dot = char_insert(dot, '\n');
3590 } else {
3591 dot_begin(); // 0
Eric Andersen822c3832001-05-07 17:37:43 +00003592 dot = char_insert(dot, '\n'); // i\n ESC
Eric Andersen3f980402001-04-04 17:31:15 +00003593 dot_prev(); // -
3594 }
3595 goto dc_i;
3596 break;
3597 case 'R': // R- continuous Replace char
Eric Andersen1c0d3112001-04-16 15:46:44 +00003598 dc5:
Eric Andersen3f980402001-04-04 17:31:15 +00003599 cmd_mode = 2;
Eric Andersen3f980402001-04-04 17:31:15 +00003600 break;
3601 case 'X': // X- delete char before dot
3602 case 'x': // x- delete the current char
3603 case 's': // s- substitute the current char
3604 if (cmdcnt-- > 1) {
3605 do_cmd(c);
3606 } // repeat cnt
3607 dir = 0;
3608 if (c == 'X')
3609 dir = -1;
3610 if (dot[dir] != '\n') {
3611 if (c == 'X')
3612 dot--; // delete prev char
3613 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3614 }
3615 if (c == 's')
3616 goto dc_i; // start insrting
3617 end_cmd_q(); // stop adding to q
3618 break;
3619 case 'Z': // Z- if modified, {write}; exit
3620 // ZZ means to save file (if necessary), then exit
3621 c1 = get_one_char();
3622 if (c1 != 'Z') {
3623 indicate_error(c);
3624 break;
3625 }
Matt Kraai1f0c4362001-12-20 23:13:26 +00003626 if (file_modified
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003627#ifdef CONFIG_FEATURE_VI_READONLY
Matt Kraai1f0c4362001-12-20 23:13:26 +00003628 && ! vi_readonly
3629 && ! readonly
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003630#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen3f980402001-04-04 17:31:15 +00003631 ) {
3632 cnt = file_write(cfn, text, end - 1);
Paul Fox61e45db2005-10-09 14:43:22 +00003633 if (cnt < 0) {
3634 if (cnt == -1)
3635 psbs("Write error: %s", strerror(errno));
3636 } else if (cnt == (end - 1 - text + 1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003637 editing = 0;
3638 }
3639 } else {
3640 editing = 0;
3641 }
3642 break;
3643 case '^': // ^- move to first non-blank on line
3644 dot_begin();
3645 dot_skip_over_ws();
3646 break;
3647 case 'b': // b- back a word
3648 case 'e': // e- end of word
3649 if (cmdcnt-- > 1) {
3650 do_cmd(c);
3651 } // repeat cnt
3652 dir = FORWARD;
3653 if (c == 'b')
3654 dir = BACK;
3655 if ((dot + dir) < text || (dot + dir) > end - 1)
3656 break;
3657 dot += dir;
3658 if (isspace(*dot)) {
3659 dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
3660 }
3661 if (isalnum(*dot) || *dot == '_') {
3662 dot = skip_thing(dot, 1, dir, S_END_ALNUM);
3663 } else if (ispunct(*dot)) {
3664 dot = skip_thing(dot, 1, dir, S_END_PUNCT);
3665 }
3666 break;
3667 case 'c': // c- change something
3668 case 'd': // d- delete something
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003669#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003670 case 'y': // y- yank something
3671 case 'Y': // Y- Yank a line
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003672#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003673 yf = YANKDEL; // assume either "c" or "d"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003674#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003675 if (c == 'y' || c == 'Y')
3676 yf = YANKONLY;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003677#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003678 c1 = 'y';
3679 if (c != 'Y')
3680 c1 = get_one_char(); // get the type of thing to delete
3681 find_range(&p, &q, c1);
3682 if (c1 == 27) { // ESC- user changed mind and wants out
3683 c = c1 = 27; // Escape- do nothing
3684 } else if (strchr("wW", c1)) {
3685 if (c == 'c') {
3686 // don't include trailing WS as part of word
3687 while (isblnk(*q)) {
3688 if (q <= text || q[-1] == '\n')
3689 break;
3690 q--;
3691 }
3692 }
3693 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen822c3832001-05-07 17:37:43 +00003694 } else if (strchr("^0bBeEft$", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003695 // single line copy text into a register and delete
3696 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen1c0d3112001-04-16 15:46:44 +00003697 } else if (strchr("cdykjHL%+-{}\r\n", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003698 // multiple line copy text into a register and delete
3699 dot = yank_delete(p, q, 1, yf); // delete lines
Eric Andersen1c0d3112001-04-16 15:46:44 +00003700 if (c == 'c') {
3701 dot = char_insert(dot, '\n');
3702 // on the last line of file don't move to prev line
3703 if (dot != (end-1)) {
3704 dot_prev();
3705 }
3706 } else if (c == 'd') {
Eric Andersen3f980402001-04-04 17:31:15 +00003707 dot_begin();
3708 dot_skip_over_ws();
3709 }
3710 } else {
3711 // could not recognize object
3712 c = c1 = 27; // error-
3713 indicate_error(c);
3714 }
3715 if (c1 != 27) {
3716 // if CHANGING, not deleting, start inserting after the delete
3717 if (c == 'c') {
3718 strcpy((char *) buf, "Change");
3719 goto dc_i; // start inserting
3720 }
3721 if (c == 'd') {
3722 strcpy((char *) buf, "Delete");
3723 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003724#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003725 if (c == 'y' || c == 'Y') {
3726 strcpy((char *) buf, "Yank");
3727 }
3728 p = reg[YDreg];
3729 q = p + strlen((char *) p);
3730 for (cnt = 0; p <= q; p++) {
3731 if (*p == '\n')
3732 cnt++;
3733 }
3734 psb("%s %d lines (%d chars) using [%c]",
3735 buf, cnt, strlen((char *) reg[YDreg]), what_reg());
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003736#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003737 end_cmd_q(); // stop adding to q
3738 }
3739 break;
3740 case 'k': // k- goto prev line, same col
3741 case VI_K_UP: // cursor key Up
3742 if (cmdcnt-- > 1) {
3743 do_cmd(c);
3744 } // repeat cnt
3745 dot_prev();
3746 dot = move_to_col(dot, ccol + offset); // try stay in same col
3747 break;
3748 case 'r': // r- replace the current char with user input
3749 c1 = get_one_char(); // get the replacement char
3750 if (*dot != '\n') {
3751 *dot = c1;
Paul Fox8552aec2005-09-16 12:20:05 +00003752 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003753 }
3754 end_cmd_q(); // stop adding to q
3755 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003756 case 't': // t- move to char prior to next x
3757 last_forward_char = get_one_char();
3758 do_cmd(';');
3759 if (*dot == last_forward_char)
3760 dot_left();
3761 last_forward_char= 0;
3762 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003763 case 'w': // w- forward a word
3764 if (cmdcnt-- > 1) {
3765 do_cmd(c);
3766 } // repeat cnt
3767 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
3768 dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
3769 } else if (ispunct(*dot)) { // we are on PUNCT
3770 dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
3771 }
3772 if (dot < end - 1)
3773 dot++; // move over word
3774 if (isspace(*dot)) {
3775 dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
3776 }
3777 break;
3778 case 'z': // z-
3779 c1 = get_one_char(); // get the replacement char
3780 cnt = 0;
3781 if (c1 == '.')
3782 cnt = (rows - 2) / 2; // put dot at center
3783 if (c1 == '-')
3784 cnt = rows - 2; // put dot at bottom
3785 screenbegin = begin_line(dot); // start dot at top
3786 dot_scroll(cnt, -1);
3787 break;
3788 case '|': // |- move to column "cmdcnt"
3789 dot = move_to_col(dot, cmdcnt - 1); // try to move to column
3790 break;
3791 case '~': // ~- flip the case of letters a-z -> A-Z
3792 if (cmdcnt-- > 1) {
3793 do_cmd(c);
3794 } // repeat cnt
3795 if (islower(*dot)) {
3796 *dot = toupper(*dot);
Paul Fox8552aec2005-09-16 12:20:05 +00003797 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003798 } else if (isupper(*dot)) {
3799 *dot = tolower(*dot);
Paul Fox8552aec2005-09-16 12:20:05 +00003800 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003801 }
3802 dot_right();
3803 end_cmd_q(); // stop adding to q
3804 break;
3805 //----- The Cursor and Function Keys -----------------------------
3806 case VI_K_HOME: // Cursor Key Home
3807 dot_begin();
3808 break;
3809 // The Fn keys could point to do_macro which could translate them
3810 case VI_K_FUN1: // Function Key F1
3811 case VI_K_FUN2: // Function Key F2
3812 case VI_K_FUN3: // Function Key F3
3813 case VI_K_FUN4: // Function Key F4
3814 case VI_K_FUN5: // Function Key F5
3815 case VI_K_FUN6: // Function Key F6
3816 case VI_K_FUN7: // Function Key F7
3817 case VI_K_FUN8: // Function Key F8
3818 case VI_K_FUN9: // Function Key F9
3819 case VI_K_FUN10: // Function Key F10
3820 case VI_K_FUN11: // Function Key F11
3821 case VI_K_FUN12: // Function Key F12
3822 break;
3823 }
3824
3825 dc1:
3826 // if text[] just became empty, add back an empty line
3827 if (end == text) {
3828 (void) char_insert(text, '\n'); // start empty buf with dummy line
3829 dot = text;
3830 }
3831 // it is OK for dot to exactly equal to end, otherwise check dot validity
3832 if (dot != end) {
3833 dot = bound_dot(dot); // make sure "dot" is valid
3834 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003835#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003836 check_context(c); // update the current context
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003837#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003838
3839 if (!isdigit(c))
3840 cmdcnt = 0; // cmd was not a number, reset cmdcnt
3841 cnt = dot - begin_line(dot);
3842 // Try to stay off of the Newline
3843 if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
3844 dot--;
3845}
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003846
3847#ifdef CONFIG_FEATURE_VI_CRASHME
3848static int totalcmds = 0;
3849static int Mp = 85; // Movement command Probability
3850static int Np = 90; // Non-movement command Probability
3851static int Dp = 96; // Delete command Probability
3852static int Ip = 97; // Insert command Probability
3853static int Yp = 98; // Yank command Probability
3854static int Pp = 99; // Put command Probability
3855static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
3856char chars[20] = "\t012345 abcdABCD-=.$";
3857char *words[20] = { "this", "is", "a", "test",
3858 "broadcast", "the", "emergency", "of",
3859 "system", "quick", "brown", "fox",
3860 "jumped", "over", "lazy", "dogs",
3861 "back", "January", "Febuary", "March"
3862};
3863char *lines[20] = {
3864 "You should have received a copy of the GNU General Public License\n",
3865 "char c, cm, *cmd, *cmd1;\n",
3866 "generate a command by percentages\n",
3867 "Numbers may be typed as a prefix to some commands.\n",
3868 "Quit, discarding changes!\n",
3869 "Forced write, if permission originally not valid.\n",
3870 "In general, any ex or ed command (such as substitute or delete).\n",
3871 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3872 "Please get w/ me and I will go over it with you.\n",
3873 "The following is a list of scheduled, committed changes.\n",
3874 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3875 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3876 "Any question about transactions please contact Sterling Huxley.\n",
3877 "I will try to get back to you by Friday, December 31.\n",
3878 "This Change will be implemented on Friday.\n",
3879 "Let me know if you have problems accessing this;\n",
3880 "Sterling Huxley recently added you to the access list.\n",
3881 "Would you like to go to lunch?\n",
3882 "The last command will be automatically run.\n",
3883 "This is too much english for a computer geek.\n",
3884};
3885char *multilines[20] = {
3886 "You should have received a copy of the GNU General Public License\n",
3887 "char c, cm, *cmd, *cmd1;\n",
3888 "generate a command by percentages\n",
3889 "Numbers may be typed as a prefix to some commands.\n",
3890 "Quit, discarding changes!\n",
3891 "Forced write, if permission originally not valid.\n",
3892 "In general, any ex or ed command (such as substitute or delete).\n",
3893 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3894 "Please get w/ me and I will go over it with you.\n",
3895 "The following is a list of scheduled, committed changes.\n",
3896 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3897 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3898 "Any question about transactions please contact Sterling Huxley.\n",
3899 "I will try to get back to you by Friday, December 31.\n",
3900 "This Change will be implemented on Friday.\n",
3901 "Let me know if you have problems accessing this;\n",
3902 "Sterling Huxley recently added you to the access list.\n",
3903 "Would you like to go to lunch?\n",
3904 "The last command will be automatically run.\n",
3905 "This is too much english for a computer geek.\n",
3906};
3907
3908// create a random command to execute
3909static void crash_dummy()
3910{
3911 static int sleeptime; // how long to pause between commands
3912 char c, cm, *cmd, *cmd1;
3913 int i, cnt, thing, rbi, startrbi, percent;
3914
3915 // "dot" movement commands
3916 cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
3917
3918 // is there already a command running?
3919 if (readed_for_parse > 0)
3920 goto cd1;
3921 cd0:
3922 startrbi = rbi = 0;
3923 sleeptime = 0; // how long to pause between commands
3924 memset(readbuffer, '\0', BUFSIZ); // clear the read buffer
3925 // generate a command by percentages
3926 percent = (int) lrand48() % 100; // get a number from 0-99
3927 if (percent < Mp) { // Movement commands
3928 // available commands
3929 cmd = cmd1;
3930 M++;
3931 } else if (percent < Np) { // non-movement commands
3932 cmd = "mz<>\'\""; // available commands
3933 N++;
3934 } else if (percent < Dp) { // Delete commands
3935 cmd = "dx"; // available commands
3936 D++;
3937 } else if (percent < Ip) { // Inset commands
3938 cmd = "iIaAsrJ"; // available commands
3939 I++;
3940 } else if (percent < Yp) { // Yank commands
3941 cmd = "yY"; // available commands
3942 Y++;
3943 } else if (percent < Pp) { // Put commands
3944 cmd = "pP"; // available commands
3945 P++;
3946 } else {
3947 // We do not know how to handle this command, try again
3948 U++;
3949 goto cd0;
3950 }
3951 // randomly pick one of the available cmds from "cmd[]"
3952 i = (int) lrand48() % strlen(cmd);
3953 cm = cmd[i];
3954 if (strchr(":\024", cm))
3955 goto cd0; // dont allow colon or ctrl-T commands
3956 readbuffer[rbi++] = cm; // put cmd into input buffer
3957
3958 // now we have the command-
3959 // there are 1, 2, and multi char commands
3960 // find out which and generate the rest of command as necessary
3961 if (strchr("dmryz<>\'\"", cm)) { // 2-char commands
3962 cmd1 = " \n\r0$^-+wWeEbBhjklHL";
3963 if (cm == 'm' || cm == '\'' || cm == '\"') { // pick a reg[]
3964 cmd1 = "abcdefghijklmnopqrstuvwxyz";
3965 }
3966 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3967 c = cmd1[thing];
3968 readbuffer[rbi++] = c; // add movement to input buffer
3969 }
3970 if (strchr("iIaAsc", cm)) { // multi-char commands
3971 if (cm == 'c') {
3972 // change some thing
3973 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3974 c = cmd1[thing];
3975 readbuffer[rbi++] = c; // add movement to input buffer
3976 }
3977 thing = (int) lrand48() % 4; // what thing to insert
3978 cnt = (int) lrand48() % 10; // how many to insert
3979 for (i = 0; i < cnt; i++) {
3980 if (thing == 0) { // insert chars
3981 readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
3982 } else if (thing == 1) { // insert words
3983 strcat((char *) readbuffer, words[(int) lrand48() % 20]);
3984 strcat((char *) readbuffer, " ");
3985 sleeptime = 0; // how fast to type
3986 } else if (thing == 2) { // insert lines
3987 strcat((char *) readbuffer, lines[(int) lrand48() % 20]);
3988 sleeptime = 0; // how fast to type
3989 } else { // insert multi-lines
3990 strcat((char *) readbuffer, multilines[(int) lrand48() % 20]);
3991 sleeptime = 0; // how fast to type
3992 }
3993 }
3994 strcat((char *) readbuffer, "\033");
3995 }
3996 readed_for_parse = strlen(readbuffer);
3997 cd1:
3998 totalcmds++;
3999 if (sleeptime > 0)
4000 (void) mysleep(sleeptime); // sleep 1/100 sec
4001}
4002
4003// test to see if there are any errors
4004static void crash_test()
4005{
4006 static time_t oldtim;
4007 time_t tim;
Glenn L McGrath7127b582002-12-03 21:48:15 +00004008 char d[2], msg[BUFSIZ];
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004009
4010 msg[0] = '\0';
4011 if (end < text) {
4012 strcat((char *) msg, "end<text ");
4013 }
4014 if (end > textend) {
4015 strcat((char *) msg, "end>textend ");
4016 }
4017 if (dot < text) {
4018 strcat((char *) msg, "dot<text ");
4019 }
4020 if (dot > end) {
4021 strcat((char *) msg, "dot>end ");
4022 }
4023 if (screenbegin < text) {
4024 strcat((char *) msg, "screenbegin<text ");
4025 }
4026 if (screenbegin > end - 1) {
4027 strcat((char *) msg, "screenbegin>end-1 ");
4028 }
4029
4030 if (strlen(msg) > 0) {
4031 alarm(0);
Glenn L McGrath7127b582002-12-03 21:48:15 +00004032 printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004033 totalcmds, last_input_char, msg, SOs, SOn);
4034 fflush(stdout);
4035 while (read(0, d, 1) > 0) {
4036 if (d[0] == '\n' || d[0] == '\r')
4037 break;
4038 }
4039 alarm(3);
4040 }
4041 tim = (time_t) time((time_t *) 0);
4042 if (tim >= (oldtim + 3)) {
4043 sprintf((char *) status_buffer,
4044 "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
4045 totalcmds, M, N, I, D, Y, P, U, end - text + 1);
4046 oldtim = tim;
4047 }
4048 return;
4049}
4050#endif /* CONFIG_FEATURE_VI_CRASHME */