blob: 74566499cd08f4b764aec2af0300924eb1d4dfac [file] [log] [blame]
Rob Landleye5e1a102006-06-21 01:15:36 +00001/* vi: set sw=4 ts=4: */
Eric Andersen3f980402001-04-04 17:31:15 +00002/*
3 * tiny vi.c: A small 'vi' clone
4 * Copyright (C) 2000, 2001 Sterling Huxley <sterling@europa.com>
5 *
Paul Foxdbf935d2006-03-27 20:29:33 +00006 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
Eric Andersen3f980402001-04-04 17:31:15 +00007 */
8
Eric Andersen3f980402001-04-04 17:31:15 +00009/*
Eric Andersen3f980402001-04-04 17:31:15 +000010 * Things To Do:
11 * EXINIT
Eric Andersen1c0d3112001-04-16 15:46:44 +000012 * $HOME/.exrc and ./.exrc
Eric Andersen3f980402001-04-04 17:31:15 +000013 * add magic to search /foo.*bar
14 * add :help command
15 * :map macros
Eric Andersen3f980402001-04-04 17:31:15 +000016 * if mark[] values were line numbers rather than pointers
17 * it would be easier to change the mark when add/delete lines
Eric Andersen1c0d3112001-04-16 15:46:44 +000018 * More intelligence in refresh()
19 * ":r !cmd" and "!cmd" to filter text through an external command
20 * A true "undo" facility
21 * An "ex" line oriented mode- maybe using "cmdedit"
Eric Andersen3f980402001-04-04 17:31:15 +000022 */
23
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000024#include "libbb.h"
Eric Andersen3f980402001-04-04 17:31:15 +000025
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +000026#define ENABLE_FEATURE_VI_CRASHME 0
27
28#if ENABLE_LOCALE_SUPPORT
Glenn L McGrath09adaca2002-12-02 21:18:10 +000029#define Isprint(c) isprint((c))
30#else
Denis Vlasenko2a51af22007-03-21 22:31:24 +000031/* 0x9b is Meta-ESC */
32#define Isprint(c) ((unsigned char)(c) >= ' ' && (c) != 0x7f && (unsigned char)(c) != 0x9b)
Glenn L McGrath09adaca2002-12-02 21:18:10 +000033#endif
34
Denis Vlasenkoe8a07882007-06-10 15:08:44 +000035enum {
36 MAX_LINELEN = CONFIG_FEATURE_VI_MAX_LEN,
37 MAX_SCR_COLS = CONFIG_FEATURE_VI_MAX_LEN,
38};
Eric Andersen3f980402001-04-04 17:31:15 +000039
40// Misc. non-Ascii keys that report an escape sequence
Denis Vlasenko2a51af22007-03-21 22:31:24 +000041#define VI_K_UP (char)128 // cursor key Up
42#define VI_K_DOWN (char)129 // cursor key Down
43#define VI_K_RIGHT (char)130 // Cursor Key Right
44#define VI_K_LEFT (char)131 // cursor key Left
45#define VI_K_HOME (char)132 // Cursor Key Home
46#define VI_K_END (char)133 // Cursor Key End
47#define VI_K_INSERT (char)134 // Cursor Key Insert
48#define VI_K_PAGEUP (char)135 // Cursor Key Page Up
49#define VI_K_PAGEDOWN (char)136 // Cursor Key Page Down
50#define VI_K_FUN1 (char)137 // Function Key F1
51#define VI_K_FUN2 (char)138 // Function Key F2
52#define VI_K_FUN3 (char)139 // Function Key F3
53#define VI_K_FUN4 (char)140 // Function Key F4
54#define VI_K_FUN5 (char)141 // Function Key F5
55#define VI_K_FUN6 (char)142 // Function Key F6
56#define VI_K_FUN7 (char)143 // Function Key F7
57#define VI_K_FUN8 (char)144 // Function Key F8
58#define VI_K_FUN9 (char)145 // Function Key F9
59#define VI_K_FUN10 (char)146 // Function Key F10
60#define VI_K_FUN11 (char)147 // Function Key F11
61#define VI_K_FUN12 (char)148 // Function Key F12
Eric Andersen3f980402001-04-04 17:31:15 +000062
Glenn L McGrath09adaca2002-12-02 21:18:10 +000063/* vt102 typical ESC sequence */
64/* terminal standout start/normal ESC sequence */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000065static const char SOs[] ALIGN1 = "\033[7m";
66static const char SOn[] ALIGN1 = "\033[0m";
Glenn L McGrath09adaca2002-12-02 21:18:10 +000067/* terminal bell sequence */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000068static const char bell[] ALIGN1 = "\007";
Glenn L McGrath09adaca2002-12-02 21:18:10 +000069/* Clear-end-of-line and Clear-end-of-screen ESC sequence */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000070static const char Ceol[] ALIGN1 = "\033[0K";
71static const char Ceos[] ALIGN1 = "\033[0J";
Glenn L McGrath09adaca2002-12-02 21:18:10 +000072/* Cursor motion arbitrary destination ESC sequence */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000073static const char CMrc[] ALIGN1 = "\033[%d;%dH";
Glenn L McGrath09adaca2002-12-02 21:18:10 +000074/* Cursor motion up and down ESC sequence */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000075static const char CMup[] ALIGN1 = "\033[A";
76static const char CMdown[] ALIGN1 = "\n";
Glenn L McGrath09adaca2002-12-02 21:18:10 +000077
78
Rob Landleybc68cd12006-03-10 19:22:06 +000079enum {
80 YANKONLY = FALSE,
81 YANKDEL = TRUE,
82 FORWARD = 1, // code depends on "1" for array index
83 BACK = -1, // code depends on "-1" for array index
84 LIMITED = 0, // how much of text[] in char_search
85 FULL = 1, // how much of text[] in char_search
Eric Andersen3f980402001-04-04 17:31:15 +000086
Rob Landleybc68cd12006-03-10 19:22:06 +000087 S_BEFORE_WS = 1, // used in skip_thing() for moving "dot"
88 S_TO_WS = 2, // used in skip_thing() for moving "dot"
89 S_OVER_WS = 3, // used in skip_thing() for moving "dot"
90 S_END_PUNCT = 4, // used in skip_thing() for moving "dot"
Denis Vlasenko8e858e22007-03-07 09:35:43 +000091 S_END_ALNUM = 5, // used in skip_thing() for moving "dot"
Rob Landleybc68cd12006-03-10 19:22:06 +000092};
Eric Andersen3f980402001-04-04 17:31:15 +000093
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +000094/* vi.c expects chars to be unsigned. */
95/* busybox build system provides that, but it's better */
96/* to audit and fix the source */
Eric Andersen3f980402001-04-04 17:31:15 +000097
Denis Vlasenkoeaabf062007-07-17 23:14:07 +000098static smallint vi_setops;
Glenn L McGrath09adaca2002-12-02 21:18:10 +000099#define VI_AUTOINDENT 1
100#define VI_SHOWMATCH 2
101#define VI_IGNORECASE 4
102#define VI_ERR_METHOD 8
103#define autoindent (vi_setops & VI_AUTOINDENT)
104#define showmatch (vi_setops & VI_SHOWMATCH )
105#define ignorecase (vi_setops & VI_IGNORECASE)
106/* indicate error with beep or flash */
107#define err_method (vi_setops & VI_ERR_METHOD)
108
Eric Andersen3f980402001-04-04 17:31:15 +0000109
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000110static smallint editing; // >0 while we are editing a file
111 // [code audit says "can be 0 or 1 only"]
112static smallint cmd_mode; // 0=command 1=insert 2=replace
113static smallint file_modified; // buffer contents changed
114static smallint last_file_modified = -1;
115static int fn_start; // index of first cmd line file name
116static int save_argc; // how many file names on cmd line
117static int cmdcnt; // repetition count
118static int rows, columns; // the terminal screen is this size
119static int crow, ccol, offset; // cursor is on Crow x Ccol with Horz Ofset
120static char *status_buffer; // mesages to the user
Paul Fox8552aec2005-09-16 12:20:05 +0000121#define STATUS_BUFFER_LEN 200
122static int have_status_msg; // is default edit status needed?
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000123 // [don't make smallint!]
Paul Fox8552aec2005-09-16 12:20:05 +0000124static int last_status_cksum; // hash of current status line
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000125static char *current_filename; // current file name
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000126//static char *text, *end; // pointers to the user data in memory
127static char *screen; // pointer to the virtual screen buffer
128static int screensize; // and its size
129static char *screenbegin; // index into text[], of top line on the screen
130//static char *dot; // where all the action takes place
Eric Andersen3f980402001-04-04 17:31:15 +0000131static int tabstop;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000132static char erase_char; // the users erase character
133static char last_input_char; // last char read from user
134static char last_forward_char; // last char searched for with 'f'
Eric Andersen3f980402001-04-04 17:31:15 +0000135
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000136#if ENABLE_FEATURE_VI_READONLY
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000137//static smallint vi_readonly, readonly;
138static smallint readonly_mode = 0;
139#define SET_READONLY_FILE(flags) ((flags) |= 0x01)
140#define SET_READONLY_MODE(flags) ((flags) |= 0x02)
141#define UNSET_READONLY_FILE(flags) ((flags) &= 0xfe)
142#else
143#define readonly_mode 0
144#define SET_READONLY_FILE(flags)
145#define SET_READONLY_MODE(flags)
146#define UNSET_READONLY_FILE(flags)
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000147#endif
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000148
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000149#if ENABLE_FEATURE_VI_DOT_CMD
150static smallint adding2q; // are we currently adding user input to q
151static char *last_modifying_cmd; // last modifying cmd for "."
152static char *ioq, *ioq_start; // pointer to string for get_one_char to "read"
153#endif
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000154#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
Eric Andersen822c3832001-05-07 17:37:43 +0000155static int last_row; // where the cursor was last moved to
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000156#endif
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000157#if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000158static int my_pid;
159#endif
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000160#if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000161static char *modifying_cmds; // cmds that modify text[]
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000162#endif
163#if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000164static char *last_search_pattern; // last pattern from a '/' or '?' search
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000165#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000166
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000167/* Moving biggest data to malloced space... */
168struct globals {
169 /* many references - keep near the top of globals */
170 char *text, *end; // pointers to the user data in memory
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000171 int text_size; // size of the allocated buffer
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000172 char *dot; // where all the action takes place
173#if ENABLE_FEATURE_VI_YANKMARK
174 char *reg[28]; // named register a-z, "D", and "U" 0-25,26,27
175 int YDreg, Ureg; // default delete register and orig line for "U"
176 char *mark[28]; // user marks points somewhere in text[]- a-z and previous context ''
177 char *context_start, *context_end;
178#endif
179 /* a few references only */
180#if ENABLE_FEATURE_VI_USE_SIGNALS
181 jmp_buf restart; // catch_sig()
182#endif
183 struct termios term_orig, term_vi; // remember what the cooked mode was
184#if ENABLE_FEATURE_VI_COLON
185 char *initial_cmds[3]; // currently 2 entries, NULL terminated
186#endif
187};
188#define G (*ptr_to_globals)
189#define text (G.text )
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000190#define text_size (G.text_size )
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000191#define end (G.end )
192#define dot (G.dot )
193#define reg (G.reg )
194#define YDreg (G.YDreg )
195#define Ureg (G.Ureg )
196#define mark (G.mark )
197#define context_start (G.context_start )
198#define context_end (G.context_end )
199#define restart (G.restart )
200#define term_orig (G.term_orig )
201#define term_vi (G.term_vi )
202#define initial_cmds (G.initial_cmds )
Eric Andersen3f980402001-04-04 17:31:15 +0000203
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000204static int init_text_buffer(char *); // init from file or create new
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000205static void edit_file(char *); // edit one file
206static void do_cmd(char); // execute a command
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000207static int next_tabstop(int);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000208static void sync_cursor(char *, int *, int *); // synchronize the screen cursor to dot
209static char *begin_line(char *); // return pointer to cur line B-o-l
210static char *end_line(char *); // return pointer to cur line E-o-l
211static char *prev_line(char *); // return pointer to prev line B-o-l
212static char *next_line(char *); // return pointer to next line B-o-l
213static char *end_screen(void); // get pointer to last char on screen
214static int count_lines(char *, char *); // count line from start to stop
215static char *find_line(int); // find begining of line #li
216static char *move_to_col(char *, int); // move "p" to column l
Eric Andersen3f980402001-04-04 17:31:15 +0000217static void dot_left(void); // move dot left- dont leave line
218static void dot_right(void); // move dot right- dont leave line
219static void dot_begin(void); // move dot to B-o-l
220static void dot_end(void); // move dot to E-o-l
221static void dot_next(void); // move dot to next line B-o-l
222static void dot_prev(void); // move dot to prev line B-o-l
223static void dot_scroll(int, int); // move the screen up or down
224static void dot_skip_over_ws(void); // move dot pat WS
225static void dot_delete(void); // delete the char at 'dot'
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000226static char *bound_dot(char *); // make sure text[0] <= P < "end"
227static char *new_screen(int, int); // malloc virtual screen memory
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000228static char *char_insert(char *, char); // insert the char c at 'p'
229static char *stupid_insert(char *, char); // stupidly insert the char c at 'p'
230static char find_range(char **, char **, char); // return pointers for an object
231static int st_test(char *, int, int, char *); // helper for skip_thing()
232static char *skip_thing(char *, int, int, int); // skip some object
233static char *find_pair(char *, char); // find matching pair () [] {}
234static char *text_hole_delete(char *, char *); // at "p", delete a 'size' byte hole
235static char *text_hole_make(char *, int); // at "p", make a 'size' byte hole
236static char *yank_delete(char *, char *, int, int); // yank text[] into register then delete
Eric Andersen3f980402001-04-04 17:31:15 +0000237static void show_help(void); // display some help info
Eric Andersen3f980402001-04-04 17:31:15 +0000238static void rawmode(void); // set "raw" mode on tty
239static void cookmode(void); // return to "cooked" mode on tty
240static int mysleep(int); // sleep for 'h' 1/100 seconds
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000241static char readit(void); // read (maybe cursor) key from stdin
242static char get_one_char(void); // read 1 char from stdin
243static int file_size(const char *); // what is the byte size of "fn"
Denis Vlasenko59a1f302007-07-14 22:43:10 +0000244#if ENABLE_FEATURE_VI_READONLY
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000245static int file_insert(const char *, char *, int);
Denis Vlasenko59a1f302007-07-14 22:43:10 +0000246#else
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000247static int file_insert(const char *, char *);
Denis Vlasenko59a1f302007-07-14 22:43:10 +0000248#endif
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000249static int file_write(char *, char *, char *);
Eric Andersen822c3832001-05-07 17:37:43 +0000250static void place_cursor(int, int, int);
Eric Andersenbff7a602001-11-17 07:15:43 +0000251static void screen_erase(void);
Eric Andersen3f980402001-04-04 17:31:15 +0000252static void clear_to_eol(void);
253static void clear_to_eos(void);
254static void standout_start(void); // send "start reverse video" sequence
255static void standout_end(void); // send "end reverse video" sequence
256static void flash(int); // flash the terminal screen
Eric Andersen3f980402001-04-04 17:31:15 +0000257static void show_status_line(void); // put a message on the bottom line
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000258static void psb(const char *, ...); // Print Status Buf
259static void psbs(const char *, ...); // Print Status Buf in standout mode
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000260static void ni(const char *); // display messages
Paul Fox8552aec2005-09-16 12:20:05 +0000261static int format_edit_status(void); // format file status on status line
Eric Andersen3f980402001-04-04 17:31:15 +0000262static void redraw(int); // force a full screen refresh
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000263static void format_line(char*, char*, int);
Eric Andersen3f980402001-04-04 17:31:15 +0000264static void refresh(int); // update the terminal from screen[]
265
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000266static void Indicate_Error(void); // use flash or beep to indicate error
267#define indicate_error(c) Indicate_Error()
Paul Fox90372ed2005-10-09 14:26:26 +0000268static void Hit_Return(void);
269
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000270#if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000271static char *char_search(char *, const char *, int, int); // search for pattern starting at p
272static int mycmp(const char *, const char *, int); // string cmp based in "ignorecase"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000273#endif
274#if ENABLE_FEATURE_VI_COLON
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000275static char *get_one_address(char *, int *); // get colon addr, if present
276static char *get_address(char *, int *, int *); // get two colon addrs, if present
277static void colon(char *); // execute the "colon" mode cmds
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000278#endif
279#if ENABLE_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000280static void winch_sig(int); // catch window size changes
281static void suspend_sig(int); // catch ctrl-Z
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000282static void catch_sig(int); // catch ctrl-C and alarm time-outs
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000283#endif
284#if ENABLE_FEATURE_VI_DOT_CMD
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000285static void start_new_cmd_q(char); // new queue for command
Eric Andersenbff7a602001-11-17 07:15:43 +0000286static void end_cmd_q(void); // stop saving input chars
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000287#else
288#define end_cmd_q() ((void)0)
289#endif
290#if ENABLE_FEATURE_VI_SETOPTS
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000291static void showmatching(char *); // show the matching pair () [] {}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000292#endif
293#if ENABLE_FEATURE_VI_YANKMARK || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) || ENABLE_FEATURE_VI_CRASHME
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000294static char *string_insert(char *, char *); // insert the string at 'p'
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000295#endif
296#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000297static char *text_yank(char *, char *, int); // save copy of "p" into a register
298static char what_reg(void); // what is letter of current YDreg
299static void check_context(char); // remember context for '' command
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000300#endif
301#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000302static void crash_dummy();
303static void crash_test();
304static int crashme = 0;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000305#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000306
307
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000308static void write1(const char *out)
309{
310 fputs(out, stdout);
311}
312
Denis Vlasenko06af2162007-02-03 17:28:39 +0000313int vi_main(int argc, char **argv);
Rob Landleydfba7412006-03-06 20:47:33 +0000314int vi_main(int argc, char **argv)
Eric Andersen3f980402001-04-04 17:31:15 +0000315{
Eric Andersend402edf2001-04-04 19:29:48 +0000316 int c;
Paul Fox8552aec2005-09-16 12:20:05 +0000317 RESERVE_CONFIG_BUFFER(STATUS_BUFFER, STATUS_BUFFER_LEN);
Eric Andersen3f980402001-04-04 17:31:15 +0000318
Denis Vlasenkocd5c7862007-05-17 16:37:22 +0000319#if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000320 my_pid = getpid();
321#endif
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000322
323 PTR_TO_GLOBALS = xzalloc(sizeof(G));
324
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000325#if ENABLE_FEATURE_VI_CRASHME
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000326 srand((long) my_pid);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000327#endif
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000328
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000329 status_buffer = STATUS_BUFFER;
Paul Fox8552aec2005-09-16 12:20:05 +0000330 last_status_cksum = 0;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000331 text = NULL;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000332
Denis Vlasenko2414a962007-07-18 22:03:40 +0000333#ifdef NO_SUCH_APPLET_YET
334 /* If we aren't "vi", we are "view" */
335 if (ENABLE_FEATURE_VI_READONLY && applet_name[2]) {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000336 SET_READONLY_MODE(readonly_mode);
Eric Andersen3f980402001-04-04 17:31:15 +0000337 }
Denis Vlasenko2414a962007-07-18 22:03:40 +0000338#endif
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000339
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000340 vi_setops = VI_AUTOINDENT | VI_SHOWMATCH | VI_IGNORECASE | VI_ERR_METHOD;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000341#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenko2414a962007-07-18 22:03:40 +0000342 memset(reg, 0, sizeof(reg)); // init the yank regs
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000343#endif
344#if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000345 modifying_cmds = (char *) "aAcCdDiIJoOpPrRsxX<>~"; // cmds modifying text[]
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000346#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000347
Denis Vlasenkof9234132007-03-21 00:03:42 +0000348 // 1- process $HOME/.exrc file (not inplemented yet)
Eric Andersen3f980402001-04-04 17:31:15 +0000349 // 2- process EXINIT variable from environment
350 // 3- process command line args
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000351#if ENABLE_FEATURE_VI_COLON
Denis Vlasenkof9234132007-03-21 00:03:42 +0000352 {
353 char *p = getenv("EXINIT");
354 if (p && *p)
355 initial_cmds[0] = xstrdup(p);
356 }
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000357#endif
358 while ((c = getopt(argc, argv, "hCR" USE_FEATURE_VI_COLON("c:"))) != -1) {
Eric Andersen3f980402001-04-04 17:31:15 +0000359 switch (c) {
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000360#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000361 case 'C':
362 crashme = 1;
363 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000364#endif
365#if ENABLE_FEATURE_VI_READONLY
Eric Andersen3f980402001-04-04 17:31:15 +0000366 case 'R': // Read-only flag
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000367 SET_READONLY_MODE(readonly_mode);
Eric Andersen3f980402001-04-04 17:31:15 +0000368 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000369#endif
Eric Andersen822c3832001-05-07 17:37:43 +0000370 //case 'r': // recover flag- ignore- we don't use tmp file
371 //case 'x': // encryption flag- ignore
372 //case 'c': // execute command first
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000373#if ENABLE_FEATURE_VI_COLON
Denis Vlasenkof9234132007-03-21 00:03:42 +0000374 case 'c': // cmd line vi command
375 if (*optarg)
376 initial_cmds[initial_cmds[0] != 0] = xstrdup(optarg);
377 break;
Eric Andersen822c3832001-05-07 17:37:43 +0000378 //case 'h': // help -- just use default
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000379#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000380 default:
381 show_help();
Eric Andersendd8500b2001-07-02 18:06:14 +0000382 return 1;
Eric Andersen3f980402001-04-04 17:31:15 +0000383 }
384 }
385
386 // The argv array can be used by the ":next" and ":rewind" commands
387 // save optind.
388 fn_start = optind; // remember first file name for :next and :rew
389 save_argc = argc;
390
391 //----- This is the main file handling loop --------------
392 if (optind >= argc) {
Eric Andersen3f980402001-04-04 17:31:15 +0000393 edit_file(0);
394 } else {
395 for (; optind < argc; optind++) {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000396 edit_file(argv[optind]);
Eric Andersen3f980402001-04-04 17:31:15 +0000397 }
398 }
399 //-----------------------------------------------------------
400
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000401 return 0;
Eric Andersen3f980402001-04-04 17:31:15 +0000402}
403
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000404/* read text from file or create an empty buf */
405/* will also update current_filename */
406static int init_text_buffer(char *fn)
407{
408 int rc;
409 int size = file_size(fn); // file size. -1 means does not exist.
410
411 /* allocate/reallocate text buffer */
412 free(text);
413 text_size = size * 2;
414 if (text_size < 10240)
415 text_size = 10240; // have a minimum size for new files
416 screenbegin = dot = end = text = xzalloc(text_size);
Denis Vlasenko2f6ae432007-07-19 22:50:47 +0000417
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000418 if (fn != current_filename) {
419 free(current_filename);
420 current_filename = xstrdup(fn);
421 }
422 if (size < 0) {
423 // file dont exist. Start empty buf with dummy line
424 char_insert(text, '\n');
425 rc = 0;
426 } else {
427 rc = file_insert(fn, text
428 USE_FEATURE_VI_READONLY(, 1));
429 }
430 file_modified = 0;
431 last_file_modified = -1;
432#if ENABLE_FEATURE_VI_YANKMARK
433 /* init the marks. */
434 memset(mark, 0, sizeof(mark));
435#endif
436 return rc;
Denis Vlasenko2f6ae432007-07-19 22:50:47 +0000437}
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000438
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000439static void edit_file(char * fn)
Eric Andersen3f980402001-04-04 17:31:15 +0000440{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000441 char c;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000442 int size;
Eric Andersen3f980402001-04-04 17:31:15 +0000443
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000444#if ENABLE_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000445 int sig;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000446#endif
447#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000448 static char *cur_line;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000449#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000450
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000451 editing = 1; // 0= exit, 1= one file, 2= multiple files
Eric Andersen3f980402001-04-04 17:31:15 +0000452 rawmode();
453 rows = 24;
454 columns = 80;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000455 size = 0;
Rob Landleye5e1a102006-06-21 01:15:36 +0000456 if (ENABLE_FEATURE_VI_WIN_RESIZE)
457 get_terminal_width_height(0, &columns, &rows);
Eric Andersen3f980402001-04-04 17:31:15 +0000458 new_screen(rows, columns); // get memory for virtual screen
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000459 init_text_buffer(fn);
Eric Andersen3f980402001-04-04 17:31:15 +0000460
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000461#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000462 YDreg = 26; // default Yank/Delete reg
463 Ureg = 27; // hold orig line for "U" cmd
Eric Andersen3f980402001-04-04 17:31:15 +0000464 mark[26] = mark[27] = text; // init "previous context"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000465#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000466
Eric Andersen3f980402001-04-04 17:31:15 +0000467 last_forward_char = last_input_char = '\0';
468 crow = 0;
469 ccol = 0;
Eric Andersen3f980402001-04-04 17:31:15 +0000470
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000471#if ENABLE_FEATURE_VI_USE_SIGNALS
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000472 catch_sig(0);
Eric Andersen3f980402001-04-04 17:31:15 +0000473 signal(SIGWINCH, winch_sig);
474 signal(SIGTSTP, suspend_sig);
475 sig = setjmp(restart);
476 if (sig != 0) {
Eric Andersen1c0d3112001-04-16 15:46:44 +0000477 screenbegin = dot = text;
Eric Andersen3f980402001-04-04 17:31:15 +0000478 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000479#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000480
Eric Andersen3f980402001-04-04 17:31:15 +0000481 cmd_mode = 0; // 0=command 1=insert 2='R'eplace
482 cmdcnt = 0;
483 tabstop = 8;
484 offset = 0; // no horizontal offset
485 c = '\0';
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000486#if ENABLE_FEATURE_VI_DOT_CMD
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000487 free(last_modifying_cmd);
488 free(ioq_start);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000489 ioq = ioq_start = last_modifying_cmd = NULL;
Eric Andersen3f980402001-04-04 17:31:15 +0000490 adding2q = 0;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000491#endif
Eric Andersen822c3832001-05-07 17:37:43 +0000492 redraw(FALSE); // dont force every col re-draw
Eric Andersen3f980402001-04-04 17:31:15 +0000493
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000494#if ENABLE_FEATURE_VI_COLON
Denis Vlasenkof9234132007-03-21 00:03:42 +0000495 {
496 char *p, *q;
497 int n = 0;
498
499 while ((p = initial_cmds[n])) {
500 do {
501 q = p;
502 p = strchr(q,'\n');
503 if (p)
Denis Vlasenko51742f42007-04-12 00:32:05 +0000504 while (*p == '\n')
Denis Vlasenkof9234132007-03-21 00:03:42 +0000505 *p++ = '\0';
506 if (*q)
507 colon(q);
508 } while (p);
509 free(initial_cmds[n]);
510 initial_cmds[n] = NULL;
511 n++;
512 }
513 }
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000514#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000515 //------This is the main Vi cmd handling loop -----------------------
516 while (editing > 0) {
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000517#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000518 if (crashme > 0) {
519 if ((end - text) > 1) {
520 crash_dummy(); // generate a random command
521 } else {
522 crashme = 0;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000523 dot = string_insert(text, "\n\n##### Ran out of text to work on. #####\n\n"); // insert the string
Eric Andersen3f980402001-04-04 17:31:15 +0000524 refresh(FALSE);
525 }
526 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000527#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000528 last_input_char = c = get_one_char(); // get a cmd from user
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000529#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000530 // save a copy of the current line- for the 'U" command
531 if (begin_line(dot) != cur_line) {
532 cur_line = begin_line(dot);
533 text_yank(begin_line(dot), end_line(dot), Ureg);
534 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000535#endif
536#if ENABLE_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000537 // These are commands that change text[].
538 // Remember the input for the "." command
539 if (!adding2q && ioq_start == 0
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000540 && strchr(modifying_cmds, c)
541 ) {
Eric Andersen3f980402001-04-04 17:31:15 +0000542 start_new_cmd_q(c);
543 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000544#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000545 do_cmd(c); // execute the user command
546 //
547 // poll to see if there is input already waiting. if we are
548 // not able to display output fast enough to keep up, skip
549 // the display update until we catch up with input.
550 if (mysleep(0) == 0) {
551 // no input pending- so update output
552 refresh(FALSE);
553 show_status_line();
554 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000555#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000556 if (crashme > 0)
557 crash_test(); // test editor variables
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000558#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000559 }
560 //-------------------------------------------------------------------
561
Eric Andersen822c3832001-05-07 17:37:43 +0000562 place_cursor(rows, 0, FALSE); // go to bottom of screen
Eric Andersen3f980402001-04-04 17:31:15 +0000563 clear_to_eol(); // Erase to end of line
564 cookmode();
565}
566
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000567//----- The Colon commands -------------------------------------
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000568#if ENABLE_FEATURE_VI_COLON
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000569static char *get_one_address(char * p, int *addr) // get colon addr, if present
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000570{
571 int st;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000572 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000573
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000574#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000575 char c;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000576#endif
577#if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenkoe8a07882007-06-10 15:08:44 +0000578 char *pat, buf[MAX_LINELEN];
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000579#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000580
581 *addr = -1; // assume no addr
582 if (*p == '.') { // the current line
583 p++;
584 q = begin_line(dot);
585 *addr = count_lines(text, q);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000586#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000587 } else if (*p == '\'') { // is this a mark addr
588 p++;
589 c = tolower(*p);
590 p++;
591 if (c >= 'a' && c <= 'z') {
592 // we have a mark
593 c = c - 'a';
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000594 q = mark[(unsigned char) c];
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000595 if (q != NULL) { // is mark valid
596 *addr = count_lines(text, q); // count lines
597 }
598 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000599#endif
600#if ENABLE_FEATURE_VI_SEARCH
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000601 } else if (*p == '/') { // a search pattern
602 q = buf;
603 for (p++; *p; p++) {
604 if (*p == '/')
605 break;
606 *q++ = *p;
607 *q = '\0';
608 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000609 pat = xstrdup(buf); // save copy of pattern
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000610 if (*p == '/')
611 p++;
612 q = char_search(dot, pat, FORWARD, FULL);
613 if (q != NULL) {
614 *addr = count_lines(text, q);
615 }
616 free(pat);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000617#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000618 } else if (*p == '$') { // the last line in file
619 p++;
620 q = begin_line(end - 1);
621 *addr = count_lines(text, q);
622 } else if (isdigit(*p)) { // specific line number
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000623 sscanf(p, "%d%n", addr, &st);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000624 p += st;
625 } else { // I don't reconise this
626 // unrecognised address- assume -1
627 *addr = -1;
628 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000629 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000630}
631
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000632static char *get_address(char *p, int *b, int *e) // get two colon addrs, if present
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000633{
634 //----- get the address' i.e., 1,3 'a,'b -----
635 // get FIRST addr, if present
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000636 while (isblank(*p))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000637 p++; // skip over leading spaces
638 if (*p == '%') { // alias for 1,$
639 p++;
640 *b = 1;
641 *e = count_lines(text, end-1);
642 goto ga0;
643 }
644 p = get_one_address(p, b);
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000645 while (isblank(*p))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000646 p++;
Eric Andersenaff114c2004-04-14 17:51:38 +0000647 if (*p == ',') { // is there a address separator
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000648 p++;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000649 while (isblank(*p))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000650 p++;
651 // get SECOND addr, if present
652 p = get_one_address(p, e);
653 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000654 ga0:
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000655 while (isblank(*p))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000656 p++; // skip over trailing spaces
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000657 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000658}
659
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000660#if ENABLE_FEATURE_VI_SET && ENABLE_FEATURE_VI_SETOPTS
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000661static void setops(const char *args, const char *opname, int flg_no,
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000662 const char *short_opname, int opt)
663{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000664 const char *a = args + flg_no;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000665 int l = strlen(opname) - 1; /* opname have + ' ' */
666
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000667 if (strncasecmp(a, opname, l) == 0
668 || strncasecmp(a, short_opname, 2) == 0
669 ) {
670 if (flg_no)
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000671 vi_setops &= ~opt;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000672 else
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000673 vi_setops |= opt;
674 }
675}
676#endif
677
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000678static void colon(char * buf)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000679{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000680 char c, *orig_buf, *buf1, *q, *r;
Denis Vlasenkoe8a07882007-06-10 15:08:44 +0000681 char *fn, cmd[MAX_LINELEN], args[MAX_LINELEN];
Bernhard Reutner-Fischerd591a362006-08-20 17:35:13 +0000682 int i, l, li, ch, b, e;
Eric Andersena9eb33d2004-08-19 19:15:06 +0000683 int useforce = FALSE, forced = FALSE;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000684
685 // :3154 // if (-e line 3154) goto it else stay put
686 // :4,33w! foo // write a portion of buffer to file "foo"
687 // :w // write all of buffer to current file
688 // :q // quit
689 // :q! // quit- dont care about modified file
690 // :'a,'z!sort -u // filter block through sort
691 // :'f // goto mark "f"
692 // :'fl // list literal the mark "f" line
693 // :.r bar // read file "bar" into buffer before dot
694 // :/123/,/abc/d // delete lines from "123" line to "abc" line
695 // :/xyz/ // goto the "xyz" line
696 // :s/find/replace/ // substitute pattern "find" with "replace"
697 // :!<cmd> // run <cmd> then return
698 //
Eric Andersen165e8cb2004-07-20 06:44:46 +0000699
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000700 if (!buf[0])
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000701 goto vc1;
702 if (*buf == ':')
703 buf++; // move past the ':'
704
Bernhard Reutner-Fischerd591a362006-08-20 17:35:13 +0000705 li = ch = i = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000706 b = e = -1;
707 q = text; // assume 1,$ for the range
708 r = end - 1;
709 li = count_lines(text, end - 1);
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000710 fn = current_filename; // default to current file
Denis Vlasenkoe8a07882007-06-10 15:08:44 +0000711 memset(cmd, '\0', MAX_LINELEN); // clear cmd[]
712 memset(args, '\0', MAX_LINELEN); // clear args[]
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000713
714 // look for optional address(es) :. :1 :1,9 :'q,'a :%
715 buf = get_address(buf, &b, &e);
716
717 // remember orig command line
718 orig_buf = buf;
719
720 // get the COMMAND into cmd[]
721 buf1 = cmd;
722 while (*buf != '\0') {
723 if (isspace(*buf))
724 break;
725 *buf1++ = *buf++;
726 }
727 // get any ARGuments
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000728 while (isblank(*buf))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000729 buf++;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000730 strcpy(args, buf);
731 buf1 = last_char_is(cmd, '!');
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000732 if (buf1) {
733 useforce = TRUE;
734 *buf1 = '\0'; // get rid of !
735 }
736 if (b >= 0) {
737 // if there is only one addr, then the addr
738 // is the line number of the single line the
739 // user wants. So, reset the end
740 // pointer to point at end of the "b" line
741 q = find_line(b); // what line is #b
742 r = end_line(q);
743 li = 1;
744 }
745 if (e >= 0) {
746 // we were given two addrs. change the
747 // end pointer to the addr given by user.
748 r = find_line(e); // what line is #e
749 r = end_line(r);
750 li = e - b + 1;
751 }
752 // ------------ now look for the command ------------
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000753 i = strlen(cmd);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000754 if (i == 0) { // :123CR goto line #123
755 if (b >= 0) {
756 dot = find_line(b); // what line is #b
757 dot_skip_over_ws();
758 }
Denis Vlasenko249fabf2006-12-19 00:29:22 +0000759 }
760#if ENABLE_FEATURE_ALLOW_EXEC
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000761 else if (strncmp(cmd, "!", 1) == 0) { // run a cmd
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000762 int retcode;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000763 // :!ls run the <cmd>
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000764 alarm(0); // wait for input- no alarms
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000765 place_cursor(rows - 1, 0, FALSE); // go to Status line
766 clear_to_eol(); // clear the line
767 cookmode();
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000768 retcode = system(orig_buf + 1); // run the cmd
769 if (retcode)
770 printf("\nshell returned %i\n\n", retcode);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000771 rawmode();
772 Hit_Return(); // let user see results
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000773 alarm(3); // done waiting for input
Denis Vlasenko249fabf2006-12-19 00:29:22 +0000774 }
775#endif
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000776 else if (strncmp(cmd, "=", i) == 0) { // where is the address
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000777 if (b < 0) { // no addr given- use defaults
778 b = e = count_lines(text, dot);
779 }
780 psb("%d", b);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000781 } else if (strncasecmp(cmd, "delete", i) == 0) { // delete lines
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000782 if (b < 0) { // no addr given- use defaults
783 q = begin_line(dot); // assume .,. for the range
784 r = end_line(dot);
785 }
786 dot = yank_delete(q, r, 1, YANKDEL); // save, then delete lines
787 dot_skip_over_ws();
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000788 } else if (strncasecmp(cmd, "edit", i) == 0) { // Edit a file
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000789 // don't edit, if the current file has been modified
790 if (file_modified && ! useforce) {
791 psbs("No write since last change (:edit! overrides)");
792 goto vc1;
793 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000794 if (args[0]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000795 // the user supplied a file name
Denis Vlasenkoe8a07882007-06-10 15:08:44 +0000796 fn = args;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000797 } else if (current_filename && current_filename[0]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000798 // no user supplied name- use the current filename
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000799 // fn = current_filename; was set by default
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000800 } else {
801 // no user file name, no current name- punt
802 psbs("No current filename");
803 goto vc1;
804 }
805
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000806 if (init_text_buffer(fn) < 0)
807 goto vc1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000808
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000809#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000810 if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
811 free(reg[Ureg]); // free orig line reg- for 'U'
812 reg[Ureg]= 0;
813 }
814 if (YDreg >= 0 && YDreg < 28 && reg[YDreg] != 0) {
815 free(reg[YDreg]); // free default yank/delete register
816 reg[YDreg]= 0;
817 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000818#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000819 // how many lines in text[]?
820 li = count_lines(text, end - 1);
821 psb("\"%s\"%s"
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000822 USE_FEATURE_VI_READONLY("%s")
823 " %dL, %dC", current_filename,
824 (file_size(fn) < 0 ? " [New file]" : ""),
825 USE_FEATURE_VI_READONLY(
826 ((readonly_mode) ? " [Readonly]" : ""),
827 )
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000828 li, ch);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000829 } else if (strncasecmp(cmd, "file", i) == 0) { // what File is this
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000830 if (b != -1 || e != -1) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000831 ni("No address allowed on this command");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000832 goto vc1;
833 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000834 if (args[0]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000835 // user wants a new filename
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000836 free(current_filename);
837 current_filename = xstrdup(args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000838 } else {
839 // user wants file status info
Paul Fox8552aec2005-09-16 12:20:05 +0000840 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000841 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000842 } else if (strncasecmp(cmd, "features", i) == 0) { // what features are available
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000843 // print out values of all features
844 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
845 clear_to_eol(); // clear the line
846 cookmode();
847 show_help();
848 rawmode();
849 Hit_Return();
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000850 } else if (strncasecmp(cmd, "list", i) == 0) { // literal print line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000851 if (b < 0) { // no addr given- use defaults
852 q = begin_line(dot); // assume .,. for the range
853 r = end_line(dot);
854 }
855 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
856 clear_to_eol(); // clear the line
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000857 puts("\r");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000858 for (; q <= r; q++) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000859 int c_is_no_print;
860
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000861 c = *q;
Denis Vlasenko2a51af22007-03-21 22:31:24 +0000862 c_is_no_print = (c & 0x80) && !Isprint(c);
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000863 if (c_is_no_print) {
864 c = '.';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000865 standout_start();
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000866 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000867 if (c == '\n') {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000868 write1("$\r");
869 } else if (c < ' ' || c == 127) {
870 putchar('^');
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000871 if (c == 127)
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000872 c = '?';
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000873 else
874 c += '@';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000875 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000876 putchar(c);
877 if (c_is_no_print)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000878 standout_end();
879 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000880#if ENABLE_FEATURE_VI_SET
881 vc2:
882#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000883 Hit_Return();
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000884 } else if (strncasecmp(cmd, "quit", i) == 0 // Quit
885 || strncasecmp(cmd, "next", i) == 0 // edit next file
886 ) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000887 if (useforce) {
888 // force end of argv list
889 if (*cmd == 'q') {
890 optind = save_argc;
891 }
892 editing = 0;
893 goto vc1;
894 }
895 // don't exit if the file been modified
896 if (file_modified) {
897 psbs("No write since last change (:%s! overrides)",
898 (*cmd == 'q' ? "quit" : "next"));
899 goto vc1;
900 }
901 // are there other file to edit
902 if (*cmd == 'q' && optind < save_argc - 1) {
903 psbs("%d more file to edit", (save_argc - optind - 1));
904 goto vc1;
905 }
906 if (*cmd == 'n' && optind >= save_argc - 1) {
907 psbs("No more files to edit");
908 goto vc1;
909 }
910 editing = 0;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000911 } else if (strncasecmp(cmd, "read", i) == 0) { // read file into text[]
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000912 fn = args;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000913 if (!fn[0]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000914 psbs("No filename given");
915 goto vc1;
916 }
917 if (b < 0) { // no addr given- use defaults
918 q = begin_line(dot); // assume "dot"
919 }
920 // read after current line- unless user said ":0r foo"
921 if (b != 0)
922 q = next_line(q);
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000923 ch = file_insert(fn, q USE_FEATURE_VI_READONLY(, 0));
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000924 if (ch < 0)
925 goto vc1; // nothing was inserted
926 // how many lines in text[]?
927 li = count_lines(q, q + ch - 1);
928 psb("\"%s\""
Denis Vlasenko59a1f302007-07-14 22:43:10 +0000929 USE_FEATURE_VI_READONLY("%s")
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000930 " %dL, %dC", fn,
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000931 USE_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000932 li, ch);
933 if (ch > 0) {
934 // if the insert is before "dot" then we need to update
935 if (q <= dot)
936 dot += ch;
Paul Fox8552aec2005-09-16 12:20:05 +0000937 file_modified++;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000938 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000939 } else if (strncasecmp(cmd, "rewind", i) == 0) { // rewind cmd line args
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000940 if (file_modified && ! useforce) {
941 psbs("No write since last change (:rewind! overrides)");
942 } else {
943 // reset the filenames to edit
944 optind = fn_start - 1;
945 editing = 0;
946 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000947#if ENABLE_FEATURE_VI_SET
Denis Vlasenkof9234132007-03-21 00:03:42 +0000948 } else if (strncasecmp(cmd, "set", i) == 0) { // set or clear features
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000949#if ENABLE_FEATURE_VI_SETOPTS
Denis Vlasenkof9234132007-03-21 00:03:42 +0000950 char *argp;
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000951#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000952 i = 0; // offset into args
Denis Vlasenkof9234132007-03-21 00:03:42 +0000953 // only blank is regarded as args delmiter. What about tab '\t' ?
954 if (!args[0] || strcasecmp(args, "all") == 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000955 // print out values of all options
956 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
957 clear_to_eol(); // clear the line
958 printf("----------------------------------------\r\n");
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000959#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000960 if (!autoindent)
961 printf("no");
962 printf("autoindent ");
963 if (!err_method)
964 printf("no");
965 printf("flash ");
966 if (!ignorecase)
967 printf("no");
968 printf("ignorecase ");
969 if (!showmatch)
970 printf("no");
971 printf("showmatch ");
972 printf("tabstop=%d ", tabstop);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000973#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000974 printf("\r\n");
975 goto vc2;
976 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000977#if ENABLE_FEATURE_VI_SETOPTS
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000978 argp = args;
Denis Vlasenkoba2fb712007-04-01 09:39:03 +0000979 while (*argp) {
Denis Vlasenkof9234132007-03-21 00:03:42 +0000980 if (strncasecmp(argp, "no", 2) == 0)
981 i = 2; // ":set noautoindent"
982 setops(argp, "autoindent ", i, "ai", VI_AUTOINDENT);
983 setops(argp, "flash ", i, "fl", VI_ERR_METHOD);
984 setops(argp, "ignorecase ", i, "ic", VI_IGNORECASE);
985 setops(argp, "showmatch ", i, "ic", VI_SHOWMATCH);
986 /* tabstopXXXX */
987 if (strncasecmp(argp + i, "tabstop=%d ", 7) == 0) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000988 sscanf(strchr(argp + i, '='), "tabstop=%d" + 7, &ch);
Denis Vlasenkof9234132007-03-21 00:03:42 +0000989 if (ch > 0 && ch < columns - 1)
990 tabstop = ch;
991 }
992 while (*argp && *argp != ' ')
993 argp++; // skip to arg delimiter (i.e. blank)
994 while (*argp && *argp == ' ')
995 argp++; // skip all delimiting blanks
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000996 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000997#endif /* FEATURE_VI_SETOPTS */
998#endif /* FEATURE_VI_SET */
999#if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001000 } else if (strncasecmp(cmd, "s", 1) == 0) { // substitute a pattern with a replacement pattern
1001 char *ls, *F, *R;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001002 int gflag;
1003
1004 // F points to the "find" pattern
1005 // R points to the "replace" pattern
1006 // replace the cmd line delimiters "/" with NULLs
1007 gflag = 0; // global replace flag
1008 c = orig_buf[1]; // what is the delimiter
1009 F = orig_buf + 2; // start of "find"
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001010 R = strchr(F, c); // middle delimiter
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001011 if (!R) goto colon_s_fail;
1012 *R++ = '\0'; // terminate "find"
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001013 buf1 = strchr(R, c);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001014 if (!buf1) goto colon_s_fail;
1015 *buf1++ = '\0'; // terminate "replace"
1016 if (*buf1 == 'g') { // :s/foo/bar/g
1017 buf1++;
1018 gflag++; // turn on gflag
1019 }
1020 q = begin_line(q);
1021 if (b < 0) { // maybe :s/foo/bar/
1022 q = begin_line(dot); // start with cur line
1023 b = count_lines(text, q); // cur line number
1024 }
1025 if (e < 0)
1026 e = b; // maybe :.s/foo/bar/
1027 for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0
1028 ls = q; // orig line start
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001029 vc4:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001030 buf1 = char_search(q, F, FORWARD, LIMITED); // search cur line only for "find"
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001031 if (buf1) {
1032 // we found the "find" pattern - delete it
1033 text_hole_delete(buf1, buf1 + strlen(F) - 1);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001034 // inset the "replace" patern
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001035 string_insert(buf1, R); // insert the string
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001036 // check for "global" :s/foo/bar/g
1037 if (gflag == 1) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001038 if ((buf1 + strlen(R)) < end_line(ls)) {
1039 q = buf1 + strlen(R);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001040 goto vc4; // don't let q move past cur line
1041 }
1042 }
1043 }
1044 q = next_line(ls);
1045 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001046#endif /* FEATURE_VI_SEARCH */
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001047 } else if (strncasecmp(cmd, "version", i) == 0) { // show software version
Rob Landleyd921b2e2006-08-03 15:41:12 +00001048 psb("%s", BB_VER " " BB_BT);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001049 } else if (strncasecmp(cmd, "write", i) == 0 // write text to file
1050 || strncasecmp(cmd, "wq", i) == 0
1051 || strncasecmp(cmd, "wn", i) == 0
1052 || strncasecmp(cmd, "x", i) == 0
1053 ) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001054 // is there a file name to write to?
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001055 if (args[0]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001056 fn = args;
1057 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001058#if ENABLE_FEATURE_VI_READONLY
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001059 if (readonly_mode && !useforce) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001060 psbs("\"%s\" File is read only", fn);
1061 goto vc3;
1062 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001063#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001064 // how many lines in text[]?
1065 li = count_lines(q, r);
1066 ch = r - q + 1;
1067 // see if file exists- if not, its just a new file request
1068 if (useforce) {
1069 // if "fn" is not write-able, chmod u+w
1070 // sprintf(syscmd, "chmod u+w %s", fn);
1071 // system(syscmd);
1072 forced = TRUE;
1073 }
1074 l = file_write(fn, q, r);
1075 if (useforce && forced) {
1076 // chmod u-w
1077 // sprintf(syscmd, "chmod u-w %s", fn);
1078 // system(syscmd);
1079 forced = FALSE;
1080 }
Paul Fox61e45db2005-10-09 14:43:22 +00001081 if (l < 0) {
1082 if (l == -1)
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001083 psbs("\"%s\" %s", fn, strerror(errno));
Paul Fox61e45db2005-10-09 14:43:22 +00001084 } else {
1085 psb("\"%s\" %dL, %dC", fn, li, l);
1086 if (q == text && r == end - 1 && l == ch) {
1087 file_modified = 0;
1088 last_file_modified = -1;
1089 }
Paul Fox9360f422006-03-27 21:51:16 +00001090 if ((cmd[0] == 'x' || cmd[1] == 'q' || cmd[1] == 'n' ||
1091 cmd[0] == 'X' || cmd[1] == 'Q' || cmd[1] == 'N')
1092 && l == ch) {
Paul Fox61e45db2005-10-09 14:43:22 +00001093 editing = 0;
1094 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001095 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001096#if ENABLE_FEATURE_VI_READONLY
1097 vc3:;
1098#endif
1099#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001100 } else if (strncasecmp(cmd, "yank", i) == 0) { // yank lines
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001101 if (b < 0) { // no addr given- use defaults
1102 q = begin_line(dot); // assume .,. for the range
1103 r = end_line(dot);
1104 }
1105 text_yank(q, r, YDreg);
1106 li = count_lines(q, r);
1107 psb("Yank %d lines (%d chars) into [%c]",
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001108 li, strlen(reg[YDreg]), what_reg());
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001109#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001110 } else {
1111 // cmd unknown
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001112 ni(cmd);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001113 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001114 vc1:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001115 dot = bound_dot(dot); // make sure "dot" is valid
1116 return;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001117#if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001118 colon_s_fail:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001119 psb(":s expression missing delimiters");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001120#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001121}
Paul Fox61e45db2005-10-09 14:43:22 +00001122
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001123#endif /* FEATURE_VI_COLON */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001124
1125static void Hit_Return(void)
1126{
1127 char c;
1128
1129 standout_start(); // start reverse video
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001130 write1("[Hit return to continue]");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001131 standout_end(); // end reverse video
1132 while ((c = get_one_char()) != '\n' && c != '\r') /*do nothing */
1133 ;
1134 redraw(TRUE); // force redraw all
1135}
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001136
Denis Vlasenko91afdf82007-07-17 23:22:49 +00001137static int next_tabstop(int col)
1138{
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001139 return col + ((tabstop - 1) - (col % tabstop));
1140}
1141
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001142//----- Synchronize the cursor to Dot --------------------------
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001143static void sync_cursor(char * d, int *row, int *col)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001144{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001145 char *beg_cur; // begin and end of "d" line
1146 char *end_scr; // begin and end of screen
1147 char *tp;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001148 int cnt, ro, co;
1149
1150 beg_cur = begin_line(d); // first char of cur line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001151
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001152 end_scr = end_screen(); // last char of screen
1153
1154 if (beg_cur < screenbegin) {
1155 // "d" is before top line on screen
1156 // how many lines do we have to move
1157 cnt = count_lines(beg_cur, screenbegin);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001158 sc1:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001159 screenbegin = beg_cur;
1160 if (cnt > (rows - 1) / 2) {
1161 // we moved too many lines. put "dot" in middle of screen
1162 for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
1163 screenbegin = prev_line(screenbegin);
1164 }
1165 }
1166 } else if (beg_cur > end_scr) {
1167 // "d" is after bottom line on screen
1168 // how many lines do we have to move
1169 cnt = count_lines(end_scr, beg_cur);
1170 if (cnt > (rows - 1) / 2)
1171 goto sc1; // too many lines
1172 for (ro = 0; ro < cnt - 1; ro++) {
1173 // move screen begin the same amount
1174 screenbegin = next_line(screenbegin);
1175 // now, move the end of screen
1176 end_scr = next_line(end_scr);
1177 end_scr = end_line(end_scr);
1178 }
1179 }
1180 // "d" is on screen- find out which row
1181 tp = screenbegin;
1182 for (ro = 0; ro < rows - 1; ro++) { // drive "ro" to correct row
1183 if (tp == beg_cur)
1184 break;
1185 tp = next_line(tp);
1186 }
1187
1188 // find out what col "d" is on
1189 co = 0;
1190 do { // drive "co" to correct column
1191 if (*tp == '\n' || *tp == '\0')
1192 break;
1193 if (*tp == '\t') {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001194 if (d == tp && cmd_mode) { /* handle tabs like real vi */
1195 break;
1196 } else {
1197 co = next_tabstop(co);
1198 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001199 } else if (*tp < ' ' || *tp == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001200 co++; // display as ^X, use 2 columns
1201 }
1202 } while (tp++ < d && ++co);
1203
1204 // "co" is the column where "dot" is.
1205 // The screen has "columns" columns.
1206 // The currently displayed columns are 0+offset -- columns+ofset
1207 // |-------------------------------------------------------------|
1208 // ^ ^ ^
1209 // offset | |------- columns ----------------|
1210 //
1211 // If "co" is already in this range then we do not have to adjust offset
1212 // but, we do have to subtract the "offset" bias from "co".
1213 // If "co" is outside this range then we have to change "offset".
1214 // If the first char of a line is a tab the cursor will try to stay
1215 // in column 7, but we have to set offset to 0.
1216
1217 if (co < 0 + offset) {
1218 offset = co;
1219 }
1220 if (co >= columns + offset) {
1221 offset = co - columns + 1;
1222 }
1223 // if the first char of the line is a tab, and "dot" is sitting on it
1224 // force offset to 0.
1225 if (d == beg_cur && *d == '\t') {
1226 offset = 0;
1227 }
1228 co -= offset;
1229
1230 *row = ro;
1231 *col = co;
1232}
1233
1234//----- Text Movement Routines ---------------------------------
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001235static char *begin_line(char * p) // return pointer to first char cur line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001236{
1237 while (p > text && p[-1] != '\n')
1238 p--; // go to cur line B-o-l
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001239 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001240}
1241
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001242static char *end_line(char * p) // return pointer to NL of cur line line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001243{
1244 while (p < end - 1 && *p != '\n')
1245 p++; // go to cur line E-o-l
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001246 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001247}
1248
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001249static inline char *dollar_line(char * p) // return pointer to just before NL line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001250{
1251 while (p < end - 1 && *p != '\n')
1252 p++; // go to cur line E-o-l
1253 // Try to stay off of the Newline
1254 if (*p == '\n' && (p - begin_line(p)) > 0)
1255 p--;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001256 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001257}
1258
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001259static char *prev_line(char * p) // return pointer first char prev line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001260{
1261 p = begin_line(p); // goto begining of cur line
1262 if (p[-1] == '\n' && p > text)
1263 p--; // step to prev line
1264 p = begin_line(p); // goto begining of prev line
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001265 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001266}
1267
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001268static char *next_line(char * p) // return pointer first char next line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001269{
1270 p = end_line(p);
1271 if (*p == '\n' && p < end - 1)
1272 p++; // step to next line
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001273 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001274}
1275
1276//----- Text Information Routines ------------------------------
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001277static char *end_screen(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001278{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001279 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001280 int cnt;
1281
1282 // find new bottom line
1283 q = screenbegin;
1284 for (cnt = 0; cnt < rows - 2; cnt++)
1285 q = next_line(q);
1286 q = end_line(q);
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001287 return q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001288}
1289
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001290static int count_lines(char * start, char * stop) // count line from start to stop
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001291{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001292 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001293 int cnt;
1294
1295 if (stop < start) { // start and stop are backwards- reverse them
1296 q = start;
1297 start = stop;
1298 stop = q;
1299 }
1300 cnt = 0;
1301 stop = end_line(stop); // get to end of this line
1302 for (q = start; q <= stop && q <= end - 1; q++) {
1303 if (*q == '\n')
1304 cnt++;
1305 }
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001306 return cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001307}
1308
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001309static char *find_line(int li) // find begining of line #li
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001310{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001311 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001312
1313 for (q = text; li > 1; li--) {
1314 q = next_line(q);
1315 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001316 return q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001317}
1318
1319//----- Dot Movement Routines ----------------------------------
1320static void dot_left(void)
1321{
1322 if (dot > text && dot[-1] != '\n')
1323 dot--;
1324}
1325
1326static void dot_right(void)
1327{
1328 if (dot < end - 1 && *dot != '\n')
1329 dot++;
1330}
1331
1332static void dot_begin(void)
1333{
1334 dot = begin_line(dot); // return pointer to first char cur line
1335}
1336
1337static void dot_end(void)
1338{
1339 dot = end_line(dot); // return pointer to last char cur line
1340}
1341
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001342static char *move_to_col(char * p, int l)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001343{
1344 int co;
1345
1346 p = begin_line(p);
1347 co = 0;
1348 do {
1349 if (*p == '\n' || *p == '\0')
1350 break;
1351 if (*p == '\t') {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001352 co = next_tabstop(co);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001353 } else if (*p < ' ' || *p == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001354 co++; // display as ^X, use 2 columns
1355 }
1356 } while (++co <= l && p++ < end);
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001357 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001358}
1359
1360static void dot_next(void)
1361{
1362 dot = next_line(dot);
1363}
1364
1365static void dot_prev(void)
1366{
1367 dot = prev_line(dot);
1368}
1369
1370static void dot_scroll(int cnt, int dir)
1371{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001372 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001373
1374 for (; cnt > 0; cnt--) {
1375 if (dir < 0) {
1376 // scroll Backwards
1377 // ctrl-Y scroll up one line
1378 screenbegin = prev_line(screenbegin);
1379 } else {
1380 // scroll Forwards
1381 // ctrl-E scroll down one line
1382 screenbegin = next_line(screenbegin);
1383 }
1384 }
1385 // make sure "dot" stays on the screen so we dont scroll off
1386 if (dot < screenbegin)
1387 dot = screenbegin;
1388 q = end_screen(); // find new bottom line
1389 if (dot > q)
1390 dot = begin_line(q); // is dot is below bottom line?
1391 dot_skip_over_ws();
1392}
1393
1394static void dot_skip_over_ws(void)
1395{
1396 // skip WS
1397 while (isspace(*dot) && *dot != '\n' && dot < end - 1)
1398 dot++;
1399}
1400
1401static void dot_delete(void) // delete the char at 'dot'
1402{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001403 text_hole_delete(dot, dot);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001404}
1405
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001406static char *bound_dot(char * p) // make sure text[0] <= P < "end"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001407{
1408 if (p >= end && end > text) {
1409 p = end - 1;
1410 indicate_error('1');
1411 }
1412 if (p < text) {
1413 p = text;
1414 indicate_error('2');
1415 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001416 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001417}
1418
1419//----- Helper Utility Routines --------------------------------
1420
1421//----------------------------------------------------------------
1422//----- Char Routines --------------------------------------------
1423/* Chars that are part of a word-
1424 * 0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
1425 * Chars that are Not part of a word (stoppers)
1426 * !"#$%&'()*+,-./:;<=>?@[\]^`{|}~
1427 * Chars that are WhiteSpace
1428 * TAB NEWLINE VT FF RETURN SPACE
1429 * DO NOT COUNT NEWLINE AS WHITESPACE
1430 */
1431
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001432static char *new_screen(int ro, int co)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001433{
1434 int li;
1435
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001436 free(screen);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001437 screensize = ro * co + 8;
Denis Vlasenkob95636c2006-12-19 23:36:04 +00001438 screen = xmalloc(screensize);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001439 // initialize the new screen. assume this will be a empty file.
1440 screen_erase();
Eric Andersenaff114c2004-04-14 17:51:38 +00001441 // non-existent text[] lines start with a tilde (~).
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001442 for (li = 1; li < ro - 1; li++) {
1443 screen[(li * co) + 0] = '~';
1444 }
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001445 return screen;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001446}
1447
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001448#if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001449static int mycmp(const char * s1, const char * s2, int len)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001450{
1451 int i;
1452
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001453 i = strncmp(s1, s2, len);
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001454 if (ENABLE_FEATURE_VI_SETOPTS && ignorecase) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001455 i = strncasecmp(s1, s2, len);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001456 }
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001457 return i;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001458}
1459
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001460// search for pattern starting at p
1461static char *char_search(char * p, const char * pat, int dir, int range)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001462{
1463#ifndef REGEX_SEARCH
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001464 char *start, *stop;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001465 int len;
1466
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001467 len = strlen(pat);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001468 if (dir == FORWARD) {
1469 stop = end - 1; // assume range is p - end-1
1470 if (range == LIMITED)
1471 stop = next_line(p); // range is to next line
1472 for (start = p; start < stop; start++) {
1473 if (mycmp(start, pat, len) == 0) {
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001474 return start;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001475 }
1476 }
1477 } else if (dir == BACK) {
1478 stop = text; // assume range is text - p
1479 if (range == LIMITED)
1480 stop = prev_line(p); // range is to prev line
1481 for (start = p - len; start >= stop; start--) {
1482 if (mycmp(start, pat, len) == 0) {
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001483 return start;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001484 }
1485 }
1486 }
1487 // pattern not found
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001488 return NULL;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001489#else /* REGEX_SEARCH */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001490 char *q;
1491 struct re_pattern_buffer preg;
1492 int i;
1493 int size, range;
1494
1495 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
1496 preg.translate = 0;
1497 preg.fastmap = 0;
1498 preg.buffer = 0;
1499 preg.allocated = 0;
1500
1501 // assume a LIMITED forward search
1502 q = next_line(p);
1503 q = end_line(q);
1504 q = end - 1;
1505 if (dir == BACK) {
1506 q = prev_line(p);
1507 q = text;
1508 }
1509 // count the number of chars to search over, forward or backward
1510 size = q - p;
1511 if (size < 0)
1512 size = p - q;
1513 // RANGE could be negative if we are searching backwards
1514 range = q - p;
1515
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001516 q = re_compile_pattern(pat, strlen(pat), &preg);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001517 if (q != 0) {
1518 // The pattern was not compiled
1519 psbs("bad search pattern: \"%s\": %s", pat, q);
1520 i = 0; // return p if pattern not compiled
1521 goto cs1;
1522 }
1523
1524 q = p;
1525 if (range < 0) {
1526 q = p - size;
1527 if (q < text)
1528 q = text;
1529 }
1530 // search for the compiled pattern, preg, in p[]
1531 // range < 0- search backward
1532 // range > 0- search forward
1533 // 0 < start < size
1534 // re_search() < 0 not found or error
1535 // re_search() > 0 index of found pattern
1536 // struct pattern char int int int struct reg
1537 // re_search (*pattern_buffer, *string, size, start, range, *regs)
1538 i = re_search(&preg, q, size, 0, range, 0);
1539 if (i == -1) {
1540 p = 0;
1541 i = 0; // return NULL if pattern not found
1542 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001543 cs1:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001544 if (dir == FORWARD) {
1545 p = p + i;
1546 } else {
1547 p = p - i;
1548 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001549 return p;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001550#endif /* REGEX_SEARCH */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001551}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001552#endif /* FEATURE_VI_SEARCH */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001553
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001554static char *char_insert(char * p, char c) // insert the char c at 'p'
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001555{
1556 if (c == 22) { // Is this an ctrl-V?
1557 p = stupid_insert(p, '^'); // use ^ to indicate literal next
1558 p--; // backup onto ^
1559 refresh(FALSE); // show the ^
1560 c = get_one_char();
1561 *p = c;
1562 p++;
Paul Fox8552aec2005-09-16 12:20:05 +00001563 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001564 } else if (c == 27) { // Is this an ESC?
1565 cmd_mode = 0;
1566 cmdcnt = 0;
1567 end_cmd_q(); // stop adding to q
Paul Fox8552aec2005-09-16 12:20:05 +00001568 last_status_cksum = 0; // force status update
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001569 if ((p[-1] != '\n') && (dot > text)) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001570 p--;
1571 }
Paul Foxd13b90b2005-07-18 22:17:25 +00001572 } else if (c == erase_char || c == 8 || c == 127) { // Is this a BS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001573 // 123456789
1574 if ((p[-1] != '\n') && (dot>text)) {
1575 p--;
1576 p = text_hole_delete(p, p); // shrink buffer 1 char
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001577 }
1578 } else {
1579 // insert a char into text[]
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001580 char *sp; // "save p"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001581
1582 if (c == 13)
1583 c = '\n'; // translate \r to \n
1584 sp = p; // remember addr of insert
1585 p = stupid_insert(p, c); // insert the char
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001586#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001587 if (showmatch && strchr(")]}", *sp) != NULL) {
1588 showmatching(sp);
1589 }
1590 if (autoindent && c == '\n') { // auto indent the new line
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001591 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001592
1593 q = prev_line(p); // use prev line as templet
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001594 for (; isblank(*q); q++) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001595 p = stupid_insert(p, *q); // insert the char
1596 }
1597 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001598#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001599 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001600 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001601}
1602
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001603static char *stupid_insert(char * p, char c) // stupidly insert the char c at 'p'
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001604{
1605 p = text_hole_make(p, 1);
1606 if (p != 0) {
1607 *p = c;
Paul Fox8552aec2005-09-16 12:20:05 +00001608 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001609 p++;
1610 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001611 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001612}
1613
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001614static char find_range(char ** start, char ** stop, char c)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001615{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001616 char *save_dot, *p, *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001617 int cnt;
1618
1619 save_dot = dot;
1620 p = q = dot;
1621
1622 if (strchr("cdy><", c)) {
1623 // these cmds operate on whole lines
1624 p = q = begin_line(p);
1625 for (cnt = 1; cnt < cmdcnt; cnt++) {
1626 q = next_line(q);
1627 }
1628 q = end_line(q);
1629 } else if (strchr("^%$0bBeEft", c)) {
1630 // These cmds operate on char positions
1631 do_cmd(c); // execute movement cmd
1632 q = dot;
1633 } else if (strchr("wW", c)) {
1634 do_cmd(c); // execute movement cmd
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001635 // if we are at the next word's first char
1636 // step back one char
1637 // but check the possibilities when it is true
Eric Andersen5cc90ea2004-02-06 10:36:08 +00001638 if (dot > text && ((isspace(dot[-1]) && !isspace(dot[0]))
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001639 || (ispunct(dot[-1]) && !ispunct(dot[0]))
1640 || (isalnum(dot[-1]) && !isalnum(dot[0]))))
1641 dot--; // move back off of next word
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001642 if (dot > text && *dot == '\n')
1643 dot--; // stay off NL
1644 q = dot;
1645 } else if (strchr("H-k{", c)) {
1646 // these operate on multi-lines backwards
1647 q = end_line(dot); // find NL
1648 do_cmd(c); // execute movement cmd
1649 dot_begin();
1650 p = dot;
1651 } else if (strchr("L+j}\r\n", c)) {
1652 // these operate on multi-lines forwards
1653 p = begin_line(dot);
1654 do_cmd(c); // execute movement cmd
1655 dot_end(); // find NL
1656 q = dot;
1657 } else {
1658 c = 27; // error- return an ESC char
1659 //break;
1660 }
1661 *start = p;
1662 *stop = q;
1663 if (q < p) {
1664 *start = q;
1665 *stop = p;
1666 }
1667 dot = save_dot;
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001668 return c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001669}
1670
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001671static int st_test(char * p, int type, int dir, char * tested)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001672{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001673 char c, c0, ci;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001674 int test, inc;
1675
1676 inc = dir;
1677 c = c0 = p[0];
1678 ci = p[inc];
1679 test = 0;
1680
1681 if (type == S_BEFORE_WS) {
1682 c = ci;
1683 test = ((!isspace(c)) || c == '\n');
1684 }
1685 if (type == S_TO_WS) {
1686 c = c0;
1687 test = ((!isspace(c)) || c == '\n');
1688 }
1689 if (type == S_OVER_WS) {
1690 c = c0;
1691 test = ((isspace(c)));
1692 }
1693 if (type == S_END_PUNCT) {
1694 c = ci;
1695 test = ((ispunct(c)));
1696 }
1697 if (type == S_END_ALNUM) {
1698 c = ci;
1699 test = ((isalnum(c)) || c == '_');
1700 }
1701 *tested = c;
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001702 return test;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001703}
1704
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001705static char *skip_thing(char * p, int linecnt, int dir, int type)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001706{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001707 char c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001708
1709 while (st_test(p, type, dir, &c)) {
1710 // make sure we limit search to correct number of lines
1711 if (c == '\n' && --linecnt < 1)
1712 break;
1713 if (dir >= 0 && p >= end - 1)
1714 break;
1715 if (dir < 0 && p <= text)
1716 break;
1717 p += dir; // move to next char
1718 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001719 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001720}
1721
1722// find matching char of pair () [] {}
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001723static char *find_pair(char * p, char c)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001724{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001725 char match, *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001726 int dir, level;
1727
1728 match = ')';
1729 level = 1;
1730 dir = 1; // assume forward
1731 switch (c) {
1732 case '(':
1733 match = ')';
1734 break;
1735 case '[':
1736 match = ']';
1737 break;
1738 case '{':
1739 match = '}';
1740 break;
1741 case ')':
1742 match = '(';
1743 dir = -1;
1744 break;
1745 case ']':
1746 match = '[';
1747 dir = -1;
1748 break;
1749 case '}':
1750 match = '{';
1751 dir = -1;
1752 break;
1753 }
1754 for (q = p + dir; text <= q && q < end; q += dir) {
1755 // look for match, count levels of pairs (( ))
1756 if (*q == c)
1757 level++; // increase pair levels
1758 if (*q == match)
1759 level--; // reduce pair level
1760 if (level == 0)
1761 break; // found matching pair
1762 }
1763 if (level != 0)
1764 q = NULL; // indicate no match
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001765 return q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001766}
1767
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001768#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001769// show the matching char of a pair, () [] {}
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001770static void showmatching(char * p)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001771{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001772 char *q, *save_dot;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001773
1774 // we found half of a pair
1775 q = find_pair(p, *p); // get loc of matching char
1776 if (q == NULL) {
1777 indicate_error('3'); // no matching char
1778 } else {
1779 // "q" now points to matching pair
1780 save_dot = dot; // remember where we are
1781 dot = q; // go to new loc
1782 refresh(FALSE); // let the user see it
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001783 mysleep(40); // give user some time
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001784 dot = save_dot; // go back to old loc
1785 refresh(FALSE);
1786 }
1787}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001788#endif /* FEATURE_VI_SETOPTS */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001789
1790// open a hole in text[]
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001791static char *text_hole_make(char * p, int size) // at "p", make a 'size' byte hole
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001792{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001793 char *src, *dest;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001794 int cnt;
1795
1796 if (size <= 0)
1797 goto thm0;
1798 src = p;
1799 dest = p + size;
1800 cnt = end - src; // the rest of buffer
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001801 if ( ((end + size) >= (text + text_size)) // TODO: realloc here
1802 || memmove(dest, src, cnt) != dest) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001803 psbs("can't create room for new characters");
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001804 p = NULL;
1805 goto thm0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001806 }
1807 memset(p, ' ', size); // clear new hole
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001808 end += size; // adjust the new END
Paul Fox8552aec2005-09-16 12:20:05 +00001809 file_modified++; // has the file been modified
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001810 thm0:
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001811 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001812}
1813
1814// close a hole in text[]
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001815static char *text_hole_delete(char * p, char * q) // delete "p" thru "q", inclusive
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001816{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001817 char *src, *dest;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001818 int cnt, hole_size;
1819
1820 // move forwards, from beginning
1821 // assume p <= q
1822 src = q + 1;
1823 dest = p;
1824 if (q < p) { // they are backward- swap them
1825 src = p + 1;
1826 dest = q;
1827 }
1828 hole_size = q - p + 1;
1829 cnt = end - src;
1830 if (src < text || src > end)
1831 goto thd0;
1832 if (dest < text || dest >= end)
1833 goto thd0;
1834 if (src >= end)
1835 goto thd_atend; // just delete the end of the buffer
1836 if (memmove(dest, src, cnt) != dest) {
1837 psbs("can't delete the character");
1838 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001839 thd_atend:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001840 end = end - hole_size; // adjust the new END
1841 if (dest >= end)
1842 dest = end - 1; // make sure dest in below end-1
1843 if (end <= text)
1844 dest = end = text; // keep pointers valid
Paul Fox8552aec2005-09-16 12:20:05 +00001845 file_modified++; // has the file been modified
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001846 thd0:
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001847 return dest;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001848}
1849
1850// copy text into register, then delete text.
1851// if dist <= 0, do not include, or go past, a NewLine
1852//
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001853static char *yank_delete(char * start, char * stop, int dist, int yf)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001854{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001855 char *p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001856
1857 // make sure start <= stop
1858 if (start > stop) {
1859 // they are backwards, reverse them
1860 p = start;
1861 start = stop;
1862 stop = p;
1863 }
1864 if (dist <= 0) {
Denis Vlasenkoe1a0d482006-10-20 13:28:22 +00001865 // we cannot cross NL boundaries
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001866 p = start;
1867 if (*p == '\n')
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001868 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001869 // dont go past a NewLine
1870 for (; p + 1 <= stop; p++) {
1871 if (p[1] == '\n') {
1872 stop = p; // "stop" just before NewLine
1873 break;
1874 }
1875 }
1876 }
1877 p = start;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001878#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001879 text_yank(start, stop, YDreg);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001880#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001881 if (yf == YANKDEL) {
1882 p = text_hole_delete(start, stop);
1883 } // delete lines
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001884 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001885}
1886
1887static void show_help(void)
1888{
1889 puts("These features are available:"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001890#if ENABLE_FEATURE_VI_SEARCH
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001891 "\n\tPattern searches with / and ?"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001892#endif
1893#if ENABLE_FEATURE_VI_DOT_CMD
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001894 "\n\tLast command repeat with \'.\'"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001895#endif
1896#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001897 "\n\tLine marking with 'x"
1898 "\n\tNamed buffers with \"x"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001899#endif
1900#if ENABLE_FEATURE_VI_READONLY
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001901 "\n\tReadonly if vi is called as \"view\""
1902 "\n\tReadonly with -R command line arg"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001903#endif
1904#if ENABLE_FEATURE_VI_SET
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001905 "\n\tSome colon mode commands with \':\'"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001906#endif
1907#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001908 "\n\tSettable options with \":set\""
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001909#endif
1910#if ENABLE_FEATURE_VI_USE_SIGNALS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001911 "\n\tSignal catching- ^C"
1912 "\n\tJob suspend and resume with ^Z"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001913#endif
1914#if ENABLE_FEATURE_VI_WIN_RESIZE
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001915 "\n\tAdapt to window re-sizes"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001916#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001917 );
1918}
1919
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001920static inline void print_literal(char * buf, const char * s) // copy s to buf, convert unprintable
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001921{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001922 unsigned char c;
1923 char b[2];
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001924
1925 b[1] = '\0';
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001926 buf[0] = '\0';
1927 if (!s[0])
1928 s = "(NULL)";
1929 for (; *s; s++) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001930 int c_is_no_print;
1931
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001932 c = *s;
Denis Vlasenko2a51af22007-03-21 22:31:24 +00001933 c_is_no_print = (c & 0x80) && !Isprint(c);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001934 if (c_is_no_print) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001935 strcat(buf, SOn);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001936 c = '.';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001937 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001938 if (c < ' ' || c == 127) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001939 strcat(buf, "^");
1940 if (c == 127)
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001941 c = '?';
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001942 else
1943 c += '@';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001944 }
1945 b[0] = c;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001946 strcat(buf, b);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001947 if (c_is_no_print)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001948 strcat(buf, SOs);
1949 if (*s == '\n')
1950 strcat(buf, "$");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001951 }
1952}
1953
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001954#if ENABLE_FEATURE_VI_DOT_CMD
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001955static void start_new_cmd_q(char c)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001956{
1957 // release old cmd
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001958 free(last_modifying_cmd);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001959 // get buffer for new cmd
Denis Vlasenkoe8a07882007-06-10 15:08:44 +00001960 last_modifying_cmd = xzalloc(MAX_LINELEN);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001961 // if there is a current cmd count put it in the buffer first
1962 if (cmdcnt > 0)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001963 sprintf(last_modifying_cmd, "%d%c", cmdcnt, c);
Paul Foxd957b952005-11-28 18:07:53 +00001964 else // just save char c onto queue
1965 last_modifying_cmd[0] = c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001966 adding2q = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001967}
1968
1969static void end_cmd_q(void)
1970{
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001971#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001972 YDreg = 26; // go back to default Yank/Delete reg
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001973#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001974 adding2q = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001975}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001976#endif /* FEATURE_VI_DOT_CMD */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001977
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001978#if ENABLE_FEATURE_VI_YANKMARK \
1979 || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) \
1980 || ENABLE_FEATURE_VI_CRASHME
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001981static char *string_insert(char * p, char * s) // insert the string at 'p'
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001982{
1983 int cnt, i;
1984
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001985 i = strlen(s);
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001986 if (text_hole_make(p, i)) {
1987 strncpy(p, s, i);
1988 for (cnt = 0; *s != '\0'; s++) {
1989 if (*s == '\n')
1990 cnt++;
1991 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001992#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001993 psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001994#endif
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001995 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001996 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001997}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001998#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001999
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002000#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002001static char *text_yank(char * p, char * q, int dest) // copy text into a register
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002002{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002003 char *t;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002004 int cnt;
2005
2006 if (q < p) { // they are backwards- reverse them
2007 t = q;
2008 q = p;
2009 p = t;
2010 }
2011 cnt = q - p + 1;
2012 t = reg[dest];
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002013 free(t); // if already a yank register, free it
Denis Vlasenkob95636c2006-12-19 23:36:04 +00002014 t = xmalloc(cnt + 1); // get a new register
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002015 memset(t, '\0', cnt + 1); // clear new text[]
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002016 strncpy(t, p, cnt); // copy text[] into bufer
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002017 reg[dest] = t;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002018 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002019}
2020
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002021static char what_reg(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002022{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002023 char c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002024
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002025 c = 'D'; // default to D-reg
2026 if (0 <= YDreg && YDreg <= 25)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002027 c = 'a' + (char) YDreg;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002028 if (YDreg == 26)
2029 c = 'D';
2030 if (YDreg == 27)
2031 c = 'U';
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002032 return c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002033}
2034
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002035static void check_context(char cmd)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002036{
2037 // A context is defined to be "modifying text"
2038 // Any modifying command establishes a new context.
2039
2040 if (dot < context_start || dot > context_end) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002041 if (strchr(modifying_cmds, cmd) != NULL) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002042 // we are trying to modify text[]- make this the current context
2043 mark[27] = mark[26]; // move cur to prev
2044 mark[26] = dot; // move local to cur
2045 context_start = prev_line(prev_line(dot));
2046 context_end = next_line(next_line(dot));
2047 //loiter= start_loiter= now;
2048 }
2049 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002050}
2051
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002052static inline char *swap_context(char * p) // goto new context for '' command make this the current context
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002053{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002054 char *tmp;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002055
2056 // the current context is in mark[26]
2057 // the previous context is in mark[27]
2058 // only swap context if other context is valid
2059 if (text <= mark[27] && mark[27] <= end - 1) {
2060 tmp = mark[27];
2061 mark[27] = mark[26];
2062 mark[26] = tmp;
2063 p = mark[26]; // where we are going- previous context
2064 context_start = prev_line(prev_line(prev_line(p)));
2065 context_end = next_line(next_line(next_line(p)));
2066 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002067 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002068}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002069#endif /* FEATURE_VI_YANKMARK */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002070
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002071//----- Set terminal attributes --------------------------------
2072static void rawmode(void)
2073{
2074 tcgetattr(0, &term_orig);
2075 term_vi = term_orig;
2076 term_vi.c_lflag &= (~ICANON & ~ECHO); // leave ISIG ON- allow intr's
2077 term_vi.c_iflag &= (~IXON & ~ICRNL);
2078 term_vi.c_oflag &= (~ONLCR);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002079 term_vi.c_cc[VMIN] = 1;
2080 term_vi.c_cc[VTIME] = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002081 erase_char = term_vi.c_cc[VERASE];
2082 tcsetattr(0, TCSANOW, &term_vi);
2083}
2084
2085static void cookmode(void)
2086{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002087 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002088 tcsetattr(0, TCSANOW, &term_orig);
2089}
2090
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002091//----- Come here when we get a window resize signal ---------
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002092#if ENABLE_FEATURE_VI_USE_SIGNALS
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002093static void winch_sig(int sig ATTRIBUTE_UNUSED)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002094{
2095 signal(SIGWINCH, winch_sig);
Rob Landleye5e1a102006-06-21 01:15:36 +00002096 if (ENABLE_FEATURE_VI_WIN_RESIZE)
Denis Vlasenko621204b2006-10-27 09:03:24 +00002097 get_terminal_width_height(0, &columns, &rows);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002098 new_screen(rows, columns); // get memory for virtual screen
2099 redraw(TRUE); // re-draw the screen
2100}
2101
2102//----- Come here when we get a continue signal -------------------
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002103static void cont_sig(int sig ATTRIBUTE_UNUSED)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002104{
2105 rawmode(); // terminal to "raw"
Paul Fox8552aec2005-09-16 12:20:05 +00002106 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002107 redraw(TRUE); // re-draw the screen
2108
2109 signal(SIGTSTP, suspend_sig);
2110 signal(SIGCONT, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002111 kill(my_pid, SIGCONT);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002112}
2113
2114//----- Come here when we get a Suspend signal -------------------
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002115static void suspend_sig(int sig ATTRIBUTE_UNUSED)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002116{
2117 place_cursor(rows - 1, 0, FALSE); // go to bottom of screen
2118 clear_to_eol(); // Erase to end of line
2119 cookmode(); // terminal to "cooked"
2120
2121 signal(SIGCONT, cont_sig);
2122 signal(SIGTSTP, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002123 kill(my_pid, SIGTSTP);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002124}
2125
2126//----- Come here when we get a signal ---------------------------
2127static void catch_sig(int sig)
2128{
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002129 signal(SIGINT, catch_sig);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002130 if (sig)
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002131 longjmp(restart, sig);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002132}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002133#endif /* FEATURE_VI_USE_SIGNALS */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002134
2135static int mysleep(int hund) // sleep for 'h' 1/100 seconds
2136{
Denis Vlasenkocd5c7862007-05-17 16:37:22 +00002137 fd_set rfds;
2138 struct timeval tv;
2139
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002140 // Don't hang- Wait 5/100 seconds- 1 Sec= 1000000
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002141 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002142 FD_ZERO(&rfds);
2143 FD_SET(0, &rfds);
2144 tv.tv_sec = 0;
2145 tv.tv_usec = hund * 10000;
2146 select(1, &rfds, NULL, NULL, &tv);
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002147 return FD_ISSET(0, &rfds);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002148}
2149
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002150#define readbuffer bb_common_bufsiz1
2151
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002152static int readed_for_parse;
2153
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002154//----- IO Routines --------------------------------------------
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002155static char readit(void) // read (maybe cursor) key from stdin
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002156{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002157 char c;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002158 int n;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002159 struct esc_cmds {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002160 const char *seq;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002161 char val;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002162 };
2163
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002164 static const struct esc_cmds esccmds[] = {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002165 {"OA", VI_K_UP}, // cursor key Up
2166 {"OB", VI_K_DOWN}, // cursor key Down
2167 {"OC", VI_K_RIGHT}, // Cursor Key Right
2168 {"OD", VI_K_LEFT}, // cursor key Left
2169 {"OH", VI_K_HOME}, // Cursor Key Home
2170 {"OF", VI_K_END}, // Cursor Key End
2171 {"[A", VI_K_UP}, // cursor key Up
2172 {"[B", VI_K_DOWN}, // cursor key Down
2173 {"[C", VI_K_RIGHT}, // Cursor Key Right
2174 {"[D", VI_K_LEFT}, // cursor key Left
2175 {"[H", VI_K_HOME}, // Cursor Key Home
2176 {"[F", VI_K_END}, // Cursor Key End
2177 {"[1~", VI_K_HOME}, // Cursor Key Home
2178 {"[2~", VI_K_INSERT}, // Cursor Key Insert
2179 {"[4~", VI_K_END}, // Cursor Key End
2180 {"[5~", VI_K_PAGEUP}, // Cursor Key Page Up
2181 {"[6~", VI_K_PAGEDOWN},// Cursor Key Page Down
2182 {"OP", VI_K_FUN1}, // Function Key F1
2183 {"OQ", VI_K_FUN2}, // Function Key F2
2184 {"OR", VI_K_FUN3}, // Function Key F3
2185 {"OS", VI_K_FUN4}, // Function Key F4
2186 {"[15~", VI_K_FUN5}, // Function Key F5
2187 {"[17~", VI_K_FUN6}, // Function Key F6
2188 {"[18~", VI_K_FUN7}, // Function Key F7
2189 {"[19~", VI_K_FUN8}, // Function Key F8
2190 {"[20~", VI_K_FUN9}, // Function Key F9
2191 {"[21~", VI_K_FUN10}, // Function Key F10
2192 {"[23~", VI_K_FUN11}, // Function Key F11
2193 {"[24~", VI_K_FUN12}, // Function Key F12
2194 {"[11~", VI_K_FUN1}, // Function Key F1
2195 {"[12~", VI_K_FUN2}, // Function Key F2
2196 {"[13~", VI_K_FUN3}, // Function Key F3
2197 {"[14~", VI_K_FUN4}, // Function Key F4
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002198 };
Denis Vlasenko80b8b392007-06-25 10:55:35 +00002199 enum { ESCCMDS_COUNT = ARRAY_SIZE(esccmds) };
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002200
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002201 alarm(0); // turn alarm OFF while we wait for input
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002202 fflush(stdout);
2203 n = readed_for_parse;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002204 // get input from User- are there already input chars in Q?
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002205 if (n <= 0) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002206 ri0:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002207 // the Q is empty, wait for a typed char
Denis Vlasenkoe8a07882007-06-10 15:08:44 +00002208 n = read(0, readbuffer, MAX_LINELEN - 1);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002209 if (n < 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002210 if (errno == EINTR)
2211 goto ri0; // interrupted sys call
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002212 if (errno == EBADF || errno == EFAULT || errno == EINVAL
Denis Vlasenko2f6ae432007-07-19 22:50:47 +00002213 || errno == EIO)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002214 editing = 0;
2215 errno = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002216 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002217 if (n <= 0)
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002218 return 0; // error
2219 if (readbuffer[0] == 27) {
Denis Vlasenkocd5c7862007-05-17 16:37:22 +00002220 fd_set rfds;
2221 struct timeval tv;
2222
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002223 // This is an ESC char. Is this Esc sequence?
2224 // Could be bare Esc key. See if there are any
2225 // more chars to read after the ESC. This would
2226 // be a Function or Cursor Key sequence.
2227 FD_ZERO(&rfds);
2228 FD_SET(0, &rfds);
2229 tv.tv_sec = 0;
2230 tv.tv_usec = 50000; // Wait 5/100 seconds- 1 Sec=1000000
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002231
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002232 // keep reading while there are input chars and room in buffer
Denis Vlasenkoe8a07882007-06-10 15:08:44 +00002233 while (select(1, &rfds, NULL, NULL, &tv) > 0 && n <= (MAX_LINELEN - 5)) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002234 // read the rest of the ESC string
Denis Vlasenkoe8a07882007-06-10 15:08:44 +00002235 int r = read(0, (void *) (readbuffer + n), MAX_LINELEN - n);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002236 if (r > 0) {
2237 n += r;
2238 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002239 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002240 }
2241 readed_for_parse = n;
2242 }
2243 c = readbuffer[0];
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002244 if (c == 27 && n > 1) {
2245 // Maybe cursor or function key?
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002246 const struct esc_cmds *eindex;
2247
2248 for (eindex = esccmds; eindex < &esccmds[ESCCMDS_COUNT]; eindex++) {
2249 int cnt = strlen(eindex->seq);
2250
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002251 if (n <= cnt)
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002252 continue;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002253 if (strncmp(eindex->seq, readbuffer + 1, cnt))
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002254 continue;
2255 // is a Cursor key- put derived value back into Q
2256 c = eindex->val;
2257 // for squeeze out the ESC sequence
2258 n = cnt + 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002259 break;
2260 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002261 if (eindex == &esccmds[ESCCMDS_COUNT]) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002262 /* defined ESC sequence not found, set only one ESC */
2263 n = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002264 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002265 } else {
2266 n = 1;
2267 }
2268 // remove key sequence from Q
2269 readed_for_parse -= n;
Denis Vlasenkoe8a07882007-06-10 15:08:44 +00002270 memmove(readbuffer, readbuffer + n, MAX_LINELEN - n);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002271 alarm(3); // we are done waiting for input, turn alarm ON
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002272 return c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002273}
2274
2275//----- IO Routines --------------------------------------------
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002276static char get_one_char(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002277{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002278 static char c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002279
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002280#if ENABLE_FEATURE_VI_DOT_CMD
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002281 // ! adding2q && ioq == 0 read()
2282 // ! adding2q && ioq != 0 *ioq
2283 // adding2q *last_modifying_cmd= read()
2284 if (!adding2q) {
2285 // we are not adding to the q.
2286 // but, we may be reading from a q
2287 if (ioq == 0) {
2288 // there is no current q, read from STDIN
2289 c = readit(); // get the users input
2290 } else {
2291 // there is a queue to get chars from first
2292 c = *ioq++;
2293 if (c == '\0') {
2294 // the end of the q, read from STDIN
2295 free(ioq_start);
2296 ioq_start = ioq = 0;
2297 c = readit(); // get the users input
2298 }
2299 }
2300 } else {
2301 // adding STDIN chars to q
2302 c = readit(); // get the users input
2303 if (last_modifying_cmd != 0) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002304 int len = strlen(last_modifying_cmd);
Denis Vlasenkoe8a07882007-06-10 15:08:44 +00002305 if (len >= MAX_LINELEN - 1) {
Eric Andersenfda2b7f2002-10-26 10:19:19 +00002306 psbs("last_modifying_cmd overrun");
2307 } else {
2308 // add new char to q
2309 last_modifying_cmd[len] = c;
2310 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002311 }
2312 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002313#else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002314 c = readit(); // get the users input
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002315#endif /* FEATURE_VI_DOT_CMD */
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002316 return c; // return the char, where ever it came from
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002317}
2318
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002319static char *get_input_line(const char * prompt) // get input line- use "status line"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002320{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002321 static char *obufp;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002322
Denis Vlasenkoe8a07882007-06-10 15:08:44 +00002323 char buf[MAX_LINELEN];
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002324 char c;
2325 int i;
2326
2327 strcpy(buf, prompt);
Paul Fox8552aec2005-09-16 12:20:05 +00002328 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002329 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
2330 clear_to_eol(); // clear the line
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002331 write1(prompt); // write out the :, /, or ? prompt
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002332
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002333 i = strlen(buf);
Denis Vlasenkoe8a07882007-06-10 15:08:44 +00002334 while (i < MAX_LINELEN) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002335 c = get_one_char(); // read user input
2336 if (c == '\n' || c == '\r' || c == 27)
2337 break; // is this end of input
Paul Foxf2de0b72005-09-13 22:20:37 +00002338 if (c == erase_char || c == 8 || c == 127) {
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00002339 // user wants to erase prev char
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002340 i--; // backup to prev char
2341 buf[i] = '\0'; // erase the char
2342 buf[i + 1] = '\0'; // null terminate buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002343 write1("\b \b"); // erase char on screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002344 if (i <= 0) { // user backs up before b-o-l, exit
2345 break;
2346 }
2347 } else {
2348 buf[i] = c; // save char in buffer
2349 buf[i + 1] = '\0'; // make sure buffer is null terminated
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002350 putchar(c); // echo the char back to user
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002351 i++;
2352 }
2353 }
2354 refresh(FALSE);
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002355 free(obufp);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002356 obufp = xstrdup(buf);
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002357 return obufp;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002358}
2359
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002360static int file_size(const char *fn) // what is the byte size of "fn"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002361{
2362 struct stat st_buf;
Denis Vlasenko59a1f302007-07-14 22:43:10 +00002363 int cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002364
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002365 cnt = -1;
Denis Vlasenko59a1f302007-07-14 22:43:10 +00002366 if (fn && fn[0] && stat(fn, &st_buf) == 0) // see if file exists
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002367 cnt = (int) st_buf.st_size;
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002368 return cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002369}
2370
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002371static int file_insert(const char * fn, char *p
2372 USE_FEATURE_VI_READONLY(, int update_ro_status))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002373{
Denis Vlasenko59a1f302007-07-14 22:43:10 +00002374 int cnt = -1;
2375 int fd, size;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002376 struct stat statbuf;
2377
2378 /* Validate file */
2379 if (stat(fn, &statbuf) < 0) {
2380 psbs("\"%s\" %s", fn, strerror(errno));
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002381 goto fi0;
2382 }
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002383 if ((statbuf.st_mode & S_IFREG) == 0) {
2384 // This is not a regular file
2385 psbs("\"%s\" Not a regular file", fn);
2386 goto fi0;
2387 }
2388 /* // this check is done by open()
2389 if ((statbuf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
2390 // dont have any read permissions
2391 psbs("\"%s\" Not readable", fn);
2392 goto fi0;
2393 }
2394 */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002395 if (p < text || p > end) {
2396 psbs("Trying to insert file outside of memory");
2397 goto fi0;
2398 }
2399
Denis Vlasenko59a1f302007-07-14 22:43:10 +00002400 // read file to buffer
2401 fd = open(fn, O_RDONLY);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002402 if (fd < 0) {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002403 psbs("\"%s\" %s", fn, strerror(errno));
Denis Vlasenko59a1f302007-07-14 22:43:10 +00002404 goto fi0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002405 }
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002406 size = statbuf.st_size;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002407 p = text_hole_make(p, size);
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002408 if (p == NULL)
2409 goto fi0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002410 cnt = read(fd, p, size);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002411 if (cnt < 0) {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002412 psbs("\"%s\" %s", fn, strerror(errno));
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002413 p = text_hole_delete(p, p + size - 1); // un-do buffer insert
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002414 } else if (cnt < size) {
2415 // There was a partial read, shrink unused space text[]
2416 p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert
Denis Vlasenkoea620772006-10-14 02:23:43 +00002417 psbs("cannot read all of file \"%s\"", fn);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002418 }
2419 if (cnt >= size)
Paul Fox8552aec2005-09-16 12:20:05 +00002420 file_modified++;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002421 close(fd);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002422 fi0:
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002423 if (ENABLE_FEATURE_VI_READONLY && update_ro_status
2424 && ((access(fn, W_OK) < 0) ||
2425 /* root will always have access()
2426 * so we check fileperms too */
2427 !(statbuf.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH))))
2428 {
Denis Vlasenko2f6ae432007-07-19 22:50:47 +00002429 SET_READONLY_FILE(readonly_mode);
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002430 }
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002431 return cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002432}
2433
Denis Vlasenko59a1f302007-07-14 22:43:10 +00002434
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002435static int file_write(char * fn, char * first, char * last)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002436{
2437 int fd, cnt, charcnt;
2438
2439 if (fn == 0) {
2440 psbs("No current filename");
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002441 return -2;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002442 }
2443 charcnt = 0;
2444 // FIXIT- use the correct umask()
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002445 fd = open(fn, (O_WRONLY | O_CREAT | O_TRUNC), 0664);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002446 if (fd < 0)
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002447 return -1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002448 cnt = last - first + 1;
2449 charcnt = write(fd, first, cnt);
2450 if (charcnt == cnt) {
2451 // good write
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002452 //file_modified = FALSE; // the file has not been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002453 } else {
2454 charcnt = 0;
2455 }
2456 close(fd);
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002457 return charcnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002458}
2459
2460//----- Terminal Drawing ---------------------------------------
2461// The terminal is made up of 'rows' line of 'columns' columns.
Eric Andersenaff114c2004-04-14 17:51:38 +00002462// classically this would be 24 x 80.
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002463// screen coordinates
2464// 0,0 ... 0,79
2465// 1,0 ... 1,79
2466// . ... .
2467// . ... .
2468// 22,0 ... 22,79
2469// 23,0 ... 23,79 status line
2470//
2471
2472//----- Move the cursor to row x col (count from 0, not 1) -------
2473static void place_cursor(int row, int col, int opti)
2474{
Denis Vlasenkoe8a07882007-06-10 15:08:44 +00002475 char cm1[MAX_LINELEN];
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002476 char *cm;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002477#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
Denis Vlasenkoe8a07882007-06-10 15:08:44 +00002478 char cm2[MAX_LINELEN];
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002479 char *screenp;
Denis Vlasenkoe8a07882007-06-10 15:08:44 +00002480 // char cm3[MAX_LINELEN];
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002481 int Rrow = last_row;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002482#endif
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002483
Denis Vlasenkoe8a07882007-06-10 15:08:44 +00002484 memset(cm1, '\0', MAX_LINELEN); // clear the buffer
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002485
2486 if (row < 0) row = 0;
2487 if (row >= rows) row = rows - 1;
2488 if (col < 0) col = 0;
2489 if (col >= columns) col = columns - 1;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002490
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002491 //----- 1. Try the standard terminal ESC sequence
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002492 sprintf(cm1, CMrc, row + 1, col + 1);
2493 cm = cm1;
2494 if (!opti)
2495 goto pc0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002496
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002497#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002498 //----- find the minimum # of chars to move cursor -------------
2499 //----- 2. Try moving with discreet chars (Newline, [back]space, ...)
Denis Vlasenkoe8a07882007-06-10 15:08:44 +00002500 memset(cm2, '\0', MAX_LINELEN); // clear the buffer
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002501
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002502 // move to the correct row
2503 while (row < Rrow) {
2504 // the cursor has to move up
2505 strcat(cm2, CMup);
2506 Rrow--;
2507 }
2508 while (row > Rrow) {
2509 // the cursor has to move down
2510 strcat(cm2, CMdown);
2511 Rrow++;
2512 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002513
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002514 // now move to the correct column
2515 strcat(cm2, "\r"); // start at col 0
2516 // just send out orignal source char to get to correct place
2517 screenp = &screen[row * columns]; // start of screen line
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002518 strncat(cm2, screenp, col);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002519
2520 //----- 3. Try some other way of moving cursor
2521 //---------------------------------------------
2522
2523 // pick the shortest cursor motion to send out
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002524 cm = cm1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002525 if (strlen(cm2) < strlen(cm)) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002526 cm = cm2;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002527 } /* else if (strlen(cm3) < strlen(cm)) {
2528 cm= cm3;
2529 } */
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002530#endif /* FEATURE_VI_OPTIMIZE_CURSOR */
2531 pc0:
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002532 write1(cm); // move the cursor
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002533}
2534
2535//----- Erase from cursor to end of line -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002536static void clear_to_eol(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002537{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002538 write1(Ceol); // Erase from cursor to end of line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002539}
2540
2541//----- Erase from cursor to end of screen -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002542static void clear_to_eos(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002543{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002544 write1(Ceos); // Erase from cursor to end of screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002545}
2546
2547//----- Start standout mode ------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002548static void standout_start(void) // send "start reverse video" sequence
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002549{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002550 write1(SOs); // Start reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002551}
2552
2553//----- End standout mode --------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002554static void standout_end(void) // send "end reverse video" sequence
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002555{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002556 write1(SOn); // End reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002557}
2558
2559//----- Flash the screen --------------------------------------
2560static void flash(int h)
2561{
2562 standout_start(); // send "start reverse video" sequence
2563 redraw(TRUE);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002564 mysleep(h);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002565 standout_end(); // send "end reverse video" sequence
2566 redraw(TRUE);
2567}
2568
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002569static void Indicate_Error(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002570{
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002571#if ENABLE_FEATURE_VI_CRASHME
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002572 if (crashme > 0)
2573 return; // generate a random command
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002574#endif
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002575 if (!err_method) {
2576 write1(bell); // send out a bell character
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002577 } else {
2578 flash(10);
2579 }
2580}
2581
2582//----- Screen[] Routines --------------------------------------
2583//----- Erase the Screen[] memory ------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002584static void screen_erase(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002585{
2586 memset(screen, ' ', screensize); // clear new screen
2587}
2588
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002589static int bufsum(char *buf, int count)
Paul Fox8552aec2005-09-16 12:20:05 +00002590{
2591 int sum = 0;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002592 char *e = buf + count;
2593
Paul Fox8552aec2005-09-16 12:20:05 +00002594 while (buf < e)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002595 sum += (unsigned char) *buf++;
Paul Fox8552aec2005-09-16 12:20:05 +00002596 return sum;
2597}
2598
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002599//----- Draw the status line at bottom of the screen -------------
2600static void show_status_line(void)
2601{
Paul Foxc3504852005-09-16 12:48:18 +00002602 int cnt = 0, cksum = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002603
Paul Fox8552aec2005-09-16 12:20:05 +00002604 // either we already have an error or status message, or we
2605 // create one.
2606 if (!have_status_msg) {
2607 cnt = format_edit_status();
2608 cksum = bufsum(status_buffer, cnt);
2609 }
2610 if (have_status_msg || ((cnt > 0 && last_status_cksum != cksum))) {
2611 last_status_cksum= cksum; // remember if we have seen this line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002612 place_cursor(rows - 1, 0, FALSE); // put cursor on status line
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002613 write1(status_buffer);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002614 clear_to_eol();
Paul Fox8552aec2005-09-16 12:20:05 +00002615 if (have_status_msg) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002616 if (((int)strlen(status_buffer) - (have_status_msg - 1)) >
Paul Fox8552aec2005-09-16 12:20:05 +00002617 (columns - 1) ) {
2618 have_status_msg = 0;
2619 Hit_Return();
2620 }
2621 have_status_msg = 0;
2622 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002623 place_cursor(crow, ccol, FALSE); // put cursor back in correct place
2624 }
Eric Andersena9eb33d2004-08-19 19:15:06 +00002625 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002626}
2627
2628//----- format the status buffer, the bottom line of screen ------
Paul Fox8552aec2005-09-16 12:20:05 +00002629// format status buffer, with STANDOUT mode
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002630static void psbs(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002631{
2632 va_list args;
2633
2634 va_start(args, format);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002635 strcpy(status_buffer, SOs); // Terminal standout mode on
2636 vsprintf(status_buffer + strlen(status_buffer), format, args);
2637 strcat(status_buffer, SOn); // Terminal standout mode off
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002638 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00002639
2640 have_status_msg = 1 + sizeof(SOs) + sizeof(SOn) - 2;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002641}
2642
Paul Fox8552aec2005-09-16 12:20:05 +00002643// format status buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002644static void psb(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002645{
2646 va_list args;
2647
2648 va_start(args, format);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002649 vsprintf(status_buffer, format, args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002650 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00002651
2652 have_status_msg = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002653}
2654
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002655static void ni(const char * s) // display messages
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002656{
Denis Vlasenkoe8a07882007-06-10 15:08:44 +00002657 char buf[MAX_LINELEN];
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002658
2659 print_literal(buf, s);
2660 psbs("\'%s\' is not implemented", buf);
2661}
2662
Paul Fox8552aec2005-09-16 12:20:05 +00002663static int format_edit_status(void) // show file status on status line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002664{
Paul Fox8552aec2005-09-16 12:20:05 +00002665 static int tot;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002666 static const char cmd_mode_indicator[] ALIGN1 = "-IR-";
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002667 int cur, percent, ret, trunc_at;
2668
Paul Fox8552aec2005-09-16 12:20:05 +00002669 // file_modified is now a counter rather than a flag. this
2670 // helps reduce the amount of line counting we need to do.
2671 // (this will cause a mis-reporting of modified status
2672 // once every MAXINT editing operations.)
2673
2674 // it would be nice to do a similar optimization here -- if
2675 // we haven't done a motion that could have changed which line
2676 // we're on, then we shouldn't have to do this count_lines()
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002677 cur = count_lines(text, dot);
Paul Fox8552aec2005-09-16 12:20:05 +00002678
2679 // reduce counting -- the total lines can't have
2680 // changed if we haven't done any edits.
2681 if (file_modified != last_file_modified) {
2682 tot = cur + count_lines(dot, end - 1) - 1;
2683 last_file_modified = file_modified;
2684 }
2685
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002686 // current line percent
2687 // ------------- ~~ ----------
2688 // total lines 100
2689 if (tot > 0) {
2690 percent = (100 * cur) / tot;
2691 } else {
2692 cur = tot = 0;
2693 percent = 100;
2694 }
Eric Andersen0ef24c62005-07-18 10:32:59 +00002695
Paul Fox8552aec2005-09-16 12:20:05 +00002696 trunc_at = columns < STATUS_BUFFER_LEN-1 ?
2697 columns : STATUS_BUFFER_LEN-1;
2698
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002699 ret = snprintf(status_buffer, trunc_at+1,
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002700#if ENABLE_FEATURE_VI_READONLY
Paul Fox8552aec2005-09-16 12:20:05 +00002701 "%c %s%s%s %d/%d %d%%",
2702#else
2703 "%c %s%s %d/%d %d%%",
2704#endif
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002705 cmd_mode_indicator[cmd_mode & 3],
2706 (current_filename != NULL ? current_filename : "No file"),
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002707#if ENABLE_FEATURE_VI_READONLY
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002708 (readonly_mode ? " [Readonly]" : ""),
Paul Fox8552aec2005-09-16 12:20:05 +00002709#endif
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002710 (file_modified ? " [Modified]" : ""),
Paul Fox8552aec2005-09-16 12:20:05 +00002711 cur, tot, percent);
2712
2713 if (ret >= 0 && ret < trunc_at)
2714 return ret; /* it all fit */
2715
2716 return trunc_at; /* had to truncate */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002717}
2718
2719//----- Force refresh of all Lines -----------------------------
2720static void redraw(int full_screen)
2721{
2722 place_cursor(0, 0, FALSE); // put cursor in correct place
2723 clear_to_eos(); // tel terminal to erase display
2724 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00002725 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002726 refresh(full_screen); // this will redraw the entire display
Paul Fox8552aec2005-09-16 12:20:05 +00002727 show_status_line();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002728}
2729
2730//----- Format a text[] line into a buffer ---------------------
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002731static void format_line(char *dest, char *src, int li)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002732{
2733 int co;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002734 char c;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002735
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002736 for (co = 0; co < MAX_SCR_COLS; co++) {
Denis Vlasenko2a51af22007-03-21 22:31:24 +00002737 c = ' '; // assume blank
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002738 if (li > 0 && co == 0) {
2739 c = '~'; // not first line, assume Tilde
2740 }
2741 // are there chars in text[] and have we gone past the end
2742 if (text < end && src < end) {
2743 c = *src++;
2744 }
2745 if (c == '\n')
2746 break;
Denis Vlasenko2a51af22007-03-21 22:31:24 +00002747 if ((c & 0x80) && !Isprint(c)) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002748 c = '.';
2749 }
Denis Vlasenko2a51af22007-03-21 22:31:24 +00002750 if ((unsigned char)(c) < ' ' || c == 0x7f) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002751 if (c == '\t') {
2752 c = ' ';
2753 // co % 8 != 7
2754 for (; (co % tabstop) != (tabstop - 1); co++) {
2755 dest[co] = c;
2756 }
2757 } else {
2758 dest[co++] = '^';
Denis Vlasenko2a51af22007-03-21 22:31:24 +00002759 if (c == 0x7f)
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002760 c = '?';
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002761 else
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002762 c += '@'; // make it visible
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002763 }
2764 }
2765 // the co++ is done here so that the column will
2766 // not be overwritten when we blank-out the rest of line
2767 dest[co] = c;
2768 if (src >= end)
2769 break;
2770 }
2771}
2772
2773//----- Refresh the changed screen lines -----------------------
2774// Copy the source line from text[] into the buffer and note
2775// if the current screenline is different from the new buffer.
2776// If they differ then that line needs redrawing on the terminal.
2777//
2778static void refresh(int full_screen)
2779{
2780 static int old_offset;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002781
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002782 int li, changed;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002783 char buf[MAX_SCR_COLS];
2784 char *tp, *sp; // pointer into text[] and screen[]
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002785#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002786 int last_li = -2; // last line that changed- for optimizing cursor movement
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002787#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002788
Rob Landleye5e1a102006-06-21 01:15:36 +00002789 if (ENABLE_FEATURE_VI_WIN_RESIZE)
2790 get_terminal_width_height(0, &columns, &rows);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002791 sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
2792 tp = screenbegin; // index into text[] of top line
2793
2794 // compare text[] to screen[] and mark screen[] lines that need updating
2795 for (li = 0; li < rows - 1; li++) {
2796 int cs, ce; // column start & end
2797 memset(buf, ' ', MAX_SCR_COLS); // blank-out the buffer
2798 buf[MAX_SCR_COLS-1] = 0; // NULL terminate the buffer
2799 // format current text line into buf
2800 format_line(buf, tp, li);
2801
2802 // skip to the end of the current text[] line
Denis Vlasenkob71c6682007-07-21 15:08:09 +00002803 while (tp < end && *tp++ != '\n') /*no-op*/;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002804
2805 // see if there are any changes between vitual screen and buf
2806 changed = FALSE; // assume no change
2807 cs= 0;
2808 ce= columns-1;
2809 sp = &screen[li * columns]; // start of screen line
2810 if (full_screen) {
2811 // force re-draw of every single column from 0 - columns-1
2812 goto re0;
2813 }
2814 // compare newly formatted buffer with virtual screen
2815 // look forward for first difference between buf and screen
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002816 for (; cs <= ce; cs++) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002817 if (buf[cs + offset] != sp[cs]) {
2818 changed = TRUE; // mark for redraw
2819 break;
2820 }
2821 }
2822
2823 // look backward for last difference between buf and screen
2824 for ( ; ce >= cs; ce--) {
2825 if (buf[ce + offset] != sp[ce]) {
2826 changed = TRUE; // mark for redraw
2827 break;
2828 }
2829 }
2830 // now, cs is index of first diff, and ce is index of last diff
2831
2832 // if horz offset has changed, force a redraw
2833 if (offset != old_offset) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002834 re0:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002835 changed = TRUE;
2836 }
2837
2838 // make a sanity check of columns indexes
2839 if (cs < 0) cs= 0;
2840 if (ce > columns-1) ce= columns-1;
2841 if (cs > ce) { cs= 0; ce= columns-1; }
2842 // is there a change between vitual screen and buf
2843 if (changed) {
2844 // copy changed part of buffer to virtual screen
2845 memmove(sp+cs, buf+(cs+offset), ce-cs+1);
2846
2847 // move cursor to column of first change
2848 if (offset != old_offset) {
2849 // opti_cur_move is still too stupid
2850 // to handle offsets correctly
2851 place_cursor(li, cs, FALSE);
2852 } else {
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002853#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002854 // if this just the next line
2855 // try to optimize cursor movement
2856 // otherwise, use standard ESC sequence
2857 place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE);
2858 last_li= li;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002859#else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002860 place_cursor(li, cs, FALSE); // use standard ESC sequence
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002861#endif /* FEATURE_VI_OPTIMIZE_CURSOR */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002862 }
2863
2864 // write line out to terminal
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002865 {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002866 int nic = ce - cs + 1;
2867 char *out = sp + cs;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002868
Denis Vlasenkobf0a2012006-12-26 10:42:51 +00002869 while (nic-- > 0) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002870 putchar(*out);
2871 out++;
2872 }
2873 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002874#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002875 last_row = li;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002876#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002877 }
2878 }
2879
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002880#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002881 place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE);
2882 last_row = crow;
2883#else
2884 place_cursor(crow, ccol, FALSE);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002885#endif
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002886
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002887 if (offset != old_offset)
2888 old_offset = offset;
2889}
2890
Eric Andersen3f980402001-04-04 17:31:15 +00002891//---------------------------------------------------------------------
2892//----- the Ascii Chart -----------------------------------------------
2893//
2894// 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel
2895// 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si
2896// 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb
2897// 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us
2898// 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 '
2899// 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f /
2900// 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7
2901// 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ?
2902// 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G
2903// 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O
2904// 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W
2905// 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _
2906// 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g
2907// 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o
2908// 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w
2909// 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del
2910//---------------------------------------------------------------------
2911
2912//----- Execute a Vi Command -----------------------------------
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002913static void do_cmd(char c)
Eric Andersen3f980402001-04-04 17:31:15 +00002914{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002915 const char *msg;
2916 char c1, *p, *q, buf[9], *save_dot;
Eric Andersen3f980402001-04-04 17:31:15 +00002917 int cnt, i, j, dir, yf;
2918
2919 c1 = c; // quiet the compiler
2920 cnt = yf = dir = 0; // quiet the compiler
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002921 msg = p = q = save_dot = buf; // quiet the compiler
Eric Andersen3f980402001-04-04 17:31:15 +00002922 memset(buf, '\0', 9); // clear buf
Eric Andersenbff7a602001-11-17 07:15:43 +00002923
Paul Fox8552aec2005-09-16 12:20:05 +00002924 show_status_line();
2925
Eric Andersenbff7a602001-11-17 07:15:43 +00002926 /* if this is a cursor key, skip these checks */
2927 switch (c) {
2928 case VI_K_UP:
2929 case VI_K_DOWN:
2930 case VI_K_LEFT:
2931 case VI_K_RIGHT:
2932 case VI_K_HOME:
2933 case VI_K_END:
2934 case VI_K_PAGEUP:
2935 case VI_K_PAGEDOWN:
2936 goto key_cmd_mode;
2937 }
2938
Eric Andersen3f980402001-04-04 17:31:15 +00002939 if (cmd_mode == 2) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002940 // flip-flop Insert/Replace mode
Denis Vlasenko2a51af22007-03-21 22:31:24 +00002941 if (c == VI_K_INSERT)
2942 goto dc_i;
Eric Andersen3f980402001-04-04 17:31:15 +00002943 // we are 'R'eplacing the current *dot with new char
2944 if (*dot == '\n') {
2945 // don't Replace past E-o-l
2946 cmd_mode = 1; // convert to insert
2947 } else {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002948 if (1 <= c || Isprint(c)) {
Eric Andersen3f980402001-04-04 17:31:15 +00002949 if (c != 27)
2950 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
2951 dot = char_insert(dot, c); // insert new char
2952 }
2953 goto dc1;
2954 }
2955 }
2956 if (cmd_mode == 1) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00002957 // hitting "Insert" twice means "R" replace mode
2958 if (c == VI_K_INSERT) goto dc5;
Eric Andersen3f980402001-04-04 17:31:15 +00002959 // insert the char c at "dot"
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002960 if (1 <= c || Isprint(c)) {
2961 dot = char_insert(dot, c);
Eric Andersen3f980402001-04-04 17:31:15 +00002962 }
2963 goto dc1;
2964 }
2965
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002966 key_cmd_mode:
Eric Andersen3f980402001-04-04 17:31:15 +00002967 switch (c) {
Eric Andersen822c3832001-05-07 17:37:43 +00002968 //case 0x01: // soh
2969 //case 0x09: // ht
2970 //case 0x0b: // vt
2971 //case 0x0e: // so
2972 //case 0x0f: // si
2973 //case 0x10: // dle
2974 //case 0x11: // dc1
2975 //case 0x13: // dc3
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002976#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen1c0d3112001-04-16 15:46:44 +00002977 case 0x14: // dc4 ctrl-T
Eric Andersen3f980402001-04-04 17:31:15 +00002978 crashme = (crashme == 0) ? 1 : 0;
Eric Andersen3f980402001-04-04 17:31:15 +00002979 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002980#endif
Eric Andersen822c3832001-05-07 17:37:43 +00002981 //case 0x16: // syn
2982 //case 0x17: // etb
2983 //case 0x18: // can
2984 //case 0x1c: // fs
2985 //case 0x1d: // gs
2986 //case 0x1e: // rs
2987 //case 0x1f: // us
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002988 //case '!': // !-
2989 //case '#': // #-
2990 //case '&': // &-
2991 //case '(': // (-
2992 //case ')': // )-
2993 //case '*': // *-
2994 //case ',': // ,-
2995 //case '=': // =-
2996 //case '@': // @-
2997 //case 'F': // F-
2998 //case 'K': // K-
2999 //case 'Q': // Q-
3000 //case 'S': // S-
3001 //case 'T': // T-
3002 //case 'V': // V-
3003 //case '[': // [-
3004 //case '\\': // \-
3005 //case ']': // ]-
3006 //case '_': // _-
3007 //case '`': // `-
3008 //case 'g': // g-
Eric Andersen1c0d3112001-04-16 15:46:44 +00003009 //case 'u': // u- FIXME- there is no undo
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003010 //case 'v': // v-
Eric Andersen3f980402001-04-04 17:31:15 +00003011 default: // unrecognised command
3012 buf[0] = c;
3013 buf[1] = '\0';
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003014 if (c < ' ') {
Eric Andersen3f980402001-04-04 17:31:15 +00003015 buf[0] = '^';
3016 buf[1] = c + '@';
3017 buf[2] = '\0';
3018 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003019 ni(buf);
Eric Andersen3f980402001-04-04 17:31:15 +00003020 end_cmd_q(); // stop adding to q
3021 case 0x00: // nul- ignore
3022 break;
3023 case 2: // ctrl-B scroll up full screen
3024 case VI_K_PAGEUP: // Cursor Key Page Up
3025 dot_scroll(rows - 2, -1);
3026 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003027#if ENABLE_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +00003028 case 0x03: // ctrl-C interrupt
3029 longjmp(restart, 1);
3030 break;
3031 case 26: // ctrl-Z suspend
3032 suspend_sig(SIGTSTP);
3033 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003034#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003035 case 4: // ctrl-D scroll down half screen
3036 dot_scroll((rows - 2) / 2, 1);
3037 break;
3038 case 5: // ctrl-E scroll down one line
3039 dot_scroll(1, 1);
3040 break;
3041 case 6: // ctrl-F scroll down full screen
3042 case VI_K_PAGEDOWN: // Cursor Key Page Down
3043 dot_scroll(rows - 2, 1);
3044 break;
3045 case 7: // ctrl-G show current status
Paul Fox8552aec2005-09-16 12:20:05 +00003046 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003047 break;
3048 case 'h': // h- move left
3049 case VI_K_LEFT: // cursor key Left
Paul Foxd13b90b2005-07-18 22:17:25 +00003050 case 8: // ctrl-H- move left (This may be ERASE char)
Denis Vlasenko2a51af22007-03-21 22:31:24 +00003051 case 0x7f: // DEL- move left (This may be ERASE char)
Eric Andersen3f980402001-04-04 17:31:15 +00003052 if (cmdcnt-- > 1) {
3053 do_cmd(c);
3054 } // repeat cnt
3055 dot_left();
3056 break;
3057 case 10: // Newline ^J
3058 case 'j': // j- goto next line, same col
3059 case VI_K_DOWN: // cursor key Down
3060 if (cmdcnt-- > 1) {
3061 do_cmd(c);
3062 } // repeat cnt
3063 dot_next(); // go to next B-o-l
3064 dot = move_to_col(dot, ccol + offset); // try stay in same col
3065 break;
3066 case 12: // ctrl-L force redraw whole screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003067 case 18: // ctrl-R force redraw
Eric Andersen822c3832001-05-07 17:37:43 +00003068 place_cursor(0, 0, FALSE); // put cursor in correct place
Eric Andersen3f980402001-04-04 17:31:15 +00003069 clear_to_eos(); // tel terminal to erase display
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003070 mysleep(10);
Eric Andersen3f980402001-04-04 17:31:15 +00003071 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00003072 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003073 refresh(TRUE); // this will redraw the entire display
3074 break;
3075 case 13: // Carriage Return ^M
3076 case '+': // +- goto next line
3077 if (cmdcnt-- > 1) {
3078 do_cmd(c);
3079 } // repeat cnt
3080 dot_next();
3081 dot_skip_over_ws();
3082 break;
3083 case 21: // ctrl-U scroll up half screen
3084 dot_scroll((rows - 2) / 2, -1);
3085 break;
3086 case 25: // ctrl-Y scroll up one line
3087 dot_scroll(1, -1);
3088 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003089 case 27: // esc
Eric Andersen3f980402001-04-04 17:31:15 +00003090 if (cmd_mode == 0)
3091 indicate_error(c);
3092 cmd_mode = 0; // stop insrting
3093 end_cmd_q();
Paul Fox8552aec2005-09-16 12:20:05 +00003094 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003095 break;
3096 case ' ': // move right
3097 case 'l': // move right
3098 case VI_K_RIGHT: // Cursor Key Right
3099 if (cmdcnt-- > 1) {
3100 do_cmd(c);
3101 } // repeat cnt
3102 dot_right();
3103 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003104#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003105 case '"': // "- name a register to use for Delete/Yank
3106 c1 = get_one_char();
3107 c1 = tolower(c1);
3108 if (islower(c1)) {
3109 YDreg = c1 - 'a';
3110 } else {
3111 indicate_error(c);
3112 }
3113 break;
3114 case '\'': // '- goto a specific mark
3115 c1 = get_one_char();
3116 c1 = tolower(c1);
3117 if (islower(c1)) {
3118 c1 = c1 - 'a';
3119 // get the b-o-l
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003120 q = mark[(unsigned char) c1];
Eric Andersen3f980402001-04-04 17:31:15 +00003121 if (text <= q && q < end) {
3122 dot = q;
3123 dot_begin(); // go to B-o-l
3124 dot_skip_over_ws();
3125 }
3126 } else if (c1 == '\'') { // goto previous context
3127 dot = swap_context(dot); // swap current and previous context
3128 dot_begin(); // go to B-o-l
3129 dot_skip_over_ws();
3130 } else {
3131 indicate_error(c);
3132 }
3133 break;
3134 case 'm': // m- Mark a line
3135 // this is really stupid. If there are any inserts or deletes
3136 // between text[0] and dot then this mark will not point to the
3137 // correct location! It could be off by many lines!
3138 // Well..., at least its quick and dirty.
3139 c1 = get_one_char();
3140 c1 = tolower(c1);
3141 if (islower(c1)) {
3142 c1 = c1 - 'a';
3143 // remember the line
3144 mark[(int) c1] = dot;
3145 } else {
3146 indicate_error(c);
3147 }
3148 break;
3149 case 'P': // P- Put register before
3150 case 'p': // p- put register after
3151 p = reg[YDreg];
3152 if (p == 0) {
3153 psbs("Nothing in register %c", what_reg());
3154 break;
3155 }
3156 // are we putting whole lines or strings
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003157 if (strchr(p, '\n') != NULL) {
Eric Andersen3f980402001-04-04 17:31:15 +00003158 if (c == 'P') {
3159 dot_begin(); // putting lines- Put above
3160 }
3161 if (c == 'p') {
3162 // are we putting after very last line?
3163 if (end_line(dot) == (end - 1)) {
3164 dot = end; // force dot to end of text[]
3165 } else {
3166 dot_next(); // next line, then put before
3167 }
3168 }
3169 } else {
3170 if (c == 'p')
3171 dot_right(); // move to right, can move to NL
3172 }
3173 dot = string_insert(dot, p); // insert the string
3174 end_cmd_q(); // stop adding to q
3175 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003176 case 'U': // U- Undo; replace current line with original version
3177 if (reg[Ureg] != 0) {
3178 p = begin_line(dot);
3179 q = end_line(dot);
3180 p = text_hole_delete(p, q); // delete cur line
3181 p = string_insert(p, reg[Ureg]); // insert orig line
3182 dot = p;
3183 dot_skip_over_ws();
3184 }
3185 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003186#endif /* FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003187 case '$': // $- goto end of line
3188 case VI_K_END: // Cursor Key End
3189 if (cmdcnt-- > 1) {
3190 do_cmd(c);
3191 } // repeat cnt
Glenn L McGrathee829062004-01-21 10:59:45 +00003192 dot = end_line(dot);
Eric Andersen3f980402001-04-04 17:31:15 +00003193 break;
3194 case '%': // %- find matching char of pair () [] {}
3195 for (q = dot; q < end && *q != '\n'; q++) {
3196 if (strchr("()[]{}", *q) != NULL) {
3197 // we found half of a pair
3198 p = find_pair(q, *q);
3199 if (p == NULL) {
3200 indicate_error(c);
3201 } else {
3202 dot = p;
3203 }
3204 break;
3205 }
3206 }
3207 if (*q == '\n')
3208 indicate_error(c);
3209 break;
3210 case 'f': // f- forward to a user specified char
3211 last_forward_char = get_one_char(); // get the search char
3212 //
Eric Andersenaff114c2004-04-14 17:51:38 +00003213 // dont separate these two commands. 'f' depends on ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003214 //
Paul Foxd13b90b2005-07-18 22:17:25 +00003215 //**** fall thru to ... ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003216 case ';': // ;- look at rest of line for last forward char
3217 if (cmdcnt-- > 1) {
Eric Andersen822c3832001-05-07 17:37:43 +00003218 do_cmd(';');
Eric Andersen3f980402001-04-04 17:31:15 +00003219 } // repeat cnt
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003220 if (last_forward_char == 0)
3221 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003222 q = dot + 1;
3223 while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
3224 q++;
3225 }
3226 if (*q == last_forward_char)
3227 dot = q;
3228 break;
3229 case '-': // -- goto prev line
3230 if (cmdcnt-- > 1) {
3231 do_cmd(c);
3232 } // repeat cnt
3233 dot_prev();
3234 dot_skip_over_ws();
3235 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003236#if ENABLE_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003237 case '.': // .- repeat the last modifying command
3238 // Stuff the last_modifying_cmd back into stdin
3239 // and let it be re-executed.
3240 if (last_modifying_cmd != 0) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003241 ioq = ioq_start = xstrdup(last_modifying_cmd);
Eric Andersen3f980402001-04-04 17:31:15 +00003242 }
3243 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003244#endif
3245#if ENABLE_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +00003246 case '?': // /- search for a pattern
3247 case '/': // /- search for a pattern
3248 buf[0] = c;
3249 buf[1] = '\0';
3250 q = get_input_line(buf); // get input line- use "status line"
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003251 if (q[0] && !q[1])
3252 goto dc3; // if no pat re-use old pat
3253 if (q[0]) { // strlen(q) > 1: new pat- save it and find
Eric Andersen3f980402001-04-04 17:31:15 +00003254 // there is a new pat
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00003255 free(last_search_pattern);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003256 last_search_pattern = xstrdup(q);
Eric Andersen3f980402001-04-04 17:31:15 +00003257 goto dc3; // now find the pattern
3258 }
3259 // user changed mind and erased the "/"- do nothing
3260 break;
3261 case 'N': // N- backward search for last pattern
3262 if (cmdcnt-- > 1) {
3263 do_cmd(c);
3264 } // repeat cnt
3265 dir = BACK; // assume BACKWARD search
3266 p = dot - 1;
3267 if (last_search_pattern[0] == '?') {
3268 dir = FORWARD;
3269 p = dot + 1;
3270 }
3271 goto dc4; // now search for pattern
3272 break;
3273 case 'n': // n- repeat search for last pattern
3274 // search rest of text[] starting at next char
3275 // if search fails return orignal "p" not the "p+1" address
3276 if (cmdcnt-- > 1) {
3277 do_cmd(c);
3278 } // repeat cnt
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003279 dc3:
Eric Andersen3f980402001-04-04 17:31:15 +00003280 if (last_search_pattern == 0) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003281 msg = "No previous regular expression";
Eric Andersen3f980402001-04-04 17:31:15 +00003282 goto dc2;
3283 }
3284 if (last_search_pattern[0] == '/') {
3285 dir = FORWARD; // assume FORWARD search
3286 p = dot + 1;
3287 }
3288 if (last_search_pattern[0] == '?') {
3289 dir = BACK;
3290 p = dot - 1;
3291 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003292 dc4:
Eric Andersen3f980402001-04-04 17:31:15 +00003293 q = char_search(p, last_search_pattern + 1, dir, FULL);
3294 if (q != NULL) {
3295 dot = q; // good search, update "dot"
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003296 msg = "";
Eric Andersen3f980402001-04-04 17:31:15 +00003297 goto dc2;
3298 }
3299 // no pattern found between "dot" and "end"- continue at top
3300 p = text;
3301 if (dir == BACK) {
3302 p = end - 1;
3303 }
3304 q = char_search(p, last_search_pattern + 1, dir, FULL);
3305 if (q != NULL) { // found something
3306 dot = q; // found new pattern- goto it
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003307 msg = "search hit BOTTOM, continuing at TOP";
Eric Andersen3f980402001-04-04 17:31:15 +00003308 if (dir == BACK) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003309 msg = "search hit TOP, continuing at BOTTOM";
Eric Andersen3f980402001-04-04 17:31:15 +00003310 }
3311 } else {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003312 msg = "Pattern not found";
Eric Andersen3f980402001-04-04 17:31:15 +00003313 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003314 dc2:
3315 if (*msg)
3316 psbs("%s", msg);
Eric Andersen3f980402001-04-04 17:31:15 +00003317 break;
3318 case '{': // {- move backward paragraph
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003319 q = char_search(dot, "\n\n", BACK, FULL);
Eric Andersen3f980402001-04-04 17:31:15 +00003320 if (q != NULL) { // found blank line
3321 dot = next_line(q); // move to next blank line
3322 }
3323 break;
3324 case '}': // }- move forward paragraph
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003325 q = char_search(dot, "\n\n", FORWARD, FULL);
Eric Andersen3f980402001-04-04 17:31:15 +00003326 if (q != NULL) { // found blank line
3327 dot = next_line(q); // move to next blank line
3328 }
3329 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003330#endif /* FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +00003331 case '0': // 0- goto begining of line
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003332 case '1': // 1-
3333 case '2': // 2-
3334 case '3': // 3-
3335 case '4': // 4-
3336 case '5': // 5-
3337 case '6': // 6-
3338 case '7': // 7-
3339 case '8': // 8-
3340 case '9': // 9-
Eric Andersen3f980402001-04-04 17:31:15 +00003341 if (c == '0' && cmdcnt < 1) {
3342 dot_begin(); // this was a standalone zero
3343 } else {
3344 cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number
3345 }
3346 break;
3347 case ':': // :- the colon mode commands
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003348 p = get_input_line(":"); // get input line- use "status line"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003349#if ENABLE_FEATURE_VI_COLON
Eric Andersen3f980402001-04-04 17:31:15 +00003350 colon(p); // execute the command
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003351#else
Eric Andersen822c3832001-05-07 17:37:43 +00003352 if (*p == ':')
3353 p++; // move past the ':'
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003354 cnt = strlen(p);
Eric Andersen822c3832001-05-07 17:37:43 +00003355 if (cnt <= 0)
3356 break;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003357 if (strncasecmp(p, "quit", cnt) == 0
3358 || strncasecmp(p, "q!", cnt) == 0 // delete lines
3359 ) {
Matt Kraai1f0c4362001-12-20 23:13:26 +00003360 if (file_modified && p[1] != '!') {
Eric Andersen3f980402001-04-04 17:31:15 +00003361 psbs("No write since last change (:quit! overrides)");
3362 } else {
3363 editing = 0;
3364 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003365 } else if (strncasecmp(p, "write", cnt) == 0
3366 || strncasecmp(p, "wq", cnt) == 0
3367 || strncasecmp(p, "wn", cnt) == 0
3368 || strncasecmp(p, "x", cnt) == 0
3369 ) {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00003370 cnt = file_write(current_filename, text, end - 1);
Paul Fox61e45db2005-10-09 14:43:22 +00003371 if (cnt < 0) {
3372 if (cnt == -1)
3373 psbs("Write error: %s", strerror(errno));
3374 } else {
3375 file_modified = 0;
3376 last_file_modified = -1;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00003377 psb("\"%s\" %dL, %dC", current_filename, count_lines(text, end - 1), cnt);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003378 if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n'
3379 || p[0] == 'X' || p[1] == 'Q' || p[1] == 'N'
3380 ) {
Paul Fox61e45db2005-10-09 14:43:22 +00003381 editing = 0;
3382 }
Eric Andersen3f980402001-04-04 17:31:15 +00003383 }
Denis Vlasenko219d14d2007-03-24 15:40:16 +00003384 } else if (strncasecmp(p, "file", cnt) == 0) {
Paul Fox8552aec2005-09-16 12:20:05 +00003385 last_status_cksum = 0; // force status update
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003386 } else if (sscanf(p, "%d", &j) > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00003387 dot = find_line(j); // go to line # j
3388 dot_skip_over_ws();
Eric Andersen3f980402001-04-04 17:31:15 +00003389 } else { // unrecognised cmd
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003390 ni(p);
Eric Andersen3f980402001-04-04 17:31:15 +00003391 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003392#endif /* !FEATURE_VI_COLON */
Eric Andersen3f980402001-04-04 17:31:15 +00003393 break;
3394 case '<': // <- Left shift something
3395 case '>': // >- Right shift something
3396 cnt = count_lines(text, dot); // remember what line we are on
3397 c1 = get_one_char(); // get the type of thing to delete
3398 find_range(&p, &q, c1);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003399 yank_delete(p, q, 1, YANKONLY); // save copy before change
Eric Andersen3f980402001-04-04 17:31:15 +00003400 p = begin_line(p);
3401 q = end_line(q);
3402 i = count_lines(p, q); // # of lines we are shifting
3403 for ( ; i > 0; i--, p = next_line(p)) {
3404 if (c == '<') {
3405 // shift left- remove tab or 8 spaces
3406 if (*p == '\t') {
3407 // shrink buffer 1 char
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003408 text_hole_delete(p, p);
Eric Andersen3f980402001-04-04 17:31:15 +00003409 } else if (*p == ' ') {
3410 // we should be calculating columns, not just SPACE
3411 for (j = 0; *p == ' ' && j < tabstop; j++) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003412 text_hole_delete(p, p);
Eric Andersen3f980402001-04-04 17:31:15 +00003413 }
3414 }
3415 } else if (c == '>') {
3416 // shift right -- add tab or 8 spaces
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003417 char_insert(p, '\t');
Eric Andersen3f980402001-04-04 17:31:15 +00003418 }
3419 }
3420 dot = find_line(cnt); // what line were we on
3421 dot_skip_over_ws();
3422 end_cmd_q(); // stop adding to q
3423 break;
3424 case 'A': // A- append at e-o-l
3425 dot_end(); // go to e-o-l
3426 //**** fall thru to ... 'a'
3427 case 'a': // a- append after current char
3428 if (*dot != '\n')
3429 dot++;
3430 goto dc_i;
3431 break;
3432 case 'B': // B- back a blank-delimited Word
3433 case 'E': // E- end of a blank-delimited word
3434 case 'W': // W- forward a blank-delimited word
3435 if (cmdcnt-- > 1) {
3436 do_cmd(c);
3437 } // repeat cnt
3438 dir = FORWARD;
3439 if (c == 'B')
3440 dir = BACK;
3441 if (c == 'W' || isspace(dot[dir])) {
3442 dot = skip_thing(dot, 1, dir, S_TO_WS);
3443 dot = skip_thing(dot, 2, dir, S_OVER_WS);
3444 }
3445 if (c != 'W')
3446 dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
3447 break;
3448 case 'C': // C- Change to e-o-l
3449 case 'D': // D- delete to e-o-l
3450 save_dot = dot;
3451 dot = dollar_line(dot); // move to before NL
3452 // copy text into a register and delete
3453 dot = yank_delete(save_dot, dot, 0, YANKDEL); // delete to e-o-l
3454 if (c == 'C')
3455 goto dc_i; // start inserting
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003456#if ENABLE_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003457 if (c == 'D')
3458 end_cmd_q(); // stop adding to q
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003459#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003460 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003461 case 'G': // G- goto to a line number (default= E-O-F)
3462 dot = end - 1; // assume E-O-F
Eric Andersen1c0d3112001-04-16 15:46:44 +00003463 if (cmdcnt > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00003464 dot = find_line(cmdcnt); // what line is #cmdcnt
Eric Andersen1c0d3112001-04-16 15:46:44 +00003465 }
3466 dot_skip_over_ws();
3467 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003468 case 'H': // H- goto top line on screen
3469 dot = screenbegin;
3470 if (cmdcnt > (rows - 1)) {
3471 cmdcnt = (rows - 1);
3472 }
3473 if (cmdcnt-- > 1) {
3474 do_cmd('+');
3475 } // repeat cnt
3476 dot_skip_over_ws();
3477 break;
3478 case 'I': // I- insert before first non-blank
3479 dot_begin(); // 0
3480 dot_skip_over_ws();
3481 //**** fall thru to ... 'i'
3482 case 'i': // i- insert before current char
3483 case VI_K_INSERT: // Cursor Key Insert
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003484 dc_i:
Eric Andersen3f980402001-04-04 17:31:15 +00003485 cmd_mode = 1; // start insrting
Eric Andersen3f980402001-04-04 17:31:15 +00003486 break;
3487 case 'J': // J- join current and next lines together
3488 if (cmdcnt-- > 2) {
3489 do_cmd(c);
3490 } // repeat cnt
3491 dot_end(); // move to NL
3492 if (dot < end - 1) { // make sure not last char in text[]
3493 *dot++ = ' '; // replace NL with space
Paul Fox8552aec2005-09-16 12:20:05 +00003494 file_modified++;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00003495 while (isblank(*dot)) { // delete leading WS
Eric Andersen3f980402001-04-04 17:31:15 +00003496 dot_delete();
3497 }
3498 }
3499 end_cmd_q(); // stop adding to q
3500 break;
3501 case 'L': // L- goto bottom line on screen
3502 dot = end_screen();
3503 if (cmdcnt > (rows - 1)) {
3504 cmdcnt = (rows - 1);
3505 }
3506 if (cmdcnt-- > 1) {
3507 do_cmd('-');
3508 } // repeat cnt
3509 dot_begin();
3510 dot_skip_over_ws();
3511 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003512 case 'M': // M- goto middle line on screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003513 dot = screenbegin;
3514 for (cnt = 0; cnt < (rows-1) / 2; cnt++)
3515 dot = next_line(dot);
3516 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003517 case 'O': // O- open a empty line above
Eric Andersen822c3832001-05-07 17:37:43 +00003518 // 0i\n ESC -i
Eric Andersen3f980402001-04-04 17:31:15 +00003519 p = begin_line(dot);
3520 if (p[-1] == '\n') {
3521 dot_prev();
3522 case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
3523 dot_end();
3524 dot = char_insert(dot, '\n');
3525 } else {
3526 dot_begin(); // 0
Eric Andersen822c3832001-05-07 17:37:43 +00003527 dot = char_insert(dot, '\n'); // i\n ESC
Eric Andersen3f980402001-04-04 17:31:15 +00003528 dot_prev(); // -
3529 }
3530 goto dc_i;
3531 break;
3532 case 'R': // R- continuous Replace char
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003533 dc5:
Eric Andersen3f980402001-04-04 17:31:15 +00003534 cmd_mode = 2;
Eric Andersen3f980402001-04-04 17:31:15 +00003535 break;
3536 case 'X': // X- delete char before dot
3537 case 'x': // x- delete the current char
3538 case 's': // s- substitute the current char
3539 if (cmdcnt-- > 1) {
3540 do_cmd(c);
3541 } // repeat cnt
3542 dir = 0;
3543 if (c == 'X')
3544 dir = -1;
3545 if (dot[dir] != '\n') {
3546 if (c == 'X')
3547 dot--; // delete prev char
3548 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3549 }
3550 if (c == 's')
3551 goto dc_i; // start insrting
3552 end_cmd_q(); // stop adding to q
3553 break;
3554 case 'Z': // Z- if modified, {write}; exit
3555 // ZZ means to save file (if necessary), then exit
3556 c1 = get_one_char();
3557 if (c1 != 'Z') {
3558 indicate_error(c);
3559 break;
3560 }
Paul Foxf0305b72006-03-28 14:18:21 +00003561 if (file_modified) {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00003562 if (ENABLE_FEATURE_VI_READONLY && readonly_mode) {
3563 psbs("\"%s\" File is read only", current_filename);
Denis Vlasenko92758142006-10-03 19:56:34 +00003564 break;
Paul Foxf0305b72006-03-28 14:18:21 +00003565 }
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00003566 cnt = file_write(current_filename, text, end - 1);
Paul Fox61e45db2005-10-09 14:43:22 +00003567 if (cnt < 0) {
3568 if (cnt == -1)
3569 psbs("Write error: %s", strerror(errno));
3570 } else if (cnt == (end - 1 - text + 1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003571 editing = 0;
3572 }
3573 } else {
3574 editing = 0;
3575 }
3576 break;
3577 case '^': // ^- move to first non-blank on line
3578 dot_begin();
3579 dot_skip_over_ws();
3580 break;
3581 case 'b': // b- back a word
3582 case 'e': // e- end of word
3583 if (cmdcnt-- > 1) {
3584 do_cmd(c);
3585 } // repeat cnt
3586 dir = FORWARD;
3587 if (c == 'b')
3588 dir = BACK;
3589 if ((dot + dir) < text || (dot + dir) > end - 1)
3590 break;
3591 dot += dir;
3592 if (isspace(*dot)) {
3593 dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
3594 }
3595 if (isalnum(*dot) || *dot == '_') {
3596 dot = skip_thing(dot, 1, dir, S_END_ALNUM);
3597 } else if (ispunct(*dot)) {
3598 dot = skip_thing(dot, 1, dir, S_END_PUNCT);
3599 }
3600 break;
3601 case 'c': // c- change something
3602 case 'd': // d- delete something
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003603#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003604 case 'y': // y- yank something
3605 case 'Y': // Y- Yank a line
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003606#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003607 yf = YANKDEL; // assume either "c" or "d"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003608#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003609 if (c == 'y' || c == 'Y')
3610 yf = YANKONLY;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003611#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003612 c1 = 'y';
3613 if (c != 'Y')
3614 c1 = get_one_char(); // get the type of thing to delete
3615 find_range(&p, &q, c1);
3616 if (c1 == 27) { // ESC- user changed mind and wants out
3617 c = c1 = 27; // Escape- do nothing
3618 } else if (strchr("wW", c1)) {
3619 if (c == 'c') {
3620 // don't include trailing WS as part of word
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00003621 while (isblank(*q)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003622 if (q <= text || q[-1] == '\n')
3623 break;
3624 q--;
3625 }
3626 }
3627 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen822c3832001-05-07 17:37:43 +00003628 } else if (strchr("^0bBeEft$", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003629 // single line copy text into a register and delete
3630 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen1c0d3112001-04-16 15:46:44 +00003631 } else if (strchr("cdykjHL%+-{}\r\n", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003632 // multiple line copy text into a register and delete
3633 dot = yank_delete(p, q, 1, yf); // delete lines
Eric Andersen1c0d3112001-04-16 15:46:44 +00003634 if (c == 'c') {
3635 dot = char_insert(dot, '\n');
3636 // on the last line of file don't move to prev line
3637 if (dot != (end-1)) {
3638 dot_prev();
3639 }
3640 } else if (c == 'd') {
Eric Andersen3f980402001-04-04 17:31:15 +00003641 dot_begin();
3642 dot_skip_over_ws();
3643 }
3644 } else {
3645 // could not recognize object
3646 c = c1 = 27; // error-
3647 indicate_error(c);
3648 }
3649 if (c1 != 27) {
3650 // if CHANGING, not deleting, start inserting after the delete
3651 if (c == 'c') {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003652 strcpy(buf, "Change");
Eric Andersen3f980402001-04-04 17:31:15 +00003653 goto dc_i; // start inserting
3654 }
3655 if (c == 'd') {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003656 strcpy(buf, "Delete");
Eric Andersen3f980402001-04-04 17:31:15 +00003657 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003658#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003659 if (c == 'y' || c == 'Y') {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003660 strcpy(buf, "Yank");
Eric Andersen3f980402001-04-04 17:31:15 +00003661 }
3662 p = reg[YDreg];
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003663 q = p + strlen(p);
Eric Andersen3f980402001-04-04 17:31:15 +00003664 for (cnt = 0; p <= q; p++) {
3665 if (*p == '\n')
3666 cnt++;
3667 }
3668 psb("%s %d lines (%d chars) using [%c]",
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003669 buf, cnt, strlen(reg[YDreg]), what_reg());
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003670#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003671 end_cmd_q(); // stop adding to q
3672 }
3673 break;
3674 case 'k': // k- goto prev line, same col
3675 case VI_K_UP: // cursor key Up
3676 if (cmdcnt-- > 1) {
3677 do_cmd(c);
3678 } // repeat cnt
3679 dot_prev();
3680 dot = move_to_col(dot, ccol + offset); // try stay in same col
3681 break;
3682 case 'r': // r- replace the current char with user input
3683 c1 = get_one_char(); // get the replacement char
3684 if (*dot != '\n') {
3685 *dot = c1;
Paul Fox8552aec2005-09-16 12:20:05 +00003686 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003687 }
3688 end_cmd_q(); // stop adding to q
3689 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003690 case 't': // t- move to char prior to next x
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00003691 last_forward_char = get_one_char();
3692 do_cmd(';');
3693 if (*dot == last_forward_char)
3694 dot_left();
3695 last_forward_char= 0;
Eric Andersen822c3832001-05-07 17:37:43 +00003696 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003697 case 'w': // w- forward a word
3698 if (cmdcnt-- > 1) {
3699 do_cmd(c);
3700 } // repeat cnt
3701 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
3702 dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
3703 } else if (ispunct(*dot)) { // we are on PUNCT
3704 dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
3705 }
3706 if (dot < end - 1)
3707 dot++; // move over word
3708 if (isspace(*dot)) {
3709 dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
3710 }
3711 break;
3712 case 'z': // z-
3713 c1 = get_one_char(); // get the replacement char
3714 cnt = 0;
3715 if (c1 == '.')
3716 cnt = (rows - 2) / 2; // put dot at center
3717 if (c1 == '-')
3718 cnt = rows - 2; // put dot at bottom
3719 screenbegin = begin_line(dot); // start dot at top
3720 dot_scroll(cnt, -1);
3721 break;
3722 case '|': // |- move to column "cmdcnt"
3723 dot = move_to_col(dot, cmdcnt - 1); // try to move to column
3724 break;
3725 case '~': // ~- flip the case of letters a-z -> A-Z
3726 if (cmdcnt-- > 1) {
3727 do_cmd(c);
3728 } // repeat cnt
3729 if (islower(*dot)) {
3730 *dot = toupper(*dot);
Paul Fox8552aec2005-09-16 12:20:05 +00003731 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003732 } else if (isupper(*dot)) {
3733 *dot = tolower(*dot);
Paul Fox8552aec2005-09-16 12:20:05 +00003734 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003735 }
3736 dot_right();
3737 end_cmd_q(); // stop adding to q
3738 break;
3739 //----- The Cursor and Function Keys -----------------------------
3740 case VI_K_HOME: // Cursor Key Home
3741 dot_begin();
3742 break;
3743 // The Fn keys could point to do_macro which could translate them
3744 case VI_K_FUN1: // Function Key F1
3745 case VI_K_FUN2: // Function Key F2
3746 case VI_K_FUN3: // Function Key F3
3747 case VI_K_FUN4: // Function Key F4
3748 case VI_K_FUN5: // Function Key F5
3749 case VI_K_FUN6: // Function Key F6
3750 case VI_K_FUN7: // Function Key F7
3751 case VI_K_FUN8: // Function Key F8
3752 case VI_K_FUN9: // Function Key F9
3753 case VI_K_FUN10: // Function Key F10
3754 case VI_K_FUN11: // Function Key F11
3755 case VI_K_FUN12: // Function Key F12
3756 break;
3757 }
3758
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003759 dc1:
Eric Andersen3f980402001-04-04 17:31:15 +00003760 // if text[] just became empty, add back an empty line
3761 if (end == text) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003762 char_insert(text, '\n'); // start empty buf with dummy line
Eric Andersen3f980402001-04-04 17:31:15 +00003763 dot = text;
3764 }
3765 // it is OK for dot to exactly equal to end, otherwise check dot validity
3766 if (dot != end) {
3767 dot = bound_dot(dot); // make sure "dot" is valid
3768 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003769#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003770 check_context(c); // update the current context
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003771#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003772
3773 if (!isdigit(c))
3774 cmdcnt = 0; // cmd was not a number, reset cmdcnt
3775 cnt = dot - begin_line(dot);
3776 // Try to stay off of the Newline
3777 if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
3778 dot--;
3779}
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003780
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003781#if ENABLE_FEATURE_VI_CRASHME
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003782static int totalcmds = 0;
3783static int Mp = 85; // Movement command Probability
3784static int Np = 90; // Non-movement command Probability
3785static int Dp = 96; // Delete command Probability
3786static int Ip = 97; // Insert command Probability
3787static int Yp = 98; // Yank command Probability
3788static int Pp = 99; // Put command Probability
3789static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003790const char chars[20] = "\t012345 abcdABCD-=.$";
3791const char *const words[20] = {
3792 "this", "is", "a", "test",
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003793 "broadcast", "the", "emergency", "of",
3794 "system", "quick", "brown", "fox",
3795 "jumped", "over", "lazy", "dogs",
3796 "back", "January", "Febuary", "March"
3797};
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003798const char *const lines[20] = {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003799 "You should have received a copy of the GNU General Public License\n",
3800 "char c, cm, *cmd, *cmd1;\n",
3801 "generate a command by percentages\n",
3802 "Numbers may be typed as a prefix to some commands.\n",
3803 "Quit, discarding changes!\n",
3804 "Forced write, if permission originally not valid.\n",
3805 "In general, any ex or ed command (such as substitute or delete).\n",
3806 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3807 "Please get w/ me and I will go over it with you.\n",
3808 "The following is a list of scheduled, committed changes.\n",
3809 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3810 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3811 "Any question about transactions please contact Sterling Huxley.\n",
3812 "I will try to get back to you by Friday, December 31.\n",
3813 "This Change will be implemented on Friday.\n",
3814 "Let me know if you have problems accessing this;\n",
3815 "Sterling Huxley recently added you to the access list.\n",
3816 "Would you like to go to lunch?\n",
3817 "The last command will be automatically run.\n",
3818 "This is too much english for a computer geek.\n",
3819};
3820char *multilines[20] = {
3821 "You should have received a copy of the GNU General Public License\n",
3822 "char c, cm, *cmd, *cmd1;\n",
3823 "generate a command by percentages\n",
3824 "Numbers may be typed as a prefix to some commands.\n",
3825 "Quit, discarding changes!\n",
3826 "Forced write, if permission originally not valid.\n",
3827 "In general, any ex or ed command (such as substitute or delete).\n",
3828 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3829 "Please get w/ me and I will go over it with you.\n",
3830 "The following is a list of scheduled, committed changes.\n",
3831 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3832 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3833 "Any question about transactions please contact Sterling Huxley.\n",
3834 "I will try to get back to you by Friday, December 31.\n",
3835 "This Change will be implemented on Friday.\n",
3836 "Let me know if you have problems accessing this;\n",
3837 "Sterling Huxley recently added you to the access list.\n",
3838 "Would you like to go to lunch?\n",
3839 "The last command will be automatically run.\n",
3840 "This is too much english for a computer geek.\n",
3841};
3842
3843// create a random command to execute
3844static void crash_dummy()
3845{
3846 static int sleeptime; // how long to pause between commands
3847 char c, cm, *cmd, *cmd1;
3848 int i, cnt, thing, rbi, startrbi, percent;
3849
3850 // "dot" movement commands
3851 cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
3852
3853 // is there already a command running?
3854 if (readed_for_parse > 0)
3855 goto cd1;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003856 cd0:
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003857 startrbi = rbi = 0;
3858 sleeptime = 0; // how long to pause between commands
Denis Vlasenkoe8a07882007-06-10 15:08:44 +00003859 memset(readbuffer, '\0', MAX_LINELEN); // clear the read buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003860 // generate a command by percentages
3861 percent = (int) lrand48() % 100; // get a number from 0-99
3862 if (percent < Mp) { // Movement commands
3863 // available commands
3864 cmd = cmd1;
3865 M++;
3866 } else if (percent < Np) { // non-movement commands
3867 cmd = "mz<>\'\""; // available commands
3868 N++;
3869 } else if (percent < Dp) { // Delete commands
3870 cmd = "dx"; // available commands
3871 D++;
3872 } else if (percent < Ip) { // Inset commands
3873 cmd = "iIaAsrJ"; // available commands
3874 I++;
3875 } else if (percent < Yp) { // Yank commands
3876 cmd = "yY"; // available commands
3877 Y++;
3878 } else if (percent < Pp) { // Put commands
3879 cmd = "pP"; // available commands
3880 P++;
3881 } else {
3882 // We do not know how to handle this command, try again
3883 U++;
3884 goto cd0;
3885 }
3886 // randomly pick one of the available cmds from "cmd[]"
3887 i = (int) lrand48() % strlen(cmd);
3888 cm = cmd[i];
3889 if (strchr(":\024", cm))
3890 goto cd0; // dont allow colon or ctrl-T commands
3891 readbuffer[rbi++] = cm; // put cmd into input buffer
3892
3893 // now we have the command-
3894 // there are 1, 2, and multi char commands
3895 // find out which and generate the rest of command as necessary
3896 if (strchr("dmryz<>\'\"", cm)) { // 2-char commands
3897 cmd1 = " \n\r0$^-+wWeEbBhjklHL";
3898 if (cm == 'm' || cm == '\'' || cm == '\"') { // pick a reg[]
3899 cmd1 = "abcdefghijklmnopqrstuvwxyz";
3900 }
3901 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3902 c = cmd1[thing];
3903 readbuffer[rbi++] = c; // add movement to input buffer
3904 }
3905 if (strchr("iIaAsc", cm)) { // multi-char commands
3906 if (cm == 'c') {
3907 // change some thing
3908 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3909 c = cmd1[thing];
3910 readbuffer[rbi++] = c; // add movement to input buffer
3911 }
3912 thing = (int) lrand48() % 4; // what thing to insert
3913 cnt = (int) lrand48() % 10; // how many to insert
3914 for (i = 0; i < cnt; i++) {
3915 if (thing == 0) { // insert chars
3916 readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
3917 } else if (thing == 1) { // insert words
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003918 strcat(readbuffer, words[(int) lrand48() % 20]);
3919 strcat(readbuffer, " ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003920 sleeptime = 0; // how fast to type
3921 } else if (thing == 2) { // insert lines
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003922 strcat(readbuffer, lines[(int) lrand48() % 20]);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003923 sleeptime = 0; // how fast to type
3924 } else { // insert multi-lines
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003925 strcat(readbuffer, multilines[(int) lrand48() % 20]);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003926 sleeptime = 0; // how fast to type
3927 }
3928 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003929 strcat(readbuffer, "\033");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003930 }
3931 readed_for_parse = strlen(readbuffer);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003932 cd1:
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003933 totalcmds++;
3934 if (sleeptime > 0)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003935 mysleep(sleeptime); // sleep 1/100 sec
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003936}
3937
3938// test to see if there are any errors
3939static void crash_test()
3940{
3941 static time_t oldtim;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003942
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003943 time_t tim;
Denis Vlasenkoe8a07882007-06-10 15:08:44 +00003944 char d[2], msg[MAX_LINELEN];
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003945
3946 msg[0] = '\0';
3947 if (end < text) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003948 strcat(msg, "end<text ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003949 }
3950 if (end > textend) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003951 strcat(msg, "end>textend ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003952 }
3953 if (dot < text) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003954 strcat(msg, "dot<text ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003955 }
3956 if (dot > end) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003957 strcat(msg, "dot>end ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003958 }
3959 if (screenbegin < text) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003960 strcat(msg, "screenbegin<text ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003961 }
3962 if (screenbegin > end - 1) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003963 strcat(msg, "screenbegin>end-1 ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003964 }
3965
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003966 if (msg[0]) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003967 alarm(0);
Glenn L McGrath7127b582002-12-03 21:48:15 +00003968 printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003969 totalcmds, last_input_char, msg, SOs, SOn);
3970 fflush(stdout);
3971 while (read(0, d, 1) > 0) {
3972 if (d[0] == '\n' || d[0] == '\r')
3973 break;
3974 }
3975 alarm(3);
3976 }
3977 tim = (time_t) time((time_t *) 0);
3978 if (tim >= (oldtim + 3)) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003979 sprintf(status_buffer,
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003980 "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
3981 totalcmds, M, N, I, D, Y, P, U, end - text + 1);
3982 oldtim = tim;
3983 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003984}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003985#endif