blob: bf537ee62078c3c7fe5cc97929c03529b28daed1 [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
Eric Andersena68ea1c2006-01-30 22:48:39 +0000350 status_buffer = (Byte *)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);
Eric Andersena68ea1c2006-01-30 22:48:39 +0000732 buf1 = (Byte*)last_char_is((char *)cmd, '!');
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000733 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();
Eric Andersena68ea1c2006-01-30 22:48:39 +0000766 system((char*)(orig_buf+1)); // run the cmd
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000767 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 }
Eric Andersena68ea1c2006-01-30 22:48:39 +0000790 if (strlen((char*)args) > 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000791 // the user supplied a file name
792 fn= args;
Eric Andersena68ea1c2006-01-30 22:48:39 +0000793 } else if (cfn != 0 && strlen((char*)cfn) > 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000794 // 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
Tim Rikerc1ef7bd2006-01-25 00:08:53 +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]))
Tim Rikerc1ef7bd2006-01-25 00:08:53 +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
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002138static void winch_sig(int sig ATTRIBUTE_UNUSED)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002139{
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 -------------------
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002149static void cont_sig(int sig ATTRIBUTE_UNUSED)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002150{
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 -------------------
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002161static void suspend_sig(int sig ATTRIBUTE_UNUSED)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002162{
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)
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002180 longjmp(restart, sig);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002181}
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
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002199 dot = bound_dot(dot); // make sure "dot" is valid
2200 longjmp(restart, sig);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002201 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002202}
2203#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
2204
2205static int mysleep(int hund) // sleep for 'h' 1/100 seconds
2206{
2207 // Don't hang- Wait 5/100 seconds- 1 Sec= 1000000
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002208 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002209 FD_ZERO(&rfds);
2210 FD_SET(0, &rfds);
2211 tv.tv_sec = 0;
2212 tv.tv_usec = hund * 10000;
2213 select(1, &rfds, NULL, NULL, &tv);
2214 return (FD_ISSET(0, &rfds));
2215}
2216
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002217#define readbuffer bb_common_bufsiz1
2218
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002219static int readed_for_parse;
2220
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002221//----- IO Routines --------------------------------------------
2222static Byte readit(void) // read (maybe cursor) key from stdin
2223{
2224 Byte c;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002225 int n;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002226 struct esc_cmds {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002227 const char *seq;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002228 Byte val;
2229 };
2230
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002231 static const struct esc_cmds esccmds[] = {
2232 {"OA", (Byte) VI_K_UP}, // cursor key Up
2233 {"OB", (Byte) VI_K_DOWN}, // cursor key Down
2234 {"OC", (Byte) VI_K_RIGHT}, // Cursor Key Right
2235 {"OD", (Byte) VI_K_LEFT}, // cursor key Left
2236 {"OH", (Byte) VI_K_HOME}, // Cursor Key Home
2237 {"OF", (Byte) VI_K_END}, // Cursor Key End
2238 {"[A", (Byte) VI_K_UP}, // cursor key Up
2239 {"[B", (Byte) VI_K_DOWN}, // cursor key Down
2240 {"[C", (Byte) VI_K_RIGHT}, // Cursor Key Right
2241 {"[D", (Byte) VI_K_LEFT}, // cursor key Left
2242 {"[H", (Byte) VI_K_HOME}, // Cursor Key Home
2243 {"[F", (Byte) VI_K_END}, // Cursor Key End
2244 {"[1~", (Byte) VI_K_HOME}, // Cursor Key Home
2245 {"[2~", (Byte) VI_K_INSERT}, // Cursor Key Insert
2246 {"[4~", (Byte) VI_K_END}, // Cursor Key End
2247 {"[5~", (Byte) VI_K_PAGEUP}, // Cursor Key Page Up
2248 {"[6~", (Byte) VI_K_PAGEDOWN}, // Cursor Key Page Down
2249 {"OP", (Byte) VI_K_FUN1}, // Function Key F1
2250 {"OQ", (Byte) VI_K_FUN2}, // Function Key F2
2251 {"OR", (Byte) VI_K_FUN3}, // Function Key F3
2252 {"OS", (Byte) VI_K_FUN4}, // Function Key F4
2253 {"[15~", (Byte) VI_K_FUN5}, // Function Key F5
2254 {"[17~", (Byte) VI_K_FUN6}, // Function Key F6
2255 {"[18~", (Byte) VI_K_FUN7}, // Function Key F7
2256 {"[19~", (Byte) VI_K_FUN8}, // Function Key F8
2257 {"[20~", (Byte) VI_K_FUN9}, // Function Key F9
2258 {"[21~", (Byte) VI_K_FUN10}, // Function Key F10
2259 {"[23~", (Byte) VI_K_FUN11}, // Function Key F11
2260 {"[24~", (Byte) VI_K_FUN12}, // Function Key F12
2261 {"[11~", (Byte) VI_K_FUN1}, // Function Key F1
2262 {"[12~", (Byte) VI_K_FUN2}, // Function Key F2
2263 {"[13~", (Byte) VI_K_FUN3}, // Function Key F3
2264 {"[14~", (Byte) VI_K_FUN4}, // Function Key F4
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002265 };
2266
2267#define ESCCMDS_COUNT (sizeof(esccmds)/sizeof(struct esc_cmds))
2268
2269 (void) alarm(0); // turn alarm OFF while we wait for input
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002270 fflush(stdout);
2271 n = readed_for_parse;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002272 // get input from User- are there already input chars in Q?
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002273 if (n <= 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002274 ri0:
2275 // the Q is empty, wait for a typed char
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002276 n = read(0, readbuffer, BUFSIZ - 1);
2277 if (n < 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002278 if (errno == EINTR)
2279 goto ri0; // interrupted sys call
2280 if (errno == EBADF)
2281 editing = 0;
2282 if (errno == EFAULT)
2283 editing = 0;
2284 if (errno == EINVAL)
2285 editing = 0;
2286 if (errno == EIO)
2287 editing = 0;
2288 errno = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002289 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002290 if(n <= 0)
2291 return 0; // error
2292 if (readbuffer[0] == 27) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002293 // This is an ESC char. Is this Esc sequence?
2294 // Could be bare Esc key. See if there are any
2295 // more chars to read after the ESC. This would
2296 // be a Function or Cursor Key sequence.
2297 FD_ZERO(&rfds);
2298 FD_SET(0, &rfds);
2299 tv.tv_sec = 0;
2300 tv.tv_usec = 50000; // Wait 5/100 seconds- 1 Sec=1000000
2301
2302 // keep reading while there are input chars and room in buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002303 while (select(1, &rfds, NULL, NULL, &tv) > 0 && n <= (BUFSIZ - 5)) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002304 // read the rest of the ESC string
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002305 int r = read(0, (void *) (readbuffer + n), BUFSIZ - n);
2306 if (r > 0) {
2307 n += r;
2308 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002309 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002310 }
2311 readed_for_parse = n;
2312 }
2313 c = readbuffer[0];
2314 if(c == 27 && n > 1) {
2315 // Maybe cursor or function key?
2316 const struct esc_cmds *eindex;
2317
2318 for (eindex = esccmds; eindex < &esccmds[ESCCMDS_COUNT]; eindex++) {
2319 int cnt = strlen(eindex->seq);
2320
2321 if(n <= cnt)
2322 continue;
2323 if(strncmp(eindex->seq, (char *) readbuffer + 1, cnt))
2324 continue;
2325 // is a Cursor key- put derived value back into Q
2326 c = eindex->val;
2327 // for squeeze out the ESC sequence
2328 n = cnt + 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002329 break;
2330 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002331 if(eindex == &esccmds[ESCCMDS_COUNT]) {
2332 /* defined ESC sequence not found, set only one ESC */
2333 n = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002334 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002335 } else {
2336 n = 1;
2337 }
2338 // remove key sequence from Q
2339 readed_for_parse -= n;
2340 memmove(readbuffer, readbuffer + n, BUFSIZ - n);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002341 (void) alarm(3); // we are done waiting for input, turn alarm ON
2342 return (c);
2343}
2344
2345//----- IO Routines --------------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002346static Byte get_one_char(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002347{
2348 static Byte c;
2349
2350#ifdef CONFIG_FEATURE_VI_DOT_CMD
2351 // ! adding2q && ioq == 0 read()
2352 // ! adding2q && ioq != 0 *ioq
2353 // adding2q *last_modifying_cmd= read()
2354 if (!adding2q) {
2355 // we are not adding to the q.
2356 // but, we may be reading from a q
2357 if (ioq == 0) {
2358 // there is no current q, read from STDIN
2359 c = readit(); // get the users input
2360 } else {
2361 // there is a queue to get chars from first
2362 c = *ioq++;
2363 if (c == '\0') {
2364 // the end of the q, read from STDIN
2365 free(ioq_start);
2366 ioq_start = ioq = 0;
2367 c = readit(); // get the users input
2368 }
2369 }
2370 } else {
2371 // adding STDIN chars to q
2372 c = readit(); // get the users input
2373 if (last_modifying_cmd != 0) {
Eric Andersenfda2b7f2002-10-26 10:19:19 +00002374 int len = strlen((char *) last_modifying_cmd);
2375 if (len + 1 >= BUFSIZ) {
2376 psbs("last_modifying_cmd overrun");
2377 } else {
2378 // add new char to q
2379 last_modifying_cmd[len] = c;
2380 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002381 }
2382 }
2383#else /* CONFIG_FEATURE_VI_DOT_CMD */
2384 c = readit(); // get the users input
2385#endif /* CONFIG_FEATURE_VI_DOT_CMD */
2386 return (c); // return the char, where ever it came from
2387}
2388
2389static Byte *get_input_line(Byte * prompt) // get input line- use "status line"
2390{
2391 Byte buf[BUFSIZ];
2392 Byte c;
2393 int i;
2394 static Byte *obufp = NULL;
2395
2396 strcpy((char *) buf, (char *) prompt);
Paul Fox8552aec2005-09-16 12:20:05 +00002397 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002398 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
2399 clear_to_eol(); // clear the line
Eric Andersena68ea1c2006-01-30 22:48:39 +00002400 write1((char *) prompt); // write out the :, /, or ? prompt
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002401
2402 for (i = strlen((char *) buf); i < BUFSIZ;) {
2403 c = get_one_char(); // read user input
2404 if (c == '\n' || c == '\r' || c == 27)
2405 break; // is this end of input
Paul Foxf2de0b72005-09-13 22:20:37 +00002406 if (c == erase_char || c == 8 || c == 127) {
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00002407 // user wants to erase prev char
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002408 i--; // backup to prev char
2409 buf[i] = '\0'; // erase the char
2410 buf[i + 1] = '\0'; // null terminate buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002411 write1("\b \b"); // erase char on screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002412 if (i <= 0) { // user backs up before b-o-l, exit
2413 break;
2414 }
2415 } else {
2416 buf[i] = c; // save char in buffer
2417 buf[i + 1] = '\0'; // make sure buffer is null terminated
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002418 putchar(c); // echo the char back to user
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002419 i++;
2420 }
2421 }
2422 refresh(FALSE);
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002423 free(obufp);
Manuel Novoa III cad53642003-03-19 09:13:01 +00002424 obufp = (Byte *) bb_xstrdup((char *) buf);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002425 return (obufp);
2426}
2427
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002428static int file_size(const Byte * fn) // what is the byte size of "fn"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002429{
2430 struct stat st_buf;
2431 int cnt, sr;
2432
Eric Andersena68ea1c2006-01-30 22:48:39 +00002433 if (fn == 0 || strlen((char *)fn) <= 0)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002434 return (-1);
2435 cnt = -1;
2436 sr = stat((char *) fn, &st_buf); // see if file exists
2437 if (sr >= 0) {
2438 cnt = (int) st_buf.st_size;
2439 }
2440 return (cnt);
2441}
2442
2443static int file_insert(Byte * fn, Byte * p, int size)
2444{
2445 int fd, cnt;
2446
2447 cnt = -1;
2448#ifdef CONFIG_FEATURE_VI_READONLY
2449 readonly = FALSE;
2450#endif /* CONFIG_FEATURE_VI_READONLY */
2451 if (fn == 0 || strlen((char*) fn) <= 0) {
2452 psbs("No filename given");
2453 goto fi0;
2454 }
2455 if (size == 0) {
2456 // OK- this is just a no-op
2457 cnt = 0;
2458 goto fi0;
2459 }
2460 if (size < 0) {
2461 psbs("Trying to insert a negative number (%d) of characters", size);
2462 goto fi0;
2463 }
2464 if (p < text || p > end) {
2465 psbs("Trying to insert file outside of memory");
2466 goto fi0;
2467 }
2468
2469 // see if we can open the file
2470#ifdef CONFIG_FEATURE_VI_READONLY
2471 if (vi_readonly) goto fi1; // do not try write-mode
2472#endif
2473 fd = open((char *) fn, O_RDWR); // assume read & write
2474 if (fd < 0) {
2475 // could not open for writing- maybe file is read only
2476#ifdef CONFIG_FEATURE_VI_READONLY
2477 fi1:
2478#endif
2479 fd = open((char *) fn, O_RDONLY); // try read-only
2480 if (fd < 0) {
2481 psbs("\"%s\" %s", fn, "could not open file");
2482 goto fi0;
2483 }
2484#ifdef CONFIG_FEATURE_VI_READONLY
2485 // got the file- read-only
2486 readonly = TRUE;
2487#endif /* CONFIG_FEATURE_VI_READONLY */
2488 }
2489 p = text_hole_make(p, size);
2490 cnt = read(fd, p, size);
2491 close(fd);
2492 if (cnt < 0) {
2493 cnt = -1;
2494 p = text_hole_delete(p, p + size - 1); // un-do buffer insert
2495 psbs("could not read file \"%s\"", fn);
2496 } else if (cnt < size) {
2497 // There was a partial read, shrink unused space text[]
2498 p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert
2499 psbs("could not read all of file \"%s\"", fn);
2500 }
2501 if (cnt >= size)
Paul Fox8552aec2005-09-16 12:20:05 +00002502 file_modified++;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002503 fi0:
2504 return (cnt);
2505}
2506
2507static int file_write(Byte * fn, Byte * first, Byte * last)
2508{
2509 int fd, cnt, charcnt;
2510
2511 if (fn == 0) {
2512 psbs("No current filename");
Paul Fox61e45db2005-10-09 14:43:22 +00002513 return (-2);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002514 }
2515 charcnt = 0;
2516 // FIXIT- use the correct umask()
2517 fd = open((char *) fn, (O_WRONLY | O_CREAT | O_TRUNC), 0664);
2518 if (fd < 0)
2519 return (-1);
2520 cnt = last - first + 1;
2521 charcnt = write(fd, first, cnt);
2522 if (charcnt == cnt) {
2523 // good write
2524 //file_modified= FALSE; // the file has not been modified
2525 } else {
2526 charcnt = 0;
2527 }
2528 close(fd);
2529 return (charcnt);
2530}
2531
2532//----- Terminal Drawing ---------------------------------------
2533// The terminal is made up of 'rows' line of 'columns' columns.
Eric Andersenaff114c2004-04-14 17:51:38 +00002534// classically this would be 24 x 80.
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002535// screen coordinates
2536// 0,0 ... 0,79
2537// 1,0 ... 1,79
2538// . ... .
2539// . ... .
2540// 22,0 ... 22,79
2541// 23,0 ... 23,79 status line
2542//
2543
2544//----- Move the cursor to row x col (count from 0, not 1) -------
2545static void place_cursor(int row, int col, int opti)
2546{
2547 char cm1[BUFSIZ];
2548 char *cm;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002549#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2550 char cm2[BUFSIZ];
2551 Byte *screenp;
2552 // char cm3[BUFSIZ];
2553 int Rrow= last_row;
2554#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002555
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002556 memset(cm1, '\0', BUFSIZ - 1); // clear the buffer
2557
2558 if (row < 0) row = 0;
2559 if (row >= rows) row = rows - 1;
2560 if (col < 0) col = 0;
2561 if (col >= columns) col = columns - 1;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002562
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002563 //----- 1. Try the standard terminal ESC sequence
2564 sprintf((char *) cm1, CMrc, row + 1, col + 1);
2565 cm= cm1;
2566 if (! opti) goto pc0;
2567
2568#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2569 //----- find the minimum # of chars to move cursor -------------
2570 //----- 2. Try moving with discreet chars (Newline, [back]space, ...)
2571 memset(cm2, '\0', BUFSIZ - 1); // clear the buffer
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002572
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002573 // move to the correct row
2574 while (row < Rrow) {
2575 // the cursor has to move up
2576 strcat(cm2, CMup);
2577 Rrow--;
2578 }
2579 while (row > Rrow) {
2580 // the cursor has to move down
2581 strcat(cm2, CMdown);
2582 Rrow++;
2583 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002584
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002585 // now move to the correct column
2586 strcat(cm2, "\r"); // start at col 0
2587 // just send out orignal source char to get to correct place
2588 screenp = &screen[row * columns]; // start of screen line
Eric Andersena68ea1c2006-01-30 22:48:39 +00002589 strncat(cm2, (char* )screenp, col);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002590
2591 //----- 3. Try some other way of moving cursor
2592 //---------------------------------------------
2593
2594 // pick the shortest cursor motion to send out
2595 cm= cm1;
2596 if (strlen(cm2) < strlen(cm)) {
2597 cm= cm2;
2598 } /* else if (strlen(cm3) < strlen(cm)) {
2599 cm= cm3;
2600 } */
2601#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2602 pc0:
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002603 write1(cm); // move the cursor
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002604}
2605
2606//----- Erase from cursor to end of line -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002607static void clear_to_eol(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002608{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002609 write1(Ceol); // Erase from cursor to end of line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002610}
2611
2612//----- Erase from cursor to end of screen -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002613static void clear_to_eos(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002614{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002615 write1(Ceos); // Erase from cursor to end of screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002616}
2617
2618//----- Start standout mode ------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002619static void standout_start(void) // send "start reverse video" sequence
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002620{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002621 write1(SOs); // Start reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002622}
2623
2624//----- End standout mode --------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002625static void standout_end(void) // send "end reverse video" sequence
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002626{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002627 write1(SOn); // End reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002628}
2629
2630//----- Flash the screen --------------------------------------
2631static void flash(int h)
2632{
2633 standout_start(); // send "start reverse video" sequence
2634 redraw(TRUE);
2635 (void) mysleep(h);
2636 standout_end(); // send "end reverse video" sequence
2637 redraw(TRUE);
2638}
2639
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002640static void Indicate_Error(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002641{
2642#ifdef CONFIG_FEATURE_VI_CRASHME
2643 if (crashme > 0)
2644 return; // generate a random command
2645#endif /* CONFIG_FEATURE_VI_CRASHME */
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002646 if (!err_method) {
2647 write1(bell); // send out a bell character
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002648 } else {
2649 flash(10);
2650 }
2651}
2652
2653//----- Screen[] Routines --------------------------------------
2654//----- Erase the Screen[] memory ------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002655static void screen_erase(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002656{
2657 memset(screen, ' ', screensize); // clear new screen
2658}
2659
Eric Andersena68ea1c2006-01-30 22:48:39 +00002660static int bufsum(unsigned char *buf, int count)
Paul Fox8552aec2005-09-16 12:20:05 +00002661{
2662 int sum = 0;
Eric Andersena68ea1c2006-01-30 22:48:39 +00002663 unsigned char *e = buf + count;
Paul Fox8552aec2005-09-16 12:20:05 +00002664 while (buf < e)
2665 sum += *buf++;
2666 return sum;
2667}
2668
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002669//----- Draw the status line at bottom of the screen -------------
2670static void show_status_line(void)
2671{
Paul Foxc3504852005-09-16 12:48:18 +00002672 int cnt = 0, cksum = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002673
Paul Fox8552aec2005-09-16 12:20:05 +00002674 // either we already have an error or status message, or we
2675 // create one.
2676 if (!have_status_msg) {
2677 cnt = format_edit_status();
2678 cksum = bufsum(status_buffer, cnt);
2679 }
2680 if (have_status_msg || ((cnt > 0 && last_status_cksum != cksum))) {
2681 last_status_cksum= cksum; // remember if we have seen this line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002682 place_cursor(rows - 1, 0, FALSE); // put cursor on status line
Eric Andersena68ea1c2006-01-30 22:48:39 +00002683 write1((char*)status_buffer);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002684 clear_to_eol();
Paul Fox8552aec2005-09-16 12:20:05 +00002685 if (have_status_msg) {
Eric Andersena68ea1c2006-01-30 22:48:39 +00002686 if (((int)strlen((char*)status_buffer) - (have_status_msg - 1)) >
Paul Fox8552aec2005-09-16 12:20:05 +00002687 (columns - 1) ) {
2688 have_status_msg = 0;
2689 Hit_Return();
2690 }
2691 have_status_msg = 0;
2692 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002693 place_cursor(crow, ccol, FALSE); // put cursor back in correct place
2694 }
Eric Andersena9eb33d2004-08-19 19:15:06 +00002695 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002696}
2697
2698//----- format the status buffer, the bottom line of screen ------
Paul Fox8552aec2005-09-16 12:20:05 +00002699// format status buffer, with STANDOUT mode
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002700static void psbs(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002701{
2702 va_list args;
2703
2704 va_start(args, format);
2705 strcpy((char *) status_buffer, SOs); // Terminal standout mode on
Eric Andersen0ef24c62005-07-18 10:32:59 +00002706 vsprintf((char *) status_buffer + strlen((char *) status_buffer), format, args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002707 strcat((char *) status_buffer, SOn); // Terminal standout mode off
2708 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00002709
2710 have_status_msg = 1 + sizeof(SOs) + sizeof(SOn) - 2;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002711
2712 return;
2713}
2714
Paul Fox8552aec2005-09-16 12:20:05 +00002715// format status buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002716static void psb(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002717{
2718 va_list args;
2719
2720 va_start(args, format);
2721 vsprintf((char *) status_buffer, format, args);
2722 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00002723
2724 have_status_msg = 1;
2725
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002726 return;
2727}
2728
2729static void ni(Byte * s) // display messages
2730{
2731 Byte buf[BUFSIZ];
2732
2733 print_literal(buf, s);
2734 psbs("\'%s\' is not implemented", buf);
2735}
2736
Paul Fox8552aec2005-09-16 12:20:05 +00002737static int format_edit_status(void) // show file status on status line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002738{
Paul Fox8552aec2005-09-16 12:20:05 +00002739 int cur, percent, ret, trunc_at;
2740 static int tot;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002741
Paul Fox8552aec2005-09-16 12:20:05 +00002742 // file_modified is now a counter rather than a flag. this
2743 // helps reduce the amount of line counting we need to do.
2744 // (this will cause a mis-reporting of modified status
2745 // once every MAXINT editing operations.)
2746
2747 // it would be nice to do a similar optimization here -- if
2748 // we haven't done a motion that could have changed which line
2749 // we're on, then we shouldn't have to do this count_lines()
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002750 cur = count_lines(text, dot);
Paul Fox8552aec2005-09-16 12:20:05 +00002751
2752 // reduce counting -- the total lines can't have
2753 // changed if we haven't done any edits.
2754 if (file_modified != last_file_modified) {
2755 tot = cur + count_lines(dot, end - 1) - 1;
2756 last_file_modified = file_modified;
2757 }
2758
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002759 // current line percent
2760 // ------------- ~~ ----------
2761 // total lines 100
2762 if (tot > 0) {
2763 percent = (100 * cur) / tot;
2764 } else {
2765 cur = tot = 0;
2766 percent = 100;
2767 }
Eric Andersen0ef24c62005-07-18 10:32:59 +00002768
Paul Fox8552aec2005-09-16 12:20:05 +00002769 trunc_at = columns < STATUS_BUFFER_LEN-1 ?
2770 columns : STATUS_BUFFER_LEN-1;
2771
2772 ret = snprintf((char *) status_buffer, trunc_at+1,
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002773#ifdef CONFIG_FEATURE_VI_READONLY
Paul Fox8552aec2005-09-16 12:20:05 +00002774 "%c %s%s%s %d/%d %d%%",
2775#else
2776 "%c %s%s %d/%d %d%%",
2777#endif
2778 (cmd_mode ? (cmd_mode == 2 ? 'R':'I'):'-'),
2779 (cfn != 0 ? (char *) cfn : "No file"),
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002780#ifdef CONFIG_FEATURE_VI_READONLY
Paul Fox8552aec2005-09-16 12:20:05 +00002781 ((vi_readonly || readonly) ? " [Read-only]" : ""),
2782#endif
2783 (file_modified ? " [modified]" : ""),
2784 cur, tot, percent);
2785
2786 if (ret >= 0 && ret < trunc_at)
2787 return ret; /* it all fit */
2788
2789 return trunc_at; /* had to truncate */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002790}
2791
2792//----- Force refresh of all Lines -----------------------------
2793static void redraw(int full_screen)
2794{
2795 place_cursor(0, 0, FALSE); // put cursor in correct place
2796 clear_to_eos(); // tel terminal to erase display
2797 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00002798 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002799 refresh(full_screen); // this will redraw the entire display
Paul Fox8552aec2005-09-16 12:20:05 +00002800 show_status_line();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002801}
2802
2803//----- Format a text[] line into a buffer ---------------------
2804static void format_line(Byte *dest, Byte *src, int li)
2805{
2806 int co;
2807 Byte c;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002808
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002809 for (co= 0; co < MAX_SCR_COLS; co++) {
2810 c= ' '; // assume blank
2811 if (li > 0 && co == 0) {
2812 c = '~'; // not first line, assume Tilde
2813 }
2814 // are there chars in text[] and have we gone past the end
2815 if (text < end && src < end) {
2816 c = *src++;
2817 }
2818 if (c == '\n')
2819 break;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002820 if (c > 127 && !Isprint(c)) {
2821 c = '.';
2822 }
2823 if (c < ' ' || c == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002824 if (c == '\t') {
2825 c = ' ';
2826 // co % 8 != 7
2827 for (; (co % tabstop) != (tabstop - 1); co++) {
2828 dest[co] = c;
2829 }
2830 } else {
2831 dest[co++] = '^';
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002832 if(c == 127)
2833 c = '?';
2834 else
2835 c += '@'; // make it visible
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002836 }
2837 }
2838 // the co++ is done here so that the column will
2839 // not be overwritten when we blank-out the rest of line
2840 dest[co] = c;
2841 if (src >= end)
2842 break;
2843 }
2844}
2845
2846//----- Refresh the changed screen lines -----------------------
2847// Copy the source line from text[] into the buffer and note
2848// if the current screenline is different from the new buffer.
2849// If they differ then that line needs redrawing on the terminal.
2850//
2851static void refresh(int full_screen)
2852{
2853 static int old_offset;
2854 int li, changed;
2855 Byte buf[MAX_SCR_COLS];
2856 Byte *tp, *sp; // pointer into text[] and screen[]
2857#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2858 int last_li= -2; // last line that changed- for optimizing cursor movement
2859#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2860
2861#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
2862 window_size_get(0);
2863#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
2864 sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
2865 tp = screenbegin; // index into text[] of top line
2866
2867 // compare text[] to screen[] and mark screen[] lines that need updating
2868 for (li = 0; li < rows - 1; li++) {
2869 int cs, ce; // column start & end
2870 memset(buf, ' ', MAX_SCR_COLS); // blank-out the buffer
2871 buf[MAX_SCR_COLS-1] = 0; // NULL terminate the buffer
2872 // format current text line into buf
2873 format_line(buf, tp, li);
2874
2875 // skip to the end of the current text[] line
2876 while (tp < end && *tp++ != '\n') /*no-op*/ ;
2877
2878 // see if there are any changes between vitual screen and buf
2879 changed = FALSE; // assume no change
2880 cs= 0;
2881 ce= columns-1;
2882 sp = &screen[li * columns]; // start of screen line
2883 if (full_screen) {
2884 // force re-draw of every single column from 0 - columns-1
2885 goto re0;
2886 }
2887 // compare newly formatted buffer with virtual screen
2888 // look forward for first difference between buf and screen
2889 for ( ; cs <= ce; cs++) {
2890 if (buf[cs + offset] != sp[cs]) {
2891 changed = TRUE; // mark for redraw
2892 break;
2893 }
2894 }
2895
2896 // look backward for last difference between buf and screen
2897 for ( ; ce >= cs; ce--) {
2898 if (buf[ce + offset] != sp[ce]) {
2899 changed = TRUE; // mark for redraw
2900 break;
2901 }
2902 }
2903 // now, cs is index of first diff, and ce is index of last diff
2904
2905 // if horz offset has changed, force a redraw
2906 if (offset != old_offset) {
2907 re0:
2908 changed = TRUE;
2909 }
2910
2911 // make a sanity check of columns indexes
2912 if (cs < 0) cs= 0;
2913 if (ce > columns-1) ce= columns-1;
2914 if (cs > ce) { cs= 0; ce= columns-1; }
2915 // is there a change between vitual screen and buf
2916 if (changed) {
2917 // copy changed part of buffer to virtual screen
2918 memmove(sp+cs, buf+(cs+offset), ce-cs+1);
2919
2920 // move cursor to column of first change
2921 if (offset != old_offset) {
2922 // opti_cur_move is still too stupid
2923 // to handle offsets correctly
2924 place_cursor(li, cs, FALSE);
2925 } else {
2926#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2927 // if this just the next line
2928 // try to optimize cursor movement
2929 // otherwise, use standard ESC sequence
2930 place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE);
2931 last_li= li;
2932#else /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2933 place_cursor(li, cs, FALSE); // use standard ESC sequence
2934#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2935 }
2936
2937 // write line out to terminal
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002938 {
2939 int nic = ce-cs+1;
Eric Andersena68ea1c2006-01-30 22:48:39 +00002940 char *out = (char*)sp+cs;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002941
2942 while(nic-- > 0) {
2943 putchar(*out);
2944 out++;
2945 }
2946 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002947#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2948 last_row = li;
2949#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2950 }
2951 }
2952
2953#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2954 place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE);
2955 last_row = crow;
2956#else
2957 place_cursor(crow, ccol, FALSE);
2958#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002959
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002960 if (offset != old_offset)
2961 old_offset = offset;
2962}
2963
Eric Andersen3f980402001-04-04 17:31:15 +00002964//---------------------------------------------------------------------
2965//----- the Ascii Chart -----------------------------------------------
2966//
2967// 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel
2968// 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si
2969// 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb
2970// 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us
2971// 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 '
2972// 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f /
2973// 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7
2974// 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ?
2975// 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G
2976// 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O
2977// 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W
2978// 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _
2979// 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g
2980// 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o
2981// 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w
2982// 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del
2983//---------------------------------------------------------------------
2984
2985//----- Execute a Vi Command -----------------------------------
2986static void do_cmd(Byte c)
2987{
2988 Byte c1, *p, *q, *msg, buf[9], *save_dot;
2989 int cnt, i, j, dir, yf;
2990
2991 c1 = c; // quiet the compiler
2992 cnt = yf = dir = 0; // quiet the compiler
2993 p = q = save_dot = msg = buf; // quiet the compiler
2994 memset(buf, '\0', 9); // clear buf
Eric Andersenbff7a602001-11-17 07:15:43 +00002995
Paul Fox8552aec2005-09-16 12:20:05 +00002996 show_status_line();
2997
Eric Andersenbff7a602001-11-17 07:15:43 +00002998 /* if this is a cursor key, skip these checks */
2999 switch (c) {
3000 case VI_K_UP:
3001 case VI_K_DOWN:
3002 case VI_K_LEFT:
3003 case VI_K_RIGHT:
3004 case VI_K_HOME:
3005 case VI_K_END:
3006 case VI_K_PAGEUP:
3007 case VI_K_PAGEDOWN:
3008 goto key_cmd_mode;
3009 }
3010
Eric Andersen3f980402001-04-04 17:31:15 +00003011 if (cmd_mode == 2) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003012 // flip-flop Insert/Replace mode
3013 if (c == VI_K_INSERT) goto dc_i;
Eric Andersen3f980402001-04-04 17:31:15 +00003014 // we are 'R'eplacing the current *dot with new char
3015 if (*dot == '\n') {
3016 // don't Replace past E-o-l
3017 cmd_mode = 1; // convert to insert
3018 } else {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003019 if (1 <= c || Isprint(c)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003020 if (c != 27)
3021 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3022 dot = char_insert(dot, c); // insert new char
3023 }
3024 goto dc1;
3025 }
3026 }
3027 if (cmd_mode == 1) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00003028 // hitting "Insert" twice means "R" replace mode
3029 if (c == VI_K_INSERT) goto dc5;
Eric Andersen3f980402001-04-04 17:31:15 +00003030 // insert the char c at "dot"
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003031 if (1 <= c || Isprint(c)) {
3032 dot = char_insert(dot, c);
Eric Andersen3f980402001-04-04 17:31:15 +00003033 }
3034 goto dc1;
3035 }
3036
Eric Andersenbff7a602001-11-17 07:15:43 +00003037key_cmd_mode:
Eric Andersen3f980402001-04-04 17:31:15 +00003038 switch (c) {
Eric Andersen822c3832001-05-07 17:37:43 +00003039 //case 0x01: // soh
3040 //case 0x09: // ht
3041 //case 0x0b: // vt
3042 //case 0x0e: // so
3043 //case 0x0f: // si
3044 //case 0x10: // dle
3045 //case 0x11: // dc1
3046 //case 0x13: // dc3
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003047#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen1c0d3112001-04-16 15:46:44 +00003048 case 0x14: // dc4 ctrl-T
Eric Andersen3f980402001-04-04 17:31:15 +00003049 crashme = (crashme == 0) ? 1 : 0;
Eric Andersen3f980402001-04-04 17:31:15 +00003050 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003051#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen822c3832001-05-07 17:37:43 +00003052 //case 0x16: // syn
3053 //case 0x17: // etb
3054 //case 0x18: // can
3055 //case 0x1c: // fs
3056 //case 0x1d: // gs
3057 //case 0x1e: // rs
3058 //case 0x1f: // us
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003059 //case '!': // !-
3060 //case '#': // #-
3061 //case '&': // &-
3062 //case '(': // (-
3063 //case ')': // )-
3064 //case '*': // *-
3065 //case ',': // ,-
3066 //case '=': // =-
3067 //case '@': // @-
3068 //case 'F': // F-
3069 //case 'K': // K-
3070 //case 'Q': // Q-
3071 //case 'S': // S-
3072 //case 'T': // T-
3073 //case 'V': // V-
3074 //case '[': // [-
3075 //case '\\': // \-
3076 //case ']': // ]-
3077 //case '_': // _-
3078 //case '`': // `-
3079 //case 'g': // g-
Eric Andersen1c0d3112001-04-16 15:46:44 +00003080 //case 'u': // u- FIXME- there is no undo
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003081 //case 'v': // v-
Eric Andersen3f980402001-04-04 17:31:15 +00003082 default: // unrecognised command
3083 buf[0] = c;
3084 buf[1] = '\0';
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003085 if (c < ' ') {
Eric Andersen3f980402001-04-04 17:31:15 +00003086 buf[0] = '^';
3087 buf[1] = c + '@';
3088 buf[2] = '\0';
3089 }
3090 ni((Byte *) buf);
3091 end_cmd_q(); // stop adding to q
3092 case 0x00: // nul- ignore
3093 break;
3094 case 2: // ctrl-B scroll up full screen
3095 case VI_K_PAGEUP: // Cursor Key Page Up
3096 dot_scroll(rows - 2, -1);
3097 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003098#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +00003099 case 0x03: // ctrl-C interrupt
3100 longjmp(restart, 1);
3101 break;
3102 case 26: // ctrl-Z suspend
3103 suspend_sig(SIGTSTP);
3104 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003105#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Eric Andersen3f980402001-04-04 17:31:15 +00003106 case 4: // ctrl-D scroll down half screen
3107 dot_scroll((rows - 2) / 2, 1);
3108 break;
3109 case 5: // ctrl-E scroll down one line
3110 dot_scroll(1, 1);
3111 break;
3112 case 6: // ctrl-F scroll down full screen
3113 case VI_K_PAGEDOWN: // Cursor Key Page Down
3114 dot_scroll(rows - 2, 1);
3115 break;
3116 case 7: // ctrl-G show current status
Paul Fox8552aec2005-09-16 12:20:05 +00003117 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003118 break;
3119 case 'h': // h- move left
3120 case VI_K_LEFT: // cursor key Left
Paul Foxd13b90b2005-07-18 22:17:25 +00003121 case 8: // ctrl-H- move left (This may be ERASE char)
3122 case 127: // DEL- move left (This may be ERASE char)
Eric Andersen3f980402001-04-04 17:31:15 +00003123 if (cmdcnt-- > 1) {
3124 do_cmd(c);
3125 } // repeat cnt
3126 dot_left();
3127 break;
3128 case 10: // Newline ^J
3129 case 'j': // j- goto next line, same col
3130 case VI_K_DOWN: // cursor key Down
3131 if (cmdcnt-- > 1) {
3132 do_cmd(c);
3133 } // repeat cnt
3134 dot_next(); // go to next B-o-l
3135 dot = move_to_col(dot, ccol + offset); // try stay in same col
3136 break;
3137 case 12: // ctrl-L force redraw whole screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003138 case 18: // ctrl-R force redraw
Eric Andersen822c3832001-05-07 17:37:43 +00003139 place_cursor(0, 0, FALSE); // put cursor in correct place
Eric Andersen3f980402001-04-04 17:31:15 +00003140 clear_to_eos(); // tel terminal to erase display
3141 (void) mysleep(10);
3142 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00003143 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003144 refresh(TRUE); // this will redraw the entire display
3145 break;
3146 case 13: // Carriage Return ^M
3147 case '+': // +- goto next line
3148 if (cmdcnt-- > 1) {
3149 do_cmd(c);
3150 } // repeat cnt
3151 dot_next();
3152 dot_skip_over_ws();
3153 break;
3154 case 21: // ctrl-U scroll up half screen
3155 dot_scroll((rows - 2) / 2, -1);
3156 break;
3157 case 25: // ctrl-Y scroll up one line
3158 dot_scroll(1, -1);
3159 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003160 case 27: // esc
Eric Andersen3f980402001-04-04 17:31:15 +00003161 if (cmd_mode == 0)
3162 indicate_error(c);
3163 cmd_mode = 0; // stop insrting
3164 end_cmd_q();
Paul Fox8552aec2005-09-16 12:20:05 +00003165 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003166 break;
3167 case ' ': // move right
3168 case 'l': // move right
3169 case VI_K_RIGHT: // Cursor Key Right
3170 if (cmdcnt-- > 1) {
3171 do_cmd(c);
3172 } // repeat cnt
3173 dot_right();
3174 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003175#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003176 case '"': // "- name a register to use for Delete/Yank
3177 c1 = get_one_char();
3178 c1 = tolower(c1);
3179 if (islower(c1)) {
3180 YDreg = c1 - 'a';
3181 } else {
3182 indicate_error(c);
3183 }
3184 break;
3185 case '\'': // '- goto a specific mark
3186 c1 = get_one_char();
3187 c1 = tolower(c1);
3188 if (islower(c1)) {
3189 c1 = c1 - 'a';
3190 // get the b-o-l
3191 q = mark[(int) c1];
3192 if (text <= q && q < end) {
3193 dot = q;
3194 dot_begin(); // go to B-o-l
3195 dot_skip_over_ws();
3196 }
3197 } else if (c1 == '\'') { // goto previous context
3198 dot = swap_context(dot); // swap current and previous context
3199 dot_begin(); // go to B-o-l
3200 dot_skip_over_ws();
3201 } else {
3202 indicate_error(c);
3203 }
3204 break;
3205 case 'm': // m- Mark a line
3206 // this is really stupid. If there are any inserts or deletes
3207 // between text[0] and dot then this mark will not point to the
3208 // correct location! It could be off by many lines!
3209 // Well..., at least its quick and dirty.
3210 c1 = get_one_char();
3211 c1 = tolower(c1);
3212 if (islower(c1)) {
3213 c1 = c1 - 'a';
3214 // remember the line
3215 mark[(int) c1] = dot;
3216 } else {
3217 indicate_error(c);
3218 }
3219 break;
3220 case 'P': // P- Put register before
3221 case 'p': // p- put register after
3222 p = reg[YDreg];
3223 if (p == 0) {
3224 psbs("Nothing in register %c", what_reg());
3225 break;
3226 }
3227 // are we putting whole lines or strings
3228 if (strchr((char *) p, '\n') != NULL) {
3229 if (c == 'P') {
3230 dot_begin(); // putting lines- Put above
3231 }
3232 if (c == 'p') {
3233 // are we putting after very last line?
3234 if (end_line(dot) == (end - 1)) {
3235 dot = end; // force dot to end of text[]
3236 } else {
3237 dot_next(); // next line, then put before
3238 }
3239 }
3240 } else {
3241 if (c == 'p')
3242 dot_right(); // move to right, can move to NL
3243 }
3244 dot = string_insert(dot, p); // insert the string
3245 end_cmd_q(); // stop adding to q
3246 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003247 case 'U': // U- Undo; replace current line with original version
3248 if (reg[Ureg] != 0) {
3249 p = begin_line(dot);
3250 q = end_line(dot);
3251 p = text_hole_delete(p, q); // delete cur line
3252 p = string_insert(p, reg[Ureg]); // insert orig line
3253 dot = p;
3254 dot_skip_over_ws();
3255 }
3256 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003257#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003258 case '$': // $- goto end of line
3259 case VI_K_END: // Cursor Key End
3260 if (cmdcnt-- > 1) {
3261 do_cmd(c);
3262 } // repeat cnt
Glenn L McGrathee829062004-01-21 10:59:45 +00003263 dot = end_line(dot);
Eric Andersen3f980402001-04-04 17:31:15 +00003264 break;
3265 case '%': // %- find matching char of pair () [] {}
3266 for (q = dot; q < end && *q != '\n'; q++) {
3267 if (strchr("()[]{}", *q) != NULL) {
3268 // we found half of a pair
3269 p = find_pair(q, *q);
3270 if (p == NULL) {
3271 indicate_error(c);
3272 } else {
3273 dot = p;
3274 }
3275 break;
3276 }
3277 }
3278 if (*q == '\n')
3279 indicate_error(c);
3280 break;
3281 case 'f': // f- forward to a user specified char
3282 last_forward_char = get_one_char(); // get the search char
3283 //
Eric Andersenaff114c2004-04-14 17:51:38 +00003284 // dont separate these two commands. 'f' depends on ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003285 //
Paul Foxd13b90b2005-07-18 22:17:25 +00003286 //**** fall thru to ... ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003287 case ';': // ;- look at rest of line for last forward char
3288 if (cmdcnt-- > 1) {
Eric Andersen822c3832001-05-07 17:37:43 +00003289 do_cmd(';');
Eric Andersen3f980402001-04-04 17:31:15 +00003290 } // repeat cnt
Eric Andersen822c3832001-05-07 17:37:43 +00003291 if (last_forward_char == 0) break;
Eric Andersen3f980402001-04-04 17:31:15 +00003292 q = dot + 1;
3293 while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
3294 q++;
3295 }
3296 if (*q == last_forward_char)
3297 dot = q;
3298 break;
3299 case '-': // -- goto prev line
3300 if (cmdcnt-- > 1) {
3301 do_cmd(c);
3302 } // repeat cnt
3303 dot_prev();
3304 dot_skip_over_ws();
3305 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003306#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003307 case '.': // .- repeat the last modifying command
3308 // Stuff the last_modifying_cmd back into stdin
3309 // and let it be re-executed.
3310 if (last_modifying_cmd != 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +00003311 ioq = ioq_start = (Byte *) bb_xstrdup((char *) last_modifying_cmd);
Eric Andersen3f980402001-04-04 17:31:15 +00003312 }
3313 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003314#endif /* CONFIG_FEATURE_VI_DOT_CMD */
3315#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +00003316 case '?': // /- search for a pattern
3317 case '/': // /- search for a pattern
3318 buf[0] = c;
3319 buf[1] = '\0';
3320 q = get_input_line(buf); // get input line- use "status line"
3321 if (strlen((char *) q) == 1)
3322 goto dc3; // if no pat re-use old pat
3323 if (strlen((char *) q) > 1) { // new pat- save it and find
3324 // there is a new pat
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00003325 free(last_search_pattern);
Manuel Novoa III cad53642003-03-19 09:13:01 +00003326 last_search_pattern = (Byte *) bb_xstrdup((char *) q);
Eric Andersen3f980402001-04-04 17:31:15 +00003327 goto dc3; // now find the pattern
3328 }
3329 // user changed mind and erased the "/"- do nothing
3330 break;
3331 case 'N': // N- backward search for last pattern
3332 if (cmdcnt-- > 1) {
3333 do_cmd(c);
3334 } // repeat cnt
3335 dir = BACK; // assume BACKWARD search
3336 p = dot - 1;
3337 if (last_search_pattern[0] == '?') {
3338 dir = FORWARD;
3339 p = dot + 1;
3340 }
3341 goto dc4; // now search for pattern
3342 break;
3343 case 'n': // n- repeat search for last pattern
3344 // search rest of text[] starting at next char
3345 // if search fails return orignal "p" not the "p+1" address
3346 if (cmdcnt-- > 1) {
3347 do_cmd(c);
3348 } // repeat cnt
3349 dc3:
3350 if (last_search_pattern == 0) {
3351 msg = (Byte *) "No previous regular expression";
3352 goto dc2;
3353 }
3354 if (last_search_pattern[0] == '/') {
3355 dir = FORWARD; // assume FORWARD search
3356 p = dot + 1;
3357 }
3358 if (last_search_pattern[0] == '?') {
3359 dir = BACK;
3360 p = dot - 1;
3361 }
3362 dc4:
3363 q = char_search(p, last_search_pattern + 1, dir, FULL);
3364 if (q != NULL) {
3365 dot = q; // good search, update "dot"
3366 msg = (Byte *) "";
3367 goto dc2;
3368 }
3369 // no pattern found between "dot" and "end"- continue at top
3370 p = text;
3371 if (dir == BACK) {
3372 p = end - 1;
3373 }
3374 q = char_search(p, last_search_pattern + 1, dir, FULL);
3375 if (q != NULL) { // found something
3376 dot = q; // found new pattern- goto it
3377 msg = (Byte *) "search hit BOTTOM, continuing at TOP";
3378 if (dir == BACK) {
3379 msg = (Byte *) "search hit TOP, continuing at BOTTOM";
3380 }
3381 } else {
3382 msg = (Byte *) "Pattern not found";
3383 }
3384 dc2:
Paul Fox8552aec2005-09-16 12:20:05 +00003385 if (*msg) psbs("%s", msg);
Eric Andersen3f980402001-04-04 17:31:15 +00003386 break;
3387 case '{': // {- move backward paragraph
3388 q = char_search(dot, (Byte *) "\n\n", BACK, FULL);
3389 if (q != NULL) { // found blank line
3390 dot = next_line(q); // move to next blank line
3391 }
3392 break;
3393 case '}': // }- move forward paragraph
3394 q = char_search(dot, (Byte *) "\n\n", FORWARD, FULL);
3395 if (q != NULL) { // found blank line
3396 dot = next_line(q); // move to next blank line
3397 }
3398 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003399#endif /* CONFIG_FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +00003400 case '0': // 0- goto begining of line
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003401 case '1': // 1-
3402 case '2': // 2-
3403 case '3': // 3-
3404 case '4': // 4-
3405 case '5': // 5-
3406 case '6': // 6-
3407 case '7': // 7-
3408 case '8': // 8-
3409 case '9': // 9-
Eric Andersen3f980402001-04-04 17:31:15 +00003410 if (c == '0' && cmdcnt < 1) {
3411 dot_begin(); // this was a standalone zero
3412 } else {
3413 cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number
3414 }
3415 break;
3416 case ':': // :- the colon mode commands
Eric Andersen3f980402001-04-04 17:31:15 +00003417 p = get_input_line((Byte *) ":"); // get input line- use "status line"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003418#ifdef CONFIG_FEATURE_VI_COLON
Eric Andersen3f980402001-04-04 17:31:15 +00003419 colon(p); // execute the command
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003420#else /* CONFIG_FEATURE_VI_COLON */
Eric Andersen822c3832001-05-07 17:37:43 +00003421 if (*p == ':')
3422 p++; // move past the ':'
3423 cnt = strlen((char *) p);
3424 if (cnt <= 0)
3425 break;
3426 if (strncasecmp((char *) p, "quit", cnt) == 0 ||
3427 strncasecmp((char *) p, "q!", cnt) == 0) { // delete lines
Matt Kraai1f0c4362001-12-20 23:13:26 +00003428 if (file_modified && p[1] != '!') {
Eric Andersen3f980402001-04-04 17:31:15 +00003429 psbs("No write since last change (:quit! overrides)");
3430 } else {
3431 editing = 0;
3432 }
Eric Andersen822c3832001-05-07 17:37:43 +00003433 } else if (strncasecmp((char *) p, "write", cnt) == 0 ||
Robert Griebla71389b2002-07-31 21:22:21 +00003434 strncasecmp((char *) p, "wq", cnt) == 0 ||
3435 strncasecmp((char *) p, "x", cnt) == 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003436 cnt = file_write(cfn, text, end - 1);
Paul Fox61e45db2005-10-09 14:43:22 +00003437 if (cnt < 0) {
3438 if (cnt == -1)
3439 psbs("Write error: %s", strerror(errno));
3440 } else {
3441 file_modified = 0;
3442 last_file_modified = -1;
3443 psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt);
3444 if (p[0] == 'x' || p[1] == 'q') {
3445 editing = 0;
3446 }
Eric Andersen3f980402001-04-04 17:31:15 +00003447 }
Eric Andersen822c3832001-05-07 17:37:43 +00003448 } else if (strncasecmp((char *) p, "file", cnt) == 0 ) {
Paul Fox8552aec2005-09-16 12:20:05 +00003449 last_status_cksum = 0; // force status update
Eric Andersen822c3832001-05-07 17:37:43 +00003450 } else if (sscanf((char *) p, "%d", &j) > 0) {
3451 dot = find_line(j); // go to line # j
3452 dot_skip_over_ws();
Eric Andersen3f980402001-04-04 17:31:15 +00003453 } else { // unrecognised cmd
Eric Andersen822c3832001-05-07 17:37:43 +00003454 ni((Byte *) p);
Eric Andersen3f980402001-04-04 17:31:15 +00003455 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003456#endif /* CONFIG_FEATURE_VI_COLON */
Eric Andersen3f980402001-04-04 17:31:15 +00003457 break;
3458 case '<': // <- Left shift something
3459 case '>': // >- Right shift something
3460 cnt = count_lines(text, dot); // remember what line we are on
3461 c1 = get_one_char(); // get the type of thing to delete
3462 find_range(&p, &q, c1);
3463 (void) yank_delete(p, q, 1, YANKONLY); // save copy before change
3464 p = begin_line(p);
3465 q = end_line(q);
3466 i = count_lines(p, q); // # of lines we are shifting
3467 for ( ; i > 0; i--, p = next_line(p)) {
3468 if (c == '<') {
3469 // shift left- remove tab or 8 spaces
3470 if (*p == '\t') {
3471 // shrink buffer 1 char
3472 (void) text_hole_delete(p, p);
3473 } else if (*p == ' ') {
3474 // we should be calculating columns, not just SPACE
3475 for (j = 0; *p == ' ' && j < tabstop; j++) {
3476 (void) text_hole_delete(p, p);
3477 }
3478 }
3479 } else if (c == '>') {
3480 // shift right -- add tab or 8 spaces
3481 (void) char_insert(p, '\t');
3482 }
3483 }
3484 dot = find_line(cnt); // what line were we on
3485 dot_skip_over_ws();
3486 end_cmd_q(); // stop adding to q
3487 break;
3488 case 'A': // A- append at e-o-l
3489 dot_end(); // go to e-o-l
3490 //**** fall thru to ... 'a'
3491 case 'a': // a- append after current char
3492 if (*dot != '\n')
3493 dot++;
3494 goto dc_i;
3495 break;
3496 case 'B': // B- back a blank-delimited Word
3497 case 'E': // E- end of a blank-delimited word
3498 case 'W': // W- forward a blank-delimited word
3499 if (cmdcnt-- > 1) {
3500 do_cmd(c);
3501 } // repeat cnt
3502 dir = FORWARD;
3503 if (c == 'B')
3504 dir = BACK;
3505 if (c == 'W' || isspace(dot[dir])) {
3506 dot = skip_thing(dot, 1, dir, S_TO_WS);
3507 dot = skip_thing(dot, 2, dir, S_OVER_WS);
3508 }
3509 if (c != 'W')
3510 dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
3511 break;
3512 case 'C': // C- Change to e-o-l
3513 case 'D': // D- delete to e-o-l
3514 save_dot = dot;
3515 dot = dollar_line(dot); // move to before NL
3516 // copy text into a register and delete
3517 dot = yank_delete(save_dot, dot, 0, YANKDEL); // delete to e-o-l
3518 if (c == 'C')
3519 goto dc_i; // start inserting
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003520#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003521 if (c == 'D')
3522 end_cmd_q(); // stop adding to q
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003523#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +00003524 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003525 case 'G': // G- goto to a line number (default= E-O-F)
3526 dot = end - 1; // assume E-O-F
Eric Andersen1c0d3112001-04-16 15:46:44 +00003527 if (cmdcnt > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00003528 dot = find_line(cmdcnt); // what line is #cmdcnt
Eric Andersen1c0d3112001-04-16 15:46:44 +00003529 }
3530 dot_skip_over_ws();
3531 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003532 case 'H': // H- goto top line on screen
3533 dot = screenbegin;
3534 if (cmdcnt > (rows - 1)) {
3535 cmdcnt = (rows - 1);
3536 }
3537 if (cmdcnt-- > 1) {
3538 do_cmd('+');
3539 } // repeat cnt
3540 dot_skip_over_ws();
3541 break;
3542 case 'I': // I- insert before first non-blank
3543 dot_begin(); // 0
3544 dot_skip_over_ws();
3545 //**** fall thru to ... 'i'
3546 case 'i': // i- insert before current char
3547 case VI_K_INSERT: // Cursor Key Insert
3548 dc_i:
3549 cmd_mode = 1; // start insrting
Eric Andersen3f980402001-04-04 17:31:15 +00003550 break;
3551 case 'J': // J- join current and next lines together
3552 if (cmdcnt-- > 2) {
3553 do_cmd(c);
3554 } // repeat cnt
3555 dot_end(); // move to NL
3556 if (dot < end - 1) { // make sure not last char in text[]
3557 *dot++ = ' '; // replace NL with space
Paul Fox8552aec2005-09-16 12:20:05 +00003558 file_modified++;
Eric Andersen3f980402001-04-04 17:31:15 +00003559 while (isblnk(*dot)) { // delete leading WS
3560 dot_delete();
3561 }
3562 }
3563 end_cmd_q(); // stop adding to q
3564 break;
3565 case 'L': // L- goto bottom line on screen
3566 dot = end_screen();
3567 if (cmdcnt > (rows - 1)) {
3568 cmdcnt = (rows - 1);
3569 }
3570 if (cmdcnt-- > 1) {
3571 do_cmd('-');
3572 } // repeat cnt
3573 dot_begin();
3574 dot_skip_over_ws();
3575 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003576 case 'M': // M- goto middle line on screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003577 dot = screenbegin;
3578 for (cnt = 0; cnt < (rows-1) / 2; cnt++)
3579 dot = next_line(dot);
3580 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003581 case 'O': // O- open a empty line above
Eric Andersen822c3832001-05-07 17:37:43 +00003582 // 0i\n ESC -i
Eric Andersen3f980402001-04-04 17:31:15 +00003583 p = begin_line(dot);
3584 if (p[-1] == '\n') {
3585 dot_prev();
3586 case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
3587 dot_end();
3588 dot = char_insert(dot, '\n');
3589 } else {
3590 dot_begin(); // 0
Eric Andersen822c3832001-05-07 17:37:43 +00003591 dot = char_insert(dot, '\n'); // i\n ESC
Eric Andersen3f980402001-04-04 17:31:15 +00003592 dot_prev(); // -
3593 }
3594 goto dc_i;
3595 break;
3596 case 'R': // R- continuous Replace char
Eric Andersen1c0d3112001-04-16 15:46:44 +00003597 dc5:
Eric Andersen3f980402001-04-04 17:31:15 +00003598 cmd_mode = 2;
Eric Andersen3f980402001-04-04 17:31:15 +00003599 break;
3600 case 'X': // X- delete char before dot
3601 case 'x': // x- delete the current char
3602 case 's': // s- substitute the current char
3603 if (cmdcnt-- > 1) {
3604 do_cmd(c);
3605 } // repeat cnt
3606 dir = 0;
3607 if (c == 'X')
3608 dir = -1;
3609 if (dot[dir] != '\n') {
3610 if (c == 'X')
3611 dot--; // delete prev char
3612 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3613 }
3614 if (c == 's')
3615 goto dc_i; // start insrting
3616 end_cmd_q(); // stop adding to q
3617 break;
3618 case 'Z': // Z- if modified, {write}; exit
3619 // ZZ means to save file (if necessary), then exit
3620 c1 = get_one_char();
3621 if (c1 != 'Z') {
3622 indicate_error(c);
3623 break;
3624 }
Matt Kraai1f0c4362001-12-20 23:13:26 +00003625 if (file_modified
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003626#ifdef CONFIG_FEATURE_VI_READONLY
Matt Kraai1f0c4362001-12-20 23:13:26 +00003627 && ! vi_readonly
3628 && ! readonly
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003629#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen3f980402001-04-04 17:31:15 +00003630 ) {
3631 cnt = file_write(cfn, text, end - 1);
Paul Fox61e45db2005-10-09 14:43:22 +00003632 if (cnt < 0) {
3633 if (cnt == -1)
3634 psbs("Write error: %s", strerror(errno));
3635 } else if (cnt == (end - 1 - text + 1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003636 editing = 0;
3637 }
3638 } else {
3639 editing = 0;
3640 }
3641 break;
3642 case '^': // ^- move to first non-blank on line
3643 dot_begin();
3644 dot_skip_over_ws();
3645 break;
3646 case 'b': // b- back a word
3647 case 'e': // e- end of word
3648 if (cmdcnt-- > 1) {
3649 do_cmd(c);
3650 } // repeat cnt
3651 dir = FORWARD;
3652 if (c == 'b')
3653 dir = BACK;
3654 if ((dot + dir) < text || (dot + dir) > end - 1)
3655 break;
3656 dot += dir;
3657 if (isspace(*dot)) {
3658 dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
3659 }
3660 if (isalnum(*dot) || *dot == '_') {
3661 dot = skip_thing(dot, 1, dir, S_END_ALNUM);
3662 } else if (ispunct(*dot)) {
3663 dot = skip_thing(dot, 1, dir, S_END_PUNCT);
3664 }
3665 break;
3666 case 'c': // c- change something
3667 case 'd': // d- delete something
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003668#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003669 case 'y': // y- yank something
3670 case 'Y': // Y- Yank a line
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003671#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003672 yf = YANKDEL; // assume either "c" or "d"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003673#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003674 if (c == 'y' || c == 'Y')
3675 yf = YANKONLY;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003676#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003677 c1 = 'y';
3678 if (c != 'Y')
3679 c1 = get_one_char(); // get the type of thing to delete
3680 find_range(&p, &q, c1);
3681 if (c1 == 27) { // ESC- user changed mind and wants out
3682 c = c1 = 27; // Escape- do nothing
3683 } else if (strchr("wW", c1)) {
3684 if (c == 'c') {
3685 // don't include trailing WS as part of word
3686 while (isblnk(*q)) {
3687 if (q <= text || q[-1] == '\n')
3688 break;
3689 q--;
3690 }
3691 }
3692 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen822c3832001-05-07 17:37:43 +00003693 } else if (strchr("^0bBeEft$", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003694 // single line copy text into a register and delete
3695 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen1c0d3112001-04-16 15:46:44 +00003696 } else if (strchr("cdykjHL%+-{}\r\n", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003697 // multiple line copy text into a register and delete
3698 dot = yank_delete(p, q, 1, yf); // delete lines
Eric Andersen1c0d3112001-04-16 15:46:44 +00003699 if (c == 'c') {
3700 dot = char_insert(dot, '\n');
3701 // on the last line of file don't move to prev line
3702 if (dot != (end-1)) {
3703 dot_prev();
3704 }
3705 } else if (c == 'd') {
Eric Andersen3f980402001-04-04 17:31:15 +00003706 dot_begin();
3707 dot_skip_over_ws();
3708 }
3709 } else {
3710 // could not recognize object
3711 c = c1 = 27; // error-
3712 indicate_error(c);
3713 }
3714 if (c1 != 27) {
3715 // if CHANGING, not deleting, start inserting after the delete
3716 if (c == 'c') {
3717 strcpy((char *) buf, "Change");
3718 goto dc_i; // start inserting
3719 }
3720 if (c == 'd') {
3721 strcpy((char *) buf, "Delete");
3722 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003723#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003724 if (c == 'y' || c == 'Y') {
3725 strcpy((char *) buf, "Yank");
3726 }
3727 p = reg[YDreg];
3728 q = p + strlen((char *) p);
3729 for (cnt = 0; p <= q; p++) {
3730 if (*p == '\n')
3731 cnt++;
3732 }
3733 psb("%s %d lines (%d chars) using [%c]",
3734 buf, cnt, strlen((char *) reg[YDreg]), what_reg());
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003735#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003736 end_cmd_q(); // stop adding to q
3737 }
3738 break;
3739 case 'k': // k- goto prev line, same col
3740 case VI_K_UP: // cursor key Up
3741 if (cmdcnt-- > 1) {
3742 do_cmd(c);
3743 } // repeat cnt
3744 dot_prev();
3745 dot = move_to_col(dot, ccol + offset); // try stay in same col
3746 break;
3747 case 'r': // r- replace the current char with user input
3748 c1 = get_one_char(); // get the replacement char
3749 if (*dot != '\n') {
3750 *dot = c1;
Paul Fox8552aec2005-09-16 12:20:05 +00003751 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003752 }
3753 end_cmd_q(); // stop adding to q
3754 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003755 case 't': // t- move to char prior to next x
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00003756 last_forward_char = get_one_char();
3757 do_cmd(';');
3758 if (*dot == last_forward_char)
3759 dot_left();
3760 last_forward_char= 0;
Eric Andersen822c3832001-05-07 17:37:43 +00003761 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003762 case 'w': // w- forward a word
3763 if (cmdcnt-- > 1) {
3764 do_cmd(c);
3765 } // repeat cnt
3766 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
3767 dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
3768 } else if (ispunct(*dot)) { // we are on PUNCT
3769 dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
3770 }
3771 if (dot < end - 1)
3772 dot++; // move over word
3773 if (isspace(*dot)) {
3774 dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
3775 }
3776 break;
3777 case 'z': // z-
3778 c1 = get_one_char(); // get the replacement char
3779 cnt = 0;
3780 if (c1 == '.')
3781 cnt = (rows - 2) / 2; // put dot at center
3782 if (c1 == '-')
3783 cnt = rows - 2; // put dot at bottom
3784 screenbegin = begin_line(dot); // start dot at top
3785 dot_scroll(cnt, -1);
3786 break;
3787 case '|': // |- move to column "cmdcnt"
3788 dot = move_to_col(dot, cmdcnt - 1); // try to move to column
3789 break;
3790 case '~': // ~- flip the case of letters a-z -> A-Z
3791 if (cmdcnt-- > 1) {
3792 do_cmd(c);
3793 } // repeat cnt
3794 if (islower(*dot)) {
3795 *dot = toupper(*dot);
Paul Fox8552aec2005-09-16 12:20:05 +00003796 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003797 } else if (isupper(*dot)) {
3798 *dot = tolower(*dot);
Paul Fox8552aec2005-09-16 12:20:05 +00003799 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003800 }
3801 dot_right();
3802 end_cmd_q(); // stop adding to q
3803 break;
3804 //----- The Cursor and Function Keys -----------------------------
3805 case VI_K_HOME: // Cursor Key Home
3806 dot_begin();
3807 break;
3808 // The Fn keys could point to do_macro which could translate them
3809 case VI_K_FUN1: // Function Key F1
3810 case VI_K_FUN2: // Function Key F2
3811 case VI_K_FUN3: // Function Key F3
3812 case VI_K_FUN4: // Function Key F4
3813 case VI_K_FUN5: // Function Key F5
3814 case VI_K_FUN6: // Function Key F6
3815 case VI_K_FUN7: // Function Key F7
3816 case VI_K_FUN8: // Function Key F8
3817 case VI_K_FUN9: // Function Key F9
3818 case VI_K_FUN10: // Function Key F10
3819 case VI_K_FUN11: // Function Key F11
3820 case VI_K_FUN12: // Function Key F12
3821 break;
3822 }
3823
3824 dc1:
3825 // if text[] just became empty, add back an empty line
3826 if (end == text) {
3827 (void) char_insert(text, '\n'); // start empty buf with dummy line
3828 dot = text;
3829 }
3830 // it is OK for dot to exactly equal to end, otherwise check dot validity
3831 if (dot != end) {
3832 dot = bound_dot(dot); // make sure "dot" is valid
3833 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003834#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003835 check_context(c); // update the current context
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003836#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003837
3838 if (!isdigit(c))
3839 cmdcnt = 0; // cmd was not a number, reset cmdcnt
3840 cnt = dot - begin_line(dot);
3841 // Try to stay off of the Newline
3842 if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
3843 dot--;
3844}
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003845
3846#ifdef CONFIG_FEATURE_VI_CRASHME
3847static int totalcmds = 0;
3848static int Mp = 85; // Movement command Probability
3849static int Np = 90; // Non-movement command Probability
3850static int Dp = 96; // Delete command Probability
3851static int Ip = 97; // Insert command Probability
3852static int Yp = 98; // Yank command Probability
3853static int Pp = 99; // Put command Probability
3854static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
3855char chars[20] = "\t012345 abcdABCD-=.$";
3856char *words[20] = { "this", "is", "a", "test",
3857 "broadcast", "the", "emergency", "of",
3858 "system", "quick", "brown", "fox",
3859 "jumped", "over", "lazy", "dogs",
3860 "back", "January", "Febuary", "March"
3861};
3862char *lines[20] = {
3863 "You should have received a copy of the GNU General Public License\n",
3864 "char c, cm, *cmd, *cmd1;\n",
3865 "generate a command by percentages\n",
3866 "Numbers may be typed as a prefix to some commands.\n",
3867 "Quit, discarding changes!\n",
3868 "Forced write, if permission originally not valid.\n",
3869 "In general, any ex or ed command (such as substitute or delete).\n",
3870 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3871 "Please get w/ me and I will go over it with you.\n",
3872 "The following is a list of scheduled, committed changes.\n",
3873 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3874 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3875 "Any question about transactions please contact Sterling Huxley.\n",
3876 "I will try to get back to you by Friday, December 31.\n",
3877 "This Change will be implemented on Friday.\n",
3878 "Let me know if you have problems accessing this;\n",
3879 "Sterling Huxley recently added you to the access list.\n",
3880 "Would you like to go to lunch?\n",
3881 "The last command will be automatically run.\n",
3882 "This is too much english for a computer geek.\n",
3883};
3884char *multilines[20] = {
3885 "You should have received a copy of the GNU General Public License\n",
3886 "char c, cm, *cmd, *cmd1;\n",
3887 "generate a command by percentages\n",
3888 "Numbers may be typed as a prefix to some commands.\n",
3889 "Quit, discarding changes!\n",
3890 "Forced write, if permission originally not valid.\n",
3891 "In general, any ex or ed command (such as substitute or delete).\n",
3892 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3893 "Please get w/ me and I will go over it with you.\n",
3894 "The following is a list of scheduled, committed changes.\n",
3895 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3896 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3897 "Any question about transactions please contact Sterling Huxley.\n",
3898 "I will try to get back to you by Friday, December 31.\n",
3899 "This Change will be implemented on Friday.\n",
3900 "Let me know if you have problems accessing this;\n",
3901 "Sterling Huxley recently added you to the access list.\n",
3902 "Would you like to go to lunch?\n",
3903 "The last command will be automatically run.\n",
3904 "This is too much english for a computer geek.\n",
3905};
3906
3907// create a random command to execute
3908static void crash_dummy()
3909{
3910 static int sleeptime; // how long to pause between commands
3911 char c, cm, *cmd, *cmd1;
3912 int i, cnt, thing, rbi, startrbi, percent;
3913
3914 // "dot" movement commands
3915 cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
3916
3917 // is there already a command running?
3918 if (readed_for_parse > 0)
3919 goto cd1;
3920 cd0:
3921 startrbi = rbi = 0;
3922 sleeptime = 0; // how long to pause between commands
3923 memset(readbuffer, '\0', BUFSIZ); // clear the read buffer
3924 // generate a command by percentages
3925 percent = (int) lrand48() % 100; // get a number from 0-99
3926 if (percent < Mp) { // Movement commands
3927 // available commands
3928 cmd = cmd1;
3929 M++;
3930 } else if (percent < Np) { // non-movement commands
3931 cmd = "mz<>\'\""; // available commands
3932 N++;
3933 } else if (percent < Dp) { // Delete commands
3934 cmd = "dx"; // available commands
3935 D++;
3936 } else if (percent < Ip) { // Inset commands
3937 cmd = "iIaAsrJ"; // available commands
3938 I++;
3939 } else if (percent < Yp) { // Yank commands
3940 cmd = "yY"; // available commands
3941 Y++;
3942 } else if (percent < Pp) { // Put commands
3943 cmd = "pP"; // available commands
3944 P++;
3945 } else {
3946 // We do not know how to handle this command, try again
3947 U++;
3948 goto cd0;
3949 }
3950 // randomly pick one of the available cmds from "cmd[]"
3951 i = (int) lrand48() % strlen(cmd);
3952 cm = cmd[i];
3953 if (strchr(":\024", cm))
3954 goto cd0; // dont allow colon or ctrl-T commands
3955 readbuffer[rbi++] = cm; // put cmd into input buffer
3956
3957 // now we have the command-
3958 // there are 1, 2, and multi char commands
3959 // find out which and generate the rest of command as necessary
3960 if (strchr("dmryz<>\'\"", cm)) { // 2-char commands
3961 cmd1 = " \n\r0$^-+wWeEbBhjklHL";
3962 if (cm == 'm' || cm == '\'' || cm == '\"') { // pick a reg[]
3963 cmd1 = "abcdefghijklmnopqrstuvwxyz";
3964 }
3965 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3966 c = cmd1[thing];
3967 readbuffer[rbi++] = c; // add movement to input buffer
3968 }
3969 if (strchr("iIaAsc", cm)) { // multi-char commands
3970 if (cm == 'c') {
3971 // change some thing
3972 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3973 c = cmd1[thing];
3974 readbuffer[rbi++] = c; // add movement to input buffer
3975 }
3976 thing = (int) lrand48() % 4; // what thing to insert
3977 cnt = (int) lrand48() % 10; // how many to insert
3978 for (i = 0; i < cnt; i++) {
3979 if (thing == 0) { // insert chars
3980 readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
3981 } else if (thing == 1) { // insert words
3982 strcat((char *) readbuffer, words[(int) lrand48() % 20]);
3983 strcat((char *) readbuffer, " ");
3984 sleeptime = 0; // how fast to type
3985 } else if (thing == 2) { // insert lines
3986 strcat((char *) readbuffer, lines[(int) lrand48() % 20]);
3987 sleeptime = 0; // how fast to type
3988 } else { // insert multi-lines
3989 strcat((char *) readbuffer, multilines[(int) lrand48() % 20]);
3990 sleeptime = 0; // how fast to type
3991 }
3992 }
3993 strcat((char *) readbuffer, "\033");
3994 }
3995 readed_for_parse = strlen(readbuffer);
3996 cd1:
3997 totalcmds++;
3998 if (sleeptime > 0)
3999 (void) mysleep(sleeptime); // sleep 1/100 sec
4000}
4001
4002// test to see if there are any errors
4003static void crash_test()
4004{
4005 static time_t oldtim;
4006 time_t tim;
Glenn L McGrath7127b582002-12-03 21:48:15 +00004007 char d[2], msg[BUFSIZ];
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004008
4009 msg[0] = '\0';
4010 if (end < text) {
4011 strcat((char *) msg, "end<text ");
4012 }
4013 if (end > textend) {
4014 strcat((char *) msg, "end>textend ");
4015 }
4016 if (dot < text) {
4017 strcat((char *) msg, "dot<text ");
4018 }
4019 if (dot > end) {
4020 strcat((char *) msg, "dot>end ");
4021 }
4022 if (screenbegin < text) {
4023 strcat((char *) msg, "screenbegin<text ");
4024 }
4025 if (screenbegin > end - 1) {
4026 strcat((char *) msg, "screenbegin>end-1 ");
4027 }
4028
4029 if (strlen(msg) > 0) {
4030 alarm(0);
Glenn L McGrath7127b582002-12-03 21:48:15 +00004031 printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004032 totalcmds, last_input_char, msg, SOs, SOn);
4033 fflush(stdout);
4034 while (read(0, d, 1) > 0) {
4035 if (d[0] == '\n' || d[0] == '\r')
4036 break;
4037 }
4038 alarm(3);
4039 }
4040 tim = (time_t) time((time_t *) 0);
4041 if (tim >= (oldtim + 3)) {
4042 sprintf((char *) status_buffer,
4043 "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
4044 totalcmds, M, N, I, D, Y, P, U, end - text + 1);
4045 oldtim = tim;
4046 }
4047 return;
4048}
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00004049#endif /* CONFIG_FEATURE_VI_CRASHME */