blob: e58d6b31037dd23a6bcc99bd9111b443a8cd30f0 [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,
Denis Vlasenko26b6fba2007-12-21 21:34:37 +000038 MAX_TABSTOP = 32, // sanity limit
Denis Vlasenkoe8a07882007-06-10 15:08:44 +000039};
Eric Andersen3f980402001-04-04 17:31:15 +000040
41// Misc. non-Ascii keys that report an escape sequence
Denis Vlasenko2a51af22007-03-21 22:31:24 +000042#define VI_K_UP (char)128 // cursor key Up
43#define VI_K_DOWN (char)129 // cursor key Down
44#define VI_K_RIGHT (char)130 // Cursor Key Right
45#define VI_K_LEFT (char)131 // cursor key Left
46#define VI_K_HOME (char)132 // Cursor Key Home
47#define VI_K_END (char)133 // Cursor Key End
48#define VI_K_INSERT (char)134 // Cursor Key Insert
49#define VI_K_PAGEUP (char)135 // Cursor Key Page Up
50#define VI_K_PAGEDOWN (char)136 // Cursor Key Page Down
51#define VI_K_FUN1 (char)137 // Function Key F1
52#define VI_K_FUN2 (char)138 // Function Key F2
53#define VI_K_FUN3 (char)139 // Function Key F3
54#define VI_K_FUN4 (char)140 // Function Key F4
55#define VI_K_FUN5 (char)141 // Function Key F5
56#define VI_K_FUN6 (char)142 // Function Key F6
57#define VI_K_FUN7 (char)143 // Function Key F7
58#define VI_K_FUN8 (char)144 // Function Key F8
59#define VI_K_FUN9 (char)145 // Function Key F9
60#define VI_K_FUN10 (char)146 // Function Key F10
61#define VI_K_FUN11 (char)147 // Function Key F11
62#define VI_K_FUN12 (char)148 // Function Key F12
Eric Andersen3f980402001-04-04 17:31:15 +000063
Glenn L McGrath09adaca2002-12-02 21:18:10 +000064/* vt102 typical ESC sequence */
65/* terminal standout start/normal ESC sequence */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000066static const char SOs[] ALIGN1 = "\033[7m";
67static const char SOn[] ALIGN1 = "\033[0m";
Glenn L McGrath09adaca2002-12-02 21:18:10 +000068/* terminal bell sequence */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000069static const char bell[] ALIGN1 = "\007";
Glenn L McGrath09adaca2002-12-02 21:18:10 +000070/* Clear-end-of-line and Clear-end-of-screen ESC sequence */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000071static const char Ceol[] ALIGN1 = "\033[0K";
72static const char Ceos[] ALIGN1 = "\033[0J";
Glenn L McGrath09adaca2002-12-02 21:18:10 +000073/* Cursor motion arbitrary destination ESC sequence */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000074static const char CMrc[] ALIGN1 = "\033[%d;%dH";
Glenn L McGrath09adaca2002-12-02 21:18:10 +000075/* Cursor motion up and down ESC sequence */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000076static const char CMup[] ALIGN1 = "\033[A";
77static const char CMdown[] ALIGN1 = "\n";
Glenn L McGrath09adaca2002-12-02 21:18:10 +000078
79
Rob Landleybc68cd12006-03-10 19:22:06 +000080enum {
81 YANKONLY = FALSE,
82 YANKDEL = TRUE,
83 FORWARD = 1, // code depends on "1" for array index
84 BACK = -1, // code depends on "-1" for array index
85 LIMITED = 0, // how much of text[] in char_search
86 FULL = 1, // how much of text[] in char_search
Eric Andersen3f980402001-04-04 17:31:15 +000087
Rob Landleybc68cd12006-03-10 19:22:06 +000088 S_BEFORE_WS = 1, // used in skip_thing() for moving "dot"
89 S_TO_WS = 2, // used in skip_thing() for moving "dot"
90 S_OVER_WS = 3, // used in skip_thing() for moving "dot"
91 S_END_PUNCT = 4, // used in skip_thing() for moving "dot"
Denis Vlasenko8e858e22007-03-07 09:35:43 +000092 S_END_ALNUM = 5, // used in skip_thing() for moving "dot"
Rob Landleybc68cd12006-03-10 19:22:06 +000093};
Eric Andersen3f980402001-04-04 17:31:15 +000094
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +000095/* vi.c expects chars to be unsigned. */
96/* busybox build system provides that, but it's better */
97/* to audit and fix the source */
Eric Andersen3f980402001-04-04 17:31:15 +000098
Denis Vlasenkoeaabf062007-07-17 23:14:07 +000099static smallint vi_setops;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000100#define VI_AUTOINDENT 1
101#define VI_SHOWMATCH 2
102#define VI_IGNORECASE 4
103#define VI_ERR_METHOD 8
104#define autoindent (vi_setops & VI_AUTOINDENT)
105#define showmatch (vi_setops & VI_SHOWMATCH )
106#define ignorecase (vi_setops & VI_IGNORECASE)
107/* indicate error with beep or flash */
108#define err_method (vi_setops & VI_ERR_METHOD)
109
Eric Andersen3f980402001-04-04 17:31:15 +0000110
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000111static smallint editing; // >0 while we are editing a file
112 // [code audit says "can be 0 or 1 only"]
113static smallint cmd_mode; // 0=command 1=insert 2=replace
114static smallint file_modified; // buffer contents changed
115static smallint last_file_modified = -1;
116static int fn_start; // index of first cmd line file name
117static int save_argc; // how many file names on cmd line
118static int cmdcnt; // repetition count
119static int rows, columns; // the terminal screen is this size
Denis Vlasenko26b6fba2007-12-21 21:34:37 +0000120static int crow, ccol; // cursor is on Crow x Ccol
121static int offset; // chars scrolled off the screen to the left
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000122static char *status_buffer; // mesages to the user
Paul Fox8552aec2005-09-16 12:20:05 +0000123#define STATUS_BUFFER_LEN 200
124static int have_status_msg; // is default edit status needed?
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000125 // [don't make smallint!]
Paul Fox8552aec2005-09-16 12:20:05 +0000126static int last_status_cksum; // hash of current status line
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000127static char *current_filename; // current file name
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000128//static char *text, *end; // pointers to the user data in memory
129static char *screen; // pointer to the virtual screen buffer
130static int screensize; // and its size
131static char *screenbegin; // index into text[], of top line on the screen
132//static char *dot; // where all the action takes place
Eric Andersen3f980402001-04-04 17:31:15 +0000133static int tabstop;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000134static char erase_char; // the users erase character
135static char last_input_char; // last char read from user
136static char last_forward_char; // last char searched for with 'f'
Eric Andersen3f980402001-04-04 17:31:15 +0000137
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000138#if ENABLE_FEATURE_VI_READONLY
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000139//static smallint vi_readonly, readonly;
140static smallint readonly_mode = 0;
Denis Vlasenko6a2f7f42007-08-16 10:35:17 +0000141#define SET_READONLY_FILE(flags) ((flags) |= 0x01)
142#define SET_READONLY_MODE(flags) ((flags) |= 0x02)
143#define UNSET_READONLY_FILE(flags) ((flags) &= 0xfe)
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000144#else
145#define readonly_mode 0
146#define SET_READONLY_FILE(flags)
147#define SET_READONLY_MODE(flags)
148#define UNSET_READONLY_FILE(flags)
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000149#endif
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000150
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000151#if ENABLE_FEATURE_VI_DOT_CMD
152static smallint adding2q; // are we currently adding user input to q
153static char *last_modifying_cmd; // last modifying cmd for "."
154static char *ioq, *ioq_start; // pointer to string for get_one_char to "read"
155#endif
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000156#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
Eric Andersen822c3832001-05-07 17:37:43 +0000157static int last_row; // where the cursor was last moved to
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000158#endif
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000159#if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000160static int my_pid;
161#endif
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000162#if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000163static char *modifying_cmds; // cmds that modify text[]
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000164#endif
165#if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000166static char *last_search_pattern; // last pattern from a '/' or '?' search
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000167#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000168
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000169/* Moving biggest data to malloced space... */
170struct globals {
171 /* many references - keep near the top of globals */
172 char *text, *end; // pointers to the user data in memory
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000173 int text_size; // size of the allocated buffer
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000174 char *dot; // where all the action takes place
175#if ENABLE_FEATURE_VI_YANKMARK
176 char *reg[28]; // named register a-z, "D", and "U" 0-25,26,27
177 int YDreg, Ureg; // default delete register and orig line for "U"
178 char *mark[28]; // user marks points somewhere in text[]- a-z and previous context ''
179 char *context_start, *context_end;
180#endif
181 /* a few references only */
182#if ENABLE_FEATURE_VI_USE_SIGNALS
183 jmp_buf restart; // catch_sig()
184#endif
185 struct termios term_orig, term_vi; // remember what the cooked mode was
186#if ENABLE_FEATURE_VI_COLON
187 char *initial_cmds[3]; // currently 2 entries, NULL terminated
188#endif
Denis Vlasenkoa96425f2007-12-09 04:13:43 +0000189 char readbuffer[MAX_LINELEN];
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000190};
191#define G (*ptr_to_globals)
192#define text (G.text )
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000193#define text_size (G.text_size )
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000194#define end (G.end )
195#define dot (G.dot )
196#define reg (G.reg )
197#define YDreg (G.YDreg )
198#define Ureg (G.Ureg )
199#define mark (G.mark )
200#define context_start (G.context_start )
201#define context_end (G.context_end )
202#define restart (G.restart )
203#define term_orig (G.term_orig )
204#define term_vi (G.term_vi )
205#define initial_cmds (G.initial_cmds )
Denis Vlasenkoa96425f2007-12-09 04:13:43 +0000206#define readbuffer (G.readbuffer )
207#define INIT_G() do { \
208 PTR_TO_GLOBALS = xzalloc(sizeof(G)); \
209} while (0)
Eric Andersen3f980402001-04-04 17:31:15 +0000210
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000211static int init_text_buffer(char *); // init from file or create new
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000212static void edit_file(char *); // edit one file
213static void do_cmd(char); // execute a command
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000214static int next_tabstop(int);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000215static void sync_cursor(char *, int *, int *); // synchronize the screen cursor to dot
216static char *begin_line(char *); // return pointer to cur line B-o-l
217static char *end_line(char *); // return pointer to cur line E-o-l
218static char *prev_line(char *); // return pointer to prev line B-o-l
219static char *next_line(char *); // return pointer to next line B-o-l
220static char *end_screen(void); // get pointer to last char on screen
221static int count_lines(char *, char *); // count line from start to stop
222static char *find_line(int); // find begining of line #li
223static char *move_to_col(char *, int); // move "p" to column l
Eric Andersen3f980402001-04-04 17:31:15 +0000224static void dot_left(void); // move dot left- dont leave line
225static void dot_right(void); // move dot right- dont leave line
226static void dot_begin(void); // move dot to B-o-l
227static void dot_end(void); // move dot to E-o-l
228static void dot_next(void); // move dot to next line B-o-l
229static void dot_prev(void); // move dot to prev line B-o-l
230static void dot_scroll(int, int); // move the screen up or down
231static void dot_skip_over_ws(void); // move dot pat WS
232static void dot_delete(void); // delete the char at 'dot'
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000233static char *bound_dot(char *); // make sure text[0] <= P < "end"
234static char *new_screen(int, int); // malloc virtual screen memory
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000235static char *char_insert(char *, char); // insert the char c at 'p'
236static char *stupid_insert(char *, char); // stupidly insert the char c at 'p'
237static char find_range(char **, char **, char); // return pointers for an object
238static int st_test(char *, int, int, char *); // helper for skip_thing()
239static char *skip_thing(char *, int, int, int); // skip some object
240static char *find_pair(char *, char); // find matching pair () [] {}
241static char *text_hole_delete(char *, char *); // at "p", delete a 'size' byte hole
242static char *text_hole_make(char *, int); // at "p", make a 'size' byte hole
243static char *yank_delete(char *, char *, int, int); // yank text[] into register then delete
Eric Andersen3f980402001-04-04 17:31:15 +0000244static void show_help(void); // display some help info
Eric Andersen3f980402001-04-04 17:31:15 +0000245static void rawmode(void); // set "raw" mode on tty
246static void cookmode(void); // return to "cooked" mode on tty
Denis Vlasenko87f3b262007-09-07 13:43:28 +0000247// sleep for 'h' 1/100 seconds, return 1/0 if stdin is (ready for read)/(not ready)
248static int mysleep(int);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000249static char readit(void); // read (maybe cursor) key from stdin
250static char get_one_char(void); // read 1 char from stdin
251static int file_size(const char *); // what is the byte size of "fn"
Denis Vlasenko59a1f302007-07-14 22:43:10 +0000252#if ENABLE_FEATURE_VI_READONLY
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000253static int file_insert(const char *, char *, int);
Denis Vlasenko59a1f302007-07-14 22:43:10 +0000254#else
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000255static int file_insert(const char *, char *);
Denis Vlasenko59a1f302007-07-14 22:43:10 +0000256#endif
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000257static int file_write(char *, char *, char *);
Denis Vlasenko26b6fba2007-12-21 21:34:37 +0000258#if !ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
259#define place_cursor(a, b, optimize) place_cursor(a, b)
260#endif
Eric Andersen822c3832001-05-07 17:37:43 +0000261static void place_cursor(int, int, int);
Eric Andersenbff7a602001-11-17 07:15:43 +0000262static void screen_erase(void);
Eric Andersen3f980402001-04-04 17:31:15 +0000263static void clear_to_eol(void);
264static void clear_to_eos(void);
265static void standout_start(void); // send "start reverse video" sequence
266static void standout_end(void); // send "end reverse video" sequence
267static void flash(int); // flash the terminal screen
Eric Andersen3f980402001-04-04 17:31:15 +0000268static void show_status_line(void); // put a message on the bottom line
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000269static void psb(const char *, ...); // Print Status Buf
270static void psbs(const char *, ...); // Print Status Buf in standout mode
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000271static void ni(const char *); // display messages
Paul Fox8552aec2005-09-16 12:20:05 +0000272static int format_edit_status(void); // format file status on status line
Eric Andersen3f980402001-04-04 17:31:15 +0000273static void redraw(int); // force a full screen refresh
Denis Vlasenko26b6fba2007-12-21 21:34:37 +0000274static int format_line(char*, char*, int);
Eric Andersen3f980402001-04-04 17:31:15 +0000275static void refresh(int); // update the terminal from screen[]
276
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000277static void Indicate_Error(void); // use flash or beep to indicate error
278#define indicate_error(c) Indicate_Error()
Paul Fox90372ed2005-10-09 14:26:26 +0000279static void Hit_Return(void);
280
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000281#if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000282static char *char_search(char *, const char *, int, int); // search for pattern starting at p
283static int mycmp(const char *, const char *, int); // string cmp based in "ignorecase"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000284#endif
285#if ENABLE_FEATURE_VI_COLON
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000286static char *get_one_address(char *, int *); // get colon addr, if present
287static char *get_address(char *, int *, int *); // get two colon addrs, if present
288static void colon(char *); // execute the "colon" mode cmds
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000289#endif
290#if ENABLE_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000291static void winch_sig(int); // catch window size changes
292static void suspend_sig(int); // catch ctrl-Z
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000293static void catch_sig(int); // catch ctrl-C and alarm time-outs
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000294#endif
295#if ENABLE_FEATURE_VI_DOT_CMD
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000296static void start_new_cmd_q(char); // new queue for command
Eric Andersenbff7a602001-11-17 07:15:43 +0000297static void end_cmd_q(void); // stop saving input chars
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000298#else
299#define end_cmd_q() ((void)0)
300#endif
301#if ENABLE_FEATURE_VI_SETOPTS
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000302static void showmatching(char *); // show the matching pair () [] {}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000303#endif
304#if ENABLE_FEATURE_VI_YANKMARK || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) || ENABLE_FEATURE_VI_CRASHME
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000305static char *string_insert(char *, char *); // insert the string at 'p'
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000306#endif
307#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000308static char *text_yank(char *, char *, int); // save copy of "p" into a register
309static char what_reg(void); // what is letter of current YDreg
310static void check_context(char); // remember context for '' command
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000311#endif
312#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000313static void crash_dummy();
314static void crash_test();
315static int crashme = 0;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000316#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000317
318
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000319static void write1(const char *out)
320{
321 fputs(out, stdout);
322}
323
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000324int vi_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Rob Landleydfba7412006-03-06 20:47:33 +0000325int vi_main(int argc, char **argv)
Eric Andersen3f980402001-04-04 17:31:15 +0000326{
Eric Andersend402edf2001-04-04 19:29:48 +0000327 int c;
Paul Fox8552aec2005-09-16 12:20:05 +0000328 RESERVE_CONFIG_BUFFER(STATUS_BUFFER, STATUS_BUFFER_LEN);
Eric Andersen3f980402001-04-04 17:31:15 +0000329
Denis Vlasenkocd5c7862007-05-17 16:37:22 +0000330#if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000331 my_pid = getpid();
332#endif
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000333
Denis Vlasenkoa96425f2007-12-09 04:13:43 +0000334 INIT_G();
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000335
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000336#if ENABLE_FEATURE_VI_CRASHME
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000337 srand((long) my_pid);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000338#endif
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000339
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000340 status_buffer = STATUS_BUFFER;
Paul Fox8552aec2005-09-16 12:20:05 +0000341 last_status_cksum = 0;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000342 text = NULL;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000343
Denis Vlasenko2414a962007-07-18 22:03:40 +0000344#ifdef NO_SUCH_APPLET_YET
345 /* If we aren't "vi", we are "view" */
346 if (ENABLE_FEATURE_VI_READONLY && applet_name[2]) {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000347 SET_READONLY_MODE(readonly_mode);
Eric Andersen3f980402001-04-04 17:31:15 +0000348 }
Denis Vlasenko2414a962007-07-18 22:03:40 +0000349#endif
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000350
Bernhard Reutner-Fischer73f56bb2007-09-22 21:18:46 +0000351 vi_setops = VI_AUTOINDENT | VI_SHOWMATCH | VI_IGNORECASE;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000352#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenko2414a962007-07-18 22:03:40 +0000353 memset(reg, 0, sizeof(reg)); // init the yank regs
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000354#endif
355#if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000356 modifying_cmds = (char *) "aAcCdDiIJoOpPrRsxX<>~"; // cmds modifying text[]
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000357#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000358
Denis Vlasenkof9234132007-03-21 00:03:42 +0000359 // 1- process $HOME/.exrc file (not inplemented yet)
Eric Andersen3f980402001-04-04 17:31:15 +0000360 // 2- process EXINIT variable from environment
361 // 3- process command line args
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000362#if ENABLE_FEATURE_VI_COLON
Denis Vlasenkof9234132007-03-21 00:03:42 +0000363 {
364 char *p = getenv("EXINIT");
365 if (p && *p)
366 initial_cmds[0] = xstrdup(p);
367 }
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000368#endif
369 while ((c = getopt(argc, argv, "hCR" USE_FEATURE_VI_COLON("c:"))) != -1) {
Eric Andersen3f980402001-04-04 17:31:15 +0000370 switch (c) {
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000371#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000372 case 'C':
373 crashme = 1;
374 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000375#endif
376#if ENABLE_FEATURE_VI_READONLY
Eric Andersen3f980402001-04-04 17:31:15 +0000377 case 'R': // Read-only flag
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000378 SET_READONLY_MODE(readonly_mode);
Eric Andersen3f980402001-04-04 17:31:15 +0000379 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000380#endif
Eric Andersen822c3832001-05-07 17:37:43 +0000381 //case 'r': // recover flag- ignore- we don't use tmp file
382 //case 'x': // encryption flag- ignore
383 //case 'c': // execute command first
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000384#if ENABLE_FEATURE_VI_COLON
Denis Vlasenkof9234132007-03-21 00:03:42 +0000385 case 'c': // cmd line vi command
386 if (*optarg)
387 initial_cmds[initial_cmds[0] != 0] = xstrdup(optarg);
388 break;
Eric Andersen822c3832001-05-07 17:37:43 +0000389 //case 'h': // help -- just use default
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000390#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000391 default:
392 show_help();
Eric Andersendd8500b2001-07-02 18:06:14 +0000393 return 1;
Eric Andersen3f980402001-04-04 17:31:15 +0000394 }
395 }
396
397 // The argv array can be used by the ":next" and ":rewind" commands
398 // save optind.
399 fn_start = optind; // remember first file name for :next and :rew
400 save_argc = argc;
401
402 //----- This is the main file handling loop --------------
403 if (optind >= argc) {
Eric Andersen3f980402001-04-04 17:31:15 +0000404 edit_file(0);
405 } else {
406 for (; optind < argc; optind++) {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000407 edit_file(argv[optind]);
Eric Andersen3f980402001-04-04 17:31:15 +0000408 }
409 }
410 //-----------------------------------------------------------
411
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000412 return 0;
Eric Andersen3f980402001-04-04 17:31:15 +0000413}
414
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000415/* read text from file or create an empty buf */
416/* will also update current_filename */
417static int init_text_buffer(char *fn)
418{
419 int rc;
420 int size = file_size(fn); // file size. -1 means does not exist.
421
422 /* allocate/reallocate text buffer */
423 free(text);
424 text_size = size * 2;
425 if (text_size < 10240)
426 text_size = 10240; // have a minimum size for new files
427 screenbegin = dot = end = text = xzalloc(text_size);
Denis Vlasenko2f6ae432007-07-19 22:50:47 +0000428
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000429 if (fn != current_filename) {
430 free(current_filename);
431 current_filename = xstrdup(fn);
432 }
433 if (size < 0) {
434 // file dont exist. Start empty buf with dummy line
435 char_insert(text, '\n');
436 rc = 0;
437 } else {
438 rc = file_insert(fn, text
439 USE_FEATURE_VI_READONLY(, 1));
440 }
441 file_modified = 0;
442 last_file_modified = -1;
443#if ENABLE_FEATURE_VI_YANKMARK
444 /* init the marks. */
445 memset(mark, 0, sizeof(mark));
446#endif
447 return rc;
Denis Vlasenko2f6ae432007-07-19 22:50:47 +0000448}
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000449
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000450static void edit_file(char * fn)
Eric Andersen3f980402001-04-04 17:31:15 +0000451{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000452 char c;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000453 int size;
Eric Andersen3f980402001-04-04 17:31:15 +0000454
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000455#if ENABLE_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000456 int sig;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000457#endif
458#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000459 static char *cur_line;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000460#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000461
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000462 editing = 1; // 0= exit, 1= one file, 2= multiple files
Eric Andersen3f980402001-04-04 17:31:15 +0000463 rawmode();
464 rows = 24;
465 columns = 80;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000466 size = 0;
Rob Landleye5e1a102006-06-21 01:15:36 +0000467 if (ENABLE_FEATURE_VI_WIN_RESIZE)
468 get_terminal_width_height(0, &columns, &rows);
Eric Andersen3f980402001-04-04 17:31:15 +0000469 new_screen(rows, columns); // get memory for virtual screen
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000470 init_text_buffer(fn);
Eric Andersen3f980402001-04-04 17:31:15 +0000471
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000472#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000473 YDreg = 26; // default Yank/Delete reg
474 Ureg = 27; // hold orig line for "U" cmd
Eric Andersen3f980402001-04-04 17:31:15 +0000475 mark[26] = mark[27] = text; // init "previous context"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000476#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000477
Eric Andersen3f980402001-04-04 17:31:15 +0000478 last_forward_char = last_input_char = '\0';
479 crow = 0;
480 ccol = 0;
Eric Andersen3f980402001-04-04 17:31:15 +0000481
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000482#if ENABLE_FEATURE_VI_USE_SIGNALS
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000483 catch_sig(0);
Eric Andersen3f980402001-04-04 17:31:15 +0000484 signal(SIGWINCH, winch_sig);
485 signal(SIGTSTP, suspend_sig);
486 sig = setjmp(restart);
487 if (sig != 0) {
Eric Andersen1c0d3112001-04-16 15:46:44 +0000488 screenbegin = dot = text;
Eric Andersen3f980402001-04-04 17:31:15 +0000489 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000490#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000491
Eric Andersen3f980402001-04-04 17:31:15 +0000492 cmd_mode = 0; // 0=command 1=insert 2='R'eplace
493 cmdcnt = 0;
494 tabstop = 8;
495 offset = 0; // no horizontal offset
496 c = '\0';
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000497#if ENABLE_FEATURE_VI_DOT_CMD
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000498 free(last_modifying_cmd);
499 free(ioq_start);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000500 ioq = ioq_start = last_modifying_cmd = NULL;
Eric Andersen3f980402001-04-04 17:31:15 +0000501 adding2q = 0;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000502#endif
Eric Andersen822c3832001-05-07 17:37:43 +0000503 redraw(FALSE); // dont force every col re-draw
Eric Andersen3f980402001-04-04 17:31:15 +0000504
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000505#if ENABLE_FEATURE_VI_COLON
Denis Vlasenkof9234132007-03-21 00:03:42 +0000506 {
507 char *p, *q;
508 int n = 0;
509
510 while ((p = initial_cmds[n])) {
511 do {
512 q = p;
513 p = strchr(q,'\n');
514 if (p)
Denis Vlasenko51742f42007-04-12 00:32:05 +0000515 while (*p == '\n')
Denis Vlasenkof9234132007-03-21 00:03:42 +0000516 *p++ = '\0';
517 if (*q)
518 colon(q);
519 } while (p);
520 free(initial_cmds[n]);
521 initial_cmds[n] = NULL;
522 n++;
523 }
524 }
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000525#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000526 //------This is the main Vi cmd handling loop -----------------------
527 while (editing > 0) {
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000528#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000529 if (crashme > 0) {
530 if ((end - text) > 1) {
531 crash_dummy(); // generate a random command
532 } else {
533 crashme = 0;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000534 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 +0000535 refresh(FALSE);
536 }
537 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000538#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000539 last_input_char = c = get_one_char(); // get a cmd from user
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000540#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000541 // save a copy of the current line- for the 'U" command
542 if (begin_line(dot) != cur_line) {
543 cur_line = begin_line(dot);
544 text_yank(begin_line(dot), end_line(dot), Ureg);
545 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000546#endif
547#if ENABLE_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000548 // These are commands that change text[].
549 // Remember the input for the "." command
550 if (!adding2q && ioq_start == 0
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000551 && strchr(modifying_cmds, c)
552 ) {
Eric Andersen3f980402001-04-04 17:31:15 +0000553 start_new_cmd_q(c);
554 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000555#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000556 do_cmd(c); // execute the user command
557 //
558 // poll to see if there is input already waiting. if we are
559 // not able to display output fast enough to keep up, skip
560 // the display update until we catch up with input.
561 if (mysleep(0) == 0) {
562 // no input pending- so update output
563 refresh(FALSE);
564 show_status_line();
565 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000566#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000567 if (crashme > 0)
568 crash_test(); // test editor variables
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000569#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000570 }
571 //-------------------------------------------------------------------
572
Eric Andersen822c3832001-05-07 17:37:43 +0000573 place_cursor(rows, 0, FALSE); // go to bottom of screen
Eric Andersen3f980402001-04-04 17:31:15 +0000574 clear_to_eol(); // Erase to end of line
575 cookmode();
576}
577
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000578//----- The Colon commands -------------------------------------
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000579#if ENABLE_FEATURE_VI_COLON
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000580static char *get_one_address(char * p, int *addr) // get colon addr, if present
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000581{
582 int st;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000583 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000584
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000585#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000586 char c;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000587#endif
588#if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenkoe8a07882007-06-10 15:08:44 +0000589 char *pat, buf[MAX_LINELEN];
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000590#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000591
592 *addr = -1; // assume no addr
593 if (*p == '.') { // the current line
594 p++;
595 q = begin_line(dot);
596 *addr = count_lines(text, q);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000597#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000598 } else if (*p == '\'') { // is this a mark addr
599 p++;
600 c = tolower(*p);
601 p++;
602 if (c >= 'a' && c <= 'z') {
603 // we have a mark
604 c = c - 'a';
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000605 q = mark[(unsigned char) c];
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000606 if (q != NULL) { // is mark valid
607 *addr = count_lines(text, q); // count lines
608 }
609 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000610#endif
611#if ENABLE_FEATURE_VI_SEARCH
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000612 } else if (*p == '/') { // a search pattern
613 q = buf;
614 for (p++; *p; p++) {
615 if (*p == '/')
616 break;
617 *q++ = *p;
618 *q = '\0';
619 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000620 pat = xstrdup(buf); // save copy of pattern
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000621 if (*p == '/')
622 p++;
623 q = char_search(dot, pat, FORWARD, FULL);
624 if (q != NULL) {
625 *addr = count_lines(text, q);
626 }
627 free(pat);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000628#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000629 } else if (*p == '$') { // the last line in file
630 p++;
631 q = begin_line(end - 1);
632 *addr = count_lines(text, q);
633 } else if (isdigit(*p)) { // specific line number
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000634 sscanf(p, "%d%n", addr, &st);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000635 p += st;
636 } else { // I don't reconise this
637 // unrecognised address- assume -1
638 *addr = -1;
639 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000640 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000641}
642
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000643static char *get_address(char *p, int *b, int *e) // get two colon addrs, if present
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000644{
645 //----- get the address' i.e., 1,3 'a,'b -----
646 // get FIRST addr, if present
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000647 while (isblank(*p))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000648 p++; // skip over leading spaces
649 if (*p == '%') { // alias for 1,$
650 p++;
651 *b = 1;
652 *e = count_lines(text, end-1);
653 goto ga0;
654 }
655 p = get_one_address(p, b);
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000656 while (isblank(*p))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000657 p++;
Eric Andersenaff114c2004-04-14 17:51:38 +0000658 if (*p == ',') { // is there a address separator
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000659 p++;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000660 while (isblank(*p))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000661 p++;
662 // get SECOND addr, if present
663 p = get_one_address(p, e);
664 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000665 ga0:
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000666 while (isblank(*p))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000667 p++; // skip over trailing spaces
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000668 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000669}
670
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000671#if ENABLE_FEATURE_VI_SET && ENABLE_FEATURE_VI_SETOPTS
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000672static void setops(const char *args, const char *opname, int flg_no,
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000673 const char *short_opname, int opt)
674{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000675 const char *a = args + flg_no;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000676 int l = strlen(opname) - 1; /* opname have + ' ' */
677
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000678 if (strncasecmp(a, opname, l) == 0
679 || strncasecmp(a, short_opname, 2) == 0
680 ) {
681 if (flg_no)
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000682 vi_setops &= ~opt;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000683 else
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000684 vi_setops |= opt;
685 }
686}
687#endif
688
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000689static void colon(char * buf)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000690{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000691 char c, *orig_buf, *buf1, *q, *r;
Denis Vlasenkoe8a07882007-06-10 15:08:44 +0000692 char *fn, cmd[MAX_LINELEN], args[MAX_LINELEN];
Bernhard Reutner-Fischerd591a362006-08-20 17:35:13 +0000693 int i, l, li, ch, b, e;
Eric Andersena9eb33d2004-08-19 19:15:06 +0000694 int useforce = FALSE, forced = FALSE;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000695
696 // :3154 // if (-e line 3154) goto it else stay put
697 // :4,33w! foo // write a portion of buffer to file "foo"
698 // :w // write all of buffer to current file
699 // :q // quit
700 // :q! // quit- dont care about modified file
701 // :'a,'z!sort -u // filter block through sort
702 // :'f // goto mark "f"
703 // :'fl // list literal the mark "f" line
704 // :.r bar // read file "bar" into buffer before dot
705 // :/123/,/abc/d // delete lines from "123" line to "abc" line
706 // :/xyz/ // goto the "xyz" line
707 // :s/find/replace/ // substitute pattern "find" with "replace"
708 // :!<cmd> // run <cmd> then return
709 //
Eric Andersen165e8cb2004-07-20 06:44:46 +0000710
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000711 if (!buf[0])
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000712 goto vc1;
713 if (*buf == ':')
714 buf++; // move past the ':'
715
Bernhard Reutner-Fischerd591a362006-08-20 17:35:13 +0000716 li = ch = i = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000717 b = e = -1;
718 q = text; // assume 1,$ for the range
719 r = end - 1;
720 li = count_lines(text, end - 1);
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000721 fn = current_filename; // default to current file
Denis Vlasenkoe8a07882007-06-10 15:08:44 +0000722 memset(cmd, '\0', MAX_LINELEN); // clear cmd[]
723 memset(args, '\0', MAX_LINELEN); // clear args[]
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000724
725 // look for optional address(es) :. :1 :1,9 :'q,'a :%
726 buf = get_address(buf, &b, &e);
727
728 // remember orig command line
729 orig_buf = buf;
730
731 // get the COMMAND into cmd[]
732 buf1 = cmd;
733 while (*buf != '\0') {
734 if (isspace(*buf))
735 break;
736 *buf1++ = *buf++;
737 }
738 // get any ARGuments
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000739 while (isblank(*buf))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000740 buf++;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000741 strcpy(args, buf);
742 buf1 = last_char_is(cmd, '!');
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000743 if (buf1) {
744 useforce = TRUE;
745 *buf1 = '\0'; // get rid of !
746 }
747 if (b >= 0) {
748 // if there is only one addr, then the addr
749 // is the line number of the single line the
750 // user wants. So, reset the end
751 // pointer to point at end of the "b" line
752 q = find_line(b); // what line is #b
753 r = end_line(q);
754 li = 1;
755 }
756 if (e >= 0) {
757 // we were given two addrs. change the
758 // end pointer to the addr given by user.
759 r = find_line(e); // what line is #e
760 r = end_line(r);
761 li = e - b + 1;
762 }
763 // ------------ now look for the command ------------
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000764 i = strlen(cmd);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000765 if (i == 0) { // :123CR goto line #123
766 if (b >= 0) {
767 dot = find_line(b); // what line is #b
768 dot_skip_over_ws();
769 }
Denis Vlasenko249fabf2006-12-19 00:29:22 +0000770 }
771#if ENABLE_FEATURE_ALLOW_EXEC
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000772 else if (strncmp(cmd, "!", 1) == 0) { // run a cmd
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000773 int retcode;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000774 // :!ls run the <cmd>
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000775 alarm(0); // wait for input- no alarms
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000776 place_cursor(rows - 1, 0, FALSE); // go to Status line
777 clear_to_eol(); // clear the line
778 cookmode();
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000779 retcode = system(orig_buf + 1); // run the cmd
780 if (retcode)
781 printf("\nshell returned %i\n\n", retcode);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000782 rawmode();
783 Hit_Return(); // let user see results
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000784 alarm(3); // done waiting for input
Denis Vlasenko249fabf2006-12-19 00:29:22 +0000785 }
786#endif
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000787 else if (strncmp(cmd, "=", i) == 0) { // where is the address
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000788 if (b < 0) { // no addr given- use defaults
789 b = e = count_lines(text, dot);
790 }
791 psb("%d", b);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000792 } else if (strncasecmp(cmd, "delete", i) == 0) { // delete lines
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000793 if (b < 0) { // no addr given- use defaults
794 q = begin_line(dot); // assume .,. for the range
795 r = end_line(dot);
796 }
797 dot = yank_delete(q, r, 1, YANKDEL); // save, then delete lines
798 dot_skip_over_ws();
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000799 } else if (strncasecmp(cmd, "edit", i) == 0) { // Edit a file
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000800 // don't edit, if the current file has been modified
801 if (file_modified && ! useforce) {
802 psbs("No write since last change (:edit! overrides)");
803 goto vc1;
804 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000805 if (args[0]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000806 // the user supplied a file name
Denis Vlasenkoe8a07882007-06-10 15:08:44 +0000807 fn = args;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000808 } else if (current_filename && current_filename[0]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000809 // no user supplied name- use the current filename
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000810 // fn = current_filename; was set by default
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000811 } else {
812 // no user file name, no current name- punt
813 psbs("No current filename");
814 goto vc1;
815 }
816
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000817 if (init_text_buffer(fn) < 0)
818 goto vc1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000819
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000820#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000821 if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
822 free(reg[Ureg]); // free orig line reg- for 'U'
823 reg[Ureg]= 0;
824 }
825 if (YDreg >= 0 && YDreg < 28 && reg[YDreg] != 0) {
826 free(reg[YDreg]); // free default yank/delete register
827 reg[YDreg]= 0;
828 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000829#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000830 // how many lines in text[]?
831 li = count_lines(text, end - 1);
832 psb("\"%s\"%s"
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000833 USE_FEATURE_VI_READONLY("%s")
834 " %dL, %dC", current_filename,
835 (file_size(fn) < 0 ? " [New file]" : ""),
836 USE_FEATURE_VI_READONLY(
837 ((readonly_mode) ? " [Readonly]" : ""),
838 )
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000839 li, ch);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000840 } else if (strncasecmp(cmd, "file", i) == 0) { // what File is this
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000841 if (b != -1 || e != -1) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000842 ni("No address allowed on this command");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000843 goto vc1;
844 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000845 if (args[0]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000846 // user wants a new filename
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000847 free(current_filename);
848 current_filename = xstrdup(args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000849 } else {
850 // user wants file status info
Paul Fox8552aec2005-09-16 12:20:05 +0000851 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000852 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000853 } else if (strncasecmp(cmd, "features", i) == 0) { // what features are available
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000854 // print out values of all features
855 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
856 clear_to_eol(); // clear the line
857 cookmode();
858 show_help();
859 rawmode();
860 Hit_Return();
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000861 } else if (strncasecmp(cmd, "list", i) == 0) { // literal print line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000862 if (b < 0) { // no addr given- use defaults
863 q = begin_line(dot); // assume .,. for the range
864 r = end_line(dot);
865 }
866 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
867 clear_to_eol(); // clear the line
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000868 puts("\r");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000869 for (; q <= r; q++) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000870 int c_is_no_print;
871
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000872 c = *q;
Denis Vlasenko2a51af22007-03-21 22:31:24 +0000873 c_is_no_print = (c & 0x80) && !Isprint(c);
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000874 if (c_is_no_print) {
875 c = '.';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000876 standout_start();
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000877 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000878 if (c == '\n') {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000879 write1("$\r");
880 } else if (c < ' ' || c == 127) {
Denis Vlasenko4daad902007-09-27 10:20:47 +0000881 bb_putchar('^');
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000882 if (c == 127)
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000883 c = '?';
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000884 else
885 c += '@';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000886 }
Denis Vlasenko4daad902007-09-27 10:20:47 +0000887 bb_putchar(c);
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000888 if (c_is_no_print)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000889 standout_end();
890 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000891#if ENABLE_FEATURE_VI_SET
892 vc2:
893#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000894 Hit_Return();
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000895 } else if (strncasecmp(cmd, "quit", i) == 0 // Quit
896 || strncasecmp(cmd, "next", i) == 0 // edit next file
897 ) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000898 if (useforce) {
899 // force end of argv list
900 if (*cmd == 'q') {
901 optind = save_argc;
902 }
903 editing = 0;
904 goto vc1;
905 }
906 // don't exit if the file been modified
907 if (file_modified) {
908 psbs("No write since last change (:%s! overrides)",
909 (*cmd == 'q' ? "quit" : "next"));
910 goto vc1;
911 }
912 // are there other file to edit
913 if (*cmd == 'q' && optind < save_argc - 1) {
914 psbs("%d more file to edit", (save_argc - optind - 1));
915 goto vc1;
916 }
917 if (*cmd == 'n' && optind >= save_argc - 1) {
918 psbs("No more files to edit");
919 goto vc1;
920 }
921 editing = 0;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000922 } else if (strncasecmp(cmd, "read", i) == 0) { // read file into text[]
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000923 fn = args;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000924 if (!fn[0]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000925 psbs("No filename given");
926 goto vc1;
927 }
928 if (b < 0) { // no addr given- use defaults
929 q = begin_line(dot); // assume "dot"
930 }
931 // read after current line- unless user said ":0r foo"
932 if (b != 0)
933 q = next_line(q);
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000934 ch = file_insert(fn, q USE_FEATURE_VI_READONLY(, 0));
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000935 if (ch < 0)
936 goto vc1; // nothing was inserted
937 // how many lines in text[]?
938 li = count_lines(q, q + ch - 1);
939 psb("\"%s\""
Denis Vlasenko59a1f302007-07-14 22:43:10 +0000940 USE_FEATURE_VI_READONLY("%s")
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000941 " %dL, %dC", fn,
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000942 USE_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000943 li, ch);
944 if (ch > 0) {
945 // if the insert is before "dot" then we need to update
946 if (q <= dot)
947 dot += ch;
Paul Fox8552aec2005-09-16 12:20:05 +0000948 file_modified++;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000949 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000950 } else if (strncasecmp(cmd, "rewind", i) == 0) { // rewind cmd line args
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000951 if (file_modified && ! useforce) {
952 psbs("No write since last change (:rewind! overrides)");
953 } else {
954 // reset the filenames to edit
955 optind = fn_start - 1;
956 editing = 0;
957 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000958#if ENABLE_FEATURE_VI_SET
Denis Vlasenkof9234132007-03-21 00:03:42 +0000959 } else if (strncasecmp(cmd, "set", i) == 0) { // set or clear features
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000960#if ENABLE_FEATURE_VI_SETOPTS
Denis Vlasenkof9234132007-03-21 00:03:42 +0000961 char *argp;
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000962#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000963 i = 0; // offset into args
Denis Vlasenkof9234132007-03-21 00:03:42 +0000964 // only blank is regarded as args delmiter. What about tab '\t' ?
965 if (!args[0] || strcasecmp(args, "all") == 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000966 // print out values of all options
967 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
968 clear_to_eol(); // clear the line
969 printf("----------------------------------------\r\n");
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000970#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000971 if (!autoindent)
972 printf("no");
973 printf("autoindent ");
974 if (!err_method)
975 printf("no");
976 printf("flash ");
977 if (!ignorecase)
978 printf("no");
979 printf("ignorecase ");
980 if (!showmatch)
981 printf("no");
982 printf("showmatch ");
983 printf("tabstop=%d ", tabstop);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000984#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000985 printf("\r\n");
986 goto vc2;
987 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000988#if ENABLE_FEATURE_VI_SETOPTS
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000989 argp = args;
Denis Vlasenkoba2fb712007-04-01 09:39:03 +0000990 while (*argp) {
Denis Vlasenkof9234132007-03-21 00:03:42 +0000991 if (strncasecmp(argp, "no", 2) == 0)
992 i = 2; // ":set noautoindent"
993 setops(argp, "autoindent ", i, "ai", VI_AUTOINDENT);
994 setops(argp, "flash ", i, "fl", VI_ERR_METHOD);
995 setops(argp, "ignorecase ", i, "ic", VI_IGNORECASE);
996 setops(argp, "showmatch ", i, "ic", VI_SHOWMATCH);
997 /* tabstopXXXX */
998 if (strncasecmp(argp + i, "tabstop=%d ", 7) == 0) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000999 sscanf(strchr(argp + i, '='), "tabstop=%d" + 7, &ch);
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00001000 if (ch > 0 && ch <= MAX_TABSTOP)
Denis Vlasenkof9234132007-03-21 00:03:42 +00001001 tabstop = ch;
1002 }
1003 while (*argp && *argp != ' ')
1004 argp++; // skip to arg delimiter (i.e. blank)
1005 while (*argp && *argp == ' ')
1006 argp++; // skip all delimiting blanks
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001007 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001008#endif /* FEATURE_VI_SETOPTS */
1009#endif /* FEATURE_VI_SET */
1010#if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001011 } else if (strncasecmp(cmd, "s", 1) == 0) { // substitute a pattern with a replacement pattern
1012 char *ls, *F, *R;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001013 int gflag;
1014
1015 // F points to the "find" pattern
1016 // R points to the "replace" pattern
1017 // replace the cmd line delimiters "/" with NULLs
1018 gflag = 0; // global replace flag
1019 c = orig_buf[1]; // what is the delimiter
1020 F = orig_buf + 2; // start of "find"
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001021 R = strchr(F, c); // middle delimiter
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001022 if (!R) goto colon_s_fail;
1023 *R++ = '\0'; // terminate "find"
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001024 buf1 = strchr(R, c);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001025 if (!buf1) goto colon_s_fail;
1026 *buf1++ = '\0'; // terminate "replace"
1027 if (*buf1 == 'g') { // :s/foo/bar/g
1028 buf1++;
1029 gflag++; // turn on gflag
1030 }
1031 q = begin_line(q);
1032 if (b < 0) { // maybe :s/foo/bar/
1033 q = begin_line(dot); // start with cur line
1034 b = count_lines(text, q); // cur line number
1035 }
1036 if (e < 0)
1037 e = b; // maybe :.s/foo/bar/
1038 for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0
1039 ls = q; // orig line start
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001040 vc4:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001041 buf1 = char_search(q, F, FORWARD, LIMITED); // search cur line only for "find"
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001042 if (buf1) {
1043 // we found the "find" pattern - delete it
1044 text_hole_delete(buf1, buf1 + strlen(F) - 1);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001045 // inset the "replace" patern
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001046 string_insert(buf1, R); // insert the string
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001047 // check for "global" :s/foo/bar/g
1048 if (gflag == 1) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001049 if ((buf1 + strlen(R)) < end_line(ls)) {
1050 q = buf1 + strlen(R);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001051 goto vc4; // don't let q move past cur line
1052 }
1053 }
1054 }
1055 q = next_line(ls);
1056 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001057#endif /* FEATURE_VI_SEARCH */
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001058 } else if (strncasecmp(cmd, "version", i) == 0) { // show software version
Rob Landleyd921b2e2006-08-03 15:41:12 +00001059 psb("%s", BB_VER " " BB_BT);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001060 } else if (strncasecmp(cmd, "write", i) == 0 // write text to file
1061 || strncasecmp(cmd, "wq", i) == 0
1062 || strncasecmp(cmd, "wn", i) == 0
1063 || strncasecmp(cmd, "x", i) == 0
1064 ) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001065 // is there a file name to write to?
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001066 if (args[0]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001067 fn = args;
1068 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001069#if ENABLE_FEATURE_VI_READONLY
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001070 if (readonly_mode && !useforce) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001071 psbs("\"%s\" File is read only", fn);
1072 goto vc3;
1073 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001074#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001075 // how many lines in text[]?
1076 li = count_lines(q, r);
1077 ch = r - q + 1;
1078 // see if file exists- if not, its just a new file request
1079 if (useforce) {
1080 // if "fn" is not write-able, chmod u+w
1081 // sprintf(syscmd, "chmod u+w %s", fn);
1082 // system(syscmd);
1083 forced = TRUE;
1084 }
1085 l = file_write(fn, q, r);
1086 if (useforce && forced) {
1087 // chmod u-w
1088 // sprintf(syscmd, "chmod u-w %s", fn);
1089 // system(syscmd);
1090 forced = FALSE;
1091 }
Paul Fox61e45db2005-10-09 14:43:22 +00001092 if (l < 0) {
1093 if (l == -1)
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001094 psbs("\"%s\" %s", fn, strerror(errno));
Paul Fox61e45db2005-10-09 14:43:22 +00001095 } else {
1096 psb("\"%s\" %dL, %dC", fn, li, l);
1097 if (q == text && r == end - 1 && l == ch) {
1098 file_modified = 0;
1099 last_file_modified = -1;
1100 }
Paul Fox9360f422006-03-27 21:51:16 +00001101 if ((cmd[0] == 'x' || cmd[1] == 'q' || cmd[1] == 'n' ||
1102 cmd[0] == 'X' || cmd[1] == 'Q' || cmd[1] == 'N')
1103 && l == ch) {
Paul Fox61e45db2005-10-09 14:43:22 +00001104 editing = 0;
1105 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001106 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001107#if ENABLE_FEATURE_VI_READONLY
1108 vc3:;
1109#endif
1110#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001111 } else if (strncasecmp(cmd, "yank", i) == 0) { // yank lines
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001112 if (b < 0) { // no addr given- use defaults
1113 q = begin_line(dot); // assume .,. for the range
1114 r = end_line(dot);
1115 }
1116 text_yank(q, r, YDreg);
1117 li = count_lines(q, r);
1118 psb("Yank %d lines (%d chars) into [%c]",
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001119 li, strlen(reg[YDreg]), what_reg());
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001120#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001121 } else {
1122 // cmd unknown
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001123 ni(cmd);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001124 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001125 vc1:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001126 dot = bound_dot(dot); // make sure "dot" is valid
1127 return;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001128#if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001129 colon_s_fail:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001130 psb(":s expression missing delimiters");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001131#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001132}
Paul Fox61e45db2005-10-09 14:43:22 +00001133
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001134#endif /* FEATURE_VI_COLON */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001135
1136static void Hit_Return(void)
1137{
1138 char c;
1139
1140 standout_start(); // start reverse video
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001141 write1("[Hit return to continue]");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001142 standout_end(); // end reverse video
1143 while ((c = get_one_char()) != '\n' && c != '\r') /*do nothing */
1144 ;
1145 redraw(TRUE); // force redraw all
1146}
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001147
Denis Vlasenko91afdf82007-07-17 23:22:49 +00001148static int next_tabstop(int col)
1149{
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001150 return col + ((tabstop - 1) - (col % tabstop));
1151}
1152
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001153//----- Synchronize the cursor to Dot --------------------------
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001154static void sync_cursor(char * d, int *row, int *col)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001155{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001156 char *beg_cur; // begin and end of "d" line
1157 char *end_scr; // begin and end of screen
1158 char *tp;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001159 int cnt, ro, co;
1160
1161 beg_cur = begin_line(d); // first char of cur line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001162
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001163 end_scr = end_screen(); // last char of screen
1164
1165 if (beg_cur < screenbegin) {
1166 // "d" is before top line on screen
1167 // how many lines do we have to move
1168 cnt = count_lines(beg_cur, screenbegin);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001169 sc1:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001170 screenbegin = beg_cur;
1171 if (cnt > (rows - 1) / 2) {
1172 // we moved too many lines. put "dot" in middle of screen
1173 for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
1174 screenbegin = prev_line(screenbegin);
1175 }
1176 }
1177 } else if (beg_cur > end_scr) {
1178 // "d" is after bottom line on screen
1179 // how many lines do we have to move
1180 cnt = count_lines(end_scr, beg_cur);
1181 if (cnt > (rows - 1) / 2)
1182 goto sc1; // too many lines
1183 for (ro = 0; ro < cnt - 1; ro++) {
1184 // move screen begin the same amount
1185 screenbegin = next_line(screenbegin);
1186 // now, move the end of screen
1187 end_scr = next_line(end_scr);
1188 end_scr = end_line(end_scr);
1189 }
1190 }
1191 // "d" is on screen- find out which row
1192 tp = screenbegin;
1193 for (ro = 0; ro < rows - 1; ro++) { // drive "ro" to correct row
1194 if (tp == beg_cur)
1195 break;
1196 tp = next_line(tp);
1197 }
1198
1199 // find out what col "d" is on
1200 co = 0;
1201 do { // drive "co" to correct column
1202 if (*tp == '\n' || *tp == '\0')
1203 break;
1204 if (*tp == '\t') {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001205 if (d == tp && cmd_mode) { /* handle tabs like real vi */
1206 break;
1207 } else {
1208 co = next_tabstop(co);
1209 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001210 } else if (*tp < ' ' || *tp == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001211 co++; // display as ^X, use 2 columns
1212 }
1213 } while (tp++ < d && ++co);
1214
1215 // "co" is the column where "dot" is.
1216 // The screen has "columns" columns.
1217 // The currently displayed columns are 0+offset -- columns+ofset
1218 // |-------------------------------------------------------------|
1219 // ^ ^ ^
1220 // offset | |------- columns ----------------|
1221 //
1222 // If "co" is already in this range then we do not have to adjust offset
1223 // but, we do have to subtract the "offset" bias from "co".
1224 // If "co" is outside this range then we have to change "offset".
1225 // If the first char of a line is a tab the cursor will try to stay
1226 // in column 7, but we have to set offset to 0.
1227
1228 if (co < 0 + offset) {
1229 offset = co;
1230 }
1231 if (co >= columns + offset) {
1232 offset = co - columns + 1;
1233 }
1234 // if the first char of the line is a tab, and "dot" is sitting on it
1235 // force offset to 0.
1236 if (d == beg_cur && *d == '\t') {
1237 offset = 0;
1238 }
1239 co -= offset;
1240
1241 *row = ro;
1242 *col = co;
1243}
1244
1245//----- Text Movement Routines ---------------------------------
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001246static char *begin_line(char * p) // return pointer to first char cur line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001247{
1248 while (p > text && p[-1] != '\n')
1249 p--; // go to cur line B-o-l
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001250 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001251}
1252
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001253static char *end_line(char * p) // return pointer to NL of cur line line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001254{
1255 while (p < end - 1 && *p != '\n')
1256 p++; // go to cur line E-o-l
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001257 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001258}
1259
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00001260static char *dollar_line(char * p) // return pointer to just before NL line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001261{
1262 while (p < end - 1 && *p != '\n')
1263 p++; // go to cur line E-o-l
1264 // Try to stay off of the Newline
1265 if (*p == '\n' && (p - begin_line(p)) > 0)
1266 p--;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001267 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001268}
1269
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001270static char *prev_line(char * p) // return pointer first char prev line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001271{
1272 p = begin_line(p); // goto begining of cur line
1273 if (p[-1] == '\n' && p > text)
1274 p--; // step to prev line
1275 p = begin_line(p); // goto begining of prev line
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001276 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001277}
1278
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001279static char *next_line(char * p) // return pointer first char next line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001280{
1281 p = end_line(p);
1282 if (*p == '\n' && p < end - 1)
1283 p++; // step to next line
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001284 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001285}
1286
1287//----- Text Information Routines ------------------------------
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001288static char *end_screen(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001289{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001290 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001291 int cnt;
1292
1293 // find new bottom line
1294 q = screenbegin;
1295 for (cnt = 0; cnt < rows - 2; cnt++)
1296 q = next_line(q);
1297 q = end_line(q);
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001298 return q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001299}
1300
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001301static int count_lines(char * start, char * stop) // count line from start to stop
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001302{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001303 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001304 int cnt;
1305
1306 if (stop < start) { // start and stop are backwards- reverse them
1307 q = start;
1308 start = stop;
1309 stop = q;
1310 }
1311 cnt = 0;
1312 stop = end_line(stop); // get to end of this line
1313 for (q = start; q <= stop && q <= end - 1; q++) {
1314 if (*q == '\n')
1315 cnt++;
1316 }
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001317 return cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001318}
1319
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001320static char *find_line(int li) // find begining of line #li
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001321{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001322 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001323
1324 for (q = text; li > 1; li--) {
1325 q = next_line(q);
1326 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001327 return q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001328}
1329
1330//----- Dot Movement Routines ----------------------------------
1331static void dot_left(void)
1332{
1333 if (dot > text && dot[-1] != '\n')
1334 dot--;
1335}
1336
1337static void dot_right(void)
1338{
1339 if (dot < end - 1 && *dot != '\n')
1340 dot++;
1341}
1342
1343static void dot_begin(void)
1344{
1345 dot = begin_line(dot); // return pointer to first char cur line
1346}
1347
1348static void dot_end(void)
1349{
1350 dot = end_line(dot); // return pointer to last char cur line
1351}
1352
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001353static char *move_to_col(char * p, int l)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001354{
1355 int co;
1356
1357 p = begin_line(p);
1358 co = 0;
1359 do {
1360 if (*p == '\n' || *p == '\0')
1361 break;
1362 if (*p == '\t') {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001363 co = next_tabstop(co);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001364 } else if (*p < ' ' || *p == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001365 co++; // display as ^X, use 2 columns
1366 }
1367 } while (++co <= l && p++ < end);
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001368 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001369}
1370
1371static void dot_next(void)
1372{
1373 dot = next_line(dot);
1374}
1375
1376static void dot_prev(void)
1377{
1378 dot = prev_line(dot);
1379}
1380
1381static void dot_scroll(int cnt, int dir)
1382{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001383 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001384
1385 for (; cnt > 0; cnt--) {
1386 if (dir < 0) {
1387 // scroll Backwards
1388 // ctrl-Y scroll up one line
1389 screenbegin = prev_line(screenbegin);
1390 } else {
1391 // scroll Forwards
1392 // ctrl-E scroll down one line
1393 screenbegin = next_line(screenbegin);
1394 }
1395 }
1396 // make sure "dot" stays on the screen so we dont scroll off
1397 if (dot < screenbegin)
1398 dot = screenbegin;
1399 q = end_screen(); // find new bottom line
1400 if (dot > q)
1401 dot = begin_line(q); // is dot is below bottom line?
1402 dot_skip_over_ws();
1403}
1404
1405static void dot_skip_over_ws(void)
1406{
1407 // skip WS
1408 while (isspace(*dot) && *dot != '\n' && dot < end - 1)
1409 dot++;
1410}
1411
1412static void dot_delete(void) // delete the char at 'dot'
1413{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001414 text_hole_delete(dot, dot);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001415}
1416
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001417static char *bound_dot(char * p) // make sure text[0] <= P < "end"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001418{
1419 if (p >= end && end > text) {
1420 p = end - 1;
1421 indicate_error('1');
1422 }
1423 if (p < text) {
1424 p = text;
1425 indicate_error('2');
1426 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001427 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001428}
1429
1430//----- Helper Utility Routines --------------------------------
1431
1432//----------------------------------------------------------------
1433//----- Char Routines --------------------------------------------
1434/* Chars that are part of a word-
1435 * 0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
1436 * Chars that are Not part of a word (stoppers)
1437 * !"#$%&'()*+,-./:;<=>?@[\]^`{|}~
1438 * Chars that are WhiteSpace
1439 * TAB NEWLINE VT FF RETURN SPACE
1440 * DO NOT COUNT NEWLINE AS WHITESPACE
1441 */
1442
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001443static char *new_screen(int ro, int co)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001444{
1445 int li;
1446
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001447 free(screen);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001448 screensize = ro * co + 8;
Denis Vlasenkob95636c2006-12-19 23:36:04 +00001449 screen = xmalloc(screensize);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001450 // initialize the new screen. assume this will be a empty file.
1451 screen_erase();
Eric Andersenaff114c2004-04-14 17:51:38 +00001452 // non-existent text[] lines start with a tilde (~).
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001453 for (li = 1; li < ro - 1; li++) {
1454 screen[(li * co) + 0] = '~';
1455 }
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001456 return screen;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001457}
1458
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001459#if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001460static int mycmp(const char * s1, const char * s2, int len)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001461{
1462 int i;
1463
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001464 i = strncmp(s1, s2, len);
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001465 if (ENABLE_FEATURE_VI_SETOPTS && ignorecase) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001466 i = strncasecmp(s1, s2, len);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001467 }
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001468 return i;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001469}
1470
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001471// search for pattern starting at p
1472static char *char_search(char * p, const char * pat, int dir, int range)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001473{
1474#ifndef REGEX_SEARCH
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001475 char *start, *stop;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001476 int len;
1477
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001478 len = strlen(pat);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001479 if (dir == FORWARD) {
1480 stop = end - 1; // assume range is p - end-1
1481 if (range == LIMITED)
1482 stop = next_line(p); // range is to next line
1483 for (start = p; start < stop; start++) {
1484 if (mycmp(start, pat, len) == 0) {
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001485 return start;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001486 }
1487 }
1488 } else if (dir == BACK) {
1489 stop = text; // assume range is text - p
1490 if (range == LIMITED)
1491 stop = prev_line(p); // range is to prev line
1492 for (start = p - len; start >= stop; start--) {
1493 if (mycmp(start, pat, len) == 0) {
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001494 return start;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001495 }
1496 }
1497 }
1498 // pattern not found
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001499 return NULL;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001500#else /* REGEX_SEARCH */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001501 char *q;
1502 struct re_pattern_buffer preg;
1503 int i;
1504 int size, range;
1505
1506 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
1507 preg.translate = 0;
1508 preg.fastmap = 0;
1509 preg.buffer = 0;
1510 preg.allocated = 0;
1511
1512 // assume a LIMITED forward search
1513 q = next_line(p);
1514 q = end_line(q);
1515 q = end - 1;
1516 if (dir == BACK) {
1517 q = prev_line(p);
1518 q = text;
1519 }
1520 // count the number of chars to search over, forward or backward
1521 size = q - p;
1522 if (size < 0)
1523 size = p - q;
1524 // RANGE could be negative if we are searching backwards
1525 range = q - p;
1526
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001527 q = re_compile_pattern(pat, strlen(pat), &preg);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001528 if (q != 0) {
1529 // The pattern was not compiled
1530 psbs("bad search pattern: \"%s\": %s", pat, q);
1531 i = 0; // return p if pattern not compiled
1532 goto cs1;
1533 }
1534
1535 q = p;
1536 if (range < 0) {
1537 q = p - size;
1538 if (q < text)
1539 q = text;
1540 }
1541 // search for the compiled pattern, preg, in p[]
1542 // range < 0- search backward
1543 // range > 0- search forward
1544 // 0 < start < size
1545 // re_search() < 0 not found or error
1546 // re_search() > 0 index of found pattern
1547 // struct pattern char int int int struct reg
1548 // re_search (*pattern_buffer, *string, size, start, range, *regs)
1549 i = re_search(&preg, q, size, 0, range, 0);
1550 if (i == -1) {
1551 p = 0;
1552 i = 0; // return NULL if pattern not found
1553 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001554 cs1:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001555 if (dir == FORWARD) {
1556 p = p + i;
1557 } else {
1558 p = p - i;
1559 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001560 return p;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001561#endif /* REGEX_SEARCH */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001562}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001563#endif /* FEATURE_VI_SEARCH */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001564
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001565static char *char_insert(char * p, char c) // insert the char c at 'p'
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001566{
1567 if (c == 22) { // Is this an ctrl-V?
1568 p = stupid_insert(p, '^'); // use ^ to indicate literal next
1569 p--; // backup onto ^
1570 refresh(FALSE); // show the ^
1571 c = get_one_char();
1572 *p = c;
1573 p++;
Paul Fox8552aec2005-09-16 12:20:05 +00001574 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001575 } else if (c == 27) { // Is this an ESC?
1576 cmd_mode = 0;
1577 cmdcnt = 0;
1578 end_cmd_q(); // stop adding to q
Paul Fox8552aec2005-09-16 12:20:05 +00001579 last_status_cksum = 0; // force status update
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001580 if ((p[-1] != '\n') && (dot > text)) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001581 p--;
1582 }
Paul Foxd13b90b2005-07-18 22:17:25 +00001583 } else if (c == erase_char || c == 8 || c == 127) { // Is this a BS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001584 // 123456789
1585 if ((p[-1] != '\n') && (dot>text)) {
1586 p--;
1587 p = text_hole_delete(p, p); // shrink buffer 1 char
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001588 }
1589 } else {
1590 // insert a char into text[]
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001591 char *sp; // "save p"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001592
1593 if (c == 13)
1594 c = '\n'; // translate \r to \n
1595 sp = p; // remember addr of insert
1596 p = stupid_insert(p, c); // insert the char
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001597#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001598 if (showmatch && strchr(")]}", *sp) != NULL) {
1599 showmatching(sp);
1600 }
1601 if (autoindent && c == '\n') { // auto indent the new line
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001602 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001603
1604 q = prev_line(p); // use prev line as templet
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001605 for (; isblank(*q); q++) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001606 p = stupid_insert(p, *q); // insert the char
1607 }
1608 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001609#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001610 }
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 *stupid_insert(char * p, char c) // stupidly insert the char c at 'p'
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001615{
1616 p = text_hole_make(p, 1);
1617 if (p != 0) {
1618 *p = c;
Paul Fox8552aec2005-09-16 12:20:05 +00001619 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001620 p++;
1621 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001622 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001623}
1624
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001625static char find_range(char ** start, char ** stop, char c)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001626{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001627 char *save_dot, *p, *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001628 int cnt;
1629
1630 save_dot = dot;
1631 p = q = dot;
1632
1633 if (strchr("cdy><", c)) {
1634 // these cmds operate on whole lines
1635 p = q = begin_line(p);
1636 for (cnt = 1; cnt < cmdcnt; cnt++) {
1637 q = next_line(q);
1638 }
1639 q = end_line(q);
1640 } else if (strchr("^%$0bBeEft", c)) {
1641 // These cmds operate on char positions
1642 do_cmd(c); // execute movement cmd
1643 q = dot;
1644 } else if (strchr("wW", c)) {
1645 do_cmd(c); // execute movement cmd
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001646 // if we are at the next word's first char
1647 // step back one char
1648 // but check the possibilities when it is true
Eric Andersen5cc90ea2004-02-06 10:36:08 +00001649 if (dot > text && ((isspace(dot[-1]) && !isspace(dot[0]))
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001650 || (ispunct(dot[-1]) && !ispunct(dot[0]))
1651 || (isalnum(dot[-1]) && !isalnum(dot[0]))))
1652 dot--; // move back off of next word
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001653 if (dot > text && *dot == '\n')
1654 dot--; // stay off NL
1655 q = dot;
1656 } else if (strchr("H-k{", c)) {
1657 // these operate on multi-lines backwards
1658 q = end_line(dot); // find NL
1659 do_cmd(c); // execute movement cmd
1660 dot_begin();
1661 p = dot;
1662 } else if (strchr("L+j}\r\n", c)) {
1663 // these operate on multi-lines forwards
1664 p = begin_line(dot);
1665 do_cmd(c); // execute movement cmd
1666 dot_end(); // find NL
1667 q = dot;
1668 } else {
1669 c = 27; // error- return an ESC char
1670 //break;
1671 }
1672 *start = p;
1673 *stop = q;
1674 if (q < p) {
1675 *start = q;
1676 *stop = p;
1677 }
1678 dot = save_dot;
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001679 return c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001680}
1681
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001682static int st_test(char * p, int type, int dir, char * tested)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001683{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001684 char c, c0, ci;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001685 int test, inc;
1686
1687 inc = dir;
1688 c = c0 = p[0];
1689 ci = p[inc];
1690 test = 0;
1691
1692 if (type == S_BEFORE_WS) {
1693 c = ci;
1694 test = ((!isspace(c)) || c == '\n');
1695 }
1696 if (type == S_TO_WS) {
1697 c = c0;
1698 test = ((!isspace(c)) || c == '\n');
1699 }
1700 if (type == S_OVER_WS) {
1701 c = c0;
1702 test = ((isspace(c)));
1703 }
1704 if (type == S_END_PUNCT) {
1705 c = ci;
1706 test = ((ispunct(c)));
1707 }
1708 if (type == S_END_ALNUM) {
1709 c = ci;
1710 test = ((isalnum(c)) || c == '_');
1711 }
1712 *tested = c;
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001713 return test;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001714}
1715
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001716static char *skip_thing(char * p, int linecnt, int dir, int type)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001717{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001718 char c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001719
1720 while (st_test(p, type, dir, &c)) {
1721 // make sure we limit search to correct number of lines
1722 if (c == '\n' && --linecnt < 1)
1723 break;
1724 if (dir >= 0 && p >= end - 1)
1725 break;
1726 if (dir < 0 && p <= text)
1727 break;
1728 p += dir; // move to next char
1729 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001730 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001731}
1732
1733// find matching char of pair () [] {}
Bernhard Reutner-Fischerd24d5c82007-10-01 18:04:42 +00001734static char *find_pair(char * p, const char c)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001735{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001736 char match, *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001737 int dir, level;
1738
1739 match = ')';
1740 level = 1;
1741 dir = 1; // assume forward
1742 switch (c) {
1743 case '(':
1744 match = ')';
1745 break;
1746 case '[':
1747 match = ']';
1748 break;
1749 case '{':
1750 match = '}';
1751 break;
1752 case ')':
1753 match = '(';
1754 dir = -1;
1755 break;
1756 case ']':
1757 match = '[';
1758 dir = -1;
1759 break;
1760 case '}':
1761 match = '{';
1762 dir = -1;
1763 break;
1764 }
1765 for (q = p + dir; text <= q && q < end; q += dir) {
1766 // look for match, count levels of pairs (( ))
1767 if (*q == c)
1768 level++; // increase pair levels
1769 if (*q == match)
1770 level--; // reduce pair level
1771 if (level == 0)
1772 break; // found matching pair
1773 }
1774 if (level != 0)
1775 q = NULL; // indicate no match
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001776 return q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001777}
1778
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001779#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001780// show the matching char of a pair, () [] {}
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001781static void showmatching(char * p)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001782{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001783 char *q, *save_dot;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001784
1785 // we found half of a pair
1786 q = find_pair(p, *p); // get loc of matching char
1787 if (q == NULL) {
1788 indicate_error('3'); // no matching char
1789 } else {
1790 // "q" now points to matching pair
1791 save_dot = dot; // remember where we are
1792 dot = q; // go to new loc
1793 refresh(FALSE); // let the user see it
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001794 mysleep(40); // give user some time
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001795 dot = save_dot; // go back to old loc
1796 refresh(FALSE);
1797 }
1798}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001799#endif /* FEATURE_VI_SETOPTS */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001800
1801// open a hole in text[]
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001802static char *text_hole_make(char * p, int size) // at "p", make a 'size' byte hole
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001803{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001804 char *src, *dest;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001805 int cnt;
1806
1807 if (size <= 0)
1808 goto thm0;
1809 src = p;
1810 dest = p + size;
1811 cnt = end - src; // the rest of buffer
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001812 if ( ((end + size) >= (text + text_size)) // TODO: realloc here
1813 || memmove(dest, src, cnt) != dest) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001814 psbs("can't create room for new characters");
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001815 p = NULL;
1816 goto thm0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001817 }
1818 memset(p, ' ', size); // clear new hole
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001819 end += size; // adjust the new END
Paul Fox8552aec2005-09-16 12:20:05 +00001820 file_modified++; // has the file been modified
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001821 thm0:
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001822 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001823}
1824
1825// close a hole in text[]
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001826static char *text_hole_delete(char * p, char * q) // delete "p" thru "q", inclusive
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001827{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001828 char *src, *dest;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001829 int cnt, hole_size;
1830
1831 // move forwards, from beginning
1832 // assume p <= q
1833 src = q + 1;
1834 dest = p;
1835 if (q < p) { // they are backward- swap them
1836 src = p + 1;
1837 dest = q;
1838 }
1839 hole_size = q - p + 1;
1840 cnt = end - src;
1841 if (src < text || src > end)
1842 goto thd0;
1843 if (dest < text || dest >= end)
1844 goto thd0;
1845 if (src >= end)
1846 goto thd_atend; // just delete the end of the buffer
1847 if (memmove(dest, src, cnt) != dest) {
1848 psbs("can't delete the character");
1849 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001850 thd_atend:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001851 end = end - hole_size; // adjust the new END
1852 if (dest >= end)
1853 dest = end - 1; // make sure dest in below end-1
1854 if (end <= text)
1855 dest = end = text; // keep pointers valid
Paul Fox8552aec2005-09-16 12:20:05 +00001856 file_modified++; // has the file been modified
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001857 thd0:
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001858 return dest;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001859}
1860
1861// copy text into register, then delete text.
1862// if dist <= 0, do not include, or go past, a NewLine
1863//
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001864static char *yank_delete(char * start, char * stop, int dist, int yf)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001865{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001866 char *p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001867
1868 // make sure start <= stop
1869 if (start > stop) {
1870 // they are backwards, reverse them
1871 p = start;
1872 start = stop;
1873 stop = p;
1874 }
1875 if (dist <= 0) {
Denis Vlasenkoe1a0d482006-10-20 13:28:22 +00001876 // we cannot cross NL boundaries
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001877 p = start;
1878 if (*p == '\n')
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001879 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001880 // dont go past a NewLine
1881 for (; p + 1 <= stop; p++) {
1882 if (p[1] == '\n') {
1883 stop = p; // "stop" just before NewLine
1884 break;
1885 }
1886 }
1887 }
1888 p = start;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001889#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001890 text_yank(start, stop, YDreg);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001891#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001892 if (yf == YANKDEL) {
1893 p = text_hole_delete(start, stop);
1894 } // delete lines
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001895 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001896}
1897
1898static void show_help(void)
1899{
1900 puts("These features are available:"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001901#if ENABLE_FEATURE_VI_SEARCH
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001902 "\n\tPattern searches with / and ?"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001903#endif
1904#if ENABLE_FEATURE_VI_DOT_CMD
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001905 "\n\tLast command repeat with \'.\'"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001906#endif
1907#if ENABLE_FEATURE_VI_YANKMARK
Bernhard Reutner-Fischerd24d5c82007-10-01 18:04:42 +00001908 "\n\tLine marking with 'x"
1909 "\n\tNamed buffers with \"x"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001910#endif
1911#if ENABLE_FEATURE_VI_READONLY
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001912 "\n\tReadonly if vi is called as \"view\""
1913 "\n\tReadonly with -R command line arg"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001914#endif
1915#if ENABLE_FEATURE_VI_SET
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001916 "\n\tSome colon mode commands with \':\'"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001917#endif
1918#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001919 "\n\tSettable options with \":set\""
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001920#endif
1921#if ENABLE_FEATURE_VI_USE_SIGNALS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001922 "\n\tSignal catching- ^C"
1923 "\n\tJob suspend and resume with ^Z"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001924#endif
1925#if ENABLE_FEATURE_VI_WIN_RESIZE
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001926 "\n\tAdapt to window re-sizes"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001927#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001928 );
1929}
1930
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00001931static void print_literal(char * buf, const char * s) // copy s to buf, convert unprintable
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001932{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001933 unsigned char c;
1934 char b[2];
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001935
1936 b[1] = '\0';
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001937 buf[0] = '\0';
1938 if (!s[0])
1939 s = "(NULL)";
1940 for (; *s; s++) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001941 int c_is_no_print;
1942
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001943 c = *s;
Denis Vlasenko2a51af22007-03-21 22:31:24 +00001944 c_is_no_print = (c & 0x80) && !Isprint(c);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001945 if (c_is_no_print) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001946 strcat(buf, SOn);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001947 c = '.';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001948 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001949 if (c < ' ' || c == 127) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001950 strcat(buf, "^");
1951 if (c == 127)
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001952 c = '?';
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001953 else
1954 c += '@';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001955 }
1956 b[0] = c;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001957 strcat(buf, b);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001958 if (c_is_no_print)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001959 strcat(buf, SOs);
1960 if (*s == '\n')
1961 strcat(buf, "$");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001962 }
1963}
1964
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001965#if ENABLE_FEATURE_VI_DOT_CMD
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001966static void start_new_cmd_q(char c)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001967{
1968 // release old cmd
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001969 free(last_modifying_cmd);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001970 // get buffer for new cmd
Denis Vlasenkoe8a07882007-06-10 15:08:44 +00001971 last_modifying_cmd = xzalloc(MAX_LINELEN);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001972 // if there is a current cmd count put it in the buffer first
1973 if (cmdcnt > 0)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001974 sprintf(last_modifying_cmd, "%d%c", cmdcnt, c);
Paul Foxd957b952005-11-28 18:07:53 +00001975 else // just save char c onto queue
1976 last_modifying_cmd[0] = c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001977 adding2q = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001978}
1979
1980static void end_cmd_q(void)
1981{
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001982#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001983 YDreg = 26; // go back to default Yank/Delete reg
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001984#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001985 adding2q = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001986}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001987#endif /* FEATURE_VI_DOT_CMD */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001988
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001989#if ENABLE_FEATURE_VI_YANKMARK \
1990 || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) \
1991 || ENABLE_FEATURE_VI_CRASHME
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001992static char *string_insert(char * p, char * s) // insert the string at 'p'
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001993{
1994 int cnt, i;
1995
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001996 i = strlen(s);
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001997 if (text_hole_make(p, i)) {
1998 strncpy(p, s, i);
1999 for (cnt = 0; *s != '\0'; s++) {
2000 if (*s == '\n')
2001 cnt++;
2002 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002003#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002004 psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002005#endif
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002006 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002007 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002008}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002009#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002010
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002011#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002012static char *text_yank(char * p, char * q, int dest) // copy text into a register
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002013{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002014 char *t;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002015 int cnt;
2016
2017 if (q < p) { // they are backwards- reverse them
2018 t = q;
2019 q = p;
2020 p = t;
2021 }
2022 cnt = q - p + 1;
2023 t = reg[dest];
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002024 free(t); // if already a yank register, free it
Denis Vlasenkob95636c2006-12-19 23:36:04 +00002025 t = xmalloc(cnt + 1); // get a new register
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002026 memset(t, '\0', cnt + 1); // clear new text[]
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002027 strncpy(t, p, cnt); // copy text[] into bufer
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002028 reg[dest] = t;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002029 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002030}
2031
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002032static char what_reg(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002033{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002034 char c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002035
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002036 c = 'D'; // default to D-reg
2037 if (0 <= YDreg && YDreg <= 25)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002038 c = 'a' + (char) YDreg;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002039 if (YDreg == 26)
2040 c = 'D';
2041 if (YDreg == 27)
2042 c = 'U';
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002043 return c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002044}
2045
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002046static void check_context(char cmd)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002047{
2048 // A context is defined to be "modifying text"
2049 // Any modifying command establishes a new context.
2050
2051 if (dot < context_start || dot > context_end) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002052 if (strchr(modifying_cmds, cmd) != NULL) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002053 // we are trying to modify text[]- make this the current context
2054 mark[27] = mark[26]; // move cur to prev
2055 mark[26] = dot; // move local to cur
2056 context_start = prev_line(prev_line(dot));
2057 context_end = next_line(next_line(dot));
2058 //loiter= start_loiter= now;
2059 }
2060 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002061}
2062
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002063static char *swap_context(char * p) // goto new context for '' command make this the current context
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002064{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002065 char *tmp;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002066
2067 // the current context is in mark[26]
2068 // the previous context is in mark[27]
2069 // only swap context if other context is valid
2070 if (text <= mark[27] && mark[27] <= end - 1) {
2071 tmp = mark[27];
2072 mark[27] = mark[26];
2073 mark[26] = tmp;
2074 p = mark[26]; // where we are going- previous context
2075 context_start = prev_line(prev_line(prev_line(p)));
2076 context_end = next_line(next_line(next_line(p)));
2077 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002078 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002079}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002080#endif /* FEATURE_VI_YANKMARK */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002081
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002082//----- Set terminal attributes --------------------------------
2083static void rawmode(void)
2084{
2085 tcgetattr(0, &term_orig);
2086 term_vi = term_orig;
2087 term_vi.c_lflag &= (~ICANON & ~ECHO); // leave ISIG ON- allow intr's
2088 term_vi.c_iflag &= (~IXON & ~ICRNL);
2089 term_vi.c_oflag &= (~ONLCR);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002090 term_vi.c_cc[VMIN] = 1;
2091 term_vi.c_cc[VTIME] = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002092 erase_char = term_vi.c_cc[VERASE];
2093 tcsetattr(0, TCSANOW, &term_vi);
2094}
2095
2096static void cookmode(void)
2097{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002098 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002099 tcsetattr(0, TCSANOW, &term_orig);
2100}
2101
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002102//----- Come here when we get a window resize signal ---------
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002103#if ENABLE_FEATURE_VI_USE_SIGNALS
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002104static void winch_sig(int sig ATTRIBUTE_UNUSED)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002105{
2106 signal(SIGWINCH, winch_sig);
Rob Landleye5e1a102006-06-21 01:15:36 +00002107 if (ENABLE_FEATURE_VI_WIN_RESIZE)
Denis Vlasenko621204b2006-10-27 09:03:24 +00002108 get_terminal_width_height(0, &columns, &rows);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002109 new_screen(rows, columns); // get memory for virtual screen
2110 redraw(TRUE); // re-draw the screen
2111}
2112
2113//----- Come here when we get a continue signal -------------------
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002114static void cont_sig(int sig ATTRIBUTE_UNUSED)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002115{
2116 rawmode(); // terminal to "raw"
Paul Fox8552aec2005-09-16 12:20:05 +00002117 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002118 redraw(TRUE); // re-draw the screen
2119
2120 signal(SIGTSTP, suspend_sig);
2121 signal(SIGCONT, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002122 kill(my_pid, SIGCONT);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002123}
2124
2125//----- Come here when we get a Suspend signal -------------------
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002126static void suspend_sig(int sig ATTRIBUTE_UNUSED)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002127{
2128 place_cursor(rows - 1, 0, FALSE); // go to bottom of screen
2129 clear_to_eol(); // Erase to end of line
2130 cookmode(); // terminal to "cooked"
2131
2132 signal(SIGCONT, cont_sig);
2133 signal(SIGTSTP, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002134 kill(my_pid, SIGTSTP);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002135}
2136
2137//----- Come here when we get a signal ---------------------------
2138static void catch_sig(int sig)
2139{
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002140 signal(SIGINT, catch_sig);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002141 if (sig)
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002142 longjmp(restart, sig);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002143}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002144#endif /* FEATURE_VI_USE_SIGNALS */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002145
2146static int mysleep(int hund) // sleep for 'h' 1/100 seconds
2147{
Denis Vlasenko87f3b262007-09-07 13:43:28 +00002148 struct pollfd pfd[1];
Denis Vlasenkocd5c7862007-05-17 16:37:22 +00002149
Denis Vlasenko87f3b262007-09-07 13:43:28 +00002150 pfd[0].fd = 0;
2151 pfd[0].events = POLLIN;
Denis Vlasenko5d61e712007-09-27 10:09:59 +00002152 return safe_poll(pfd, 1, hund*10) > 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002153}
2154
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002155static int chars_to_parse;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002156
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002157//----- IO Routines --------------------------------------------
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002158static char readit(void) // read (maybe cursor) key from stdin
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002159{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002160 char c;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002161 int n;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002162 struct esc_cmds {
Denis Vlasenko4f95e5a2007-10-11 10:10:15 +00002163 const char seq[4];
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002164 char val;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002165 };
2166
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002167 static const struct esc_cmds esccmds[] = {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002168 {"OA", VI_K_UP}, // cursor key Up
2169 {"OB", VI_K_DOWN}, // cursor key Down
2170 {"OC", VI_K_RIGHT}, // Cursor Key Right
2171 {"OD", VI_K_LEFT}, // cursor key Left
2172 {"OH", VI_K_HOME}, // Cursor Key Home
2173 {"OF", VI_K_END}, // Cursor Key End
2174 {"[A", VI_K_UP}, // cursor key Up
2175 {"[B", VI_K_DOWN}, // cursor key Down
2176 {"[C", VI_K_RIGHT}, // Cursor Key Right
2177 {"[D", VI_K_LEFT}, // cursor key Left
2178 {"[H", VI_K_HOME}, // Cursor Key Home
2179 {"[F", VI_K_END}, // Cursor Key End
2180 {"[1~", VI_K_HOME}, // Cursor Key Home
2181 {"[2~", VI_K_INSERT}, // Cursor Key Insert
2182 {"[4~", VI_K_END}, // Cursor Key End
2183 {"[5~", VI_K_PAGEUP}, // Cursor Key Page Up
2184 {"[6~", VI_K_PAGEDOWN},// Cursor Key Page Down
2185 {"OP", VI_K_FUN1}, // Function Key F1
2186 {"OQ", VI_K_FUN2}, // Function Key F2
2187 {"OR", VI_K_FUN3}, // Function Key F3
2188 {"OS", VI_K_FUN4}, // Function Key F4
Denis Vlasenko4f95e5a2007-10-11 10:10:15 +00002189 // careful: these have no terminating NUL!
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002190 {"[15~", VI_K_FUN5}, // Function Key F5
2191 {"[17~", VI_K_FUN6}, // Function Key F6
2192 {"[18~", VI_K_FUN7}, // Function Key F7
2193 {"[19~", VI_K_FUN8}, // Function Key F8
2194 {"[20~", VI_K_FUN9}, // Function Key F9
2195 {"[21~", VI_K_FUN10}, // Function Key F10
2196 {"[23~", VI_K_FUN11}, // Function Key F11
2197 {"[24~", VI_K_FUN12}, // Function Key F12
2198 {"[11~", VI_K_FUN1}, // Function Key F1
2199 {"[12~", VI_K_FUN2}, // Function Key F2
2200 {"[13~", VI_K_FUN3}, // Function Key F3
2201 {"[14~", VI_K_FUN4}, // Function Key F4
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002202 };
Denis Vlasenko80b8b392007-06-25 10:55:35 +00002203 enum { ESCCMDS_COUNT = ARRAY_SIZE(esccmds) };
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002204
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002205 alarm(0); // turn alarm OFF while we wait for input
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002206 fflush(stdout);
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002207 n = chars_to_parse;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002208 // get input from User- are there already input chars in Q?
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002209 if (n <= 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002210 // the Q is empty, wait for a typed char
Denis Vlasenko4f95e5a2007-10-11 10:10:15 +00002211 n = safe_read(0, readbuffer, MAX_LINELEN - 1);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002212 if (n < 0) {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002213 if (errno == EBADF || errno == EFAULT || errno == EINVAL
Denis Vlasenko2f6ae432007-07-19 22:50:47 +00002214 || errno == EIO)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002215 editing = 0;
2216 errno = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002217 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002218 if (n <= 0)
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002219 return 0; // error
2220 if (readbuffer[0] == 27) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002221 // This is an ESC char. Is this Esc sequence?
2222 // Could be bare Esc key. See if there are any
2223 // more chars to read after the ESC. This would
2224 // be a Function or Cursor Key sequence.
Denis Vlasenko87f3b262007-09-07 13:43:28 +00002225 struct pollfd pfd[1];
2226 pfd[0].fd = 0;
2227 pfd[0].events = POLLIN;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002228 // keep reading while there are input chars and room in buffer
Denis Vlasenko4f95e5a2007-10-11 10:10:15 +00002229 while (safe_poll(pfd, 1, 0) > 0 && n <= (MAX_LINELEN - 5)) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002230 // read the rest of the ESC string
Denis Vlasenko4f95e5a2007-10-11 10:10:15 +00002231 int r = safe_read(0, readbuffer + n, MAX_LINELEN - n);
Denis Vlasenko87f3b262007-09-07 13:43:28 +00002232 if (r > 0)
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002233 n += r;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002234 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002235 }
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002236 chars_to_parse = n;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002237 }
2238 c = readbuffer[0];
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002239 if (c == 27 && n > 1) {
2240 // Maybe cursor or function key?
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002241 const struct esc_cmds *eindex;
2242
2243 for (eindex = esccmds; eindex < &esccmds[ESCCMDS_COUNT]; eindex++) {
Denis Vlasenko4f95e5a2007-10-11 10:10:15 +00002244 int cnt = strnlen(eindex->seq, 4);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002245
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002246 if (n <= cnt)
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002247 continue;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002248 if (strncmp(eindex->seq, readbuffer + 1, cnt))
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002249 continue;
2250 // is a Cursor key- put derived value back into Q
2251 c = eindex->val;
2252 // for squeeze out the ESC sequence
2253 n = cnt + 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002254 break;
2255 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002256 if (eindex == &esccmds[ESCCMDS_COUNT]) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002257 /* defined ESC sequence not found, set only one ESC */
2258 n = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002259 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002260 } else {
2261 n = 1;
2262 }
2263 // remove key sequence from Q
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002264 chars_to_parse -= n;
Denis Vlasenkoe8a07882007-06-10 15:08:44 +00002265 memmove(readbuffer, readbuffer + n, MAX_LINELEN - n);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002266 alarm(3); // we are done waiting for input, turn alarm ON
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002267 return c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002268}
2269
2270//----- IO Routines --------------------------------------------
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002271static char get_one_char(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002272{
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002273 char c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002274
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002275#if ENABLE_FEATURE_VI_DOT_CMD
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002276 // ! adding2q && ioq == 0 read()
2277 // ! adding2q && ioq != 0 *ioq
2278 // adding2q *last_modifying_cmd= read()
2279 if (!adding2q) {
2280 // we are not adding to the q.
2281 // but, we may be reading from a q
2282 if (ioq == 0) {
2283 // there is no current q, read from STDIN
2284 c = readit(); // get the users input
2285 } else {
2286 // there is a queue to get chars from first
2287 c = *ioq++;
2288 if (c == '\0') {
2289 // the end of the q, read from STDIN
2290 free(ioq_start);
2291 ioq_start = ioq = 0;
2292 c = readit(); // get the users input
2293 }
2294 }
2295 } else {
2296 // adding STDIN chars to q
2297 c = readit(); // get the users input
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002298 if (last_modifying_cmd != NULL) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002299 int len = strlen(last_modifying_cmd);
Denis Vlasenkoe8a07882007-06-10 15:08:44 +00002300 if (len >= MAX_LINELEN - 1) {
Eric Andersenfda2b7f2002-10-26 10:19:19 +00002301 psbs("last_modifying_cmd overrun");
2302 } else {
2303 // add new char to q
2304 last_modifying_cmd[len] = c;
2305 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002306 }
2307 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002308#else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002309 c = readit(); // get the users input
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002310#endif /* FEATURE_VI_DOT_CMD */
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002311 return c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002312}
2313
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002314static char *get_input_line(const char * prompt) // get input line- use "status line"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002315{
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002316 static char *buf; // [MAX_LINELEN]
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002317
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002318 char c;
2319 int i;
2320
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002321 if (!buf) buf = xmalloc(MAX_LINELEN);
2322
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002323 strcpy(buf, prompt);
Paul Fox8552aec2005-09-16 12:20:05 +00002324 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002325 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
2326 clear_to_eol(); // clear the line
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002327 write1(prompt); // write out the :, /, or ? prompt
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002328
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002329 i = strlen(buf);
Denis Vlasenkoe8a07882007-06-10 15:08:44 +00002330 while (i < MAX_LINELEN) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002331 c = get_one_char(); // read user input
2332 if (c == '\n' || c == '\r' || c == 27)
2333 break; // is this end of input
Paul Foxf2de0b72005-09-13 22:20:37 +00002334 if (c == erase_char || c == 8 || c == 127) {
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00002335 // user wants to erase prev char
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002336 i--; // backup to prev char
2337 buf[i] = '\0'; // erase the char
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002338 //buf[i + 1] = '\0'; // null terminate buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002339 write1("\b \b"); // erase char on screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002340 if (i <= 0) { // user backs up before b-o-l, exit
2341 break;
2342 }
2343 } else {
2344 buf[i] = c; // save char in buffer
2345 buf[i + 1] = '\0'; // make sure buffer is null terminated
Denis Vlasenko4daad902007-09-27 10:20:47 +00002346 bb_putchar(c); // echo the char back to user
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002347 i++;
2348 }
2349 }
2350 refresh(FALSE);
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002351 return buf;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002352}
2353
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002354static int file_size(const char *fn) // what is the byte size of "fn"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002355{
2356 struct stat st_buf;
Denis Vlasenko59a1f302007-07-14 22:43:10 +00002357 int cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002358
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002359 cnt = -1;
Denis Vlasenko59a1f302007-07-14 22:43:10 +00002360 if (fn && fn[0] && stat(fn, &st_buf) == 0) // see if file exists
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002361 cnt = (int) st_buf.st_size;
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002362 return cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002363}
2364
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002365static int file_insert(const char * fn, char *p
2366 USE_FEATURE_VI_READONLY(, int update_ro_status))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002367{
Denis Vlasenko59a1f302007-07-14 22:43:10 +00002368 int cnt = -1;
2369 int fd, size;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002370 struct stat statbuf;
2371
2372 /* Validate file */
2373 if (stat(fn, &statbuf) < 0) {
2374 psbs("\"%s\" %s", fn, strerror(errno));
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002375 goto fi0;
2376 }
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002377 if ((statbuf.st_mode & S_IFREG) == 0) {
2378 // This is not a regular file
2379 psbs("\"%s\" Not a regular file", fn);
2380 goto fi0;
2381 }
2382 /* // this check is done by open()
2383 if ((statbuf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
2384 // dont have any read permissions
2385 psbs("\"%s\" Not readable", fn);
2386 goto fi0;
2387 }
2388 */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002389 if (p < text || p > end) {
2390 psbs("Trying to insert file outside of memory");
2391 goto fi0;
2392 }
2393
Denis Vlasenko59a1f302007-07-14 22:43:10 +00002394 // read file to buffer
2395 fd = open(fn, O_RDONLY);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002396 if (fd < 0) {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002397 psbs("\"%s\" %s", fn, strerror(errno));
Denis Vlasenko59a1f302007-07-14 22:43:10 +00002398 goto fi0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002399 }
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002400 size = statbuf.st_size;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002401 p = text_hole_make(p, size);
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002402 if (p == NULL)
2403 goto fi0;
Denis Vlasenko4f95e5a2007-10-11 10:10:15 +00002404 cnt = safe_read(fd, p, size);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002405 if (cnt < 0) {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002406 psbs("\"%s\" %s", fn, strerror(errno));
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002407 p = text_hole_delete(p, p + size - 1); // un-do buffer insert
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002408 } else if (cnt < size) {
2409 // There was a partial read, shrink unused space text[]
2410 p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert
Denis Vlasenkoea620772006-10-14 02:23:43 +00002411 psbs("cannot read all of file \"%s\"", fn);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002412 }
2413 if (cnt >= size)
Paul Fox8552aec2005-09-16 12:20:05 +00002414 file_modified++;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002415 close(fd);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002416 fi0:
Denis Vlasenko856be772007-08-17 08:29:48 +00002417#if ENABLE_FEATURE_VI_READONLY
2418 if (update_ro_status
2419 && ((access(fn, W_OK) < 0) ||
2420 /* root will always have access()
2421 * so we check fileperms too */
2422 !(statbuf.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH))
2423 )
2424 ) {
Denis Vlasenko2f6ae432007-07-19 22:50:47 +00002425 SET_READONLY_FILE(readonly_mode);
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002426 }
Denis Vlasenko856be772007-08-17 08:29:48 +00002427#endif
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002428 return cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002429}
2430
Denis Vlasenko59a1f302007-07-14 22:43:10 +00002431
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002432static int file_write(char * fn, char * first, char * last)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002433{
2434 int fd, cnt, charcnt;
2435
2436 if (fn == 0) {
2437 psbs("No current filename");
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002438 return -2;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002439 }
2440 charcnt = 0;
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002441 fd = open(fn, (O_WRONLY | O_CREAT | O_TRUNC), 0666);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002442 if (fd < 0)
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002443 return -1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002444 cnt = last - first + 1;
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002445 charcnt = full_write(fd, first, cnt);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002446 if (charcnt == cnt) {
2447 // good write
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002448 //file_modified = FALSE; // the file has not been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002449 } else {
2450 charcnt = 0;
2451 }
2452 close(fd);
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002453 return charcnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002454}
2455
2456//----- Terminal Drawing ---------------------------------------
2457// The terminal is made up of 'rows' line of 'columns' columns.
Eric Andersenaff114c2004-04-14 17:51:38 +00002458// classically this would be 24 x 80.
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002459// screen coordinates
2460// 0,0 ... 0,79
2461// 1,0 ... 1,79
2462// . ... .
2463// . ... .
2464// 22,0 ... 22,79
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002465// 23,0 ... 23,79 <- status line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002466
2467//----- Move the cursor to row x col (count from 0, not 1) -------
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002468static void place_cursor(int row, int col, int optimize)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002469{
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002470 char cm1[32];
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002471 char *cm;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002472
2473 if (row < 0) row = 0;
2474 if (row >= rows) row = rows - 1;
2475 if (col < 0) col = 0;
2476 if (col >= columns) col = columns - 1;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002477
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002478 //----- 1. Try the standard terminal ESC sequence
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002479 sprintf(cm1, CMrc, row + 1, col + 1);
2480 cm = cm1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002481
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002482#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002483 if (optimize && col < 16) {
2484 char cm2[MAX_LINELEN]; // better size estimate?
2485 char *screenp;
2486 int Rrow = last_row;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002487
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002488 //----- find the minimum # of chars to move cursor -------------
2489 //----- 2. Try moving with discreet chars (Newline, [back]space, ...)
2490 cm2[0] = '\0';
2491
2492 // move to the correct row
2493 while (row < Rrow) {
2494 // the cursor has to move up
2495 strcat(cm2, CMup);
2496 Rrow--;
2497 }
2498 while (row > Rrow) {
2499 // the cursor has to move down
2500 strcat(cm2, CMdown);
2501 Rrow++;
2502 }
2503
2504 // now move to the correct column
2505 strcat(cm2, "\r"); // start at col 0
2506 // just send out orignal source char to get to correct place
2507 screenp = &screen[row * columns]; // start of screen line
2508 strncat(cm2, screenp, col);
2509
2510 // pick the shortest cursor motion to send out
2511 if (strlen(cm2) < strlen(cm)) {
2512 cm = cm2;
2513 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002514 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002515#endif /* FEATURE_VI_OPTIMIZE_CURSOR */
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002516 write1(cm);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002517}
2518
2519//----- Erase from cursor to end of line -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002520static void clear_to_eol(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002521{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002522 write1(Ceol); // Erase from cursor to end of line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002523}
2524
2525//----- Erase from cursor to end of screen -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002526static void clear_to_eos(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002527{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002528 write1(Ceos); // Erase from cursor to end of screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002529}
2530
2531//----- Start standout mode ------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002532static void standout_start(void) // send "start reverse video" sequence
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002533{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002534 write1(SOs); // Start reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002535}
2536
2537//----- End standout mode --------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002538static void standout_end(void) // send "end reverse video" sequence
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002539{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002540 write1(SOn); // End reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002541}
2542
2543//----- Flash the screen --------------------------------------
2544static void flash(int h)
2545{
2546 standout_start(); // send "start reverse video" sequence
2547 redraw(TRUE);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002548 mysleep(h);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002549 standout_end(); // send "end reverse video" sequence
2550 redraw(TRUE);
2551}
2552
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002553static void Indicate_Error(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002554{
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002555#if ENABLE_FEATURE_VI_CRASHME
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002556 if (crashme > 0)
2557 return; // generate a random command
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002558#endif
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002559 if (!err_method) {
2560 write1(bell); // send out a bell character
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002561 } else {
2562 flash(10);
2563 }
2564}
2565
2566//----- Screen[] Routines --------------------------------------
2567//----- Erase the Screen[] memory ------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002568static void screen_erase(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002569{
2570 memset(screen, ' ', screensize); // clear new screen
2571}
2572
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002573static int bufsum(char *buf, int count)
Paul Fox8552aec2005-09-16 12:20:05 +00002574{
2575 int sum = 0;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002576 char *e = buf + count;
2577
Paul Fox8552aec2005-09-16 12:20:05 +00002578 while (buf < e)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002579 sum += (unsigned char) *buf++;
Paul Fox8552aec2005-09-16 12:20:05 +00002580 return sum;
2581}
2582
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002583//----- Draw the status line at bottom of the screen -------------
2584static void show_status_line(void)
2585{
Paul Foxc3504852005-09-16 12:48:18 +00002586 int cnt = 0, cksum = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002587
Paul Fox8552aec2005-09-16 12:20:05 +00002588 // either we already have an error or status message, or we
2589 // create one.
2590 if (!have_status_msg) {
2591 cnt = format_edit_status();
2592 cksum = bufsum(status_buffer, cnt);
2593 }
2594 if (have_status_msg || ((cnt > 0 && last_status_cksum != cksum))) {
2595 last_status_cksum= cksum; // remember if we have seen this line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002596 place_cursor(rows - 1, 0, FALSE); // put cursor on status line
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002597 write1(status_buffer);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002598 clear_to_eol();
Paul Fox8552aec2005-09-16 12:20:05 +00002599 if (have_status_msg) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002600 if (((int)strlen(status_buffer) - (have_status_msg - 1)) >
Paul Fox8552aec2005-09-16 12:20:05 +00002601 (columns - 1) ) {
2602 have_status_msg = 0;
2603 Hit_Return();
2604 }
2605 have_status_msg = 0;
2606 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002607 place_cursor(crow, ccol, FALSE); // put cursor back in correct place
2608 }
Eric Andersena9eb33d2004-08-19 19:15:06 +00002609 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002610}
2611
2612//----- format the status buffer, the bottom line of screen ------
Paul Fox8552aec2005-09-16 12:20:05 +00002613// format status buffer, with STANDOUT mode
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002614static void psbs(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002615{
2616 va_list args;
2617
2618 va_start(args, format);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002619 strcpy(status_buffer, SOs); // Terminal standout mode on
2620 vsprintf(status_buffer + strlen(status_buffer), format, args);
2621 strcat(status_buffer, SOn); // Terminal standout mode off
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002622 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00002623
2624 have_status_msg = 1 + sizeof(SOs) + sizeof(SOn) - 2;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002625}
2626
Paul Fox8552aec2005-09-16 12:20:05 +00002627// format status buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002628static void psb(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002629{
2630 va_list args;
2631
2632 va_start(args, format);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002633 vsprintf(status_buffer, format, args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002634 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00002635
2636 have_status_msg = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002637}
2638
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002639static void ni(const char * s) // display messages
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002640{
Denis Vlasenkoe8a07882007-06-10 15:08:44 +00002641 char buf[MAX_LINELEN];
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002642
2643 print_literal(buf, s);
2644 psbs("\'%s\' is not implemented", buf);
2645}
2646
Paul Fox8552aec2005-09-16 12:20:05 +00002647static int format_edit_status(void) // show file status on status line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002648{
Paul Fox8552aec2005-09-16 12:20:05 +00002649 static int tot;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002650 static const char cmd_mode_indicator[] ALIGN1 = "-IR-";
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002651 int cur, percent, ret, trunc_at;
2652
Paul Fox8552aec2005-09-16 12:20:05 +00002653 // file_modified is now a counter rather than a flag. this
2654 // helps reduce the amount of line counting we need to do.
2655 // (this will cause a mis-reporting of modified status
2656 // once every MAXINT editing operations.)
2657
2658 // it would be nice to do a similar optimization here -- if
2659 // we haven't done a motion that could have changed which line
2660 // we're on, then we shouldn't have to do this count_lines()
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002661 cur = count_lines(text, dot);
Paul Fox8552aec2005-09-16 12:20:05 +00002662
2663 // reduce counting -- the total lines can't have
2664 // changed if we haven't done any edits.
2665 if (file_modified != last_file_modified) {
2666 tot = cur + count_lines(dot, end - 1) - 1;
2667 last_file_modified = file_modified;
2668 }
2669
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002670 // current line percent
2671 // ------------- ~~ ----------
2672 // total lines 100
2673 if (tot > 0) {
2674 percent = (100 * cur) / tot;
2675 } else {
2676 cur = tot = 0;
2677 percent = 100;
2678 }
Eric Andersen0ef24c62005-07-18 10:32:59 +00002679
Paul Fox8552aec2005-09-16 12:20:05 +00002680 trunc_at = columns < STATUS_BUFFER_LEN-1 ?
2681 columns : STATUS_BUFFER_LEN-1;
2682
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002683 ret = snprintf(status_buffer, trunc_at+1,
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002684#if ENABLE_FEATURE_VI_READONLY
Paul Fox8552aec2005-09-16 12:20:05 +00002685 "%c %s%s%s %d/%d %d%%",
2686#else
2687 "%c %s%s %d/%d %d%%",
2688#endif
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002689 cmd_mode_indicator[cmd_mode & 3],
2690 (current_filename != NULL ? current_filename : "No file"),
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002691#if ENABLE_FEATURE_VI_READONLY
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002692 (readonly_mode ? " [Readonly]" : ""),
Paul Fox8552aec2005-09-16 12:20:05 +00002693#endif
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002694 (file_modified ? " [Modified]" : ""),
Paul Fox8552aec2005-09-16 12:20:05 +00002695 cur, tot, percent);
2696
2697 if (ret >= 0 && ret < trunc_at)
2698 return ret; /* it all fit */
2699
2700 return trunc_at; /* had to truncate */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002701}
2702
2703//----- Force refresh of all Lines -----------------------------
2704static void redraw(int full_screen)
2705{
2706 place_cursor(0, 0, FALSE); // put cursor in correct place
2707 clear_to_eos(); // tel terminal to erase display
2708 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00002709 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002710 refresh(full_screen); // this will redraw the entire display
Paul Fox8552aec2005-09-16 12:20:05 +00002711 show_status_line();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002712}
2713
2714//----- Format a text[] line into a buffer ---------------------
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002715// Returns number of leading chars which should be ignored
2716// (return value is always <= offset)
2717static int format_line(char *dest, char *src, int li)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002718{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002719 char c;
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002720 int co;
2721 int ofs = offset;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002722
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002723 memset(dest, ' ', MAX_SCR_COLS);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002724 for (co = 0; co < MAX_SCR_COLS; co++) {
Denis Vlasenko2a51af22007-03-21 22:31:24 +00002725 c = ' '; // assume blank
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002726 if (li > 0 && co == 0) {
2727 c = '~'; // not first line, assume Tilde
2728 }
2729 // are there chars in text[] and have we gone past the end
2730 if (text < end && src < end) {
2731 c = *src++;
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002732
2733 if (c == '\n')
2734 break;
2735 if ((c & 0x80) && !Isprint(c)) {
2736 c = '.';
2737 }
2738 if ((unsigned char)(c) < ' ' || c == 0x7f) {
2739 if (c == '\t') {
2740 c = ' ';
2741 // co % 8 != 7
2742 while ((co % tabstop) != (tabstop - 1)) {
2743 dest[co++] = c;
2744 if (co >= MAX_SCR_COLS)
2745 goto ret;
2746 }
2747 } else {
2748 dest[co++] = '^';
2749 if (co >= MAX_SCR_COLS)
2750 goto ret;
2751 if (c == 0x7f)
2752 c = '?';
2753 else
2754 c += '@'; // make it visible
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002755 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002756 }
2757 }
2758 // the co++ is done here so that the column will
2759 // not be overwritten when we blank-out the rest of line
2760 dest[co] = c;
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002761 // discard scrolled-off portion, in tabstop-sized pieces
2762 if (ofs >= tabstop && co >= tabstop) {
2763 co -= tabstop;
2764 ofs -= tabstop;
2765 memset(&dest[co + 1], ' ', tabstop);
2766 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002767 if (src >= end)
2768 break;
2769 }
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002770 ret:
2771 if (co < ofs) {
2772 // entire line has scrolled off, make it entirely blank
2773 memset(dest, ' ', MAX_SCR_COLS);
2774 ofs = 0;
2775 }
2776 dest[MAX_SCR_COLS-1] = '\0';
2777 return ofs;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002778}
2779
2780//----- Refresh the changed screen lines -----------------------
2781// Copy the source line from text[] into the buffer and note
2782// if the current screenline is different from the new buffer.
2783// If they differ then that line needs redrawing on the terminal.
2784//
2785static void refresh(int full_screen)
2786{
2787 static int old_offset;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002788
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002789 int li, changed;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002790 char buf[MAX_SCR_COLS];
2791 char *tp, *sp; // pointer into text[] and screen[]
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002792#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002793 int last_li = -2; // last line that changed- for optimizing cursor movement
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002794#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002795
Rob Landleye5e1a102006-06-21 01:15:36 +00002796 if (ENABLE_FEATURE_VI_WIN_RESIZE)
2797 get_terminal_width_height(0, &columns, &rows);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002798 sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
2799 tp = screenbegin; // index into text[] of top line
2800
2801 // compare text[] to screen[] and mark screen[] lines that need updating
2802 for (li = 0; li < rows - 1; li++) {
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002803 int ofs;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002804 int cs, ce; // column start & end
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002805 // format current text line into buf
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002806 ofs = format_line(buf, tp, li);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002807
2808 // skip to the end of the current text[] line
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002809 while (tp < end && *tp++ != '\n')
2810 continue;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002811
2812 // see if there are any changes between vitual screen and buf
2813 changed = FALSE; // assume no change
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002814 cs = 0;
2815 ce = columns - 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002816 sp = &screen[li * columns]; // start of screen line
2817 if (full_screen) {
2818 // force re-draw of every single column from 0 - columns-1
2819 goto re0;
2820 }
2821 // compare newly formatted buffer with virtual screen
2822 // look forward for first difference between buf and screen
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002823 for (; cs <= ce; cs++) {
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002824 if (buf[cs + ofs] != sp[cs]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002825 changed = TRUE; // mark for redraw
2826 break;
2827 }
2828 }
2829
2830 // look backward for last difference between buf and screen
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002831 for (; ce >= cs; ce--) {
2832 if (buf[ce + ofs] != sp[ce]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002833 changed = TRUE; // mark for redraw
2834 break;
2835 }
2836 }
2837 // now, cs is index of first diff, and ce is index of last diff
2838
2839 // if horz offset has changed, force a redraw
2840 if (offset != old_offset) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002841 re0:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002842 changed = TRUE;
2843 }
2844
2845 // make a sanity check of columns indexes
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002846 if (cs < 0) cs = 0;
2847 if (ce > columns - 1) ce = columns - 1;
2848 if (cs > ce) { cs = 0; ce = columns - 1; }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002849 // is there a change between vitual screen and buf
2850 if (changed) {
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002851 // copy changed part of buffer to virtual screen
2852 memmove(sp+cs, buf+(cs+ofs), ce-cs+1);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002853
2854 // move cursor to column of first change
2855 if (offset != old_offset) {
2856 // opti_cur_move is still too stupid
2857 // to handle offsets correctly
2858 place_cursor(li, cs, FALSE);
2859 } else {
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002860#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002861 // if this just the next line
2862 // try to optimize cursor movement
2863 // otherwise, use standard ESC sequence
2864 place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE);
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002865 last_li = li;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002866#else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002867 place_cursor(li, cs, FALSE); // use standard ESC sequence
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002868#endif /* FEATURE_VI_OPTIMIZE_CURSOR */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002869 }
2870
2871 // write line out to terminal
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002872 {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002873 int nic = ce - cs + 1;
2874 char *out = sp + cs;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002875
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002876 while (--nic >= 0) {
Denis Vlasenko4daad902007-09-27 10:20:47 +00002877 bb_putchar(*out);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002878 out++;
2879 }
2880 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002881#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002882 last_row = li;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002883#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002884 }
2885 }
2886
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002887#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002888 place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE);
2889 last_row = crow;
2890#else
2891 place_cursor(crow, ccol, FALSE);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002892#endif
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002893
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002894 old_offset = offset;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002895}
2896
Eric Andersen3f980402001-04-04 17:31:15 +00002897//---------------------------------------------------------------------
2898//----- the Ascii Chart -----------------------------------------------
2899//
2900// 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel
2901// 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si
2902// 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb
2903// 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us
2904// 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 '
2905// 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f /
2906// 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7
2907// 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ?
2908// 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G
2909// 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O
2910// 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W
2911// 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _
2912// 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g
2913// 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o
2914// 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w
2915// 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del
2916//---------------------------------------------------------------------
2917
2918//----- Execute a Vi Command -----------------------------------
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002919static void do_cmd(char c)
Eric Andersen3f980402001-04-04 17:31:15 +00002920{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002921 const char *msg;
2922 char c1, *p, *q, buf[9], *save_dot;
Eric Andersen3f980402001-04-04 17:31:15 +00002923 int cnt, i, j, dir, yf;
2924
2925 c1 = c; // quiet the compiler
2926 cnt = yf = dir = 0; // quiet the compiler
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002927 msg = p = q = save_dot = buf; // quiet the compiler
Eric Andersen3f980402001-04-04 17:31:15 +00002928 memset(buf, '\0', 9); // clear buf
Eric Andersenbff7a602001-11-17 07:15:43 +00002929
Paul Fox8552aec2005-09-16 12:20:05 +00002930 show_status_line();
2931
Eric Andersenbff7a602001-11-17 07:15:43 +00002932 /* if this is a cursor key, skip these checks */
2933 switch (c) {
2934 case VI_K_UP:
2935 case VI_K_DOWN:
2936 case VI_K_LEFT:
2937 case VI_K_RIGHT:
2938 case VI_K_HOME:
2939 case VI_K_END:
2940 case VI_K_PAGEUP:
2941 case VI_K_PAGEDOWN:
2942 goto key_cmd_mode;
2943 }
2944
Eric Andersen3f980402001-04-04 17:31:15 +00002945 if (cmd_mode == 2) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002946 // flip-flop Insert/Replace mode
Denis Vlasenko2a51af22007-03-21 22:31:24 +00002947 if (c == VI_K_INSERT)
2948 goto dc_i;
Eric Andersen3f980402001-04-04 17:31:15 +00002949 // we are 'R'eplacing the current *dot with new char
2950 if (*dot == '\n') {
2951 // don't Replace past E-o-l
2952 cmd_mode = 1; // convert to insert
2953 } else {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002954 if (1 <= c || Isprint(c)) {
Eric Andersen3f980402001-04-04 17:31:15 +00002955 if (c != 27)
2956 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
2957 dot = char_insert(dot, c); // insert new char
2958 }
2959 goto dc1;
2960 }
2961 }
2962 if (cmd_mode == 1) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00002963 // hitting "Insert" twice means "R" replace mode
2964 if (c == VI_K_INSERT) goto dc5;
Eric Andersen3f980402001-04-04 17:31:15 +00002965 // insert the char c at "dot"
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002966 if (1 <= c || Isprint(c)) {
2967 dot = char_insert(dot, c);
Eric Andersen3f980402001-04-04 17:31:15 +00002968 }
2969 goto dc1;
2970 }
2971
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002972 key_cmd_mode:
Eric Andersen3f980402001-04-04 17:31:15 +00002973 switch (c) {
Eric Andersen822c3832001-05-07 17:37:43 +00002974 //case 0x01: // soh
2975 //case 0x09: // ht
2976 //case 0x0b: // vt
2977 //case 0x0e: // so
2978 //case 0x0f: // si
2979 //case 0x10: // dle
2980 //case 0x11: // dc1
2981 //case 0x13: // dc3
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002982#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen1c0d3112001-04-16 15:46:44 +00002983 case 0x14: // dc4 ctrl-T
Eric Andersen3f980402001-04-04 17:31:15 +00002984 crashme = (crashme == 0) ? 1 : 0;
Eric Andersen3f980402001-04-04 17:31:15 +00002985 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002986#endif
Eric Andersen822c3832001-05-07 17:37:43 +00002987 //case 0x16: // syn
2988 //case 0x17: // etb
2989 //case 0x18: // can
2990 //case 0x1c: // fs
2991 //case 0x1d: // gs
2992 //case 0x1e: // rs
2993 //case 0x1f: // us
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002994 //case '!': // !-
2995 //case '#': // #-
2996 //case '&': // &-
2997 //case '(': // (-
2998 //case ')': // )-
2999 //case '*': // *-
3000 //case ',': // ,-
3001 //case '=': // =-
3002 //case '@': // @-
3003 //case 'F': // F-
3004 //case 'K': // K-
3005 //case 'Q': // Q-
3006 //case 'S': // S-
3007 //case 'T': // T-
3008 //case 'V': // V-
3009 //case '[': // [-
3010 //case '\\': // \-
3011 //case ']': // ]-
3012 //case '_': // _-
3013 //case '`': // `-
3014 //case 'g': // g-
Eric Andersen1c0d3112001-04-16 15:46:44 +00003015 //case 'u': // u- FIXME- there is no undo
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003016 //case 'v': // v-
Eric Andersen3f980402001-04-04 17:31:15 +00003017 default: // unrecognised command
3018 buf[0] = c;
3019 buf[1] = '\0';
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003020 if (c < ' ') {
Eric Andersen3f980402001-04-04 17:31:15 +00003021 buf[0] = '^';
3022 buf[1] = c + '@';
3023 buf[2] = '\0';
3024 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003025 ni(buf);
Eric Andersen3f980402001-04-04 17:31:15 +00003026 end_cmd_q(); // stop adding to q
3027 case 0x00: // nul- ignore
3028 break;
3029 case 2: // ctrl-B scroll up full screen
3030 case VI_K_PAGEUP: // Cursor Key Page Up
3031 dot_scroll(rows - 2, -1);
3032 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003033#if ENABLE_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +00003034 case 0x03: // ctrl-C interrupt
3035 longjmp(restart, 1);
3036 break;
3037 case 26: // ctrl-Z suspend
3038 suspend_sig(SIGTSTP);
3039 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003040#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003041 case 4: // ctrl-D scroll down half screen
3042 dot_scroll((rows - 2) / 2, 1);
3043 break;
3044 case 5: // ctrl-E scroll down one line
3045 dot_scroll(1, 1);
3046 break;
3047 case 6: // ctrl-F scroll down full screen
3048 case VI_K_PAGEDOWN: // Cursor Key Page Down
3049 dot_scroll(rows - 2, 1);
3050 break;
3051 case 7: // ctrl-G show current status
Paul Fox8552aec2005-09-16 12:20:05 +00003052 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003053 break;
3054 case 'h': // h- move left
3055 case VI_K_LEFT: // cursor key Left
Paul Foxd13b90b2005-07-18 22:17:25 +00003056 case 8: // ctrl-H- move left (This may be ERASE char)
Denis Vlasenko2a51af22007-03-21 22:31:24 +00003057 case 0x7f: // DEL- move left (This may be ERASE char)
Eric Andersen3f980402001-04-04 17:31:15 +00003058 if (cmdcnt-- > 1) {
3059 do_cmd(c);
3060 } // repeat cnt
3061 dot_left();
3062 break;
3063 case 10: // Newline ^J
3064 case 'j': // j- goto next line, same col
3065 case VI_K_DOWN: // cursor key Down
3066 if (cmdcnt-- > 1) {
3067 do_cmd(c);
3068 } // repeat cnt
3069 dot_next(); // go to next B-o-l
3070 dot = move_to_col(dot, ccol + offset); // try stay in same col
3071 break;
3072 case 12: // ctrl-L force redraw whole screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003073 case 18: // ctrl-R force redraw
Eric Andersen822c3832001-05-07 17:37:43 +00003074 place_cursor(0, 0, FALSE); // put cursor in correct place
Eric Andersen3f980402001-04-04 17:31:15 +00003075 clear_to_eos(); // tel terminal to erase display
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003076 mysleep(10);
Eric Andersen3f980402001-04-04 17:31:15 +00003077 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00003078 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003079 refresh(TRUE); // this will redraw the entire display
3080 break;
3081 case 13: // Carriage Return ^M
3082 case '+': // +- goto next line
3083 if (cmdcnt-- > 1) {
3084 do_cmd(c);
3085 } // repeat cnt
3086 dot_next();
3087 dot_skip_over_ws();
3088 break;
3089 case 21: // ctrl-U scroll up half screen
3090 dot_scroll((rows - 2) / 2, -1);
3091 break;
3092 case 25: // ctrl-Y scroll up one line
3093 dot_scroll(1, -1);
3094 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003095 case 27: // esc
Eric Andersen3f980402001-04-04 17:31:15 +00003096 if (cmd_mode == 0)
3097 indicate_error(c);
3098 cmd_mode = 0; // stop insrting
3099 end_cmd_q();
Paul Fox8552aec2005-09-16 12:20:05 +00003100 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003101 break;
3102 case ' ': // move right
3103 case 'l': // move right
3104 case VI_K_RIGHT: // Cursor Key Right
3105 if (cmdcnt-- > 1) {
3106 do_cmd(c);
3107 } // repeat cnt
3108 dot_right();
3109 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003110#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003111 case '"': // "- name a register to use for Delete/Yank
3112 c1 = get_one_char();
3113 c1 = tolower(c1);
3114 if (islower(c1)) {
3115 YDreg = c1 - 'a';
3116 } else {
3117 indicate_error(c);
3118 }
3119 break;
3120 case '\'': // '- goto a specific mark
3121 c1 = get_one_char();
3122 c1 = tolower(c1);
3123 if (islower(c1)) {
3124 c1 = c1 - 'a';
3125 // get the b-o-l
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003126 q = mark[(unsigned char) c1];
Eric Andersen3f980402001-04-04 17:31:15 +00003127 if (text <= q && q < end) {
3128 dot = q;
3129 dot_begin(); // go to B-o-l
3130 dot_skip_over_ws();
3131 }
3132 } else if (c1 == '\'') { // goto previous context
3133 dot = swap_context(dot); // swap current and previous context
3134 dot_begin(); // go to B-o-l
3135 dot_skip_over_ws();
3136 } else {
3137 indicate_error(c);
3138 }
3139 break;
3140 case 'm': // m- Mark a line
3141 // this is really stupid. If there are any inserts or deletes
3142 // between text[0] and dot then this mark will not point to the
3143 // correct location! It could be off by many lines!
3144 // Well..., at least its quick and dirty.
3145 c1 = get_one_char();
3146 c1 = tolower(c1);
3147 if (islower(c1)) {
3148 c1 = c1 - 'a';
3149 // remember the line
3150 mark[(int) c1] = dot;
3151 } else {
3152 indicate_error(c);
3153 }
3154 break;
3155 case 'P': // P- Put register before
3156 case 'p': // p- put register after
3157 p = reg[YDreg];
3158 if (p == 0) {
3159 psbs("Nothing in register %c", what_reg());
3160 break;
3161 }
3162 // are we putting whole lines or strings
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003163 if (strchr(p, '\n') != NULL) {
Eric Andersen3f980402001-04-04 17:31:15 +00003164 if (c == 'P') {
3165 dot_begin(); // putting lines- Put above
3166 }
3167 if (c == 'p') {
3168 // are we putting after very last line?
3169 if (end_line(dot) == (end - 1)) {
3170 dot = end; // force dot to end of text[]
3171 } else {
3172 dot_next(); // next line, then put before
3173 }
3174 }
3175 } else {
3176 if (c == 'p')
3177 dot_right(); // move to right, can move to NL
3178 }
3179 dot = string_insert(dot, p); // insert the string
3180 end_cmd_q(); // stop adding to q
3181 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003182 case 'U': // U- Undo; replace current line with original version
3183 if (reg[Ureg] != 0) {
3184 p = begin_line(dot);
3185 q = end_line(dot);
3186 p = text_hole_delete(p, q); // delete cur line
3187 p = string_insert(p, reg[Ureg]); // insert orig line
3188 dot = p;
3189 dot_skip_over_ws();
3190 }
3191 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003192#endif /* FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003193 case '$': // $- goto end of line
3194 case VI_K_END: // Cursor Key End
3195 if (cmdcnt-- > 1) {
3196 do_cmd(c);
3197 } // repeat cnt
Glenn L McGrathee829062004-01-21 10:59:45 +00003198 dot = end_line(dot);
Eric Andersen3f980402001-04-04 17:31:15 +00003199 break;
3200 case '%': // %- find matching char of pair () [] {}
3201 for (q = dot; q < end && *q != '\n'; q++) {
3202 if (strchr("()[]{}", *q) != NULL) {
3203 // we found half of a pair
3204 p = find_pair(q, *q);
3205 if (p == NULL) {
3206 indicate_error(c);
3207 } else {
3208 dot = p;
3209 }
3210 break;
3211 }
3212 }
3213 if (*q == '\n')
3214 indicate_error(c);
3215 break;
3216 case 'f': // f- forward to a user specified char
3217 last_forward_char = get_one_char(); // get the search char
3218 //
Eric Andersenaff114c2004-04-14 17:51:38 +00003219 // dont separate these two commands. 'f' depends on ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003220 //
Paul Foxd13b90b2005-07-18 22:17:25 +00003221 //**** fall thru to ... ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003222 case ';': // ;- look at rest of line for last forward char
3223 if (cmdcnt-- > 1) {
Eric Andersen822c3832001-05-07 17:37:43 +00003224 do_cmd(';');
Eric Andersen3f980402001-04-04 17:31:15 +00003225 } // repeat cnt
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003226 if (last_forward_char == 0)
3227 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003228 q = dot + 1;
3229 while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
3230 q++;
3231 }
3232 if (*q == last_forward_char)
3233 dot = q;
3234 break;
3235 case '-': // -- goto prev line
3236 if (cmdcnt-- > 1) {
3237 do_cmd(c);
3238 } // repeat cnt
3239 dot_prev();
3240 dot_skip_over_ws();
3241 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003242#if ENABLE_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003243 case '.': // .- repeat the last modifying command
3244 // Stuff the last_modifying_cmd back into stdin
3245 // and let it be re-executed.
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003246 if (last_modifying_cmd != NULL) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003247 ioq = ioq_start = xstrdup(last_modifying_cmd);
Eric Andersen3f980402001-04-04 17:31:15 +00003248 }
3249 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003250#endif
3251#if ENABLE_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +00003252 case '?': // /- search for a pattern
3253 case '/': // /- search for a pattern
3254 buf[0] = c;
3255 buf[1] = '\0';
3256 q = get_input_line(buf); // get input line- use "status line"
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003257 if (q[0] && !q[1])
3258 goto dc3; // if no pat re-use old pat
3259 if (q[0]) { // strlen(q) > 1: new pat- save it and find
Eric Andersen3f980402001-04-04 17:31:15 +00003260 // there is a new pat
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00003261 free(last_search_pattern);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003262 last_search_pattern = xstrdup(q);
Eric Andersen3f980402001-04-04 17:31:15 +00003263 goto dc3; // now find the pattern
3264 }
3265 // user changed mind and erased the "/"- do nothing
3266 break;
3267 case 'N': // N- backward search for last pattern
3268 if (cmdcnt-- > 1) {
3269 do_cmd(c);
3270 } // repeat cnt
3271 dir = BACK; // assume BACKWARD search
3272 p = dot - 1;
3273 if (last_search_pattern[0] == '?') {
3274 dir = FORWARD;
3275 p = dot + 1;
3276 }
3277 goto dc4; // now search for pattern
3278 break;
3279 case 'n': // n- repeat search for last pattern
3280 // search rest of text[] starting at next char
3281 // if search fails return orignal "p" not the "p+1" address
3282 if (cmdcnt-- > 1) {
3283 do_cmd(c);
3284 } // repeat cnt
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003285 dc3:
Eric Andersen3f980402001-04-04 17:31:15 +00003286 if (last_search_pattern == 0) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003287 msg = "No previous regular expression";
Eric Andersen3f980402001-04-04 17:31:15 +00003288 goto dc2;
3289 }
3290 if (last_search_pattern[0] == '/') {
3291 dir = FORWARD; // assume FORWARD search
3292 p = dot + 1;
3293 }
3294 if (last_search_pattern[0] == '?') {
3295 dir = BACK;
3296 p = dot - 1;
3297 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003298 dc4:
Eric Andersen3f980402001-04-04 17:31:15 +00003299 q = char_search(p, last_search_pattern + 1, dir, FULL);
3300 if (q != NULL) {
3301 dot = q; // good search, update "dot"
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003302 msg = "";
Eric Andersen3f980402001-04-04 17:31:15 +00003303 goto dc2;
3304 }
3305 // no pattern found between "dot" and "end"- continue at top
3306 p = text;
3307 if (dir == BACK) {
3308 p = end - 1;
3309 }
3310 q = char_search(p, last_search_pattern + 1, dir, FULL);
3311 if (q != NULL) { // found something
3312 dot = q; // found new pattern- goto it
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003313 msg = "search hit BOTTOM, continuing at TOP";
Eric Andersen3f980402001-04-04 17:31:15 +00003314 if (dir == BACK) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003315 msg = "search hit TOP, continuing at BOTTOM";
Eric Andersen3f980402001-04-04 17:31:15 +00003316 }
3317 } else {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003318 msg = "Pattern not found";
Eric Andersen3f980402001-04-04 17:31:15 +00003319 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003320 dc2:
3321 if (*msg)
3322 psbs("%s", msg);
Eric Andersen3f980402001-04-04 17:31:15 +00003323 break;
3324 case '{': // {- move backward paragraph
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003325 q = char_search(dot, "\n\n", BACK, 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;
3330 case '}': // }- move forward paragraph
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003331 q = char_search(dot, "\n\n", FORWARD, FULL);
Eric Andersen3f980402001-04-04 17:31:15 +00003332 if (q != NULL) { // found blank line
3333 dot = next_line(q); // move to next blank line
3334 }
3335 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003336#endif /* FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +00003337 case '0': // 0- goto begining of line
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003338 case '1': // 1-
3339 case '2': // 2-
3340 case '3': // 3-
3341 case '4': // 4-
3342 case '5': // 5-
3343 case '6': // 6-
3344 case '7': // 7-
3345 case '8': // 8-
3346 case '9': // 9-
Eric Andersen3f980402001-04-04 17:31:15 +00003347 if (c == '0' && cmdcnt < 1) {
3348 dot_begin(); // this was a standalone zero
3349 } else {
3350 cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number
3351 }
3352 break;
3353 case ':': // :- the colon mode commands
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003354 p = get_input_line(":"); // get input line- use "status line"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003355#if ENABLE_FEATURE_VI_COLON
Eric Andersen3f980402001-04-04 17:31:15 +00003356 colon(p); // execute the command
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003357#else
Eric Andersen822c3832001-05-07 17:37:43 +00003358 if (*p == ':')
3359 p++; // move past the ':'
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003360 cnt = strlen(p);
Eric Andersen822c3832001-05-07 17:37:43 +00003361 if (cnt <= 0)
3362 break;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003363 if (strncasecmp(p, "quit", cnt) == 0
3364 || strncasecmp(p, "q!", cnt) == 0 // delete lines
3365 ) {
Matt Kraai1f0c4362001-12-20 23:13:26 +00003366 if (file_modified && p[1] != '!') {
Eric Andersen3f980402001-04-04 17:31:15 +00003367 psbs("No write since last change (:quit! overrides)");
3368 } else {
3369 editing = 0;
3370 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003371 } else if (strncasecmp(p, "write", cnt) == 0
3372 || strncasecmp(p, "wq", cnt) == 0
3373 || strncasecmp(p, "wn", cnt) == 0
3374 || strncasecmp(p, "x", cnt) == 0
3375 ) {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00003376 cnt = file_write(current_filename, text, end - 1);
Paul Fox61e45db2005-10-09 14:43:22 +00003377 if (cnt < 0) {
3378 if (cnt == -1)
3379 psbs("Write error: %s", strerror(errno));
3380 } else {
3381 file_modified = 0;
3382 last_file_modified = -1;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00003383 psb("\"%s\" %dL, %dC", current_filename, count_lines(text, end - 1), cnt);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003384 if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n'
3385 || p[0] == 'X' || p[1] == 'Q' || p[1] == 'N'
3386 ) {
Paul Fox61e45db2005-10-09 14:43:22 +00003387 editing = 0;
3388 }
Eric Andersen3f980402001-04-04 17:31:15 +00003389 }
Denis Vlasenko219d14d2007-03-24 15:40:16 +00003390 } else if (strncasecmp(p, "file", cnt) == 0) {
Paul Fox8552aec2005-09-16 12:20:05 +00003391 last_status_cksum = 0; // force status update
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003392 } else if (sscanf(p, "%d", &j) > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00003393 dot = find_line(j); // go to line # j
3394 dot_skip_over_ws();
Eric Andersen3f980402001-04-04 17:31:15 +00003395 } else { // unrecognised cmd
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003396 ni(p);
Eric Andersen3f980402001-04-04 17:31:15 +00003397 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003398#endif /* !FEATURE_VI_COLON */
Eric Andersen3f980402001-04-04 17:31:15 +00003399 break;
3400 case '<': // <- Left shift something
3401 case '>': // >- Right shift something
3402 cnt = count_lines(text, dot); // remember what line we are on
3403 c1 = get_one_char(); // get the type of thing to delete
3404 find_range(&p, &q, c1);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003405 yank_delete(p, q, 1, YANKONLY); // save copy before change
Eric Andersen3f980402001-04-04 17:31:15 +00003406 p = begin_line(p);
3407 q = end_line(q);
3408 i = count_lines(p, q); // # of lines we are shifting
3409 for ( ; i > 0; i--, p = next_line(p)) {
3410 if (c == '<') {
3411 // shift left- remove tab or 8 spaces
3412 if (*p == '\t') {
3413 // shrink buffer 1 char
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003414 text_hole_delete(p, p);
Eric Andersen3f980402001-04-04 17:31:15 +00003415 } else if (*p == ' ') {
3416 // we should be calculating columns, not just SPACE
3417 for (j = 0; *p == ' ' && j < tabstop; j++) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003418 text_hole_delete(p, p);
Eric Andersen3f980402001-04-04 17:31:15 +00003419 }
3420 }
3421 } else if (c == '>') {
3422 // shift right -- add tab or 8 spaces
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003423 char_insert(p, '\t');
Eric Andersen3f980402001-04-04 17:31:15 +00003424 }
3425 }
3426 dot = find_line(cnt); // what line were we on
3427 dot_skip_over_ws();
3428 end_cmd_q(); // stop adding to q
3429 break;
3430 case 'A': // A- append at e-o-l
3431 dot_end(); // go to e-o-l
3432 //**** fall thru to ... 'a'
3433 case 'a': // a- append after current char
3434 if (*dot != '\n')
3435 dot++;
3436 goto dc_i;
3437 break;
3438 case 'B': // B- back a blank-delimited Word
3439 case 'E': // E- end of a blank-delimited word
3440 case 'W': // W- forward a blank-delimited word
3441 if (cmdcnt-- > 1) {
3442 do_cmd(c);
3443 } // repeat cnt
3444 dir = FORWARD;
3445 if (c == 'B')
3446 dir = BACK;
3447 if (c == 'W' || isspace(dot[dir])) {
3448 dot = skip_thing(dot, 1, dir, S_TO_WS);
3449 dot = skip_thing(dot, 2, dir, S_OVER_WS);
3450 }
3451 if (c != 'W')
3452 dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
3453 break;
3454 case 'C': // C- Change to e-o-l
3455 case 'D': // D- delete to e-o-l
3456 save_dot = dot;
3457 dot = dollar_line(dot); // move to before NL
3458 // copy text into a register and delete
3459 dot = yank_delete(save_dot, dot, 0, YANKDEL); // delete to e-o-l
3460 if (c == 'C')
3461 goto dc_i; // start inserting
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003462#if ENABLE_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003463 if (c == 'D')
3464 end_cmd_q(); // stop adding to q
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003465#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003466 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003467 case 'G': // G- goto to a line number (default= E-O-F)
3468 dot = end - 1; // assume E-O-F
Eric Andersen1c0d3112001-04-16 15:46:44 +00003469 if (cmdcnt > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00003470 dot = find_line(cmdcnt); // what line is #cmdcnt
Eric Andersen1c0d3112001-04-16 15:46:44 +00003471 }
3472 dot_skip_over_ws();
3473 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003474 case 'H': // H- goto top line on screen
3475 dot = screenbegin;
3476 if (cmdcnt > (rows - 1)) {
3477 cmdcnt = (rows - 1);
3478 }
3479 if (cmdcnt-- > 1) {
3480 do_cmd('+');
3481 } // repeat cnt
3482 dot_skip_over_ws();
3483 break;
3484 case 'I': // I- insert before first non-blank
3485 dot_begin(); // 0
3486 dot_skip_over_ws();
3487 //**** fall thru to ... 'i'
3488 case 'i': // i- insert before current char
3489 case VI_K_INSERT: // Cursor Key Insert
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003490 dc_i:
Eric Andersen3f980402001-04-04 17:31:15 +00003491 cmd_mode = 1; // start insrting
Eric Andersen3f980402001-04-04 17:31:15 +00003492 break;
3493 case 'J': // J- join current and next lines together
3494 if (cmdcnt-- > 2) {
3495 do_cmd(c);
3496 } // repeat cnt
3497 dot_end(); // move to NL
3498 if (dot < end - 1) { // make sure not last char in text[]
3499 *dot++ = ' '; // replace NL with space
Paul Fox8552aec2005-09-16 12:20:05 +00003500 file_modified++;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00003501 while (isblank(*dot)) { // delete leading WS
Eric Andersen3f980402001-04-04 17:31:15 +00003502 dot_delete();
3503 }
3504 }
3505 end_cmd_q(); // stop adding to q
3506 break;
3507 case 'L': // L- goto bottom line on screen
3508 dot = end_screen();
3509 if (cmdcnt > (rows - 1)) {
3510 cmdcnt = (rows - 1);
3511 }
3512 if (cmdcnt-- > 1) {
3513 do_cmd('-');
3514 } // repeat cnt
3515 dot_begin();
3516 dot_skip_over_ws();
3517 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003518 case 'M': // M- goto middle line on screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003519 dot = screenbegin;
3520 for (cnt = 0; cnt < (rows-1) / 2; cnt++)
3521 dot = next_line(dot);
3522 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003523 case 'O': // O- open a empty line above
Eric Andersen822c3832001-05-07 17:37:43 +00003524 // 0i\n ESC -i
Eric Andersen3f980402001-04-04 17:31:15 +00003525 p = begin_line(dot);
3526 if (p[-1] == '\n') {
3527 dot_prev();
3528 case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
3529 dot_end();
3530 dot = char_insert(dot, '\n');
3531 } else {
3532 dot_begin(); // 0
Eric Andersen822c3832001-05-07 17:37:43 +00003533 dot = char_insert(dot, '\n'); // i\n ESC
Eric Andersen3f980402001-04-04 17:31:15 +00003534 dot_prev(); // -
3535 }
3536 goto dc_i;
3537 break;
3538 case 'R': // R- continuous Replace char
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003539 dc5:
Eric Andersen3f980402001-04-04 17:31:15 +00003540 cmd_mode = 2;
Eric Andersen3f980402001-04-04 17:31:15 +00003541 break;
3542 case 'X': // X- delete char before dot
3543 case 'x': // x- delete the current char
3544 case 's': // s- substitute the current char
3545 if (cmdcnt-- > 1) {
3546 do_cmd(c);
3547 } // repeat cnt
3548 dir = 0;
3549 if (c == 'X')
3550 dir = -1;
3551 if (dot[dir] != '\n') {
3552 if (c == 'X')
3553 dot--; // delete prev char
3554 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3555 }
3556 if (c == 's')
3557 goto dc_i; // start insrting
3558 end_cmd_q(); // stop adding to q
3559 break;
3560 case 'Z': // Z- if modified, {write}; exit
3561 // ZZ means to save file (if necessary), then exit
3562 c1 = get_one_char();
3563 if (c1 != 'Z') {
3564 indicate_error(c);
3565 break;
3566 }
Paul Foxf0305b72006-03-28 14:18:21 +00003567 if (file_modified) {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00003568 if (ENABLE_FEATURE_VI_READONLY && readonly_mode) {
3569 psbs("\"%s\" File is read only", current_filename);
Denis Vlasenko92758142006-10-03 19:56:34 +00003570 break;
Paul Foxf0305b72006-03-28 14:18:21 +00003571 }
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00003572 cnt = file_write(current_filename, text, end - 1);
Paul Fox61e45db2005-10-09 14:43:22 +00003573 if (cnt < 0) {
3574 if (cnt == -1)
3575 psbs("Write error: %s", strerror(errno));
3576 } else if (cnt == (end - 1 - text + 1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003577 editing = 0;
3578 }
3579 } else {
3580 editing = 0;
3581 }
3582 break;
3583 case '^': // ^- move to first non-blank on line
3584 dot_begin();
3585 dot_skip_over_ws();
3586 break;
3587 case 'b': // b- back a word
3588 case 'e': // e- end of word
3589 if (cmdcnt-- > 1) {
3590 do_cmd(c);
3591 } // repeat cnt
3592 dir = FORWARD;
3593 if (c == 'b')
3594 dir = BACK;
3595 if ((dot + dir) < text || (dot + dir) > end - 1)
3596 break;
3597 dot += dir;
3598 if (isspace(*dot)) {
3599 dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
3600 }
3601 if (isalnum(*dot) || *dot == '_') {
3602 dot = skip_thing(dot, 1, dir, S_END_ALNUM);
3603 } else if (ispunct(*dot)) {
3604 dot = skip_thing(dot, 1, dir, S_END_PUNCT);
3605 }
3606 break;
3607 case 'c': // c- change something
3608 case 'd': // d- delete something
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003609#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003610 case 'y': // y- yank something
3611 case 'Y': // Y- Yank a line
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003612#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003613 yf = YANKDEL; // assume either "c" or "d"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003614#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003615 if (c == 'y' || c == 'Y')
3616 yf = YANKONLY;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003617#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003618 c1 = 'y';
3619 if (c != 'Y')
3620 c1 = get_one_char(); // get the type of thing to delete
3621 find_range(&p, &q, c1);
3622 if (c1 == 27) { // ESC- user changed mind and wants out
3623 c = c1 = 27; // Escape- do nothing
3624 } else if (strchr("wW", c1)) {
3625 if (c == 'c') {
3626 // don't include trailing WS as part of word
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00003627 while (isblank(*q)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003628 if (q <= text || q[-1] == '\n')
3629 break;
3630 q--;
3631 }
3632 }
3633 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen822c3832001-05-07 17:37:43 +00003634 } else if (strchr("^0bBeEft$", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003635 // single line copy text into a register and delete
3636 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen1c0d3112001-04-16 15:46:44 +00003637 } else if (strchr("cdykjHL%+-{}\r\n", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003638 // multiple line copy text into a register and delete
3639 dot = yank_delete(p, q, 1, yf); // delete lines
Eric Andersen1c0d3112001-04-16 15:46:44 +00003640 if (c == 'c') {
3641 dot = char_insert(dot, '\n');
3642 // on the last line of file don't move to prev line
3643 if (dot != (end-1)) {
3644 dot_prev();
3645 }
3646 } else if (c == 'd') {
Eric Andersen3f980402001-04-04 17:31:15 +00003647 dot_begin();
3648 dot_skip_over_ws();
3649 }
3650 } else {
3651 // could not recognize object
3652 c = c1 = 27; // error-
3653 indicate_error(c);
3654 }
3655 if (c1 != 27) {
3656 // if CHANGING, not deleting, start inserting after the delete
3657 if (c == 'c') {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003658 strcpy(buf, "Change");
Eric Andersen3f980402001-04-04 17:31:15 +00003659 goto dc_i; // start inserting
3660 }
3661 if (c == 'd') {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003662 strcpy(buf, "Delete");
Eric Andersen3f980402001-04-04 17:31:15 +00003663 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003664#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003665 if (c == 'y' || c == 'Y') {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003666 strcpy(buf, "Yank");
Eric Andersen3f980402001-04-04 17:31:15 +00003667 }
3668 p = reg[YDreg];
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003669 q = p + strlen(p);
Eric Andersen3f980402001-04-04 17:31:15 +00003670 for (cnt = 0; p <= q; p++) {
3671 if (*p == '\n')
3672 cnt++;
3673 }
3674 psb("%s %d lines (%d chars) using [%c]",
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003675 buf, cnt, strlen(reg[YDreg]), what_reg());
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003676#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003677 end_cmd_q(); // stop adding to q
3678 }
3679 break;
3680 case 'k': // k- goto prev line, same col
3681 case VI_K_UP: // cursor key Up
3682 if (cmdcnt-- > 1) {
3683 do_cmd(c);
3684 } // repeat cnt
3685 dot_prev();
3686 dot = move_to_col(dot, ccol + offset); // try stay in same col
3687 break;
3688 case 'r': // r- replace the current char with user input
3689 c1 = get_one_char(); // get the replacement char
3690 if (*dot != '\n') {
3691 *dot = c1;
Paul Fox8552aec2005-09-16 12:20:05 +00003692 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003693 }
3694 end_cmd_q(); // stop adding to q
3695 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003696 case 't': // t- move to char prior to next x
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00003697 last_forward_char = get_one_char();
3698 do_cmd(';');
3699 if (*dot == last_forward_char)
3700 dot_left();
3701 last_forward_char= 0;
Eric Andersen822c3832001-05-07 17:37:43 +00003702 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003703 case 'w': // w- forward a word
3704 if (cmdcnt-- > 1) {
3705 do_cmd(c);
3706 } // repeat cnt
3707 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
3708 dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
3709 } else if (ispunct(*dot)) { // we are on PUNCT
3710 dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
3711 }
3712 if (dot < end - 1)
3713 dot++; // move over word
3714 if (isspace(*dot)) {
3715 dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
3716 }
3717 break;
3718 case 'z': // z-
3719 c1 = get_one_char(); // get the replacement char
3720 cnt = 0;
3721 if (c1 == '.')
3722 cnt = (rows - 2) / 2; // put dot at center
3723 if (c1 == '-')
3724 cnt = rows - 2; // put dot at bottom
3725 screenbegin = begin_line(dot); // start dot at top
3726 dot_scroll(cnt, -1);
3727 break;
3728 case '|': // |- move to column "cmdcnt"
3729 dot = move_to_col(dot, cmdcnt - 1); // try to move to column
3730 break;
3731 case '~': // ~- flip the case of letters a-z -> A-Z
3732 if (cmdcnt-- > 1) {
3733 do_cmd(c);
3734 } // repeat cnt
3735 if (islower(*dot)) {
3736 *dot = toupper(*dot);
Paul Fox8552aec2005-09-16 12:20:05 +00003737 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003738 } else if (isupper(*dot)) {
3739 *dot = tolower(*dot);
Paul Fox8552aec2005-09-16 12:20:05 +00003740 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003741 }
3742 dot_right();
3743 end_cmd_q(); // stop adding to q
3744 break;
3745 //----- The Cursor and Function Keys -----------------------------
3746 case VI_K_HOME: // Cursor Key Home
3747 dot_begin();
3748 break;
3749 // The Fn keys could point to do_macro which could translate them
3750 case VI_K_FUN1: // Function Key F1
3751 case VI_K_FUN2: // Function Key F2
3752 case VI_K_FUN3: // Function Key F3
3753 case VI_K_FUN4: // Function Key F4
3754 case VI_K_FUN5: // Function Key F5
3755 case VI_K_FUN6: // Function Key F6
3756 case VI_K_FUN7: // Function Key F7
3757 case VI_K_FUN8: // Function Key F8
3758 case VI_K_FUN9: // Function Key F9
3759 case VI_K_FUN10: // Function Key F10
3760 case VI_K_FUN11: // Function Key F11
3761 case VI_K_FUN12: // Function Key F12
3762 break;
3763 }
3764
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003765 dc1:
Eric Andersen3f980402001-04-04 17:31:15 +00003766 // if text[] just became empty, add back an empty line
3767 if (end == text) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003768 char_insert(text, '\n'); // start empty buf with dummy line
Eric Andersen3f980402001-04-04 17:31:15 +00003769 dot = text;
3770 }
3771 // it is OK for dot to exactly equal to end, otherwise check dot validity
3772 if (dot != end) {
3773 dot = bound_dot(dot); // make sure "dot" is valid
3774 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003775#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003776 check_context(c); // update the current context
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003777#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003778
3779 if (!isdigit(c))
3780 cmdcnt = 0; // cmd was not a number, reset cmdcnt
3781 cnt = dot - begin_line(dot);
3782 // Try to stay off of the Newline
3783 if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
3784 dot--;
3785}
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003786
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003787#if ENABLE_FEATURE_VI_CRASHME
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003788static int totalcmds = 0;
3789static int Mp = 85; // Movement command Probability
3790static int Np = 90; // Non-movement command Probability
3791static int Dp = 96; // Delete command Probability
3792static int Ip = 97; // Insert command Probability
3793static int Yp = 98; // Yank command Probability
3794static int Pp = 99; // Put command Probability
3795static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003796const char chars[20] = "\t012345 abcdABCD-=.$";
3797const char *const words[20] = {
3798 "this", "is", "a", "test",
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003799 "broadcast", "the", "emergency", "of",
3800 "system", "quick", "brown", "fox",
3801 "jumped", "over", "lazy", "dogs",
3802 "back", "January", "Febuary", "March"
3803};
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003804const char *const lines[20] = {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003805 "You should have received a copy of the GNU General Public License\n",
3806 "char c, cm, *cmd, *cmd1;\n",
3807 "generate a command by percentages\n",
3808 "Numbers may be typed as a prefix to some commands.\n",
3809 "Quit, discarding changes!\n",
3810 "Forced write, if permission originally not valid.\n",
3811 "In general, any ex or ed command (such as substitute or delete).\n",
3812 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3813 "Please get w/ me and I will go over it with you.\n",
3814 "The following is a list of scheduled, committed changes.\n",
3815 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3816 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3817 "Any question about transactions please contact Sterling Huxley.\n",
3818 "I will try to get back to you by Friday, December 31.\n",
3819 "This Change will be implemented on Friday.\n",
3820 "Let me know if you have problems accessing this;\n",
3821 "Sterling Huxley recently added you to the access list.\n",
3822 "Would you like to go to lunch?\n",
3823 "The last command will be automatically run.\n",
3824 "This is too much english for a computer geek.\n",
3825};
3826char *multilines[20] = {
3827 "You should have received a copy of the GNU General Public License\n",
3828 "char c, cm, *cmd, *cmd1;\n",
3829 "generate a command by percentages\n",
3830 "Numbers may be typed as a prefix to some commands.\n",
3831 "Quit, discarding changes!\n",
3832 "Forced write, if permission originally not valid.\n",
3833 "In general, any ex or ed command (such as substitute or delete).\n",
3834 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3835 "Please get w/ me and I will go over it with you.\n",
3836 "The following is a list of scheduled, committed changes.\n",
3837 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3838 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3839 "Any question about transactions please contact Sterling Huxley.\n",
3840 "I will try to get back to you by Friday, December 31.\n",
3841 "This Change will be implemented on Friday.\n",
3842 "Let me know if you have problems accessing this;\n",
3843 "Sterling Huxley recently added you to the access list.\n",
3844 "Would you like to go to lunch?\n",
3845 "The last command will be automatically run.\n",
3846 "This is too much english for a computer geek.\n",
3847};
3848
3849// create a random command to execute
3850static void crash_dummy()
3851{
3852 static int sleeptime; // how long to pause between commands
3853 char c, cm, *cmd, *cmd1;
3854 int i, cnt, thing, rbi, startrbi, percent;
3855
3856 // "dot" movement commands
3857 cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
3858
3859 // is there already a command running?
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003860 if (chars_to_parse > 0)
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003861 goto cd1;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003862 cd0:
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003863 startrbi = rbi = 0;
3864 sleeptime = 0; // how long to pause between commands
Denis Vlasenkoe8a07882007-06-10 15:08:44 +00003865 memset(readbuffer, '\0', MAX_LINELEN); // clear the read buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003866 // generate a command by percentages
3867 percent = (int) lrand48() % 100; // get a number from 0-99
3868 if (percent < Mp) { // Movement commands
3869 // available commands
3870 cmd = cmd1;
3871 M++;
3872 } else if (percent < Np) { // non-movement commands
3873 cmd = "mz<>\'\""; // available commands
3874 N++;
3875 } else if (percent < Dp) { // Delete commands
3876 cmd = "dx"; // available commands
3877 D++;
3878 } else if (percent < Ip) { // Inset commands
3879 cmd = "iIaAsrJ"; // available commands
3880 I++;
3881 } else if (percent < Yp) { // Yank commands
3882 cmd = "yY"; // available commands
3883 Y++;
3884 } else if (percent < Pp) { // Put commands
3885 cmd = "pP"; // available commands
3886 P++;
3887 } else {
3888 // We do not know how to handle this command, try again
3889 U++;
3890 goto cd0;
3891 }
3892 // randomly pick one of the available cmds from "cmd[]"
3893 i = (int) lrand48() % strlen(cmd);
3894 cm = cmd[i];
3895 if (strchr(":\024", cm))
3896 goto cd0; // dont allow colon or ctrl-T commands
3897 readbuffer[rbi++] = cm; // put cmd into input buffer
3898
3899 // now we have the command-
3900 // there are 1, 2, and multi char commands
3901 // find out which and generate the rest of command as necessary
3902 if (strchr("dmryz<>\'\"", cm)) { // 2-char commands
3903 cmd1 = " \n\r0$^-+wWeEbBhjklHL";
3904 if (cm == 'm' || cm == '\'' || cm == '\"') { // pick a reg[]
3905 cmd1 = "abcdefghijklmnopqrstuvwxyz";
3906 }
3907 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3908 c = cmd1[thing];
3909 readbuffer[rbi++] = c; // add movement to input buffer
3910 }
3911 if (strchr("iIaAsc", cm)) { // multi-char commands
3912 if (cm == 'c') {
3913 // change some thing
3914 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3915 c = cmd1[thing];
3916 readbuffer[rbi++] = c; // add movement to input buffer
3917 }
3918 thing = (int) lrand48() % 4; // what thing to insert
3919 cnt = (int) lrand48() % 10; // how many to insert
3920 for (i = 0; i < cnt; i++) {
3921 if (thing == 0) { // insert chars
3922 readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
3923 } else if (thing == 1) { // insert words
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003924 strcat(readbuffer, words[(int) lrand48() % 20]);
3925 strcat(readbuffer, " ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003926 sleeptime = 0; // how fast to type
3927 } else if (thing == 2) { // insert lines
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003928 strcat(readbuffer, lines[(int) lrand48() % 20]);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003929 sleeptime = 0; // how fast to type
3930 } else { // insert multi-lines
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003931 strcat(readbuffer, multilines[(int) lrand48() % 20]);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003932 sleeptime = 0; // how fast to type
3933 }
3934 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003935 strcat(readbuffer, "\033");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003936 }
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003937 chars_to_parse = strlen(readbuffer);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003938 cd1:
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003939 totalcmds++;
3940 if (sleeptime > 0)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003941 mysleep(sleeptime); // sleep 1/100 sec
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003942}
3943
3944// test to see if there are any errors
3945static void crash_test()
3946{
3947 static time_t oldtim;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003948
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003949 time_t tim;
Denis Vlasenkoe8a07882007-06-10 15:08:44 +00003950 char d[2], msg[MAX_LINELEN];
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003951
3952 msg[0] = '\0';
3953 if (end < text) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003954 strcat(msg, "end<text ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003955 }
3956 if (end > textend) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003957 strcat(msg, "end>textend ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003958 }
3959 if (dot < text) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003960 strcat(msg, "dot<text ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003961 }
3962 if (dot > end) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003963 strcat(msg, "dot>end ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003964 }
3965 if (screenbegin < text) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003966 strcat(msg, "screenbegin<text ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003967 }
3968 if (screenbegin > end - 1) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003969 strcat(msg, "screenbegin>end-1 ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003970 }
3971
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003972 if (msg[0]) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003973 alarm(0);
Glenn L McGrath7127b582002-12-03 21:48:15 +00003974 printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003975 totalcmds, last_input_char, msg, SOs, SOn);
3976 fflush(stdout);
Denis Vlasenko4f95e5a2007-10-11 10:10:15 +00003977 while (safe_read(0, d, 1) > 0) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003978 if (d[0] == '\n' || d[0] == '\r')
3979 break;
3980 }
3981 alarm(3);
3982 }
3983 tim = (time_t) time((time_t *) 0);
3984 if (tim >= (oldtim + 3)) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003985 sprintf(status_buffer,
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003986 "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
3987 totalcmds, M, N, I, D, Y, P, U, end - text + 1);
3988 oldtim = tim;
3989 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003990}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003991#endif